Give every hero toggle a keyboard focus ring
Each panel's hero on/off toggle (Tailscale, Dropbox Wi-Fi, Bluetooth, network, audio mute) now has a reachable "header" cursor section, a focus ring around the hero icon, Enter/Space activation, a letter shortcut, and hover parity. The hero is inset by heroRingPad so the ring stays inside the Flickable/ScrollView clip box instead of being cut off. Bluetooth and audio get a virtual "header" section above their device sections, so the adapter/mute can be toggled by keyboard even when no device rows exist. Network repurposes its previously dead header section. For the PanelHero-based panels (Tailscale, Dropbox), route panel state through the wrapper's `header` id: inside a PanelHero iconComponent `root` resolves to PanelHero, not the Panel, so `root.headerHasCursor` and `root.setHeaderCursor()` silently referenced the wrong object. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
835181f112
commit
228b5ac3ba
@@ -48,6 +48,8 @@ Panel {
|
||||
readonly property var exitNodes: displayExitNodes()
|
||||
readonly property bool showExitNodes: tailscale.active && (exitNodes.length > 0 || tailscale.mullvadRegions.length > 0)
|
||||
readonly property var filteredMullvadRegions: filteredMullvadRegionNodes()
|
||||
readonly property bool headerHasCursor: cursorActive && focusSection === "header"
|
||||
readonly property int heroRingPad: Style.space(6)
|
||||
readonly property color iconColor: tailscale.active ? foreground : dim
|
||||
readonly property color barIconColor: tailscale.active ? barForeground : Qt.darker(barForeground, 1.55)
|
||||
readonly property color hoverFill: bar ? Style.hoverFillFor(bar.foreground, Color.accent) : "transparent"
|
||||
@@ -317,6 +319,12 @@ Panel {
|
||||
focusSection = "auth"
|
||||
}
|
||||
|
||||
function setHeaderCursor() {
|
||||
cursorActive = true
|
||||
focusSection = "header"
|
||||
headerIndex = 0
|
||||
}
|
||||
|
||||
implicitWidth: button.implicitWidth
|
||||
implicitHeight: button.implicitHeight
|
||||
|
||||
@@ -429,11 +437,18 @@ Panel {
|
||||
Item {
|
||||
id: header
|
||||
width: parent.width
|
||||
implicitHeight: hero.implicitHeight
|
||||
implicitHeight: hero.implicitHeight + root.heroRingPad
|
||||
// Exposed for the hero's iconComponent, whose `root` resolves to
|
||||
// PanelHero (not this Panel) — reach panel state via `header`.
|
||||
readonly property bool ringVisible: root.headerHasCursor
|
||||
readonly property int ringPad: root.heroRingPad
|
||||
function focusHero() { root.setHeaderCursor() }
|
||||
|
||||
PanelHero {
|
||||
id: hero
|
||||
width: parent.width
|
||||
x: root.heroRingPad
|
||||
y: root.heroRingPad
|
||||
width: parent.width - root.heroRingPad
|
||||
title: tailscale.installed ? (tailscale.selfName || "Tailscale") : "Tailscale"
|
||||
meta: tailscale.active ? root.heroPhraseText : "Tailscale is disconnected"
|
||||
foreground: root.foreground
|
||||
@@ -444,6 +459,18 @@ Panel {
|
||||
implicitWidth: icon.implicitWidth
|
||||
implicitHeight: icon.implicitHeight
|
||||
|
||||
// Keyboard focus ring around the hero toggle. The hero is
|
||||
// inset by heroRingPad so this ring stays inside the
|
||||
// Flickable's clip box instead of being cut off.
|
||||
BorderSurface {
|
||||
anchors.fill: icon
|
||||
anchors.margins: -header.ringPad
|
||||
color: "transparent"
|
||||
radius: Style.cornerRadius
|
||||
visible: header.ringVisible
|
||||
borderSpec: Border.controlSpec("hover-cursor", hero.foreground, Color.accent)
|
||||
}
|
||||
|
||||
TailscaleIcon {
|
||||
id: icon
|
||||
iconSize: Style.font.display
|
||||
@@ -460,10 +487,7 @@ Panel {
|
||||
hoverEnabled: true
|
||||
enabled: tailscale.installed && !tailscale.busy
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.focusSection = "header"
|
||||
root.headerIndex = 0
|
||||
}
|
||||
onContainsMouseChanged: if (containsMouse) header.focusHero()
|
||||
onClicked: tailscale.toggleTailscale()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user