From 42dee4d3994de7517f4d27c943a9c41f7c6fb9e4 Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Mon, 18 May 2026 11:52:23 -0400 Subject: [PATCH] Standardize cursor chrome to fill-only across qs.Ui CursorSurface defines the canonical cursor chrome: a tinted fill, no border. Three components had inconsistent treatments that the recent standardization pass left half-finished: - PillButton kept a 1px foreground border on hot (cursor or hover) via a cursorBordered: true default. - CursorPill specifically had to override cursorBordered: false to match the rest of the kit. - ChoiceButton thickened its border to 2px on hasCursor, which mixed the cursor visual with the Tab-focus visual. Drop the cursorBordered escape hatch from PillButton (no caller other than CursorPill ever used it), drop CursorPill's now-redundant override, and remove hasCursor from ChoiceButton's border.width thickening. Update Toggle's class comment (it still claimed cursor mirrored Tab focus, which f3ba5055 broke without updating the doc). Now: hot/hasCursor = fill only, activeFocus = accent border ring, selected/active = stronger fill or accent border per component semantic. One canonical pattern across the kit. --- shell/Ui/ChoiceButton.qml | 7 ++++--- shell/Ui/CursorPill.qml | 2 -- shell/Ui/PillButton.qml | 9 +++++---- shell/Ui/Toggle.qml | 11 ++++++----- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/shell/Ui/ChoiceButton.qml b/shell/Ui/ChoiceButton.qml index 611f1b2e..ac8005c2 100644 --- a/shell/Ui/ChoiceButton.qml +++ b/shell/Ui/ChoiceButton.qml @@ -18,8 +18,9 @@ Rectangle { // Panel-cursor flag. Same role as PillButton.hasCursor: panels that own // their own cursor state bind this to drive the keyboard highlight - // separately from real activeFocus. Visuals match the activeFocus look - // (foreground 2px border) so cursor and Tab focus read the same. + // separately from real activeFocus. Cursor renders as a fill only — + // CursorSurface is the canonical chrome — while Tab focus adds the + // accent border ring on top. property bool hasCursor: false property bool borderlessHighlight: false @@ -48,7 +49,7 @@ Rectangle { ? accent : (activeFocus ? foreground : Qt.rgba(foreground.r, foreground.g, foreground.b, 0.4)) border.width: borderlessHighlight ? (activeFocus ? 2 : 0) - : (selected ? 2 : (activeFocus || hasCursor ? 2 : 1)) + : (selected ? 2 : (activeFocus ? 2 : 1)) Behavior on color { ColorAnimation { duration: 100 } } diff --git a/shell/Ui/CursorPill.qml b/shell/Ui/CursorPill.qml index fcb877df..da299455 100644 --- a/shell/Ui/CursorPill.qml +++ b/shell/Ui/CursorPill.qml @@ -20,8 +20,6 @@ import QtQuick PillButton { id: root - cursorBordered: false - signal hovered(bool isHovered) HoverHandler { diff --git a/shell/Ui/PillButton.qml b/shell/Ui/PillButton.qml index e85a220f..3812b867 100644 --- a/shell/Ui/PillButton.qml +++ b/shell/Ui/PillButton.qml @@ -38,10 +38,12 @@ Rectangle { // Persistent 1px foreground border at idle. Use for "primary" form buttons // (Save, Apply, + Add widget) so they read as buttons before the cursor - // hits them. The hot/cursor state paints its own 1px border, so changing - // this doesn't affect panel-pill visuals. + // hits them. + // + // The hot/cursor state is a fill only, matching CursorSurface's canonical + // chrome and the other panel primitives (Toggle, PanelActionButton). The + // accent border ring is reserved for Tab focus on `focusable` buttons. property bool bordered: false - property bool cursorBordered: true activeFocusOnTab: focusable Keys.onReturnPressed: if (focusable) root.clicked() @@ -95,7 +97,6 @@ Rectangle { : hot ? hoverBackground : (active ? activeBackground : background) border.width: _showFocusRing ? Style.focusBorderWidth - : hot ? (cursorBordered ? 1 : 0) : (bordered ? 1 : 0) border.color: _showFocusRing ? Style.focusBorderColor : foreground diff --git a/shell/Ui/Toggle.qml b/shell/Ui/Toggle.qml index 76a17cd7..81f9703a 100644 --- a/shell/Ui/Toggle.qml +++ b/shell/Ui/Toggle.qml @@ -6,9 +6,10 @@ import qs.Commons // flip `checked` in response (the component is stateless about the actual // value so it composes cleanly with model-driven UI). // -// Focus styling follows the shared Style tokens (accent border + tinted -// fill on activeFocus) so keyboard nav looks the same here as on -// ChoiceButton and other focusable Ui components. +// Cursor and focus styling match the rest of the kit: hasCursor (panel +// keyboard cursor / mouse hover) is a fill only, mirroring CursorSurface; +// activeFocus (Tab focus) adds the accent border ring on top via the +// shared Style tokens. // // `rounded` auto-detects from Style.cornerRadius so the switch follows // the theme: pill shape on round-corners themes, square on sharp. @@ -22,8 +23,8 @@ Rectangle { // Panel-cursor flag. Same role as PillButton.hasCursor / ChoiceButton.hasCursor: // panels with their own keyboard cursor bind this to drive the highlight - // separately from activeFocus. Visuals match the activeFocus look (accent - // border + tinted fill via Style tokens) so cursor and Tab focus read the same. + // separately from activeFocus. Renders as a tinted fill only — the accent + // border ring is reserved for Tab focus (activeFocus). property bool hasCursor: false // Switch shape follows the theme by default: pill on round, square on sharp.