diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2026-09-02 18:26:45 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2026-09-02 18:26:45 +0800 |
| commit | f7a43077fe4e139dd7e3f46f448e0951740ec148 (patch) | |
| tree | f6a68248fe42bd899e99049bab7fdb7e5d26ea60 /services | |
| parent | 2f7576faf493b4667b02dcb60ddbf3aa0a9f654b (diff) | |
fix: qmlfmt
IListView: animation
Diffstat (limited to 'services')
| -rw-r--r-- | services/AppSearch.qml | 230 | ||||
| -rw-r--r-- | services/Clip.qml | 494 | ||||
| -rw-r--r-- | services/Emoji.qml | 90 | ||||
| -rw-r--r-- | services/Mpris.qml | 300 | ||||
| -rw-r--r-- | services/Network.qml | 136 | ||||
| -rw-r--r-- | services/NetworkUsage.qml | 106 | ||||
| -rw-r--r-- | services/Persistent.qml | 38 | ||||
| -rw-r--r-- | services/Resource.qml | 198 | ||||
| -rw-r--r-- | services/TimeDate.qml | 128 |
9 files changed, 860 insertions, 860 deletions
diff --git a/services/AppSearch.qml b/services/AppSearch.qml index d56213f..b8294b6 100644 --- a/services/AppSearch.qml +++ b/services/AppSearch.qml @@ -6,119 +6,119 @@ import qs.config import qs.components Searchable { - id: root - - list: Array.from(DesktopEntries.applications.values).sort((a, b) => a.name.localeCompare(b.name)) - scoreThreshold: General.appSearchFuzzySearchThreshold - algorithm: Searchable.SearchAlgorithm.Levendist - keys: ["name", "genericName", "execString", "keywords", "comment", "categories", "icon"] - - property var substitutions: ({ - "code-url-handler": "visual-studio-code", - "Code": "visual-studio-code", - "gnome-tweaks": "org.gnome.tweaks", - "pavucontrol-qt": "pavucontrol", - "wps": "wps-office2019-kprometheus", - "wpsoffice": "wps-office2019-kprometheus", - "footclient": "foot", - "zen": "zen-browser", - "brave-browser": "brave-desktop" - }) - - property var regexSubstitutions: [ - { - regex: /^steam_app_(\d+)$/, - replace: "steam_icon_$1" - }, - { - regex: /Minecraft.*/, - replace: "minecraft" - }, - { - regex: /.*polkit.*/, - replace: "system-lock-screen" - }, - { - regex: /gcr.prompter/, - replace: "system-lock-screen" - } - ] - - property var iconExistsCache: ({}) - property var guessIconCache: ({}) - - function iconExists(iconName) { - if (!iconName || iconName.length === 0) { - return false; - } - if (root.iconExistsCache.hasOwnProperty(iconName)) { - return root.iconExistsCache[iconName]; - } - const exists = Quickshell.iconPath(iconName, true).length > 0 && !iconName.includes("image-missing"); - root.iconExistsCache[iconName] = exists; - return exists; - } - - function guessIcon(str) { - if (!str || str.length === 0) { - return "image-missing"; - } - if (root.guessIconCache[str]) { - return root.guessIconCache[str]; - } - - var result; - - if (substitutions[str]) { - result = substitutions[str]; - } else { - result = null; - for (var i = 0; i < regexSubstitutions.length; i++) { - var { - regex, - replace - } = regexSubstitutions[i]; - var replaced = str.replace(regex, replace); - if (replaced !== str) { - result = replaced; - break; - } - } - } - - if (!result && root.iconExists(str)) { - result = str; - } - - if (!result) { - var guessStr = str.split(".").slice(-1)[0].toLowerCase(); - if (root.iconExists(guessStr)) { - result = guessStr; - } - } - - if (!result) { - var kebab = str.toLowerCase().replace(/\s+/g, "-"); - if (root.iconExists(kebab)) { - result = kebab; - } - } - - if (!result) { - const matches = root.query(str); - if (matches.length > 0) { - const candidate = matches[0].icon; - if (root.iconExists(candidate)) { - result = candidate; - } - } - } - - if (!result) { - result = str; - } - - root.guessIconCache[str] = result; - return result; - } + id: root + + list: Array.from(DesktopEntries.applications.values).sort((a, b) => a.name.localeCompare(b.name)) + scoreThreshold: General.appSearchFuzzySearchThreshold + algorithm: Searchable.SearchAlgorithm.Levendist + keys: ["name", "genericName", "execString", "keywords", "comment", "categories", "icon"] + + property var substitutions: ({ + "code-url-handler": "visual-studio-code", + "Code": "visual-studio-code", + "gnome-tweaks": "org.gnome.tweaks", + "pavucontrol-qt": "pavucontrol", + "wps": "wps-office2019-kprometheus", + "wpsoffice": "wps-office2019-kprometheus", + "footclient": "foot", + "zen": "zen-browser", + "brave-browser": "brave-desktop" + }) + + property var regexSubstitutions: [ + { + regex: /^steam_app_(\d+)$/, + replace: "steam_icon_$1" + }, + { + regex: /Minecraft.*/, + replace: "minecraft" + }, + { + regex: /.*polkit.*/, + replace: "system-lock-screen" + }, + { + regex: /gcr.prompter/, + replace: "system-lock-screen" + } + ] + + property var iconExistsCache: ({}) + property var guessIconCache: ({}) + + function iconExists(iconName) { + if (!iconName || iconName.length === 0) { + return false; + } + if (root.iconExistsCache.hasOwnProperty(iconName)) { + return root.iconExistsCache[iconName]; + } + const exists = Quickshell.iconPath(iconName, true).length > 0 && !iconName.includes("image-missing"); + root.iconExistsCache[iconName] = exists; + return exists; + } + + function guessIcon(str) { + if (!str || str.length === 0) { + return "image-missing"; + } + if (root.guessIconCache[str]) { + return root.guessIconCache[str]; + } + + var result; + + if (substitutions[str]) { + result = substitutions[str]; + } else { + result = null; + for (var i = 0; i < regexSubstitutions.length; i++) { + var { + regex, + replace + } = regexSubstitutions[i]; + var replaced = str.replace(regex, replace); + if (replaced !== str) { + result = replaced; + break; + } + } + } + + if (!result && root.iconExists(str)) { + result = str; + } + + if (!result) { + var guessStr = str.split(".").slice(-1)[0].toLowerCase(); + if (root.iconExists(guessStr)) { + result = guessStr; + } + } + + if (!result) { + var kebab = str.toLowerCase().replace(/\s+/g, "-"); + if (root.iconExists(kebab)) { + result = kebab; + } + } + + if (!result) { + const matches = root.query(str); + if (matches.length > 0) { + const candidate = matches[0].icon; + if (root.iconExists(candidate)) { + result = candidate; + } + } + } + + if (!result) { + result = str; + } + + root.guessIconCache[str] = result; + return result; + } } diff --git a/services/Clip.qml b/services/Clip.qml index 2289dd2..4960dd9 100644 --- a/services/Clip.qml +++ b/services/Clip.qml @@ -11,251 +11,251 @@ import qs.config import "../utils/string_utils.js" as StringUtils Searchable { - id: root - - key: "data" - algorithm: General.defaultSearchAlgorithm - - property list<string> _clipHist: [] - property list<string> _clipImg: [] - property list<string> _clipImgIds: _clipImg.map(p => p.split("/").pop().split(".").shift()) - property var _clipMetadata: ({}) - property var _clipDecoded: ({}) - property int _pendingReorderId: -1 - - function getType(text: string): string { - if (!text) - return; - if (text.startsWith("http://") || text.startsWith("https://")) - return "web"; - if (text.startsWith("file://")) - return "file"; - - return "text"; - } - - list: { - if (!_clipHist?.length) - return []; - - const imgMap = _clipImgIds.slice(0, Math.min(_clipImg.length, _clipImgIds.length)).reduce((map, id, i) => { - if (id) - map[id] = _clipImg[i]; - return map; - }, {}); - - return _clipHist.filter(raw => raw).map((raw, idx) => { - const tab = raw.indexOf("\t"); - let idNum = -1; - let payload = raw; - - if (tab > 0) { - const n = parseInt(raw.slice(0, tab), 10); - if (!Number.isNaN(n)) { - idNum = n; - payload = raw.slice(tab + 1); - } - } - - const imgPath = idNum >= 0 ? imgMap[idNum] || "" : ""; - const isImg = !!imgPath; - const metadata = _clipMetadata[idNum] || {}; - - return { - index: idNum, - type: isImg ? "image" : root.getType(payload), - data: isImg ? imgPath : payload, - raw: raw, - timestamp: metadata.timestamp || "", - appId: metadata.appId || "", - appTitle: metadata.appTitle || "", - appIcon: Quickshell.iconPath(AppSearch.guessIcon(metadata.appId), "image-missing"), - image: imgPath, - decoded: root._clipDecoded[idx] || "" - }; - }); - } - - Persistent { - id: clipMetaStore - filePath: Directories.clipHistMetaDataPath - adapter: JsonAdapter { - property var metadata: ({}) - } - fileView.onLoaded: { - const meta = adapter.metadata; - for (const k in meta) { - if (meta[k].timestamp && typeof meta[k].timestamp === "string") { - meta[k].timestamp = new Date(meta[k].timestamp); - } - } - root._clipMetadata = meta; - } - } - - on_ClipMetadataChanged: { - clipMetaStore.adapter.metadata = _clipMetadata; - } - - function transformSearch(search) { - return search.toLowerCase(); - } - - function decodeAndCopy(text) { - const tab = text.indexOf("\t"); - if (tab > 0) { - const idStr = text.slice(0, tab); - const n = parseInt(idStr, 10); - if (!Number.isNaN(n)) - _pendingReorderId = n; - } - Quickshell.execDetached(["sh", "-c", `cliphist decode '${StringUtils.shellSingleQuoteEscape(text)}' | wl-copy`]); - } - - function copy(text) { - _pendingReorderId = -1; - Quickshell.execDetached(["wl-copy", text]); - } - - function _update() { - clipProc.running = true; - clipDecodeProc.running = true; - imgProc.running = true; - } - - function _captureMetadata() { - const now = new Date(); - const activeToplevel = ToplevelManager.activeToplevel; - - Qt.callLater(() => { - clipProc.running = true; - clipProc.stdout.onceFinished = () => { - const lines = clipProc.stdout.text.split(/\r?\n/); - const firstLine = lines[0] || ""; - const tab = firstLine.indexOf("\t"); - - if (tab > 0) { - const idStr = firstLine.slice(0, tab); - const n = parseInt(idStr, 10); - if (!Number.isNaN(n)) { - if (_clipMetadata[idStr]) { - _pendingReorderId = -1; - return; - } - - let newMetadata = {}; - for (var k in _clipMetadata) { - if (_clipMetadata.hasOwnProperty(k)) - newMetadata[k] = _clipMetadata[k]; - } - - if (_pendingReorderId >= 0) { - if (_clipMetadata[_pendingReorderId]) { - newMetadata[idStr] = _clipMetadata[_pendingReorderId]; - } - _pendingReorderId = -1; - } else { - if (!newMetadata[idStr]) { - newMetadata[idStr] = { - timestamp: now, - appId: activeToplevel ? activeToplevel.appId : "", - appTitle: activeToplevel ? activeToplevel.title : "" - }; - } - } - _clipMetadata = newMetadata; - } - } - }; - }); - } - - Process { - id: clipProc - running: true - command: ["cliphist", "-preview-width", 9 ** 9, "-max-items", 9 ** 9, "list"] - stdout: StdioCollector { - property bool pending: false - property var onceFinished: null - - onStreamFinished: () => { - pending = true; - Qt.callLater(() => { - if (pending) { - pending = false; - _clipHist = this.text.split(/\r?\n/); - if (onceFinished) { - onceFinished(); - onceFinished = null; - } - } - }); - } - } - } - - Process { - id: clipDecodeProc - property var tmp: ({}) - property int index: 0 - property string separator: "\0\x01\x02CLIP_SEP_7b9a4c8d\x02\x01\0" - running: true - command: ["sh", "-c", 'cliphist list | while IFS= read -r line; do\n' + ' cliphist decode "$line"; ' + 'printf "\\0\\x01\\x02CLIP_SEP_7b9a4c8d\\x02\\x01\\0";\n' + 'done'] - onStarted: { - clipDecodeProc.tmp = []; - clipDecodeProc.index = 0; - } - onExited: { - if (clipDecodeProc.tmp) - root._clipDecoded = clipDecodeProc.tmp; - } - stdout: SplitParser { - onRead: data => { - const parts = data.split(clipDecodeProc.separator); - - if (!clipDecodeProc.tmp[clipDecodeProc.index]) - clipDecodeProc.tmp[clipDecodeProc.index] = ""; - - clipDecodeProc.tmp[clipDecodeProc.index] += parts[0]; - - for (let i = 1; i < parts.length; i++) { - clipDecodeProc.index++; - clipDecodeProc.tmp[clipDecodeProc.index] = parts[i]; - } - } - } - } - - Process { - id: imgProc - running: true - command: ["sh", Quickshell.shellDir + "/utils/cliphist-img.sh"] - stdout: StdioCollector { - property bool pending: false - onStreamFinished: () => { - pending = true; - Qt.callLater(() => { - if (pending) { - pending = false; - _clipImg = this.text.split(/\r?\n/); - } - }); - } - } - } - - Process { - id: watchProc - running: true - command: ["wl-paste", "-w", "echo"] - onRunningChanged: if (!running) - running = true - - stdout: SplitParser { - onRead: line => { - _captureMetadata(); - _update(); - } - } - } + id: root + + key: "data" + algorithm: General.defaultSearchAlgorithm + + property list<string> _clipHist: [] + property list<string> _clipImg: [] + property list<string> _clipImgIds: _clipImg.map(p => p.split("/").pop().split(".").shift()) + property var _clipMetadata: ({}) + property var _clipDecoded: ({}) + property int _pendingReorderId: -1 + + function getType(text: string): string { + if (!text) + return; + if (text.startsWith("http://") || text.startsWith("https://")) + return "web"; + if (text.startsWith("file://")) + return "file"; + + return "text"; + } + + list: { + if (!_clipHist?.length) + return []; + + const imgMap = _clipImgIds.slice(0, Math.min(_clipImg.length, _clipImgIds.length)).reduce((map, id, i) => { + if (id) + map[id] = _clipImg[i]; + return map; + }, {}); + + return _clipHist.filter(raw => raw).map((raw, idx) => { + const tab = raw.indexOf("\t"); + let idNum = -1; + let payload = raw; + + if (tab > 0) { + const n = parseInt(raw.slice(0, tab), 10); + if (!Number.isNaN(n)) { + idNum = n; + payload = raw.slice(tab + 1); + } + } + + const imgPath = idNum >= 0 ? imgMap[idNum] || "" : ""; + const isImg = !!imgPath; + const metadata = _clipMetadata[idNum] || {}; + + return { + index: idNum, + type: isImg ? "image" : root.getType(payload), + data: isImg ? imgPath : payload, + raw: raw, + timestamp: metadata.timestamp || "", + appId: metadata.appId || "", + appTitle: metadata.appTitle || "", + appIcon: Quickshell.iconPath(AppSearch.guessIcon(metadata.appId), "image-missing"), + image: imgPath, + decoded: root._clipDecoded[idx] || "" + }; + }); + } + + Persistent { + id: clipMetaStore + filePath: Directories.clipHistMetaDataPath + adapter: JsonAdapter { + property var metadata: ({}) + } + fileView.onLoaded: { + const meta = adapter.metadata; + for (const k in meta) { + if (meta[k].timestamp && typeof meta[k].timestamp === "string") { + meta[k].timestamp = new Date(meta[k].timestamp); + } + } + root._clipMetadata = meta; + } + } + + on_ClipMetadataChanged: { + clipMetaStore.adapter.metadata = _clipMetadata; + } + + function transformSearch(search) { + return search.toLowerCase(); + } + + function decodeAndCopy(text) { + const tab = text.indexOf("\t"); + if (tab > 0) { + const idStr = text.slice(0, tab); + const n = parseInt(idStr, 10); + if (!Number.isNaN(n)) + _pendingReorderId = n; + } + Quickshell.execDetached(["sh", "-c", `cliphist decode '${StringUtils.shellSingleQuoteEscape(text)}' | wl-copy`]); + } + + function copy(text) { + _pendingReorderId = -1; + Quickshell.execDetached(["wl-copy", text]); + } + + function _update() { + clipProc.running = true; + clipDecodeProc.running = true; + imgProc.running = true; + } + + function _captureMetadata() { + const now = new Date(); + const activeToplevel = ToplevelManager.activeToplevel; + + Qt.callLater(() => { + clipProc.running = true; + clipProc.stdout.onceFinished = () => { + const lines = clipProc.stdout.text.split(/\r?\n/); + const firstLine = lines[0] || ""; + const tab = firstLine.indexOf("\t"); + + if (tab > 0) { + const idStr = firstLine.slice(0, tab); + const n = parseInt(idStr, 10); + if (!Number.isNaN(n)) { + if (_clipMetadata[idStr]) { + _pendingReorderId = -1; + return; + } + + let newMetadata = {}; + for (var k in _clipMetadata) { + if (_clipMetadata.hasOwnProperty(k)) + newMetadata[k] = _clipMetadata[k]; + } + + if (_pendingReorderId >= 0) { + if (_clipMetadata[_pendingReorderId]) { + newMetadata[idStr] = _clipMetadata[_pendingReorderId]; + } + _pendingReorderId = -1; + } else { + if (!newMetadata[idStr]) { + newMetadata[idStr] = { + timestamp: now, + appId: activeToplevel ? activeToplevel.appId : "", + appTitle: activeToplevel ? activeToplevel.title : "" + }; + } + } + _clipMetadata = newMetadata; + } + } + }; + }); + } + + Process { + id: clipProc + running: true + command: ["cliphist", "-preview-width", 9 ** 9, "-max-items", 9 ** 9, "list"] + stdout: StdioCollector { + property bool pending: false + property var onceFinished: null + + onStreamFinished: () => { + pending = true; + Qt.callLater(() => { + if (pending) { + pending = false; + _clipHist = this.text.split(/\r?\n/); + if (onceFinished) { + onceFinished(); + onceFinished = null; + } + } + }); + } + } + } + + Process { + id: clipDecodeProc + property var tmp: ({}) + property int index: 0 + property string separator: "\0\x01\x02CLIP_SEP_7b9a4c8d\x02\x01\0" + running: true + command: ["sh", "-c", 'cliphist list | while IFS= read -r line; do\n' + ' cliphist decode "$line"; ' + 'printf "\\0\\x01\\x02CLIP_SEP_7b9a4c8d\\x02\\x01\\0";\n' + 'done'] + onStarted: { + clipDecodeProc.tmp = []; + clipDecodeProc.index = 0; + } + onExited: { + if (clipDecodeProc.tmp) + root._clipDecoded = clipDecodeProc.tmp; + } + stdout: SplitParser { + onRead: data => { + const parts = data.split(clipDecodeProc.separator); + + if (!clipDecodeProc.tmp[clipDecodeProc.index]) + clipDecodeProc.tmp[clipDecodeProc.index] = ""; + + clipDecodeProc.tmp[clipDecodeProc.index] += parts[0]; + + for (let i = 1; i < parts.length; i++) { + clipDecodeProc.index++; + clipDecodeProc.tmp[clipDecodeProc.index] = parts[i]; + } + } + } + } + + Process { + id: imgProc + running: true + command: ["sh", Quickshell.shellDir + "/utils/cliphist-img.sh"] + stdout: StdioCollector { + property bool pending: false + onStreamFinished: () => { + pending = true; + Qt.callLater(() => { + if (pending) { + pending = false; + _clipImg = this.text.split(/\r?\n/); + } + }); + } + } + } + + Process { + id: watchProc + running: true + command: ["wl-paste", "-w", "echo"] + onRunningChanged: if (!running) + running = true + + stdout: SplitParser { + onRead: line => { + _captureMetadata(); + _update(); + } + } + } } diff --git a/services/Emoji.qml b/services/Emoji.qml index 108fbd9..697840b 100644 --- a/services/Emoji.qml +++ b/services/Emoji.qml @@ -6,49 +6,49 @@ import QtQuick import qs.components Searchable { - id: root - - property var _emojis: [] - property var _emojiFile: FileView { - path: "/usr/share/rofi-emoji/all_emojis.txt" - onTextChanged: _parseEmojis() - } - - list: _emojis - keys: ["name", "keywords", "category", "subcategory", "emoji"] - algorithm: Searchable.SearchAlgorithm.Include - - function transformSearch(search) { - return search.toLowerCase(); - } - - function _parseEmojis() { - const text = _emojiFile.text(); - if (!text) { - console.warn("Emoji service: failed to load", _emojiFile.path); - _emojis = []; - return; - } - - const lines = text.split("\n").filter(l => l.trim()); - const out = []; - for (let i = 0; i < lines.length; ++i) { - const parts = lines[i].split("\t"); - if (parts.length < 5) - continue; - const [emoji, category, subcategory, name, keywords] = parts; - out.push({ - emoji: emoji, - category: category, - subcategory: subcategory, - name: name, - keywords: keywords - }); - } - _emojis = out; - } - - function copy(emoji) { - Clip.copy(emoji); - } + id: root + + property var _emojis: [] + property var _emojiFile: FileView { + path: "/usr/share/rofi-emoji/all_emojis.txt" + onTextChanged: _parseEmojis() + } + + list: _emojis + keys: ["name", "keywords", "category", "subcategory", "emoji"] + algorithm: Searchable.SearchAlgorithm.Include + + function transformSearch(search) { + return search.toLowerCase(); + } + + function _parseEmojis() { + const text = _emojiFile.text(); + if (!text) { + console.warn("Emoji service: failed to load", _emojiFile.path); + _emojis = []; + return; + } + + const lines = text.split("\n").filter(l => l.trim()); + const out = []; + for (let i = 0; i < lines.length; ++i) { + const parts = lines[i].split("\t"); + if (parts.length < 5) + continue; + const [emoji, category, subcategory, name, keywords] = parts; + out.push({ + emoji: emoji, + category: category, + subcategory: subcategory, + name: name, + keywords: keywords + }); + } + _emojis = out; + } + + function copy(emoji) { + Clip.copy(emoji); + } } diff --git a/services/Mpris.qml b/services/Mpris.qml index dc103f8..c73ed3f 100644 --- a/services/Mpris.qml +++ b/services/Mpris.qml @@ -16,154 +16,154 @@ import qs.config * A service that provides easy access to the active Mpris player. */ Singleton { - id: root - property MprisPlayer trackedPlayer: null - property MprisPlayer activePlayer: Mpris.players.values.find(i => i.desktopEntry === Bar.preferedPlayer) ?? trackedPlayer ?? Mpris.players.values[0] ?? null - signal trackChanged(reverse: bool) - - property bool __reverse: false - - property var activeTrack - - Timer { - running: root.activePlayer?.playbackState == MprisPlaybackState.Playing && root.activePlayer?.positionSupported - interval: 1000 - repeat: true - onTriggered: root.activePlayer.positionChanged() - } - - Instantiator { - model: Mpris.players - - Connections { - required property MprisPlayer modelData - target: modelData - - Component.onCompleted: { - if (root.trackedPlayer == null || modelData.isPlaying) { - root.trackedPlayer = modelData; - } - } - - Component.onDestruction: { - if (root.trackedPlayer == null || !root.trackedPlayer.isPlaying) { - for (const player of Mpris.players.values) { - if (player.playbackState.isPlaying) { - root.trackedPlayer = player; - break; - } - } - - if (trackedPlayer == null && Mpris.players.values.length != 0) { - trackedPlayer = Mpris.players.values[0]; - } - } - } - - function onPlaybackStateChanged() { - if (root.trackedPlayer !== modelData) - root.trackedPlayer = modelData; - } - } - } - - Connections { - target: activePlayer - - function onPostTrackChanged() { - root.updateTrack(); - } - - function onTrackArtUrlChanged() { - // console.log("arturl:", activePlayer.trackArtUrl) - // root.updateTrack(); - if (root.activePlayer.uniqueId == root.activeTrack.uniqueId && root.activePlayer.trackArtUrl != root.activeTrack.artUrl) { - // cantata likes to send cover updates *BEFORE* updating the track info. - // as such, art url changes shouldn't be able to break the reverse animation - const r = root.__reverse; - root.updateTrack(); - root.__reverse = r; - } - } - // not working https://github.com/quickshell-mirror/quickshell/issues/111 - // function onEntered() { - // } - } - - onActivePlayerChanged: this.updateTrack() - - function updateTrack() { - // console.log(`update: ${this.activePlayer?.trackTitle ?? ""} : ${this.activePlayer?.trackArtists}`); - this.activeTrack = { - uniqueId: this.activePlayer?.uniqueId ?? 0, - artUrl: this.activePlayer?.trackArtUrl ?? "", - title: this.activePlayer?.trackTitle || qsTr("Unknown Title"), - artist: this.activePlayer?.trackArtist || qsTr("Unknown Artist"), - album: this.activePlayer?.trackAlbum || qsTr("Unknown Album"), - position: this.activePlayer?.position ?? 0, - length: this.activePlayer?.length ?? 0 - }; - - this.trackChanged(__reverse); - this.__reverse = false; - } - - function updateProgress() { - this.activePlayer.position; - } - - property bool isPlaying: this.activePlayer && this.activePlayer.isPlaying - property bool canTogglePlaying: this.activePlayer?.canTogglePlaying ?? false - function togglePlaying() { - if (this.canTogglePlaying) - this.activePlayer.togglePlaying(); - } - - property bool canGoPrevious: this.activePlayer?.canGoPrevious ?? false - function previous() { - if (this.canGoPrevious) { - this.__reverse = true; - this.activePlayer.previous(); - } - } - - property bool canGoNext: this.activePlayer?.canGoNext ?? false - function next() { - if (this.canGoNext) { - this.__reverse = false; - this.activePlayer.next(); - } - } - - property bool canChangeVolume: this.activePlayer && this.activePlayer.volumeSupported && this.activePlayer.canControl - - property bool loopSupported: this.activePlayer && this.activePlayer.loopSupported && this.activePlayer.canControl - property var loopState: this.activePlayer?.loopState ?? MprisLoopState.None - function setLoopState(loopState: var) { - if (this.loopSupported) { - this.activePlayer.loopState = loopState; - } - } - - property bool shuffleSupported: this.activePlayer && this.activePlayer.shuffleSupported && this.activePlayer.canControl - property bool hasShuffle: this.activePlayer?.shuffle ?? false - function setShuffle(shuffle: bool) { - if (this.shuffleSupported) { - this.activePlayer.shuffle = shuffle; - } - } - - function setActivePlayer(player: MprisPlayer) { - const targetPlayer = player ?? Mpris.players[0]; - console.log(`[Mpris] Active player ${targetPlayer} << ${activePlayer}`); - - if (targetPlayer && this.activePlayer) { - this.__reverse = Mpris.players.indexOf(targetPlayer) < Mpris.players.indexOf(this.activePlayer); - } else { - // always animate forward if going to null - this.__reverse = false; - } - - this.trackedPlayer = targetPlayer; - } + id: root + property MprisPlayer trackedPlayer: null + property MprisPlayer activePlayer: Mpris.players.values.find(i => i.desktopEntry === Bar.preferedPlayer) ?? trackedPlayer ?? Mpris.players.values[0] ?? null + signal trackChanged(reverse: bool) + + property bool __reverse: false + + property var activeTrack + + Timer { + running: root.activePlayer?.playbackState == MprisPlaybackState.Playing && root.activePlayer?.positionSupported + interval: 1000 + repeat: true + onTriggered: root.activePlayer.positionChanged() + } + + Instantiator { + model: Mpris.players + + Connections { + required property MprisPlayer modelData + target: modelData + + Component.onCompleted: { + if (root.trackedPlayer == null || modelData.isPlaying) { + root.trackedPlayer = modelData; + } + } + + Component.onDestruction: { + if (root.trackedPlayer == null || !root.trackedPlayer.isPlaying) { + for (const player of Mpris.players.values) { + if (player.playbackState.isPlaying) { + root.trackedPlayer = player; + break; + } + } + + if (trackedPlayer == null && Mpris.players.values.length != 0) { + trackedPlayer = Mpris.players.values[0]; + } + } + } + + function onPlaybackStateChanged() { + if (root.trackedPlayer !== modelData) + root.trackedPlayer = modelData; + } + } + } + + Connections { + target: activePlayer + + function onPostTrackChanged() { + root.updateTrack(); + } + + function onTrackArtUrlChanged() { + // console.log("arturl:", activePlayer.trackArtUrl) + // root.updateTrack(); + if (root.activePlayer.uniqueId == root.activeTrack.uniqueId && root.activePlayer.trackArtUrl != root.activeTrack.artUrl) { + // cantata likes to send cover updates *BEFORE* updating the track info. + // as such, art url changes shouldn't be able to break the reverse animation + const r = root.__reverse; + root.updateTrack(); + root.__reverse = r; + } + } + // not working https://github.com/quickshell-mirror/quickshell/issues/111 + // function onEntered() { + // } + } + + onActivePlayerChanged: this.updateTrack() + + function updateTrack() { + // console.log(`update: ${this.activePlayer?.trackTitle ?? ""} : ${this.activePlayer?.trackArtists}`); + this.activeTrack = { + uniqueId: this.activePlayer?.uniqueId ?? 0, + artUrl: this.activePlayer?.trackArtUrl ?? "", + title: this.activePlayer?.trackTitle || qsTr("Unknown Title"), + artist: this.activePlayer?.trackArtist || qsTr("Unknown Artist"), + album: this.activePlayer?.trackAlbum || qsTr("Unknown Album"), + position: this.activePlayer?.position ?? 0, + length: this.activePlayer?.length ?? 0 + }; + + this.trackChanged(__reverse); + this.__reverse = false; + } + + function updateProgress() { + this.activePlayer.position; + } + + property bool isPlaying: this.activePlayer && this.activePlayer.isPlaying + property bool canTogglePlaying: this.activePlayer?.canTogglePlaying ?? false + function togglePlaying() { + if (this.canTogglePlaying) + this.activePlayer.togglePlaying(); + } + + property bool canGoPrevious: this.activePlayer?.canGoPrevious ?? false + function previous() { + if (this.canGoPrevious) { + this.__reverse = true; + this.activePlayer.previous(); + } + } + + property bool canGoNext: this.activePlayer?.canGoNext ?? false + function next() { + if (this.canGoNext) { + this.__reverse = false; + this.activePlayer.next(); + } + } + + property bool canChangeVolume: this.activePlayer && this.activePlayer.volumeSupported && this.activePlayer.canControl + + property bool loopSupported: this.activePlayer && this.activePlayer.loopSupported && this.activePlayer.canControl + property var loopState: this.activePlayer?.loopState ?? MprisLoopState.None + function setLoopState(loopState: var) { + if (this.loopSupported) { + this.activePlayer.loopState = loopState; + } + } + + property bool shuffleSupported: this.activePlayer && this.activePlayer.shuffleSupported && this.activePlayer.canControl + property bool hasShuffle: this.activePlayer?.shuffle ?? false + function setShuffle(shuffle: bool) { + if (this.shuffleSupported) { + this.activePlayer.shuffle = shuffle; + } + } + + function setActivePlayer(player: MprisPlayer) { + const targetPlayer = player ?? Mpris.players[0]; + console.log(`[Mpris] Active player ${targetPlayer} << ${activePlayer}`); + + if (targetPlayer && this.activePlayer) { + this.__reverse = Mpris.players.indexOf(targetPlayer) < Mpris.players.indexOf(this.activePlayer); + } else { + // always animate forward if going to null + this.__reverse = false; + } + + this.trackedPlayer = targetPlayer; + } } diff --git a/services/Network.qml b/services/Network.qml index 231841a..bfe6c12 100644 --- a/services/Network.qml +++ b/services/Network.qml @@ -9,78 +9,78 @@ import QtQuick import qs.config Singleton { - id: root + id: root - property bool wifi: true - property bool ethernet: false - property string networkName: "" - property int networkStrength - property string state: ethernet ? Icons.resource.network.wifi : (networkName.length >= 0 && networkName !== "lo") ? (networkStrength >= 90 ? Icons.resource.network.wifi.max : networkStrength >= 80 ? Icons.resource.network.wifi.high : networkStrength >= 60 ? Icons.resource.network.wifi.mid : networkStrength >= 40 ? Icons.resource.network.wifi.low : networkStrength >= 20 ? Icons.resource.network.wifi.min : Icons.resource.network.disconnected) : Icons.resource.network.disconnected + property bool wifi: true + property bool ethernet: false + property string networkName: "" + property int networkStrength + property string state: ethernet ? Icons.resource.network.wifi : (networkName.length >= 0 && networkName !== "lo") ? (networkStrength >= 90 ? Icons.resource.network.wifi.max : networkStrength >= 80 ? Icons.resource.network.wifi.high : networkStrength >= 60 ? Icons.resource.network.wifi.mid : networkStrength >= 40 ? Icons.resource.network.wifi.low : networkStrength >= 20 ? Icons.resource.network.wifi.min : Icons.resource.network.disconnected) : Icons.resource.network.disconnected - function update() { - updateConnectionType.startCheck(); - updateNetworkName.running = true; - updateNetworkStrength.running = true; - } + function update() { + updateConnectionType.startCheck(); + updateNetworkName.running = true; + updateNetworkStrength.running = true; + } - Timer { - interval: General.resourceUpdateInterval - running: true - repeat: true - onTriggered: { - root.update(); - interval = General.resourceUpdateInterval * 1000; - } - } + Timer { + interval: General.resourceUpdateInterval + running: true + repeat: true + onTriggered: { + root.update(); + interval = General.resourceUpdateInterval * 1000; + } + } - Process { - id: updateConnectionType - property string buffer - command: ["nmcli", "-t", "-f", "NAME,TYPE,DEVICE", "c", "show", "--active"] - running: true - function startCheck() { - buffer = ""; - updateConnectionType.running = true; - } - stdout: SplitParser { - onRead: data => { - updateConnectionType.buffer += data + "\n"; - } - } - onExited: (exitCode, exitStatus) => { - const lines = updateConnectionType.buffer.trim().split('\n'); - let hasEthernet = false; - let hasWifi = false; - lines.forEach(line => { - if (line.includes("ethernet")) - hasEthernet = true; - else if (line.includes("wireless")) - hasWifi = true; - }); - root.ethernet = hasEthernet; - root.wifi = hasWifi; - } - } + Process { + id: updateConnectionType + property string buffer + command: ["nmcli", "-t", "-f", "NAME,TYPE,DEVICE", "c", "show", "--active"] + running: true + function startCheck() { + buffer = ""; + updateConnectionType.running = true; + } + stdout: SplitParser { + onRead: data => { + updateConnectionType.buffer += data + "\n"; + } + } + onExited: (exitCode, exitStatus) => { + const lines = updateConnectionType.buffer.trim().split('\n'); + let hasEthernet = false; + let hasWifi = false; + lines.forEach(line => { + if (line.includes("ethernet")) + hasEthernet = true; + else if (line.includes("wireless")) + hasWifi = true; + }); + root.ethernet = hasEthernet; + root.wifi = hasWifi; + } + } - Process { - id: updateNetworkName - command: ["sh", "-c", "nmcli -t -f NAME c show --active | head -1"] - running: true - stdout: SplitParser { - onRead: data => { - root.networkName = data; - } - } - } + Process { + id: updateNetworkName + command: ["sh", "-c", "nmcli -t -f NAME c show --active | head -1"] + running: true + stdout: SplitParser { + onRead: data => { + root.networkName = data; + } + } + } - Process { - id: updateNetworkStrength - running: true - command: ["sh", "-c", "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\*/{if (NR!=1) {print $2}}'"] - stdout: SplitParser { - onRead: data => { - root.networkStrength = parseInt(data); - } - } - } + Process { + id: updateNetworkStrength + running: true + command: ["sh", "-c", "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\*/{if (NR!=1) {print $2}}'"] + stdout: SplitParser { + onRead: data => { + root.networkStrength = parseInt(data); + } + } + } } diff --git a/services/NetworkUsage.qml b/services/NetworkUsage.qml index c503891..9179e52 100644 --- a/services/NetworkUsage.qml +++ b/services/NetworkUsage.qml @@ -8,65 +8,65 @@ import "../utils/networkUsage.js" as Network import qs.config Singleton { - property int up: 0 - property string upUnit: "" - property int down: 0 - property string downUnit: "" + property int up: 0 + property string upUnit: "" + property int down: 0 + property string downUnit: "" - property string __iface: "" - property var __prev: ({ - rx: 0, - tx: 0 - }) + property string __iface: "" + property var __prev: ({ + rx: 0, + tx: 0 + }) - Timer { - interval: General.resourceUpdateInterval * 1000 - running: true - repeat: true - onTriggered: update() - } + Timer { + interval: General.resourceUpdateInterval * 1000 + running: true + repeat: true + onTriggered: update() + } - function update() { - try { - __iface = Network.defaultInterface(getRouteProc.stdout.text); - } catch (e) { - getRouteProc.running = true; - } - const cur = Network.getRxTxBytes(getNetDevProc.stdout.text, __iface); - const sec = General.resourceUpdateInterval; - const rawUp = (cur.tx - __prev.tx) / sec; - const rawDown = (cur.rx - __prev.rx) / sec; + function update() { + try { + __iface = Network.defaultInterface(getRouteProc.stdout.text); + } catch (e) { + getRouteProc.running = true; + } + const cur = Network.getRxTxBytes(getNetDevProc.stdout.text, __iface); + const sec = General.resourceUpdateInterval; + const rawUp = (cur.tx - __prev.tx) / sec; + const rawDown = (cur.rx - __prev.rx) / sec; - up = Network.formatSize(rawUp, false); - upUnit = Network.formatSize(rawUp, true); - down = Network.formatSize(rawDown, false); - downUnit = Network.formatSize(rawDown, true); + up = Network.formatSize(rawUp, false); + upUnit = Network.formatSize(rawUp, true); + down = Network.formatSize(rawDown, false); + downUnit = Network.formatSize(rawDown, true); - __prev = cur; - getRouteProc.running = true; - getNetDevProc.running = true; - } + __prev = cur; + getRouteProc.running = true; + getNetDevProc.running = true; + } - Process { - id: getRouteProc - running: true - command: ["ip", "route"] - stdout: StdioCollector {} - } + Process { + id: getRouteProc + running: true + command: ["ip", "route"] + stdout: StdioCollector {} + } - Process { - id: getNetDevProc - running: true - command: ["cat", "/proc/net/dev"] - stdout: StdioCollector {} - } + Process { + id: getNetDevProc + running: true + command: ["cat", "/proc/net/dev"] + stdout: StdioCollector {} + } - Component.onCompleted: { - getRouteProc.running = true; - getNetDevProc.running = true; - if (getRouteProc.stdout.text) - __iface = Network.defaultInterface(getRouteProc.stdout.text); - if (getNetDevProc.stdout.text) - __prev = Network.getRxTxBytes(getNetDevProc.stdout.text, __iface); - } + Component.onCompleted: { + getRouteProc.running = true; + getNetDevProc.running = true; + if (getRouteProc.stdout.text) + __iface = Network.defaultInterface(getRouteProc.stdout.text); + if (getNetDevProc.stdout.text) + __prev = Network.getRxTxBytes(getNetDevProc.stdout.text, __iface); + } } diff --git a/services/Persistent.qml b/services/Persistent.qml index 3a911bf..9077ce9 100644 --- a/services/Persistent.qml +++ b/services/Persistent.qml @@ -4,24 +4,24 @@ import Quickshell.Io import qs.utils Scope { - id: root - property alias adapter: fileView.adapter - property alias fileView: fileView - property string fileDir: Directories.state - property string fileName: "states.json" - property string filePath: `${root.fileDir}/${root.fileName}` + id: root + property alias adapter: fileView.adapter + property alias fileView: fileView + property string fileDir: Directories.state + property string fileName: "states.json" + property string filePath: `${root.fileDir}/${root.fileName}` - FileView { - id: fileView - path: root.filePath - watchChanges: true - onFileChanged: reload() - onAdapterUpdated: writeAdapter() - onLoadFailed: error => { - console.log("Failed to load persistent states file:", error); - if (error == FileViewError.FileNotFound) { - writeAdapter(); - } - } - } + FileView { + id: fileView + path: root.filePath + watchChanges: true + onFileChanged: reload() + onAdapterUpdated: writeAdapter() + onLoadFailed: error => { + console.log("Failed to load persistent states file:", error); + if (error == FileViewError.FileNotFound) { + writeAdapter(); + } + } + } } diff --git a/services/Resource.qml b/services/Resource.qml index 3c6c6ea..e94d8c0 100644 --- a/services/Resource.qml +++ b/services/Resource.qml @@ -7,114 +7,114 @@ import Quickshell.Io import qs.config Singleton { - id: root + id: root - property double memoryTotal: 1 - property double memoryFree: 1 - property double memoryUsed: memoryTotal - memoryFree - property double memoryUsedPercentage: memoryUsed / memoryTotal - property double swapTotal: 1 - property double swapFree: 1 - property double swapUsed: swapTotal - swapFree - property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0 + property double memoryTotal: 1 + property double memoryFree: 1 + property double memoryUsed: memoryTotal - memoryFree + property double memoryUsedPercentage: memoryUsed / memoryTotal + property double swapTotal: 1 + property double swapFree: 1 + property double swapUsed: swapTotal - swapFree + property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0 - property double cpuUsage: 0 - property double cpuTemp: 0 - property double cpuFreq: 0 - property var previousCpuStats + property double cpuUsage: 0 + property double cpuTemp: 0 + property double cpuFreq: 0 + property var previousCpuStats - property int gpuUsage: 0 - property int gpuTemp: 0 + property int gpuUsage: 0 + property int gpuTemp: 0 - Timer { - interval: General.resourceUpdateInterval - running: true - repeat: true - onTriggered: { - fileMeminfo.reload(); - fileStat.reload(); - fileCpuThermal.reload(); - fileCpuInfo.reload(); + Timer { + interval: General.resourceUpdateInterval + running: true + repeat: true + onTriggered: { + fileMeminfo.reload(); + fileStat.reload(); + fileCpuThermal.reload(); + fileCpuInfo.reload(); - const textMeminfo = fileMeminfo.text(); - memoryTotal = Number(textMeminfo.match(/MemTotal:\s*(\d+)/)[1] ?? 1); - memoryFree = Number(textMeminfo.match(/MemAvailable:\s*(\d+)/)[1] ?? 0); - memoryUsed = memoryTotal - memoryFree; - memoryUsedPercentage = memoryUsed / memoryTotal; - swapTotal = Number(textMeminfo.match(/SwapTotal:\s*(\d+)/)[1] ?? 1); - swapFree = Number(textMeminfo.match(/SwapFree:\s*(\d+)/)[1] ?? 0); + const textMeminfo = fileMeminfo.text(); + memoryTotal = Number(textMeminfo.match(/MemTotal:\s*(\d+)/)[1] ?? 1); + memoryFree = Number(textMeminfo.match(/MemAvailable:\s*(\d+)/)[1] ?? 0); + memoryUsed = memoryTotal - memoryFree; + memoryUsedPercentage = memoryUsed / memoryTotal; + swapTotal = Number(textMeminfo.match(/SwapTotal:\s*(\d+)/)[1] ?? 1); + swapFree = Number(textMeminfo.match(/SwapFree:\s*(\d+)/)[1] ?? 0); - const textStat = fileStat.text(); - const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/); - if (cpuLine) { - const stats = cpuLine.slice(1).map(Number); - const total = stats.reduce((a, b) => a + b, 0); - const idle = stats[3]; - if (root.previousCpuStats) { - const td = total - root.previousCpuStats.total; - const id = idle - root.previousCpuStats.idle; - cpuUsage = td > 0 ? (1 - id / td) : 0; - } - root.previousCpuStats = { - total, - idle - }; - } + const textStat = fileStat.text(); + const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/); + if (cpuLine) { + const stats = cpuLine.slice(1).map(Number); + const total = stats.reduce((a, b) => a + b, 0); + const idle = stats[3]; + if (root.previousCpuStats) { + const td = total - root.previousCpuStats.total; + const id = idle - root.previousCpuStats.idle; + cpuUsage = td > 0 ? (1 - id / td) : 0; + } + root.previousCpuStats = { + total, + idle + }; + } - const cpuTempText = fileCpuThermal.text(); - cpuTemp = cpuTempText ? parseInt(cpuTempText.trim()) / 100000 : 0; + const cpuTempText = fileCpuThermal.text(); + cpuTemp = cpuTempText ? parseInt(cpuTempText.trim()) / 100000 : 0; - const textCpuInfo = fileCpuInfo.text(); - let sum = 0, cnt = 0; - const re = /^cpu MHz\s*:\s*([\d.]+)/mg; - let m; - while ((m = re.exec(textCpuInfo)) !== null) { - sum += Number(m[1]); - cnt++; - } - cpuFreq = cnt ? sum / cnt : 0; + const textCpuInfo = fileCpuInfo.text(); + let sum = 0, cnt = 0; + const re = /^cpu MHz\s*:\s*([\d.]+)/mg; + let m; + while ((m = re.exec(textCpuInfo)) !== null) { + sum += Number(m[1]); + cnt++; + } + cpuFreq = cnt ? sum / cnt : 0; - gpuUsageProc.running = true; - gpuTempProc.running = true; - interval = General.resourceUpdateInterval * cpuUsage > 0.7 || memoryUsedPercentage > 0.7 || cpuTemp > 0.7 ? 100 : 1000; - } - } + gpuUsageProc.running = true; + gpuTempProc.running = true; + interval = General.resourceUpdateInterval * cpuUsage > 0.7 || memoryUsedPercentage > 0.7 || cpuTemp > 0.7 ? 100 : 1000; + } + } - FileView { - id: fileMeminfo - path: "/proc/meminfo" - } - FileView { - id: fileStat - path: "/proc/stat" - } - FileView { - id: fileCpuThermal - path: General.cpuThermalPath - } - FileView { - id: fileCpuInfo - path: "/proc/cpuinfo" - } + FileView { + id: fileMeminfo + path: "/proc/meminfo" + } + FileView { + id: fileStat + path: "/proc/stat" + } + FileView { + id: fileCpuThermal + path: General.cpuThermalPath + } + FileView { + id: fileCpuInfo + path: "/proc/cpuinfo" + } - Process { - id: gpuUsageProc - running: true - command: ["nvidia-smi", "--query-gpu=utilization.gpu", "--format=csv,noheader,nounits"] - stdout: StdioCollector { - onStreamFinished: { - root.gpuUsage = parseInt(this.text.trim()); - } - } - } - Process { - id: gpuTempProc - running: true - command: ["nvidia-smi", "--query-gpu=temperature.gpu", "--format=csv,noheader,nounits"] - stdout: StdioCollector { - onStreamFinished: { - root.gpuTemp = parseInt(this.text.trim()); - } - } - } + Process { + id: gpuUsageProc + running: true + command: ["nvidia-smi", "--query-gpu=utilization.gpu", "--format=csv,noheader,nounits"] + stdout: StdioCollector { + onStreamFinished: { + root.gpuUsage = parseInt(this.text.trim()); + } + } + } + Process { + id: gpuTempProc + running: true + command: ["nvidia-smi", "--query-gpu=temperature.gpu", "--format=csv,noheader,nounits"] + stdout: StdioCollector { + onStreamFinished: { + root.gpuTemp = parseInt(this.text.trim()); + } + } + } } diff --git a/services/TimeDate.qml b/services/TimeDate.qml index f066f46..ebefbc1 100644 --- a/services/TimeDate.qml +++ b/services/TimeDate.qml @@ -4,79 +4,79 @@ import Quickshell import QtQuick Singleton { - id: root - readonly property string time: { - // fuck you qt give me my 12 hr time - var h = clock.date.getHours(); - var m = clock.date.getMinutes(); - var s = clock.date.getSeconds(); - h = h % 12; - if (h === 0) - h = 12; - return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s; - } - readonly property int hours: { - var h = clock.date.getHours(); - h = h % 12; - if (h === 0) - h = 12; - return h; - } - readonly property int minutes: clock.date.getMinutes() - readonly property int seconds: clock.date.getSeconds() + id: root + readonly property string time: { + // fuck you qt give me my 12 hr time + var h = clock.date.getHours(); + var m = clock.date.getMinutes(); + var s = clock.date.getSeconds(); + h = h % 12; + if (h === 0) + h = 12; + return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s; + } + readonly property int hours: { + var h = clock.date.getHours(); + h = h % 12; + if (h === 0) + h = 12; + return h; + } + readonly property int minutes: clock.date.getMinutes() + readonly property int seconds: clock.date.getSeconds() - readonly property string date: Qt.formatDateTime(clock.date, "dddd dd/MM") + readonly property string date: Qt.formatDateTime(clock.date, "dddd dd/MM") - function sinceWhen(d) { - if (typeof d === "string") - return; + function sinceWhen(d) { + if (typeof d === "string") + return; - var now = clock.date; + var now = clock.date; - function startOfDay(x) { - return new Date(x.getFullYear(), x.getMonth(), x.getDate(), 0, 0, 0, 0); - } + function startOfDay(x) { + return new Date(x.getFullYear(), x.getMonth(), x.getDate(), 0, 0, 0, 0); + } - function startOfWeek(x) { - var dow = x.getDay(); - var mondayOffset = (dow + 6) % 7; - var sod = startOfDay(x); - sod.setDate(sod.getDate() - mondayOffset); - return sod; - } + function startOfWeek(x) { + var dow = x.getDay(); + var mondayOffset = (dow + 6) % 7; + var sod = startOfDay(x); + sod.setDate(sod.getDate() - mondayOffset); + return sod; + } - function fmt12h(dateObj) { - var h24 = dateObj.getHours(); - var m = dateObj.getMinutes(); - var h12 = h24 % 12; - if (h12 === 0) - h12 = 12; - var ampm = h24 < 12 ? "AM" : "PM"; - return ((h12 < 10 ? "0" : "") + h12 + ":" + (m < 10 ? "0" : "") + m + " " + ampm); - } + function fmt12h(dateObj) { + var h24 = dateObj.getHours(); + var m = dateObj.getMinutes(); + var h12 = h24 % 12; + if (h12 === 0) + h12 = 12; + var ampm = h24 < 12 ? "AM" : "PM"; + return ((h12 < 10 ? "0" : "") + h12 + ":" + (m < 10 ? "0" : "") + m + " " + ampm); + } - function weekdayName(dateObj) { - return Qt.formatDateTime(dateObj, "dddd"); - } + function weekdayName(dateObj) { + return Qt.formatDateTime(dateObj, "dddd"); + } - var nowSOD = startOfDay(now).getTime(); - var dSOD = startOfDay(d).getTime(); - if (nowSOD === dSOD) { - return fmt12h(d); - } + var nowSOD = startOfDay(now).getTime(); + var dSOD = startOfDay(d).getTime(); + if (nowSOD === dSOD) { + return fmt12h(d); + } - var nowSOW = startOfWeek(now).getTime(); - var dSOW = startOfWeek(d).getTime(); - if (nowSOW === dSOW) { - return weekdayName(d) + " " + fmt12h(d); - } + var nowSOW = startOfWeek(now).getTime(); + var dSOW = startOfWeek(d).getTime(); + if (nowSOW === dSOW) { + return weekdayName(d) + " " + fmt12h(d); + } - var ymd = Qt.formatDateTime(d, "yyyy-MM-dd"); - return ymd + " at " + fmt12h(d); - } + var ymd = Qt.formatDateTime(d, "yyyy-MM-dd"); + return ymd + " at " + fmt12h(d); + } - SystemClock { - id: clock - precision: SystemClock.Seconds - } + SystemClock { + id: clock + precision: SystemClock.Seconds + } } |
