Add hasCursor + hovered surface to four kit primitives
This commit is contained in:
@@ -31,7 +31,23 @@ Item {
|
||||
property int popupRowHeight: 28
|
||||
property bool showLabel: true
|
||||
|
||||
// Panel-cursor flag. When true, the trigger renders the same focus ring
|
||||
// as Tab-focus so a panel's keyboard cursor lands here identically.
|
||||
// Emits `hovered(bool)` on pointer enter/leave so the panel can keep
|
||||
// its cursor state in sync with the mouse.
|
||||
property bool hasCursor: false
|
||||
|
||||
// popupOpen + open()/close()/toggle() let a parent panel know when the
|
||||
// dropdown owns keys (its embedded ListView is active) and suspend its
|
||||
// own keyCatcher so j/k inside the popup don't double-drive the panel
|
||||
// cursor.
|
||||
readonly property bool popupOpen: popup.opened
|
||||
function open() { popup.open() }
|
||||
function close() { popup.close() }
|
||||
function toggle() { popup.opened ? popup.close() : popup.open() }
|
||||
|
||||
signal changed(string value)
|
||||
signal hovered(bool isHovered)
|
||||
|
||||
function optionValue(o) {
|
||||
return (o && typeof o === "object") ? String(o.value) : String(o)
|
||||
@@ -67,15 +83,22 @@ Item {
|
||||
width: parent.width
|
||||
height: root.rowHeight
|
||||
radius: Style.cornerRadius
|
||||
|
||||
readonly property bool _focused: trigger.activeFocus || root.hasCursor
|
||||
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b,
|
||||
trigger.activeFocus ? 0.08 : 0.04)
|
||||
border.color: trigger.activeFocus
|
||||
trigger._focused ? 0.08 : 0.04)
|
||||
border.color: trigger._focused
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.4)
|
||||
border.width: trigger.activeFocus ? Style.focusBorderWidth : 1
|
||||
border.width: trigger._focused ? Style.focusBorderWidth : 1
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: root.hovered(hovered)
|
||||
}
|
||||
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter
|
||||
|| event.key === Qt.Key_Space || event.key === Qt.Key_Down) {
|
||||
|
||||
@@ -18,6 +18,12 @@ import qs.Commons
|
||||
// in form contexts (the bar settings widget cards) where Tab walks a list
|
||||
// of controls; leave it false for the right-edge actions on panel rows
|
||||
// where the row's CursorSurface owns the keyboard cursor.
|
||||
//
|
||||
// Set `hasCursor: true` to have the button render the same fill as a
|
||||
// mouse hover — so a panel's keyboard cursor lands on it identically.
|
||||
// Use this when a PanelActionButton is itself the cursor target (rather
|
||||
// than living inside a CursorSurface row). Emits `hovered(bool)` on
|
||||
// pointer enter/leave so the panel can update its cursor state to match.
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
@@ -31,8 +37,10 @@ Rectangle {
|
||||
property real size: 22
|
||||
|
||||
property bool focusable: false
|
||||
property bool hasCursor: false
|
||||
|
||||
signal clicked()
|
||||
signal hovered(bool isHovered)
|
||||
|
||||
activeFocusOnTab: focusable
|
||||
Keys.onReturnPressed: if (focusable) root.clicked()
|
||||
@@ -44,10 +52,11 @@ Rectangle {
|
||||
radius: Style.cornerRadius
|
||||
|
||||
readonly property bool _showFocusRing: focusable && activeFocus
|
||||
readonly property bool _hot: (mouse.containsMouse || root.hasCursor) && root.enabled
|
||||
|
||||
color: _showFocusRing
|
||||
? Style.focusFillColor
|
||||
: (mouse.containsMouse && root.enabled
|
||||
: (_hot
|
||||
? Qt.rgba(hoverColor.r, hoverColor.g, hoverColor.b, 0.20)
|
||||
: "transparent")
|
||||
border.width: _showFocusRing ? Style.focusBorderWidth : 0
|
||||
@@ -59,7 +68,7 @@ Rectangle {
|
||||
anchors.centerIn: parent
|
||||
text: root.iconText
|
||||
color: root.enabled
|
||||
? (mouse.containsMouse ? root.hoverColor : Qt.darker(root.foreground, 1.3))
|
||||
? (root._hot ? root.hoverColor : Qt.darker(root.foreground, 1.3))
|
||||
: Qt.darker(root.foreground, 2.0)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: root.fontSize
|
||||
@@ -71,6 +80,7 @@ Rectangle {
|
||||
hoverEnabled: true
|
||||
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
enabled: root.enabled
|
||||
onContainsMouseChanged: root.hovered(containsMouse)
|
||||
onClicked: {
|
||||
if (root.focusable) root.forceActiveFocus()
|
||||
root.clicked()
|
||||
|
||||
@@ -57,10 +57,10 @@ Grouped by what they're for, not alphabetically.
|
||||
| `CursorPill` | `PillButton` that participates in a panel's single-cursor model. Adds a `hovered(bool)` signal so the panel can update its cursor state on mouse enter/leave. Use for DNS-pill / header-pill / segmented-choice patterns. |
|
||||
| `ChoiceButton` | A single button in a mutually-exclusive choice group (segmented control). `selected` uses accent fill+border; focus uses `Style.focusBorderColor` so keyboard nav reads differently from selection. |
|
||||
| `Toggle` | Title + description + switch. Click anywhere on the row to flip; caller updates `checked` in response. `rounded` auto-detects from `Style.cornerRadius` so the switch is a pill on round-corners themes and square on sharp; override per-instance to force one or the other. |
|
||||
| `TextField` | Single-line input. Inherits from Qt Quick Controls `TextField` so all of its base API (text, placeholderText, accepted, validator, ...) is available. Adds `password: bool`, `foreground` / `accent` / `selectionTint` color overrides, and `horizontalPadding` / `verticalPadding` size knobs. Focus styling uses `Style.focusBorderColor` to match `Toggle` and `ChoiceButton`. |
|
||||
| `Dropdown` | Single-select dropdown with a themed popup (no platform-native ComboBox chrome). `options` accepts `string[]` or `[{ value, label }]`. Keyboard: Tab to focus trigger, Enter/Space opens, j/k or arrows walk options, Enter selects. |
|
||||
| `SearchableDropdown` | `Dropdown` with an embedded search field at the top of the popup that filters options as you type. Use when the option count is high enough that scanning is friction (e.g. bar settings "+ Add widget"). Options can also carry a `description` string that the filter matches against. |
|
||||
| `PanelActionButton` | 22×22 right-edge action button (confirm, forget, unpair). `hoverColor` swaps between default foreground tint and urgent (red) tint. `focusable: true` enables Tab-focus with an accent ring — used for the bar settings widget-card row controls. |
|
||||
| `TextField` | Single-line input. Inherits from Qt Quick Controls `TextField` so all of its base API (text, placeholderText, accepted, validator, ...) is available. Adds `password: bool`, `foreground` / `accent` / `selectionTint` color overrides, and `horizontalPadding` / `verticalPadding` size knobs. Focus styling uses `Style.focusBorderColor` to match `Toggle` and `ChoiceButton`. `hasCursor` paints the same focus ring so a panel cursor lands on the field identically; emits `hovered(bool)`. |
|
||||
| `Dropdown` | Single-select dropdown with a themed popup (no platform-native ComboBox chrome). `options` accepts `string[]` or `[{ value, label }]`. Keyboard: Tab to focus trigger, Enter/Space opens, j/k or arrows walk options, Enter selects. `hasCursor` paints the focus ring on the trigger; emits `hovered(bool)`. `popupOpen` plus `open()` / `close()` / `toggle()` let a parent panel suspend its own key catcher while the popup owns keys. |
|
||||
| `SearchableDropdown` | `Dropdown` with an embedded search field at the top of the popup that filters options as you type. Use when the option count is high enough that scanning is friction (e.g. bar settings "+ Add widget"). Options can also carry a `description` string that the filter matches against. Same `hasCursor` / `popupOpen` / `open()` / `close()` / `toggle()` / `hovered(bool)` surface as `Dropdown`. |
|
||||
| `PanelActionButton` | 22×22 right-edge action button (confirm, forget, unpair). `hoverColor` swaps between default foreground tint and urgent (red) tint. `focusable: true` enables Tab-focus with an accent ring — used for the bar settings widget-card row controls. `hasCursor: true` paints the hover fill so a panel cursor can land on the button directly when it isn't living inside a `CursorSurface` row; emits `hovered(bool)`. |
|
||||
| `PanelSlider` | Volume/progress slider. Drag, click track, or wheel. `moved(value)` fires per change, `released(value)` once at end. (Named to avoid colliding with `QtQuick.Controls.Slider`.) |
|
||||
| `WidgetButton` | Bar widget chrome — for the strip itself, not for inside panels. |
|
||||
|
||||
@@ -87,6 +87,15 @@ in this kit default-bind to `Color.foreground` / `Color.accent` /
|
||||
|
||||
## Conventions
|
||||
|
||||
- Every interactive primitive exposes `hasCursor: bool` and emits
|
||||
`hovered(bool)` so a parent panel can wire it into the same
|
||||
cursor-model recipe used by the wifi / audio / bluetooth / monitor
|
||||
panels: the panel root owns `focusSection` + `selectedIndex`, each
|
||||
element binds `hasCursor: root.focusSection === "X" && root.selectedIndex === N`,
|
||||
and `onHovered` updates the same root state on pointer enter/leave.
|
||||
Popups (`Dropdown`, `SearchableDropdown`) also expose `popupOpen` so
|
||||
the panel's `PanelKeyCatcher` can be `blocked` while the popup owns
|
||||
keyboard input.
|
||||
- Components are stateless about the values they display. They emit
|
||||
signals and let the caller mutate. Don't bake panel-specific state
|
||||
machines into kit components.
|
||||
|
||||
@@ -34,7 +34,23 @@ Item {
|
||||
property int popupMinHeight: 220
|
||||
property bool showLabel: true
|
||||
|
||||
// Panel-cursor flag. When true, the trigger renders the same focus ring
|
||||
// as Tab-focus so a panel's keyboard cursor lands here identically.
|
||||
// Emits `hovered(bool)` on pointer enter/leave so the panel can keep
|
||||
// its cursor state in sync with the mouse.
|
||||
property bool hasCursor: false
|
||||
|
||||
// popupOpen + open()/close()/toggle() let a parent panel know when the
|
||||
// dropdown owns keys (search field + result list are active) and
|
||||
// suspend its own keyCatcher so typing into the filter doesn't drive
|
||||
// the panel cursor.
|
||||
readonly property bool popupOpen: popup.opened
|
||||
function open() { popup.open() }
|
||||
function close() { popup.close() }
|
||||
function toggle() { popup.opened ? popup.close() : popup.open() }
|
||||
|
||||
signal changed(string value)
|
||||
signal hovered(bool isHovered)
|
||||
|
||||
function optionValue(o) {
|
||||
return (o && typeof o === "object") ? String(o.value) : String(o)
|
||||
@@ -88,15 +104,22 @@ Item {
|
||||
width: parent.width
|
||||
height: root.rowHeight
|
||||
radius: Style.cornerRadius
|
||||
|
||||
readonly property bool _focused: trigger.activeFocus || root.hasCursor
|
||||
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b,
|
||||
trigger.activeFocus ? 0.08 : 0.04)
|
||||
border.color: trigger.activeFocus
|
||||
trigger._focused ? 0.08 : 0.04)
|
||||
border.color: trigger._focused
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.4)
|
||||
border.width: trigger.activeFocus ? Style.focusBorderWidth : 1
|
||||
border.width: trigger._focused ? Style.focusBorderWidth : 1
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: root.hovered(hovered)
|
||||
}
|
||||
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter
|
||||
|| event.key === Qt.Key_Space || event.key === Qt.Key_Down) {
|
||||
|
||||
@@ -26,6 +26,17 @@ TextField {
|
||||
property real horizontalPadding: 10
|
||||
property real verticalPadding: 7
|
||||
|
||||
// Panel-cursor flag. When true (and the field isn't already focused),
|
||||
// the background paints the same accent ring as activeFocus so the
|
||||
// panel's keyboard cursor lands here identically to a mouse hover.
|
||||
// Emits `hovered(bool)` on pointer enter/leave so the panel can update
|
||||
// its cursor state to match.
|
||||
property bool hasCursor: false
|
||||
|
||||
signal hovered(bool isHovered)
|
||||
|
||||
readonly property bool _focused: activeFocus || hasCursor
|
||||
|
||||
echoMode: password ? TextInput.Password : TextInput.Normal
|
||||
color: foreground
|
||||
selectionColor: selectionTint
|
||||
@@ -39,11 +50,15 @@ TextField {
|
||||
|
||||
background: Rectangle {
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b,
|
||||
root.activeFocus ? 0.08 : 0.04)
|
||||
border.color: root.activeFocus
|
||||
root._focused ? 0.08 : 0.04)
|
||||
border.color: root._focused
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
|
||||
border.width: root.activeFocus ? Style.focusBorderWidth : 1
|
||||
border.width: root._focused ? Style.focusBorderWidth : 1
|
||||
radius: Style.cornerRadius
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: root.hovered(hovered)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user