Extract CursorPill, consolidate panel-cursor pill bases

This commit is contained in:
Ryan Hughes
2026-05-17 23:16:56 -04:00
parent b751109c6b
commit 3e9104de60
4 changed files with 54 additions and 57 deletions
@@ -0,0 +1,28 @@
import QtQuick
// PillButton that participates in a panel's single-cursor model. Use
// inside a row of pills (DNS providers, bluetooth header actions, choice
// chips) where mouse hover and keyboard cursor should land in the same
// place.
//
// Caller binds `hasCursor` to the panel's cursor state and listens to
// `hovered(bool)` to update that state when the mouse enters or leaves.
// This is structurally a wrapper around PillButton with one extra
// HoverHandler — but extracting it lets every panel use the same wiring
// idiom and lets plugin authors drop into the same cursor model without
// touching internals.
//
// Why HoverHandler instead of a MouseArea overlay: HoverHandler doesn't
// steal pointer events from PillButton's internal click MouseArea, so
// clicks still reach the underlying button. An overlay MouseArea with
// acceptedButtons: Qt.NoButton works but is fragile around tooltip
// timing and event propagation.
PillButton {
id: root
signal hovered(bool isHovered)
HoverHandler {
onHoveredChanged: root.hovered(hovered)
}
}
@@ -1,6 +1,7 @@
module qs.Ui
ChoiceButton 1.0 ChoiceButton.qml
CursorPill 1.0 CursorPill.qml
CursorSurface 1.0 CursorSurface.qml
KeyboardPanel 1.0 KeyboardPanel.qml
PanelActionButton 1.0 PanelActionButton.qml
@@ -465,11 +465,11 @@ Item {
}
}
// Header pill — wraps PillButton with cursor-state binding so it
// participates in the same single-cursor model as device rows. We don't
// use CursorSurface here because the pill already provides its
// own active/hover visuals and we want them to layer correctly.
component HeaderPill: PillButton {
// Header pill: a CursorPill bound into the panel's "header" cursor
// section. CursorPill collapses what used to be a PillButton subclass +
// overlay MouseArea into one component; we keep the pillIndex / activated
// shim here so the three header pill instantiations stay readable.
component HeaderPill: CursorPill {
id: pill
required property int pillIndex
property bool pillEnabled: true
@@ -485,23 +485,13 @@ Item {
enabled: pillEnabled
opacity: pillEnabled ? 1 : 0.4
// Hand off to PillButton's built-in cursor visuals so keyboard cursor and
// mouse hover render identically (fill + 1px border).
hasCursor: root.focusSection === "header" && root.selectedIndex === pillIndex
onClicked: pill.activated()
// Mouse hover updates root cursor state so keyboard + mouse share one
// selection model.
MouseArea {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
propagateComposedEvents: true
onContainsMouseChanged: if (containsMouse) {
root.focusSection = "header"
root.selectedIndex = pill.pillIndex
}
onHovered: function(isHovered) {
if (!isHovered) return
root.focusSection = "header"
root.selectedIndex = pill.pillIndex
}
}
@@ -862,51 +862,29 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
// One DNS provider pill. The cursor + current visuals come entirely from
// CursorSurface; this component just binds them to the panel's cursor
// state and renders the label/tooltip/click target.
component DnsProviderPill: CursorSurface {
component DnsProviderPill: CursorPill {
id: pill
required property string provider
required property int index
property string tooltipText: ""
signal clicked()
hasCursor: root.focusSection === "dns" && root.dnsIndex === index
current: root.dnsProvider === provider
text: provider
foreground: root.bar.foreground
fill: root.activeFill
tooltipBackground: root.bar.background
tooltipForeground: root.bar.foreground
fontFamily: root.bar.fontFamily
horizontalPadding: 10
verticalPadding: 6
implicitWidth: pillLabel.implicitWidth + 20
implicitHeight: pillLabel.implicitHeight + 12
// Map the panel's domain semantics onto CursorPill's structural props:
// `current DNS` is the pill's `active` fill; the keyboard cursor lights
// up `hasCursor`.
active: root.dnsProvider === provider
hasCursor: root.focusSection === "dns" && root.dnsIndex === index
Text {
id: pillLabel
anchors.centerIn: parent
text: pill.provider
color: root.bar.foreground
font.family: root.bar.fontFamily
font.pixelSize: 12
}
MouseArea {
id: pillMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: if (containsMouse) {
root.focusSection = "dns"
root.dnsIndex = pill.index
}
onClicked: pill.clicked()
}
PanelToolTip {
visible: pill.tooltipText !== "" && pillMouse.containsMouse
text: pill.tooltipText
panelForeground: root.bar.foreground
panelBackground: root.bar.background
fontFamily: root.bar.fontFamily
onHovered: function(isHovered) {
if (!isHovered) return
root.focusSection = "dns"
root.dnsIndex = pill.index
}
}