diff --git a/Modules/DankBar/Widgets/RunningApps.qml b/Modules/DankBar/Widgets/RunningApps.qml index facf0a90..8c3fce4d 100644 --- a/Modules/DankBar/Widgets/RunningApps.qml +++ b/Modules/DankBar/Widgets/RunningApps.qml @@ -21,21 +21,27 @@ Item { property real barThickness: 48 readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS property Item windowRoot: (Window.window ? Window.window.contentItem : null) - property int _workspaceUpdateTrigger: 0 property int _desktopEntriesUpdateTrigger: 0 + property int _toplevelsUpdateTrigger: 0 + readonly property var sortedToplevels: { - _workspaceUpdateTrigger + _toplevelsUpdateTrigger const toplevels = CompositorService.sortedToplevels - if (!toplevels || toplevels.length === 0) - return [] + if (!toplevels || toplevels.length === 0) return [] if (SettingsData.runningAppsCurrentWorkspace) { - const filtered = CompositorService.filterCurrentWorkspace(toplevels, parentScreen?.name) - return filtered || [] + return CompositorService.filterCurrentWorkspace(toplevels, parentScreen?.name) || [] } return toplevels } + Connections { + target: CompositorService + function onToplevelsChanged() { + _toplevelsUpdateTrigger++ + } + } + Connections { target: DesktopEntries function onApplicationsChanged() { @@ -89,36 +95,6 @@ Item { height: isVertical ? calculatedSize : barThickness visible: windowCount > 0 - Connections { - target: NiriService - function onAllWorkspacesChanged() { - _workspaceUpdateTrigger++ - } - function onWindowsChanged() { - _workspaceUpdateTrigger++ - } - } - - Connections { - target: Hyprland - function onFocusedWorkspaceChanged() { - _workspaceUpdateTrigger++ - } - } - - Connections { - target: Hyprland.workspaces - function onValuesChanged() { - _workspaceUpdateTrigger++ - } - } - - Connections { - target: Hyprland.toplevels - function onValuesChanged() { - _workspaceUpdateTrigger++ - } - } Rectangle { id: visualBackground @@ -241,7 +217,10 @@ Item { Repeater { id: windowRepeater - model: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels + model: ScriptModel { + values: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels + objectProp: SettingsData.runningAppsGroupByApp ? "appId" : "address" + } delegate: Item { id: delegateItem @@ -470,7 +449,10 @@ Item { Repeater { id: windowRepeater - model: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels + model: ScriptModel { + values: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels + objectProp: SettingsData.runningAppsGroupByApp ? "appId" : "address" + } delegate: Item { id: delegateItem diff --git a/Modules/Dock/DockApps.qml b/Modules/Dock/DockApps.qml index fe2d28ac..f2b6e843 100644 --- a/Modules/Dock/DockApps.qml +++ b/Modules/Dock/DockApps.qml @@ -238,7 +238,7 @@ Item { Connections { target: CompositorService - function onSortedToplevelsChanged() { + function onToplevelsChanged() { dockModel.updateModel() } } diff --git a/Services/CompositorService.qml b/Services/CompositorService.qml index 793ae3e8..430e826d 100644 --- a/Services/CompositorService.qml +++ b/Services/CompositorService.qml @@ -21,17 +21,10 @@ Singleton { readonly property string swaySocket: Quickshell.env("SWAYSOCK") property bool useNiriSorting: isNiri && NiriService - property var sortedToplevels: sortedToplevelsCache - property var sortedToplevelsCache: [] - + property var sortedToplevels: [] property bool _sortScheduled: false - property bool _refreshScheduled: false - property bool _hasRefreshedOnce: false - property var _coordCache: ({}) - property int _refreshCount: 0 - property real _refreshWindowStart: 0 - readonly property int _maxRefreshesPerSecond: 3 + signal toplevelsChanged() function getScreenScale(screen) { if (!screen) return 1 @@ -59,46 +52,27 @@ Singleton { } Timer { - id: refreshTimer - interval: 40 + id: sortDebounceTimer + interval: 100 repeat: false onTriggered: { - try { - Hyprland.refreshToplevels() - } catch(e) {} - _refreshScheduled = false - _hasRefreshedOnce = true - scheduleSort() + _sortScheduled = false + if (isHyprland) { + try { + Hyprland.refreshToplevels() + } catch(e) { + console.warn("CompositorService: Failed to refresh toplevels:", e) + } + } + sortedToplevels = computeSortedToplevels() + toplevelsChanged() } } function scheduleSort() { if (_sortScheduled) return _sortScheduled = true - Qt.callLater(function() { - _sortScheduled = false - sortedToplevelsCache = computeSortedToplevels() - }) - } - - function scheduleRefresh() { - if (!isHyprland) return - if (_refreshScheduled) return - - const now = Date.now() - if (now - _refreshWindowStart > 1000) { - _refreshCount = 0 - _refreshWindowStart = now - } - - if (_refreshCount >= _maxRefreshesPerSecond) { - console.warn("CompositorService: Refresh rate limit exceeded, skipping refresh") - return - } - - _refreshCount++ - _refreshScheduled = true - refreshTimer.restart() + sortDebounceTimer.restart() } Connections { @@ -106,19 +80,28 @@ Singleton { function onValuesChanged() { root.scheduleSort() } } Connections { - target: Hyprland.toplevels - function onValuesChanged() { - root.scheduleSort() + target: isHyprland ? Hyprland : null + enabled: isHyprland + + function onRawEvent(event) { + if (event.name === "openwindow" || + event.name === "closewindow" || + event.name === "movewindow" || + event.name === "movewindowv2" || + event.name === "workspace" || + event.name === "workspacev2" || + event.name === "focusedmon" || + event.name === "focusedmonv2" || + event.name === "activewindow" || + event.name === "activewindowv2" || + event.name === "changefloatingmode" || + event.name === "fullscreen" || + event.name === "moveintogroup" || + event.name === "moveoutofgroup") { + root.scheduleSort() + } } } - Connections { - target: Hyprland.workspaces - function onValuesChanged() { root.scheduleSort() } - } - Connections { - target: Hyprland - function onFocusedWorkspaceChanged() { root.scheduleSort() } - } Connections { target: NiriService function onWindowsChanged() { root.scheduleSort() } @@ -165,7 +148,6 @@ Singleton { function sortHyprlandToplevelsSafe() { if (!Hyprland.toplevels || !Hyprland.toplevels.values) return [] - if (_refreshScheduled && sortedToplevelsCache.length > 0) return sortedToplevelsCache const items = Array.from(Hyprland.toplevels.values) @@ -177,21 +159,7 @@ Singleton { } catch(e) { return fb } } - let currentAddresses = new Set() - for (let i = 0; i < items.length; i++) { - const addr = items[i]?.address - if (addr) currentAddresses.add(addr) - } - - for (let cachedAddr in _coordCache) { - if (!currentAddresses.has(cachedAddr)) { - delete _coordCache[cachedAddr] - } - } - let snap = [] - let missingAnyPosition = false - let hasNewWindow = false for (let i = 0; i < items.length; i++) { const t = items[i] if (!t) continue @@ -208,23 +176,8 @@ Singleton { const wsId = _get(li, ["workspace", "id"], null) ?? _get(t, ["workspace", "id"], Number.MAX_SAFE_INTEGER) const at = _get(li, ["at"], null) - let atX = (at !== null && at !== undefined && typeof at[0] === "number") ? at[0] : NaN - let atY = (at !== null && at !== undefined && typeof at[1] === "number") ? at[1] : NaN - - if (!(atX === atX) || !(atY === atY)) { - const cached = _coordCache[addr] - if (cached) { - atX = cached.x - atY = cached.y - } else { - if (addr) hasNewWindow = true - missingAnyPosition = true - atX = 1e9 - atY = 1e9 - } - } else if (addr) { - _coordCache[addr] = { x: atX, y: atY } - } + let atX = (at !== null && at !== undefined && typeof at[0] === "number") ? at[0] : 1e9 + let atY = (at !== null && at !== undefined && typeof at[1] === "number") ? at[1] : 1e9 const relX = Number.isFinite(monX) ? (atX - monX) : atX const relY = Number.isFinite(monY) ? (atY - monY) : atY @@ -242,10 +195,6 @@ Singleton { }) } - if (missingAnyPosition && hasNewWindow && !_hasRefreshedOnce) { - scheduleRefresh() - } - const groups = new Map() for (const it of snap) { const key = it.monKey + "::" + it.wsId @@ -400,9 +349,6 @@ Singleton { isSway = false compositor = "hyprland" console.info("CompositorService: Detected Hyprland") - try { - Hyprland.refreshToplevels() - } catch(e) {} return } diff --git a/Services/DesktopService.qml b/Services/DesktopService.qml index 3edef672..b243de28 100644 --- a/Services/DesktopService.qml +++ b/Services/DesktopService.qml @@ -32,11 +32,9 @@ Singleton { } for (const appId of appIds){ let icon = Quickshell.iconPath(entry?.icon, true) - console.log(icon) if (icon && icon !== "") return icon let execPath = entry?.execString?.replace(/\/bin.*/, "") - console.log(execPath) if (!execPath) continue //Check that the app is installed with nix/guix @@ -46,13 +44,11 @@ Singleton { let iconPath = `${basePath}/share/icons/hicolor/scalable/apps/${appId}.svg` icon = Quickshell.iconPath(iconPath, true) - console.log(icon) if (icon && icon !== "") return icon for (const size of sizes) { iconPath = `${basePath}/share/icons/hicolor/${size}/apps/${appId}.png` icon = Quickshell.iconPath(iconPath, true) - console.log(icon) if (icon && icon !== "") return icon } } diff --git a/Services/IdleService.qml b/Services/IdleService.qml index b0d4301f..f1f5bb7b 100644 --- a/Services/IdleService.qml +++ b/Services/IdleService.qml @@ -118,12 +118,12 @@ Singleton { import Quickshell.Wayland IdleInhibitor { - active: false + enabled: false } ` mediaInhibitor = Qt.createQmlObject(inhibitorString, root, "IdleService.MediaInhibitor") - mediaInhibitor.active = Qt.binding(() => root.idleInhibitorAvailable && SettingsData.preventIdleForMedia && root.mediaPlaying) + mediaInhibitor.enabled = Qt.binding(() => root.idleInhibitorAvailable && SettingsData.preventIdleForMedia && root.mediaPlaying) } } catch (e) { console.warn("IdleService: Error creating IdleMonitors:", e) diff --git a/Widgets/DankSlideout.qml b/Widgets/DankSlideout.qml index 18988597..f955d316 100644 --- a/Widgets/DankSlideout.qml +++ b/Widgets/DankSlideout.qml @@ -3,6 +3,7 @@ import QtQuick.Controls import Quickshell import Quickshell.Wayland import qs.Common +import qs.Services import qs.Widgets pragma ComponentBehavior: Bound