From 8b858d4093a832db6818b386ee906a69130aaaf7 Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Mon, 18 May 2026 12:19:23 -0400 Subject: [PATCH] Add outline cursor mode to CursorSurface for slider rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wide content rows (slider rows in audio / monitor panels) opted out of the cursor fill by setting fill: "transparent" on their CursorSurface, which left them with no cursor visual at all — j/k landed there silently with no way to know what was selected. Add an `outline: bool` flag to CursorSurface. When true, hasCursor paints an accent border (Style.focusBorderColor at Style.focusBorderWidth) instead of a fill, leaving the row's chrome fully visible underneath. Use for slider rows where a fill would obscure the track. Audio output / input slider rows and the monitor brightness row opt in. The dev gallery's slider section replaces its bespoke Rectangle wrapper with the same CursorSurface { outline: true }, so what the gallery documents is what the panels ship. --- shell/Ui/CursorSurface.qml | 24 ++++++++++++++++++---- shell/plugins/bar/widgets/audioPanel.qml | 4 ++-- shell/plugins/bar/widgets/monitorPanel.qml | 2 +- shell/plugins/dev-gallery/GalleryPanel.qml | 14 +++++-------- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/shell/Ui/CursorSurface.qml b/shell/Ui/CursorSurface.qml index a2ac5c0e..77e66f2b 100644 --- a/shell/Ui/CursorSurface.qml +++ b/shell/Ui/CursorSurface.qml @@ -6,20 +6,36 @@ import qs.Commons // hover updates the panel's cursor state at the root; visuals derive from // `hasCursor` / `current`. That's what guarantees a single highlight on // screen at any time across both keyboard and mouse interaction. +// +// Two cursor visuals are supported: +// +// default (outline: false) — paint a tinted fill across the row when +// hasCursor is true. Use for narrow text rows (wifi networks, audio +// devices, menu items) where fill reads cleanly. +// +// outline: true — paint an accent border instead of a fill. Use for +// wide content rows where a fill would obscure the row's chrome +// (slider rows in audio / monitor panels). The fill / currentFill +// props are ignored in this mode. Rectangle { id: root property bool hasCursor: false property bool current: false + property bool outline: false - property color foreground: "#cacccc" + property color foreground: Color.foreground property color fill: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.08) property color currentFill: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.18) radius: Style.cornerRadius - color: hasCursor ? fill : (current ? currentFill : "transparent") - border.width: 0 - border.color: foreground + + color: root.outline + ? "transparent" + : (hasCursor ? fill : (current ? currentFill : "transparent")) + + border.color: root.outline && hasCursor ? Style.focusBorderColor : foreground + border.width: root.outline && hasCursor ? Style.focusBorderWidth : 0 Behavior on color { ColorAnimation { duration: 60 } diff --git a/shell/plugins/bar/widgets/audioPanel.qml b/shell/plugins/bar/widgets/audioPanel.qml index 61893aae..45cb7920 100644 --- a/shell/plugins/bar/widgets/audioPanel.qml +++ b/shell/plugins/bar/widgets/audioPanel.qml @@ -454,7 +454,7 @@ Item { hasCursor: root.focusSection === "output" && root.selectedIndex === -1 onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(outputSliderRow) foreground: root.bar.foreground - fill: "transparent" + outline: true Row { id: outputSliderInner @@ -571,7 +571,7 @@ Item { hasCursor: root.focusSection === "input" && root.selectedIndex === -1 onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(inputSliderRow) foreground: root.bar.foreground - fill: "transparent" + outline: true Row { id: inputSliderInner diff --git a/shell/plugins/bar/widgets/monitorPanel.qml b/shell/plugins/bar/widgets/monitorPanel.qml index 7e4ff853..a72b7705 100644 --- a/shell/plugins/bar/widgets/monitorPanel.qml +++ b/shell/plugins/bar/widgets/monitorPanel.qml @@ -415,7 +415,7 @@ Item { hasCursor: root.focusSection === "brightness" && root.selectedIndex === -1 onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(brightnessRow) foreground: root.bar.foreground - fill: "transparent" + outline: true Row { id: brightnessInner diff --git a/shell/plugins/dev-gallery/GalleryPanel.qml b/shell/plugins/dev-gallery/GalleryPanel.qml index 55710816..0f977381 100644 --- a/shell/plugins/dev-gallery/GalleryPanel.qml +++ b/shell/plugins/dev-gallery/GalleryPanel.qml @@ -1121,18 +1121,14 @@ Item { wrapMode: Text.WordWrap } - Rectangle { + CursorSurface { id: sliderWrapper width: parent.width implicitHeight: sliderRow.implicitHeight + 24 - readonly property bool focused: root.focusSection === "slider" - color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04) - radius: Style.cornerRadius - border.color: focused - ? Style.focusBorderColor - : Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10) - border.width: focused ? Style.focusBorderWidth : 1 - onFocusedChanged: if (focused) root.ensureCursorVisible(this) + outline: true + foreground: root.foreground + hasCursor: root.focusSection === "slider" + onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(this) HoverHandler { onHoveredChanged: if (hovered) {