From 60144ca3debb8a2ad979ccd384b4fe1867a5e42b Mon Sep 17 00:00:00 2001 From: reakjra Date: Thu, 6 Nov 2025 14:23:13 +0100 Subject: [PATCH 01/25] Feature (Overlay): MangoHud Fps Limiter widget --- .../ii/modules/common/Persistent.qml | 6 ++ .../quickshell/ii/modules/overlay/Overlay.qml | 12 ++- .../ii/modules/overlay/OverlayContent.qml | 1 + .../ii/modules/overlay/OverlayContext.qml | 1 + .../overlay/OverlayWidgetDelegateChooser.qml | 2 + .../modules/overlay/fpsLimiter/FpsLimiter.qml | 10 ++ .../overlay/fpsLimiter/FpsLimiterContent.qml | 92 +++++++++++++++++++ 7 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml create mode 100644 dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 814938452..7e077b2c3 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -93,6 +93,12 @@ Singleton { property real x: 55 property real y: 188 } + property JsonObject fpsLimiter: JsonObject { + property bool pinned: false + property bool clickthrough: false + property real x: 100 + property real y: 100 + } } property JsonObject timer: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml index 5054b9d1c..2fd33fcd7 100644 --- a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml +++ b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml @@ -25,7 +25,7 @@ Scope { exclusionMode: ExclusionMode.Ignore WlrLayershell.namespace: "quickshell:overlay" WlrLayershell.layer: WlrLayer.Overlay - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + WlrLayershell.keyboardFocus: GlobalStates.overlayOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None visible: true color: "transparent" @@ -43,6 +43,16 @@ Scope { right: true } + HyprlandFocusGrab { + id: grab + windows: [overlayWindow] + active: GlobalStates.overlayOpen + onCleared: () => { + if (!active) + GlobalStates.overlayOpen = false; + } + } + OverlayContent { id: overlayContent anchors.fill: parent diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml index 838267b80..546962185 100644 --- a/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml @@ -12,6 +12,7 @@ import qs.modules.overlay.crosshair Item { id: root + focus: true readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true Keys.onPressed: (event) => { // Esc to close diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml index 1417c8467..cc54d379f 100644 --- a/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml @@ -7,6 +7,7 @@ Singleton { readonly property list availableWidgets: [ { identifier: "crosshair", materialSymbol: "point_scan" }, + { identifier: "fpsLimiter", materialSymbol: "speed" }, { identifier: "volumeMixer", materialSymbol: "volume_up" } ] diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml index e7420f625..dffdfaaee 100644 --- a/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml @@ -8,6 +8,7 @@ import Quickshell import Quickshell.Bluetooth import qs.modules.overlay.crosshair import qs.modules.overlay.volumeMixer +import qs.modules.overlay.fpsLimiter DelegateChooser { id: root @@ -15,4 +16,5 @@ DelegateChooser { DelegateChoice { roleValue: "crosshair"; Crosshair {} } DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} } + DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} } } diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml new file mode 100644 index 000000000..fe3ab9df4 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml @@ -0,0 +1,10 @@ +import QtQuick +import Quickshell +import qs.modules.common +import qs.modules.overlay + +StyledOverlayWidget { + id: root + title: "FPS Limiter" + contentItem: FpsLimiterContent {} +} diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml new file mode 100644 index 000000000..fae1aca78 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml @@ -0,0 +1,92 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Io +import qs.modules.common +import qs.modules.common.widgets + +Rectangle { + id: root + color: Appearance.m3colors.m3surfaceContainer + property real padding: 20 + implicitWidth: contentColumn.implicitWidth + padding * 2 + implicitHeight: contentColumn.implicitHeight + padding * 2 + + function applyLimit() { + var fpsValue = parseInt(fpsField.text); + if (isNaN(fpsValue) || fpsValue < 0) { + return; + } + + var cfgPaths = [ + "~/.config/MangoHud/MangoHud.conf", + ]; // MangoHud config files + + var updateCommands = cfgPaths.map(path => { + return "if grep -q '^fps_limit=' " + path + "; " + + "then sed -i 's/^fps_limit=.*/fps_limit=" + fpsValue + "/' " + path + "; " + + "else echo 'fps_limit=" + fpsValue + "' >> " + path + "; fi"; + }).join("; "); + + var cmd = updateCommands + "; pkill -SIGUSR2 mangohud"; + + fpsSetter.command = ["bash", "-c", cmd]; + fpsSetter.startDetached(); + + // Clear the field after applying + fpsField.text = ""; + } + + Keys.onPressed: event => { + if (event.key === Qt.Key_Escape) { + fpsField.text = ""; + event.accepted = true; + } + } + + ColumnLayout { + id: contentColumn + anchors.centerIn: parent + spacing: 15 + + RowLayout { + Layout.fillWidth: true + spacing: 10 + + ToolbarTextField { + id: fpsField + Layout.fillWidth: true + Layout.preferredWidth: 200 + placeholderText: qsTr("Set FPS limit (e.g. 80)") + inputMethodHints: Qt.ImhDigitsOnly + focus: true + + Keys.onReturnPressed: { + root.applyLimit(); + event.accepted = true; + } + } + + RippleButton { + id: applyButton + implicitWidth: 36 + implicitHeight: 36 + buttonRadius: Appearance.rounding.full + onClicked: { + root.applyLimit(); + } + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Appearance.font.pixelSize.title + text: "keyboard_return" + } + } + } + + Process { + id: fpsSetter + } + } +} From f4c32f89f2df6337ed71e14b4703f837e030821b Mon Sep 17 00:00:00 2001 From: reakjra Date: Thu, 6 Nov 2025 22:41:14 +0100 Subject: [PATCH 02/25] Added proposed fixes and implemented some animations+error text --- .../quickshell/ii/modules/overlay/Overlay.qml | 11 +++++ .../ii/modules/overlay/OverlayContext.qml | 2 +- .../modules/overlay/fpsLimiter/FpsLimiter.qml | 2 +- .../overlay/fpsLimiter/FpsLimiterContent.qml | 48 +++++++++++++++++-- 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml index 2fd33fcd7..efb37cd89 100644 --- a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml +++ b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml @@ -47,12 +47,23 @@ Scope { id: grab windows: [overlayWindow] active: GlobalStates.overlayOpen + onActiveChanged: { + if (active) { + // Quick workspace flick to unlock cursor from game's relative mode + cursorUnlockProcess.running = true; + } + } onCleared: () => { if (!active) GlobalStates.overlayOpen = false; } } + Process { + id: cursorUnlockProcess + command: ["bash", "-c", "ws=$(hyprctl activeworkspace -j | jq -r '.id'); hyprctl --batch \"keyword animations:enabled 0 ; dispatch workspace empty ; dispatch workspace $ws ; keyword animations:enabled 1\""] + } + OverlayContent { id: overlayContent anchors.fill: parent diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml index cc54d379f..dcfeae099 100644 --- a/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml @@ -7,7 +7,7 @@ Singleton { readonly property list availableWidgets: [ { identifier: "crosshair", materialSymbol: "point_scan" }, - { identifier: "fpsLimiter", materialSymbol: "speed" }, + { identifier: "fpsLimiter", materialSymbol: "animation" }, { identifier: "volumeMixer", materialSymbol: "volume_up" } ] diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml index fe3ab9df4..24cac5387 100644 --- a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml @@ -5,6 +5,6 @@ import qs.modules.overlay StyledOverlayWidget { id: root - title: "FPS Limiter" + title: "MangoHud FPS" contentItem: FpsLimiterContent {} } diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml index fae1aca78..c83e83de9 100644 --- a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml @@ -1,3 +1,4 @@ +import qs.services import QtQuick import QtQuick.Layouts import QtQuick.Controls @@ -10,12 +11,33 @@ Rectangle { id: root color: Appearance.m3colors.m3surfaceContainer property real padding: 20 + property bool showCheckIcon: false + property bool showError: false implicitWidth: contentColumn.implicitWidth + padding * 2 implicitHeight: contentColumn.implicitHeight + padding * 2 + Timer { + id: iconResetTimer + interval: 1000 + onTriggered: { + root.showCheckIcon = false; + } + } + + Timer { + id: errorResetTimer + interval: 1000 + onTriggered: { + root.showError = false; + } + } + function applyLimit() { var fpsValue = parseInt(fpsField.text); if (isNaN(fpsValue) || fpsValue < 0) { + root.showError = true; + errorResetTimer.restart(); + fpsField.text = ""; return; } @@ -34,6 +56,9 @@ Rectangle { fpsSetter.command = ["bash", "-c", cmd]; fpsSetter.startDetached(); + root.showCheckIcon = true; + iconResetTimer.restart(); + // Clear the field after applying fpsField.text = ""; } @@ -41,7 +66,7 @@ Rectangle { Keys.onPressed: event => { if (event.key === Qt.Key_Escape) { fpsField.text = ""; - event.accepted = true; + event.onAccepted(); } } @@ -58,13 +83,13 @@ Rectangle { id: fpsField Layout.fillWidth: true Layout.preferredWidth: 200 - placeholderText: qsTr("Set FPS limit (e.g. 80)") + placeholderText: root.showError ? Translation.tr("Insert a valid number!") : Translation.tr("Set FPS limit (e.g. 80)") inputMethodHints: Qt.ImhDigitsOnly focus: true Keys.onReturnPressed: { root.applyLimit(); - event.accepted = true; + event.onAccepted(); } } @@ -80,7 +105,22 @@ Rectangle { anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter font.pixelSize: Appearance.font.pixelSize.title - text: "keyboard_return" + text: root.showError ? "close" : (root.showCheckIcon ? "check" : "save") + rotation: (root.showCheckIcon || root.showError) ? 360 : 0 + color: root.showError ? "#ef5350" : (root.showCheckIcon ? Appearance.m3colors.m3primary : Appearance.m3colors.m3onSurface) + + Behavior on rotation { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + + Behavior on color { + ColorAnimation { + duration: 200 + } + } } } } From 7f49daf422a0f5f4dcbd2f1eaffe0cf9a6301f01 Mon Sep 17 00:00:00 2001 From: reakjra Date: Thu, 6 Nov 2025 22:44:40 +0100 Subject: [PATCH 03/25] undo personal edits by mistake --- .../.config/quickshell/ii/modules/overlay/Overlay.qml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml index efb37cd89..2fd33fcd7 100644 --- a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml +++ b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml @@ -47,23 +47,12 @@ Scope { id: grab windows: [overlayWindow] active: GlobalStates.overlayOpen - onActiveChanged: { - if (active) { - // Quick workspace flick to unlock cursor from game's relative mode - cursorUnlockProcess.running = true; - } - } onCleared: () => { if (!active) GlobalStates.overlayOpen = false; } } - Process { - id: cursorUnlockProcess - command: ["bash", "-c", "ws=$(hyprctl activeworkspace -j | jq -r '.id'); hyprctl --batch \"keyword animations:enabled 0 ; dispatch workspace empty ; dispatch workspace $ws ; keyword animations:enabled 1\""] - } - OverlayContent { id: overlayContent anchors.fill: parent From 1766375348bae573ae2061c877456db73dfb9211 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 14:00:17 +0100 Subject: [PATCH 04/25] fix weird max cpu freq --- .../quickshell/ii/services/ResourceUsage.qml | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/dots/.config/quickshell/ii/services/ResourceUsage.qml b/dots/.config/quickshell/ii/services/ResourceUsage.qml index e42022cb5..df823adf7 100644 --- a/dots/.config/quickshell/ii/services/ResourceUsage.qml +++ b/dots/.config/quickshell/ii/services/ResourceUsage.qml @@ -67,7 +67,6 @@ Singleton { // Reload files fileMeminfo.reload() fileStat.reload() - fileCpuinfo.reload() // Parse memory and swap usage const textMeminfo = fileMeminfo.text() @@ -93,29 +92,6 @@ Singleton { previousCpuStats = { total, idle } } - // Parse max CPU frequency - const textCpuinfo = fileCpuinfo.text() - // Try to find 'cpu max MHz', fallback to highest 'cpu MHz' - let maxMHz = 0 - let match - // Try cpu max MHz (modern kernels) - match = textCpuinfo.match(/cpu max MHz\s*:\s*([\d.]+)/) - if (match) { - maxMHz = Number(match[1]) - } else { - // Fallback: find all cpu MHz lines and take the max - let mhzRegex = /cpu MHz\s*:\s*([\d.]+)/g - let mhzMatch - let mhzList = [] - while ((mhzMatch = mhzRegex.exec(textCpuinfo)) !== null) { - mhzList.push(Number(mhzMatch[1])) - } - if (mhzList.length > 0) { - maxMHz = Math.max.apply(null, mhzList) - } - } - root.maxAvailableCpuString = maxMHz > 0 ? (maxMHz / 1000).toFixed(1) + "GHz" : "--" - root.updateHistories() interval = Config.options?.resources?.updateInterval ?? 3000 } @@ -123,5 +99,16 @@ Singleton { FileView { id: fileMeminfo; path: "/proc/meminfo" } FileView { id: fileStat; path: "/proc/stat" } - FileView { id: fileCpuinfo; path: "/proc/cpuinfo" } + + Process { + id: findCpuMaxFreqProc + command: ["bash", "-c", "lscpu | grep 'CPU max MHz' | awk '{print $4}'"] + running: true + stdout: StdioCollector { + id: outputCollector + onStreamFinished: { + root.maxAvailableCpuString = (parseFloat(outputCollector.text) / 1000).toFixed(0) + " GHz" + } + } + } } From fd8f569477776c34302747a7f4db26e49b75b8cc Mon Sep 17 00:00:00 2001 From: "Celestial.y" Date: Fri, 7 Nov 2025 22:27:44 +0800 Subject: [PATCH 05/25] Add comment (#2220) --- dots/.config/hypr/hyprland/execs.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dots/.config/hypr/hyprland/execs.conf b/dots/.config/hypr/hyprland/execs.conf index 252e3afa8..c273da531 100644 --- a/dots/.config/hypr/hyprland/execs.conf +++ b/dots/.config/hypr/hyprland/execs.conf @@ -23,3 +23,5 @@ exec-once = wl-paste --type image --watch bash -c 'cliphist store && qs -c $qsCo # Cursor exec-once = hyprctl setcursor Bibata-Modern-Classic 24 +# Fix dock pinned apps not launching properly (https://github.com/end-4/dots-hyprland/issues/2200) +# exec-once = sleep 3.5 && hyprctl reload && sleep 0.5 && touch ~/.config/quickshell/ii/shell.qml From e50ea627e80df3b417cb51d3a0ba4e9c2b105bba Mon Sep 17 00:00:00 2001 From: reakjra Date: Fri, 7 Nov 2025 16:28:45 +0100 Subject: [PATCH 06/25] Fix non-sensical logic, timers and animations. --- .../overlay/fpsLimiter/FpsLimiterContent.qml | 55 ++++++------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml index c83e83de9..f971258aa 100644 --- a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml @@ -10,9 +10,8 @@ import qs.modules.common.widgets Rectangle { id: root color: Appearance.m3colors.m3surfaceContainer - property real padding: 20 - property bool showCheckIcon: false - property bool showError: false + property real padding: 16 + property string iconState: "normal" implicitWidth: contentColumn.implicitWidth + padding * 2 implicitHeight: contentColumn.implicitHeight + padding * 2 @@ -20,23 +19,15 @@ Rectangle { id: iconResetTimer interval: 1000 onTriggered: { - root.showCheckIcon = false; - } - } - - Timer { - id: errorResetTimer - interval: 1000 - onTriggered: { - root.showError = false; + root.iconState = "normal"; } } function applyLimit() { var fpsValue = parseInt(fpsField.text); if (isNaN(fpsValue) || fpsValue < 0) { - root.showError = true; - errorResetTimer.restart(); + root.iconState = "error"; + iconResetTimer.restart(); fpsField.text = ""; return; } @@ -56,20 +47,13 @@ Rectangle { fpsSetter.command = ["bash", "-c", cmd]; fpsSetter.startDetached(); - root.showCheckIcon = true; + root.iconState = "success"; iconResetTimer.restart(); // Clear the field after applying fpsField.text = ""; } - Keys.onPressed: event => { - if (event.key === Qt.Key_Escape) { - fpsField.text = ""; - event.onAccepted(); - } - } - ColumnLayout { id: contentColumn anchors.centerIn: parent @@ -82,21 +66,21 @@ Rectangle { ToolbarTextField { id: fpsField Layout.fillWidth: true - Layout.preferredWidth: 200 - placeholderText: root.showError ? Translation.tr("Insert a valid number!") : Translation.tr("Set FPS limit (e.g. 80)") + Layout.preferredWidth: 180 + placeholderText: root.iconState === "error" ? Translation.tr("Insert a valid number") : Translation.tr("Set FPS limit (e.g. 80)") inputMethodHints: Qt.ImhDigitsOnly focus: true - Keys.onReturnPressed: { + onAccepted: { root.applyLimit(); - event.onAccepted(); + event.accepted = true; } } RippleButton { id: applyButton - implicitWidth: 36 - implicitHeight: 36 + implicitWidth: 25 + implicitHeight: 25 buttonRadius: Appearance.rounding.full onClicked: { root.applyLimit(); @@ -105,21 +89,16 @@ Rectangle { anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter font.pixelSize: Appearance.font.pixelSize.title - text: root.showError ? "close" : (root.showCheckIcon ? "check" : "save") - rotation: (root.showCheckIcon || root.showError) ? 360 : 0 - color: root.showError ? "#ef5350" : (root.showCheckIcon ? Appearance.m3colors.m3primary : Appearance.m3colors.m3onSurface) + text: root.iconState === "error" ? "close" : (root.iconState === "success" ? "check" : "save") + rotation: root.iconState !== "normal" ? 360 : 0 + color: root.iconState === "error" ? "#ef5350" : (root.iconState === "success" ? Appearance.m3colors.m3primary : Appearance.m3colors.m3onSurface) Behavior on rotation { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } Behavior on color { - ColorAnimation { - duration: 200 - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } } From 23b471edc2002368e8c37d720cb75418be2a6527 Mon Sep 17 00:00:00 2001 From: reakjra Date: Fri, 7 Nov 2025 16:34:29 +0100 Subject: [PATCH 07/25] Fix non-sensical logic, timers and animations. --- .../ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml index f971258aa..74173db18 100644 --- a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml @@ -73,7 +73,6 @@ Rectangle { onAccepted: { root.applyLimit(); - event.accepted = true; } } From 082f12084d2a79354e3c768105532a9cdbe2c598 Mon Sep 17 00:00:00 2001 From: "Celestial.y" Date: Fri, 7 Nov 2025 23:59:20 +0800 Subject: [PATCH 08/25] Fix #2200 --- dots/.config/hypr/hyprland/execs.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/hypr/hyprland/execs.conf b/dots/.config/hypr/hyprland/execs.conf index c273da531..e4f33f6aa 100644 --- a/dots/.config/hypr/hyprland/execs.conf +++ b/dots/.config/hypr/hyprland/execs.conf @@ -24,4 +24,4 @@ exec-once = wl-paste --type image --watch bash -c 'cliphist store && qs -c $qsCo exec-once = hyprctl setcursor Bibata-Modern-Classic 24 # Fix dock pinned apps not launching properly (https://github.com/end-4/dots-hyprland/issues/2200) -# exec-once = sleep 3.5 && hyprctl reload && sleep 0.5 && touch ~/.config/quickshell/ii/shell.qml +exec-once = sleep 3.5 && hyprctl reload && sleep 0.5 && touch ~/.config/quickshell/ii/shell.qml From 2c88a71eedee8e8c0292c721d9a8b240d70b2712 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:21:59 +0100 Subject: [PATCH 09/25] overlay: make widgets have proper round corners when pinned --- .../ii/modules/common/Persistent.qml | 2 +- .../modules/overlay/StyledOverlayWidget.qml | 32 ++++++++++++------- .../modules/overlay/crosshair/Crosshair.qml | 6 +++- .../ii/modules/overlay/recorder/Recorder.qml | 1 + .../modules/overlay/resources/Resources.qml | 1 + .../overlay/volumeMixer/VolumeMixer.qml | 1 + 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index f2de212c9..43da3467e 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -104,7 +104,7 @@ Singleton { property bool pinned: false property bool clickthrough: false property real x: 80 - property real y: 250 + property real y: 280 } } diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml index 5e6a0ce3f..42d61c3bd 100644 --- a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -21,6 +21,7 @@ AbstractOverlayWidget { id: root required property Item contentItem + property bool fancyBorders: true required property var modelData readonly property string identifier: modelData.identifier @@ -29,6 +30,8 @@ AbstractOverlayWidget { property var persistentStateEntry: Persistent.states.overlay[identifier] property real radius: Appearance.rounding.windowRounding property real minWidth: 250 + property real padding: 6 + property real contentRadius: radius - padding draggable: GlobalStates.overlayOpen x: Math.round(persistentStateEntry.x) // Round or it'll be blurry @@ -96,11 +99,15 @@ AbstractOverlayWidget { Rectangle { id: border anchors.fill: parent - color: "transparent" + color: (root.fancyBorders && GlobalStates.overlayOpen) ? Appearance.colors.colLayer1 : "transparent" radius: root.radius border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1) border.width: 1 + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + layer.enabled: GlobalStates.overlayOpen layer.effect: OpacityMask { maskSource: Rectangle { @@ -110,25 +117,23 @@ AbstractOverlayWidget { } } - Column { + ColumnLayout { id: contentColumn - z: -1 + z: root.fancyBorders ? 0 : -1 anchors.fill: parent + spacing: 0 // Title bar Rectangle { id: titleBar opacity: GlobalStates.overlayOpen ? 1 : 0 - anchors { - left: parent.left - right: parent.right - } - property real padding: 2 + Layout.fillWidth: true + property real padding: 6 implicitWidth: titleBarRow.implicitWidth + padding * 2 implicitHeight: titleBarRow.implicitHeight + padding * 2 - color: Appearance.m3colors.m3surfaceContainer - border.color: Appearance.colors.colOutlineVariant - border.width: 1 + color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1 + // border.color: Appearance.colors.colOutlineVariant + // border.width: 1 RowLayout { id: titleBarRow @@ -191,7 +196,10 @@ AbstractOverlayWidget { // Content Item { id: contentContainer - anchors.horizontalCenter: parent.horizontalCenter + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: root.fancyBorders ? root.padding : 0 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter implicitWidth: root.contentItem.implicitWidth implicitHeight: root.contentItem.implicitHeight children: [root.contentItem] diff --git a/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml b/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml index abb1cbf26..bb60bd1fd 100644 --- a/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml +++ b/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml @@ -1,9 +1,13 @@ import QtQuick +import QtQuick.Layouts import Quickshell import qs.modules.common import qs.modules.overlay StyledOverlayWidget { id: root - contentItem: CrosshairContent {} + fancyBorders: false // Crosshair should be see-through + contentItem: CrosshairContent { + anchors.centerIn: parent + } } diff --git a/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml b/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml index 78d747ed6..e853819b4 100644 --- a/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml +++ b/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml @@ -13,6 +13,7 @@ StyledOverlayWidget { contentItem: Rectangle { id: contentItem anchors.centerIn: parent + radius: root.contentRadius color: Appearance.m3colors.m3surfaceContainer property real padding: 8 implicitHeight: contentColumn.implicitHeight + padding * 2 diff --git a/dots/.config/quickshell/ii/modules/overlay/resources/Resources.qml b/dots/.config/quickshell/ii/modules/overlay/resources/Resources.qml index f5f5ff286..1b78b867e 100644 --- a/dots/.config/quickshell/ii/modules/overlay/resources/Resources.qml +++ b/dots/.config/quickshell/ii/modules/overlay/resources/Resources.qml @@ -39,6 +39,7 @@ StyledOverlayWidget { id: contentItem anchors.centerIn: parent color: Appearance.m3colors.m3surfaceContainer + radius: root.contentRadius property real padding: 4 implicitWidth: 350 implicitHeight: 200 diff --git a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml index 14aa53006..487f470b5 100644 --- a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml +++ b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml @@ -10,6 +10,7 @@ StyledOverlayWidget { contentItem: Rectangle { anchors.centerIn: parent color: Appearance.m3colors.m3surfaceContainer + radius: root.contentRadius property real padding: 16 implicitHeight: 600 implicitWidth: 350 From 843025bc646048ba24f24cd21367c4b3d0171808 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:33:32 +0100 Subject: [PATCH 10/25] overlay: make volume mixer has both output and input --- .../ii/modules/common/Persistent.qml | 1 + .../modules/overlay/resources/Resources.qml | 2 +- .../overlay/volumeMixer/VolumeMixer.qml | 46 +++++++++++++++++-- .../volumeMixer/VolumeDialogContent.qml | 2 + 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 43da3467e..82670296e 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -105,6 +105,7 @@ Singleton { property bool clickthrough: false property real x: 80 property real y: 280 + property int tabIndex: 0 } } diff --git a/dots/.config/quickshell/ii/modules/overlay/resources/Resources.qml b/dots/.config/quickshell/ii/modules/overlay/resources/Resources.qml index 1b78b867e..fe32d2a91 100644 --- a/dots/.config/quickshell/ii/modules/overlay/resources/Resources.qml +++ b/dots/.config/quickshell/ii/modules/overlay/resources/Resources.qml @@ -50,7 +50,7 @@ StyledOverlayWidget { fill: parent margins: parent.padding } - spacing: 10 + spacing: 8 SecondaryTabBar { id: tabBar diff --git a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml index 487f470b5..68c572579 100644 --- a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml +++ b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml @@ -1,7 +1,10 @@ import QtQuick +import QtQuick.Controls import QtQuick.Layouts import Quickshell +import qs.services import qs.modules.common +import qs.modules.common.widgets import qs.modules.overlay import qs.modules.sidebarRight.volumeMixer @@ -15,11 +18,44 @@ StyledOverlayWidget { implicitHeight: 600 implicitWidth: 350 - VolumeDialogContent { - anchors.fill: parent - anchors.margins: parent.padding - isSink: true - } + ColumnLayout { + id: contentColumn + anchors { + fill: parent + margins: parent.padding + } + spacing: 8 + SecondaryTabBar { + id: tabBar + + currentIndex: Persistent.states.overlay.volumeMixer.tabIndex + onCurrentIndexChanged: { + Persistent.states.overlay.volumeMixer.tabIndex = tabBar.currentIndex; + } + + SecondaryTabButton { + buttonIcon: "media_output" + buttonText: Translation.tr("Output") + } + SecondaryTabButton { + buttonIcon: "mic" + buttonText: Translation.tr("Input") + } + } + SwipeView { + id: swipeView + Layout.fillWidth: true + Layout.fillHeight: true + currentIndex: Persistent.states.overlay.volumeMixer.tabIndex + onCurrentIndexChanged: { + Persistent.states.overlay.volumeMixer.tabIndex = swipeView.currentIndex; + } + clip: true + + VolumeDialogContent { isSink: true } + VolumeDialogContent { isSink: false } + } + } } } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialogContent.qml b/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialogContent.qml index 56f524968..a276128d9 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialogContent.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialogContent.qml @@ -29,6 +29,7 @@ ColumnLayout { Layout.topMargin: -22 Layout.leftMargin: 0 Layout.rightMargin: 0 + color: Appearance.colors.colOutlineVariant } DialogSectionListView { @@ -56,6 +57,7 @@ ColumnLayout { Layout.topMargin: -22 Layout.leftMargin: 0 Layout.rightMargin: 0 + color: Appearance.colors.colOutlineVariant } DialogSectionListView { From cd952729f488afd45ce892188d8dedfdc90ce5ce Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:45:42 +0100 Subject: [PATCH 11/25] overlay: adjust titlebar-content gap --- .../quickshell/ii/modules/overlay/StyledOverlayWidget.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml index 42d61c3bd..e20eb417b 100644 --- a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -141,6 +141,7 @@ AbstractOverlayWidget { fill: parent margins: titleBar.padding leftMargin: titleBar.padding + 8 + bottomMargin: root.fancyBorders ? 0 : titleBar.padding } spacing: 0 From daa4dd7b0fddaf8edd6420dac1994ff7433bffcd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 20:33:54 +0100 Subject: [PATCH 12/25] overlay: fps limiter: fix weird container and button size --- .../ii/modules/common/Persistent.qml | 4 +- .../common/widgets/IconToolbarButton.qml | 1 + .../modules/overlay/fpsLimiter/FpsLimiter.qml | 4 +- .../overlay/fpsLimiter/FpsLimiterContent.qml | 71 +++++++------------ 4 files changed, 31 insertions(+), 49 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index c4c0b2f1a..3d75d11ef 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -109,8 +109,8 @@ Singleton { property JsonObject fpsLimiter: JsonObject { property bool pinned: false property bool clickthrough: false - property real x: 100 - property real y: 100 + property real x: 1600 + property real y: 630 } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/IconToolbarButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/IconToolbarButton.qml index df87dcb0e..6e2fd4166 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/IconToolbarButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/IconToolbarButton.qml @@ -18,5 +18,6 @@ ToolbarButton { iconSize: 22 text: iconBtn.text color: iconBtn.colText + animateChange: true } } diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml index 24cac5387..7628d58be 100644 --- a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiter.qml @@ -6,5 +6,7 @@ import qs.modules.overlay StyledOverlayWidget { id: root title: "MangoHud FPS" - contentItem: FpsLimiterContent {} + contentItem: FpsLimiterContent { + radius: root.contentRadius + } } diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml index 74173db18..5857c5a21 100644 --- a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml @@ -12,8 +12,9 @@ Rectangle { color: Appearance.m3colors.m3surfaceContainer property real padding: 16 property string iconState: "normal" - implicitWidth: contentColumn.implicitWidth + padding * 2 - implicitHeight: contentColumn.implicitHeight + padding * 2 + anchors.fill: parent + implicitWidth: content.implicitWidth + (padding * 2) + implicitHeight: content.implicitHeight + (padding * 2) Timer { id: iconResetTimer @@ -54,57 +55,35 @@ Rectangle { fpsField.text = ""; } - ColumnLayout { - id: contentColumn + Process { + id: fpsSetter + } + + RowLayout { + id: content anchors.centerIn: parent - spacing: 15 + spacing: 4 - RowLayout { + ToolbarTextField { + id: fpsField Layout.fillWidth: true - spacing: 10 + Layout.preferredWidth: 200 + placeholderText: root.iconState === "error" ? Translation.tr("Insert a valid number") : Translation.tr("Set FPS limit (e.g. 80)") + inputMethodHints: Qt.ImhDigitsOnly + focus: true - ToolbarTextField { - id: fpsField - Layout.fillWidth: true - Layout.preferredWidth: 180 - placeholderText: root.iconState === "error" ? Translation.tr("Insert a valid number") : Translation.tr("Set FPS limit (e.g. 80)") - inputMethodHints: Qt.ImhDigitsOnly - focus: true - - onAccepted: { - root.applyLimit(); - } - } - - RippleButton { - id: applyButton - implicitWidth: 25 - implicitHeight: 25 - buttonRadius: Appearance.rounding.full - onClicked: { - root.applyLimit(); - } - contentItem: MaterialSymbol { - anchors.centerIn: parent - horizontalAlignment: Text.AlignHCenter - font.pixelSize: Appearance.font.pixelSize.title - text: root.iconState === "error" ? "close" : (root.iconState === "success" ? "check" : "save") - rotation: root.iconState !== "normal" ? 360 : 0 - color: root.iconState === "error" ? "#ef5350" : (root.iconState === "success" ? Appearance.m3colors.m3primary : Appearance.m3colors.m3onSurface) - - Behavior on rotation { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - - Behavior on color { - animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) - } - } + onAccepted: { + root.applyLimit(); } } - Process { - id: fpsSetter + IconToolbarButton { + id: applyButton + text: root.iconState === "error" ? "close" : (root.iconState === "success" ? "check" : "save") + enabled: root.iconState === "normal" && fpsField.text.length > 0 + onClicked: { + root.applyLimit(); + } } } } From a91fe7db309920e927297b3d6c958cd1a79ca969 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 20:49:48 +0100 Subject: [PATCH 13/25] overlay: fps limiter: use enums for states --- .../overlay/fpsLimiter/FpsLimiterContent.qml | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml index 5857c5a21..a67bf40c1 100644 --- a/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml +++ b/dots/.config/quickshell/ii/modules/overlay/fpsLimiter/FpsLimiterContent.qml @@ -9,10 +9,13 @@ import qs.modules.common.widgets Rectangle { id: root - color: Appearance.m3colors.m3surfaceContainer - property real padding: 16 - property string iconState: "normal" + + enum State { Normal, Success, Error } + anchors.fill: parent + property real padding: 16 + property var currentState: FpsLimiterContent.State.Normal + color: Appearance.m3colors.m3surfaceContainer implicitWidth: content.implicitWidth + (padding * 2) implicitHeight: content.implicitHeight + (padding * 2) @@ -20,14 +23,14 @@ Rectangle { id: iconResetTimer interval: 1000 onTriggered: { - root.iconState = "normal"; + root.currentState = FpsLimiterContent.State.Normal; } } function applyLimit() { var fpsValue = parseInt(fpsField.text); if (isNaN(fpsValue) || fpsValue < 0) { - root.iconState = "error"; + root.currentState = FpsLimiterContent.State.Error; iconResetTimer.restart(); fpsField.text = ""; return; @@ -48,7 +51,7 @@ Rectangle { fpsSetter.command = ["bash", "-c", cmd]; fpsSetter.startDetached(); - root.iconState = "success"; + root.currentState = FpsLimiterContent.State.Success; iconResetTimer.restart(); // Clear the field after applying @@ -68,7 +71,7 @@ Rectangle { id: fpsField Layout.fillWidth: true Layout.preferredWidth: 200 - placeholderText: root.iconState === "error" ? Translation.tr("Insert a valid number") : Translation.tr("Set FPS limit (e.g. 80)") + placeholderText: root.currentState === FpsLimiterContent.State.Error ? Translation.tr("Enter a valid number") : Translation.tr("Set FPS limit") inputMethodHints: Qt.ImhDigitsOnly focus: true @@ -79,8 +82,13 @@ Rectangle { IconToolbarButton { id: applyButton - text: root.iconState === "error" ? "close" : (root.iconState === "success" ? "check" : "save") - enabled: root.iconState === "normal" && fpsField.text.length > 0 + text: switch (root.currentState) { + case FpsLimiterContent.State.Error: return "close"; + case FpsLimiterContent.State.Success: return "check"; + case FpsLimiterContent.State.Normal: + default: return "save"; + } + enabled: root.currentState === FpsLimiterContent.State.Normal && fpsField.text.length > 0 onClicked: { root.applyLimit(); } From 917fae6d4f92d71d004a3970cbb7d45ea42071b7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 20:54:23 +0100 Subject: [PATCH 14/25] overlay: fps limiter: adjust default position --- dots/.config/quickshell/ii/modules/common/Persistent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 3d75d11ef..33ea644dd 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -109,7 +109,7 @@ Singleton { property JsonObject fpsLimiter: JsonObject { property bool pinned: false property bool clickthrough: false - property real x: 1600 + property real x: 1576 property real y: 630 } } From 20b3d2498ed819d69f4561b0fc10ffb82d358171 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:05:59 +0100 Subject: [PATCH 15/25] overlay: add delay to focus grab --- .../quickshell/ii/modules/overlay/Overlay.qml | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml index 2fd33fcd7..5401e2f28 100644 --- a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml +++ b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml @@ -46,10 +46,24 @@ Scope { HyprlandFocusGrab { id: grab windows: [overlayWindow] - active: GlobalStates.overlayOpen + active: false onCleared: () => { - if (!active) - GlobalStates.overlayOpen = false; + if (!active) GlobalStates.overlayOpen = false; + } + } + + Connections { + target: GlobalStates + function onOverlayOpenChanged() { + delayedGrabTimer.start(); + } + } + + Timer { + id: delayedGrabTimer + interval: Appearance.animation.elementMoveFast.duration + onTriggered: { + grab.active = GlobalStates.overlayOpen; } } From 3fe8377309c41f0b9828d1edf8e4b74303cfe06c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:06:31 +0100 Subject: [PATCH 16/25] make timer restart instead of start --- dots/.config/quickshell/ii/modules/overlay/Overlay.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml index 5401e2f28..71ba510c6 100644 --- a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml +++ b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml @@ -55,7 +55,7 @@ Scope { Connections { target: GlobalStates function onOverlayOpenChanged() { - delayedGrabTimer.start(); + delayedGrabTimer.restart(); } } From a831d393c1e00bac8256d8c11bde60574859a577 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:08:30 +0100 Subject: [PATCH 17/25] fix some undefined warnings --- .../modules/sidebarRight/volumeMixer/VolumeMixerEntry.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixerEntry.qml b/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixerEntry.qml index 503f83af0..a00335fb0 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixerEntry.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeMixerEntry.qml @@ -28,10 +28,10 @@ Item { sourceSize.height: size source: { let icon; - icon = AppSearch.guessIcon(root.node.properties["application.icon-name"]); + icon = AppSearch.guessIcon(root.node?.properties["application.icon-name"] ?? ""); if (AppSearch.iconExists(icon)) return Quickshell.iconPath(icon, "image-missing"); - icon = AppSearch.guessIcon(root.node.properties["node.name"]); + icon = AppSearch.guessIcon(root.node?.properties["node.name"] ?? ""); return Quickshell.iconPath(icon, "image-missing"); } } @@ -47,7 +47,7 @@ Item { elide: Text.ElideRight text: { // application.name -> description -> name - const app = root.node.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name); + const app = root.node?.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name); const media = root.node.properties["media.name"]; return media != undefined ? `${app} • ${media}` : app; } @@ -55,7 +55,7 @@ Item { StyledSlider { id: slider - value: root.node.audio.volume + value: root.node?.audio.volume ?? 0 onMoved: root.node.audio.volume = value configuration: StyledSlider.Configuration.S } From 87181585aa92a5b31794b192368a2134cfa29487 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:09:39 +0100 Subject: [PATCH 18/25] overlay: add config option for clickthrough widget opacity --- dots/.config/quickshell/ii/modules/common/Config.qml | 1 + .../quickshell/ii/modules/overlay/StyledOverlayWidget.qml | 1 + 2 files changed, 2 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index d064d2533..363c4c906 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -380,6 +380,7 @@ Singleton { property JsonObject overlay: JsonObject { property bool openingZoomAnimation: true property bool darkenScreen: true + property real clickthroughOpacity: 0.7 } property JsonObject overview: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml index e20eb417b..013885cf4 100644 --- a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -44,6 +44,7 @@ AbstractOverlayWidget { maximumX: root.parent.width - root.width maximumY: root.parent.height - root.height } + opacity: (GlobalStates.overlayOpen || !clickthrough) ? 1.0 : Config.options.overlay.clickthroughOpacity // Guarded states & registration funcs property bool open: Persistent.states.overlay.open From 1cc04e118ffb852c81a2f9bac5889c89540ab68a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:10:21 +0100 Subject: [PATCH 19/25] overlay: hide some unnecessary buttons --- .../quickshell/ii/modules/overlay/StyledOverlayWidget.qml | 6 +++++- .../quickshell/ii/modules/overlay/crosshair/Crosshair.qml | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml index 013885cf4..1f60cf9a4 100644 --- a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -22,6 +22,8 @@ AbstractOverlayWidget { required property Item contentItem property bool fancyBorders: true + property bool showCenterButton: false + property bool showClickabilityButton: true required property var modelData readonly property string identifier: modelData.identifier @@ -144,7 +146,7 @@ AbstractOverlayWidget { leftMargin: titleBar.padding + 8 bottomMargin: root.fancyBorders ? 0 : titleBar.padding } - spacing: 0 + spacing: 2 MaterialSymbol { text: root.materialSymbol @@ -160,6 +162,7 @@ AbstractOverlayWidget { } TitlebarButton { + visible: root.showCenterButton materialSymbol: "recenter" onClicked: root.center() StyledToolTip { @@ -168,6 +171,7 @@ AbstractOverlayWidget { } TitlebarButton { + visible: (root.pinned && root.showClickabilityButton) materialSymbol: "mouse" toggled: !root.clickthrough onClicked: root.toggleClickthrough() diff --git a/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml b/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml index bb60bd1fd..4a92aa43e 100644 --- a/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml +++ b/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml @@ -7,6 +7,11 @@ import qs.modules.overlay StyledOverlayWidget { id: root fancyBorders: false // Crosshair should be see-through + showCenterButton: true + opacity: 1 // The crosshair itself already has transparency if configured + showClickabilityButton: false + clickthrough: true + contentItem: CrosshairContent { anchors.centerIn: parent } From 26f2a9f3fdc4f0c82afe98bb537ec23323ea858b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:11:11 +0100 Subject: [PATCH 20/25] fix more null warnings --- .../quickshell/ii/modules/overlay/StyledOverlayWidget.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml index 1f60cf9a4..a8b6fdbaf 100644 --- a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -43,8 +43,8 @@ AbstractOverlayWidget { drag { minimumX: 0 minimumY: 0 - maximumX: root.parent.width - root.width - maximumY: root.parent.height - root.height + maximumX: root.parent?.width - root.width + maximumY: root.parent?.height - root.height } opacity: (GlobalStates.overlayOpen || !clickthrough) ? 1.0 : Config.options.overlay.clickthroughOpacity From 1d07260fd0e5bdc5e76f5953d820211b5ea44232 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:24:37 +0100 Subject: [PATCH 21/25] add kb focus border for group buttons --- .../quickshell/ii/modules/common/widgets/GroupButton.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml b/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml index de9ce5603..080439613 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/GroupButton.qml @@ -117,7 +117,7 @@ Button { }; } - + property bool tabbedTo: root.focus && (focusReason === Qt.TabFocusReason || focusReason === Qt.BacktabFocusReason) background: Rectangle { id: buttonBackground topLeftRadius: root.leftRadius @@ -130,6 +130,9 @@ Button { Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } + + border.width: root.tabbedTo ? 2 : 0 + border.color: Appearance.colors.colSecondary } contentItem: StyledText { From 758d40fc8b23cb6dd7ac469b39ef63cd503a7e48 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:33:54 +0100 Subject: [PATCH 22/25] overlay: adjust volume mixer padding --- .../modules/overlay/StyledOverlayWidget.qml | 1 - .../overlay/volumeMixer/VolumeMixer.qml | 26 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml index a8b6fdbaf..bfc029deb 100644 --- a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -143,7 +143,6 @@ AbstractOverlayWidget { anchors { fill: parent margins: titleBar.padding - leftMargin: titleBar.padding + 8 bottomMargin: root.fancyBorders ? 0 : titleBar.padding } spacing: 2 diff --git a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml index 68c572579..d08b22a8d 100644 --- a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml +++ b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml @@ -14,7 +14,7 @@ StyledOverlayWidget { anchors.centerIn: parent color: Appearance.m3colors.m3surfaceContainer radius: root.contentRadius - property real padding: 16 + property real padding: 6 implicitHeight: 600 implicitWidth: 350 @@ -53,8 +53,28 @@ StyledOverlayWidget { } clip: true - VolumeDialogContent { isSink: true } - VolumeDialogContent { isSink: false } + PaddedVolumeDialogContent { + isSink: true + } + PaddedVolumeDialogContent { + isSink: false + } + } + } + } + + component PaddedVolumeDialogContent: Item { + id: paddedVolumeDialogContent + property alias isSink: volDialogContent.isSink + property real padding: 12 + implicitWidth: volDialogContent.implicitWidth + padding * 2 + implicitHeight: volDialogContent.implicitHeight + padding * 2 + + VolumeDialogContent { + id: volDialogContent + anchors { + fill: parent + margins: paddedVolumeDialogContent.padding } } } From ae52e28afb71a3e532ca62b823330b7d9324d140 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:35:50 +0100 Subject: [PATCH 23/25] overlay: adjust default crosshair position --- dots/.config/quickshell/ii/modules/common/Persistent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 2969919c8..c5e4395d4 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -85,7 +85,7 @@ Singleton { property bool pinned: false property bool clickthrough: true property real x: 835 - property real y: 490 + property real y: 482 } property JsonObject recorder: JsonObject { property bool pinned: false From 229c9d5e78560118cfeeea7db942d1286ce5d199 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:50:13 +0100 Subject: [PATCH 24/25] overlay: adjust titlebar spacing --- .../quickshell/ii/modules/common/Persistent.qml | 2 +- .../ii/modules/overlay/StyledOverlayWidget.qml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index c5e4395d4..5848b1683 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -85,7 +85,7 @@ Singleton { property bool pinned: false property bool clickthrough: true property real x: 835 - property real y: 482 + property real y: 483 } property JsonObject recorder: JsonObject { property bool pinned: false diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml index bfc029deb..f6c21199b 100644 --- a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -89,7 +89,7 @@ AbstractOverlayWidget { function center() { const targetX = (root.parent.width - contentColumn.width) / 2 - const targetY = (root.parent.height - contentItem.height) / 2 - titleBar.implicitHeight + const targetY = (root.parent.height - contentItem.height) / 2 - titleBar.implicitHeight + border.border.width root.x = targetX root.y = targetY root.savePosition(targetX, targetY) @@ -131,9 +131,8 @@ AbstractOverlayWidget { id: titleBar opacity: GlobalStates.overlayOpen ? 1 : 0 Layout.fillWidth: true - property real padding: 6 - implicitWidth: titleBarRow.implicitWidth + padding * 2 - implicitHeight: titleBarRow.implicitHeight + padding * 2 + implicitWidth: titleBarRow.implicitWidth + root.padding * 2 + implicitHeight: titleBarRow.implicitHeight + root.padding * 2 color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1 // border.color: Appearance.colors.colOutlineVariant // border.width: 1 @@ -142,13 +141,13 @@ AbstractOverlayWidget { id: titleBarRow anchors { fill: parent - margins: titleBar.padding - bottomMargin: root.fancyBorders ? 0 : titleBar.padding + margins: root.padding } spacing: 2 MaterialSymbol { text: root.materialSymbol + Layout.leftMargin: 6 iconSize: 20 Layout.alignment: Qt.AlignVCenter Layout.rightMargin: 4 @@ -204,6 +203,7 @@ AbstractOverlayWidget { Layout.fillWidth: true Layout.fillHeight: true Layout.margins: root.fancyBorders ? root.padding : 0 + Layout.topMargin: -border.border.width // Border of a rectangle is drawn inside its bounds, so we do this to make the gap not too big Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter implicitWidth: root.contentItem.implicitWidth implicitHeight: root.contentItem.implicitHeight From 994985ecaef23790f53c1d5c17d5d3a06a2b7731 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 7 Nov 2025 22:13:56 +0100 Subject: [PATCH 25/25] overlay: recorder: make open folder button work with custom path --- .../.config/quickshell/ii/modules/overlay/recorder/Recorder.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml b/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml index e853819b4..07e7c8d9f 100644 --- a/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml +++ b/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml @@ -76,7 +76,7 @@ StyledOverlayWidget { colRipple: Appearance.colors.colLayer3Active onClicked: { GlobalStates.overlayOpen = false; - Qt.openUrlExternally(Directories.videos); + Qt.openUrlExternally(`file://${Config.options.screenRecord.savePath}`); } contentItem: Row { anchors.centerIn: parent