Refine shell cursor highlights

This commit is contained in:
Ryan Hughes
2026-05-19 01:02:16 -04:00
parent efd46234f0
commit 832244c10e
11 changed files with 206 additions and 66 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ normal-border-alpha = 0.4
hover-cursor-color = "foreground" hover-cursor-color = "foreground"
hover-cursor-fill-alpha = 0.08 hover-cursor-fill-alpha = 0.08
hover-cursor-border-width = 1 hover-cursor-border-width = 1
hover-cursor-border-alpha = 1.0 hover-cursor-border-alpha = 0.25
# Selected: persistent chosen/current state. # Selected: persistent chosen/current state.
selected-color = "foreground" selected-color = "foreground"
+1 -1
View File
@@ -115,7 +115,7 @@ QtObject {
readonly property real selectionFillAlpha: styleAlpha("selection-fill-alpha", 0.35) readonly property real selectionFillAlpha: styleAlpha("selection-fill-alpha", 0.35)
readonly property real normalBorderAlpha: styleAlpha(["normal-border-alpha", "idle-border-alpha"], 0.4) readonly property real normalBorderAlpha: styleAlpha(["normal-border-alpha", "idle-border-alpha"], 0.4)
readonly property real hoverBorderAlpha: styleAlpha(["hover-cursor-border-alpha", "hover-border-alpha"], 1.0) readonly property real hoverBorderAlpha: styleAlpha(["hover-cursor-border-alpha", "hover-border-alpha"], 0.25)
readonly property real selectedBorderAlpha: styleAlpha("selected-border-alpha", 1.0) readonly property real selectedBorderAlpha: styleAlpha("selected-border-alpha", 1.0)
readonly property real focusBorderAlpha: styleAlpha("focus-border-alpha", hoverBorderAlpha) readonly property real focusBorderAlpha: styleAlpha("focus-border-alpha", hoverBorderAlpha)
+16 -7
View File
@@ -105,6 +105,7 @@ Item {
// keyboard + mouse like wifi does. // keyboard + mouse like wifi does.
property string focusSection: "output" property string focusSection: "output"
property int selectedIndex: -1 property int selectedIndex: -1
property bool cursorActive: false
readonly property color hoverFill: bar readonly property color hoverFill: bar
? Style.hoverFillFor(bar.foreground, Color.accent) ? Style.hoverFillFor(bar.foreground, Color.accent)
@@ -216,7 +217,8 @@ Item {
onPopupOpenChanged: { onPopupOpenChanged: {
if (popupOpen) { if (popupOpen) {
focusSection = "output" focusSection = "output"
selectedIndex = -1 // start on the output slider selectedIndex = -1 // first keyboard cursor reveal starts on the output slider
cursorActive = false
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() }) Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
} }
} }
@@ -488,15 +490,17 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
id: keyCatcher id: keyCatcher
anchors.fill: parent anchors.fill: parent
onMoveRequested: function(dx, dy) { onMoveRequested: function(dx, dy) {
if (!root.cursorActive) { root.cursorActive = true; return }
if (dy !== 0) root.moveCursor(dy) if (dy !== 0) root.moveCursor(dy)
else if (dx !== 0) root.adjustVolume(dx * 0.05) else if (dx !== 0) root.adjustVolume(dx * 0.05)
} }
onActivateRequested: root.activateCursor() onActivateRequested: if (root.cursorActive) root.activateCursor()
onCloseRequested: root.closePopout() onCloseRequested: root.closePopout()
onTextKey: function(t) { onTextKey: function(t) {
// 'm' mutes whatever the cursor is on: focused section's slider // 'm' mutes whatever the cursor is on: focused section's slider
// for output/input, the focused stream for streams. // for output/input, the focused stream for streams.
if (t === "m" || t === "M") { if (t === "m" || t === "M") {
if (!root.cursorActive) return
if (root.focusSection === "streams" && root.selectedIndex >= 0 if (root.focusSection === "streams" && root.selectedIndex >= 0
&& root.selectedIndex < root.audioStreams.length) { && root.selectedIndex < root.audioStreams.length) {
var s = root.audioStreams[root.selectedIndex] var s = root.audioStreams[root.selectedIndex]
@@ -556,7 +560,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
id: outputSliderRow id: outputSliderRow
width: parent.width width: parent.width
height: outputSliderInner.implicitHeight + Style.spacing.controlGap height: outputSliderInner.implicitHeight + Style.spacing.controlGap
hasCursor: root.focusSection === "output" && root.selectedIndex === -1 hasCursor: root.cursorActive && root.focusSection === "output" && root.selectedIndex === -1
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(outputSliderRow) onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(outputSliderRow)
foreground: root.bar.foreground foreground: root.bar.foreground
outline: true outline: true
@@ -620,6 +624,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
propagateComposedEvents: true propagateComposedEvents: true
onContainsMouseChanged: if (containsMouse) { onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.focusSection = "output" root.focusSection = "output"
root.selectedIndex = -1 root.selectedIndex = -1
} }
@@ -673,7 +678,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
visible: !!root.source visible: !!root.source
width: parent.width width: parent.width
height: inputSliderInner.implicitHeight + Style.spacing.controlGap height: inputSliderInner.implicitHeight + Style.spacing.controlGap
hasCursor: root.focusSection === "input" && root.selectedIndex === -1 hasCursor: root.cursorActive && root.focusSection === "input" && root.selectedIndex === -1
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(inputSliderRow) onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(inputSliderRow)
foreground: root.bar.foreground foreground: root.bar.foreground
outline: true outline: true
@@ -737,6 +742,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
propagateComposedEvents: true propagateComposedEvents: true
onContainsMouseChanged: if (containsMouse) { onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.focusSection = "input" root.focusSection = "input"
root.selectedIndex = -1 root.selectedIndex = -1
} }
@@ -797,7 +803,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
required property int rowIndex required property int rowIndex
readonly property bool isActive: root.sink && node && root.sink.id === node.id readonly property bool isActive: root.sink && node && root.sink.id === node.id
hasCursor: root.focusSection === "output" && root.selectedIndex === rowIndex hasCursor: root.cursorActive && root.focusSection === "output" && root.selectedIndex === rowIndex
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(sinkRow) onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(sinkRow)
current: isActive current: isActive
foreground: root.bar.foreground foreground: root.bar.foreground
@@ -850,6 +856,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: if (containsMouse) { onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.focusSection = "output" root.focusSection = "output"
root.selectedIndex = sinkRow.rowIndex root.selectedIndex = sinkRow.rowIndex
} }
@@ -864,7 +871,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
required property int rowIndex required property int rowIndex
readonly property bool isActive: root.source && node && root.source.id === node.id readonly property bool isActive: root.source && node && root.source.id === node.id
hasCursor: root.focusSection === "input" && root.selectedIndex === rowIndex hasCursor: root.cursorActive && root.focusSection === "input" && root.selectedIndex === rowIndex
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(sourceRow) onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(sourceRow)
current: isActive current: isActive
foreground: root.bar.foreground foreground: root.bar.foreground
@@ -917,6 +924,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: if (containsMouse) { onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.focusSection = "input" root.focusSection = "input"
root.selectedIndex = sourceRow.rowIndex root.selectedIndex = sourceRow.rowIndex
} }
@@ -936,7 +944,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
readonly property real streamVolume: node && node.audio ? node.audio.volume : 0 readonly property real streamVolume: node && node.audio ? node.audio.volume : 0
readonly property bool streamMuted: node && node.audio ? node.audio.muted : false readonly property bool streamMuted: node && node.audio ? node.audio.muted : false
hasCursor: root.focusSection === "streams" && root.selectedIndex === rowIndex hasCursor: root.cursorActive && root.focusSection === "streams" && root.selectedIndex === rowIndex
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(streamRow) onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(streamRow)
foreground: root.bar.foreground foreground: root.bar.foreground
fill: root.hoverFill fill: root.hoverFill
@@ -1020,6 +1028,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
propagateComposedEvents: true propagateComposedEvents: true
onContainsMouseChanged: if (containsMouse) { onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.focusSection = "streams" root.focusSection = "streams"
root.selectedIndex = streamRow.rowIndex root.selectedIndex = streamRow.rowIndex
} }
+10 -5
View File
@@ -100,7 +100,8 @@ Item {
// never from containsMouse. Mouse hover updates root cursor state too, // never from containsMouse. Mouse hover updates root cursor state too,
// guaranteeing one highlight on screen. // guaranteeing one highlight on screen.
property string focusSection: "header" property string focusSection: "header"
property int selectedIndex: 1 // default = toggle pill property int selectedIndex: 1 // default = toggle pill once the cursor is revealed
property bool cursorActive: false
readonly property int headerPillCount: 2 readonly property int headerPillCount: 2
// Stable identity for the focused known device. The known list is sorted // Stable identity for the focused known device. The known list is sorted
@@ -224,6 +225,7 @@ Item {
if (adapter && adapter.enabled && !adapter.discovering) adapter.discovering = true if (adapter && adapter.enabled && !adapter.discovering) adapter.discovering = true
if (knownDevices.length > 0) { focusSection = "known"; selectedIndex = 0 } if (knownDevices.length > 0) { focusSection = "known"; selectedIndex = 0 }
else { focusSection = "header"; selectedIndex = 1 } else { focusSection = "header"; selectedIndex = 1 }
cursorActive = false
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() }) Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
} }
} }
@@ -369,12 +371,13 @@ Item {
id: keyCatcher id: keyCatcher
anchors.fill: parent anchors.fill: parent
onMoveRequested: function(dx, dy) { onMoveRequested: function(dx, dy) {
if (!root.cursorActive) { root.cursorActive = true; return }
if (dy !== 0) root.moveCursor(dy) if (dy !== 0) root.moveCursor(dy)
else if (dx !== 0) root.moveCursorH(dx) else if (dx !== 0) root.moveCursorH(dx)
} }
onActivateRequested: root.activateCursor() onActivateRequested: if (root.cursorActive) root.activateCursor()
onCloseRequested: root.closePopout() onCloseRequested: root.closePopout()
onDeleteRequested: root.deleteSelected() onDeleteRequested: if (root.cursorActive) root.deleteSelected()
Column { Column {
id: column id: column
@@ -523,11 +526,12 @@ Item {
enabled: pillEnabled enabled: pillEnabled
opacity: pillEnabled ? 1 : 0.4 opacity: pillEnabled ? 1 : 0.4
hasCursor: root.focusSection === "header" && root.selectedIndex === pillIndex hasCursor: root.cursorActive && root.focusSection === "header" && root.selectedIndex === pillIndex
onClicked: pill.activated() onClicked: pill.activated()
onHovered: function(isHovered) { onHovered: function(isHovered) {
if (!isHovered) return if (!isHovered) return
root.cursorActive = true
root.focusSection = "header" root.focusSection = "header"
root.selectedIndex = pill.pillIndex root.selectedIndex = pill.pillIndex
} }
@@ -548,7 +552,7 @@ Item {
readonly property int devState: dev && dev.state !== undefined ? dev.state : -1 readonly property int devState: dev && dev.state !== undefined ? dev.state : -1
readonly property string sectionName: isDiscovered ? "discovered" : "known" readonly property string sectionName: isDiscovered ? "discovered" : "known"
hasCursor: root.focusSection === sectionName && root.selectedIndex === rowIndex hasCursor: root.cursorActive && root.focusSection === sectionName && root.selectedIndex === rowIndex
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(row) onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(row)
current: isConnected current: isConnected
foreground: root.bar.foreground foreground: root.bar.foreground
@@ -623,6 +627,7 @@ Item {
cursorShape: row.dev ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: row.dev ? Qt.PointingHandCursor : Qt.ArrowCursor
onContainsMouseChanged: if (containsMouse) { onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.focusSection = row.sectionName root.focusSection = row.sectionName
root.selectedIndex = row.rowIndex root.selectedIndex = row.rowIndex
} }
+12 -5
View File
@@ -40,6 +40,7 @@ Item {
readonly property var scaleValues: ["1", "1.25", "1.6", "2", "3", "4"] readonly property var scaleValues: ["1", "1.25", "1.6", "2", "3", "4"]
property string focusSection: "scale" property string focusSection: "scale"
property int selectedIndex: 0 property int selectedIndex: 0
property bool cursorActive: false
readonly property var visibleSections: { readonly property var visibleSections: {
var list = [] var list = []
@@ -271,7 +272,8 @@ Item {
Component.onCompleted: refresh() Component.onCompleted: refresh()
// KeyboardPanel takes Exclusive focus at map-time, so SUPER-bound IPC // KeyboardPanel takes Exclusive focus at map-time, so SUPER-bound IPC
// summons land with j/k ready to navigate. Seed the cursor on each open. // summons land with j/k ready to navigate. Keep a default landing point,
// but don't paint the cursor until hover or the first navigation key.
onPopupOpenChanged: { onPopupOpenChanged: {
if (popupOpen) { if (popupOpen) {
refresh() refresh()
@@ -282,6 +284,7 @@ Item {
focusSection = "scale" focusSection = "scale"
selectedIndex = 0 selectedIndex = 0
} }
cursorActive = false
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() }) Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
} }
} }
@@ -374,13 +377,14 @@ Item {
id: keyCatcher id: keyCatcher
anchors.fill: parent anchors.fill: parent
onMoveRequested: function(dx, dy) { onMoveRequested: function(dx, dy) {
if (!root.cursorActive) { root.cursorActive = true; return }
if (dy !== 0) root.moveCursor(dy) if (dy !== 0) root.moveCursor(dy)
else if (dx !== 0) { else if (dx !== 0) {
if (root.focusSection === "brightness") root.adjustBrightness(dx * 5) if (root.focusSection === "brightness") root.adjustBrightness(dx * 5)
else if (root.focusSection === "scale") root.moveCursorH(dx) else if (root.focusSection === "scale") root.moveCursorH(dx)
} }
} }
onActivateRequested: root.activateCursor() onActivateRequested: if (root.cursorActive) root.activateCursor()
onCloseRequested: root.closePopout() onCloseRequested: root.closePopout()
ScrollView { ScrollView {
@@ -412,7 +416,7 @@ Item {
visible: root.brightnessAvailable visible: root.brightnessAvailable
width: parent.width width: parent.width
height: brightnessInner.implicitHeight + Style.spacing.controlGap height: brightnessInner.implicitHeight + Style.spacing.controlGap
hasCursor: root.focusSection === "brightness" && root.selectedIndex === -1 hasCursor: root.cursorActive && root.focusSection === "brightness" && root.selectedIndex === -1
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(brightnessRow) onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(brightnessRow)
foreground: root.bar.foreground foreground: root.bar.foreground
outline: true outline: true
@@ -465,6 +469,7 @@ Item {
HoverHandler { HoverHandler {
onHoveredChanged: if (hovered) { onHoveredChanged: if (hovered) {
root.cursorActive = true
root.focusSection = "brightness" root.focusSection = "brightness"
root.selectedIndex = -1 root.selectedIndex = -1
} }
@@ -514,10 +519,11 @@ Item {
horizontalPadding: 0 horizontalPadding: 0
verticalPadding: Style.spacing.controlPaddingY verticalPadding: Style.spacing.controlPaddingY
active: root.normalizeScale(root.monitorScale) === root.normalizeScale(modelData) active: root.normalizeScale(root.monitorScale) === root.normalizeScale(modelData)
hasCursor: root.focusSection === "scale" && root.selectedIndex === index hasCursor: root.cursorActive && root.focusSection === "scale" && root.selectedIndex === index
onClicked: root.setScale(modelData) onClicked: root.setScale(modelData)
onHovered: function(h) { onHovered: function(h) {
if (h) { if (h) {
root.cursorActive = true
root.focusSection = "scale" root.focusSection = "scale"
root.selectedIndex = index root.selectedIndex = index
} }
@@ -555,10 +561,11 @@ Item {
foreground: root.bar.foreground foreground: root.bar.foreground
accent: root.bar.foreground accent: root.bar.foreground
fontFamily: root.bar.fontFamily fontFamily: root.bar.fontFamily
hasCursor: root.focusSection === "monitors" && root.selectedIndex === index hasCursor: root.cursorActive && root.focusSection === "monitors" && root.selectedIndex === index
onClicked: root.toggleDisplay(modelData.name, modelData.enabled) onClicked: root.toggleDisplay(modelData.name, modelData.enabled)
onHovered: function(h) { onHovered: function(h) {
if (h) { if (h) {
root.cursorActive = true
root.focusSection = "monitors" root.focusSection = "monitors"
root.selectedIndex = index root.selectedIndex = index
} }
+76 -20
View File
@@ -47,14 +47,38 @@ Item {
// Index into `wifiNetworks` for keyboard navigation. -1 = no selection. // Index into `wifiNetworks` for keyboard navigation. -1 = no selection.
property int selectedIndex: -1 property int selectedIndex: -1
property bool cursorActive: false
// Keyboard focus zone for the panel. j/k crosses the boundary: from the // Keyboard focus zone for the panel. j/k crosses row boundaries:
// top of the wifi list a k goes back up to the DNS row, j from DNS drops // header actions ⇄ DNS row ⇄ Wi-Fi networks. h/l move within header
// into the wifi list. h/l only mean something while focused on DNS. // actions or DNS providers.
property string focusSection: "dns" // "dns" | "wifi" property string focusSection: "dns" // "header" | "dns" | "wifi"
property int headerIndex: 0
readonly property bool canDisconnect: info.type === "wifi" && !!info.ssid
readonly property int headerActionCount: canDisconnect ? 2 : 1
readonly property var dnsProviders: ["DHCP", "Cloudflare", "Google", "Custom"] readonly property var dnsProviders: ["DHCP", "Cloudflare", "Google", "Custom"]
property int dnsIndex: 0 property int dnsIndex: 0
onHeaderActionCountChanged: clampHeaderIndex()
function clampHeaderIndex() {
var max = Math.max(0, headerActionCount - 1)
if (headerIndex > max) headerIndex = max
if (headerIndex < 0) headerIndex = 0
}
function selectHeaderByDelta(delta) {
headerIndex = Math.max(0, Math.min(headerActionCount - 1, headerIndex + delta))
}
function activateHeader() {
if (canDisconnect && headerIndex === 0) {
if (!busy) disconnect(info.ssid)
return
}
refresh(true)
}
function selectDnsByDelta(delta) { function selectDnsByDelta(delta) {
dnsIndex = Math.max(0, Math.min(dnsProviders.length - 1, dnsIndex + delta)) dnsIndex = Math.max(0, Math.min(dnsProviders.length - 1, dnsIndex + delta))
} }
@@ -65,10 +89,10 @@ Item {
} }
// Single cursor model: exactly one highlighted spot across the whole // Single cursor model: exactly one highlighted spot across the whole
// panel, located via `focusSection` + (`selectedIndex` | `dnsIndex`). // panel, located via `focusSection` + (`headerIndex` | `dnsIndex` |
// Mouse hover and keyboard nav both mutate this state at the root; items // `selectedIndex`). Mouse hover and keyboard nav both mutate this state
// never read containsMouse for visuals. See CursorSurface for the // at the root; items never read containsMouse for visuals. See
// shared chrome (fill / border) shared by NetworkRow and DnsProviderPill. // CursorSurface for the shared chrome shared by rows and pills.
readonly property color hoverFill: bar ? Style.hoverFillFor(bar.foreground, Color.accent) : "transparent" readonly property color hoverFill: bar ? Style.hoverFillFor(bar.foreground, Color.accent) : "transparent"
readonly property color selectedFill: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "transparent" readonly property color selectedFill: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "transparent"
@@ -83,6 +107,7 @@ Item {
focusSection = wifiNetworks.length > 0 ? "wifi" : "dns" focusSection = wifiNetworks.length > 0 ? "wifi" : "dns"
var idx = dnsProviders.indexOf(dnsProvider) var idx = dnsProviders.indexOf(dnsProvider)
dnsIndex = idx >= 0 ? idx : 0 dnsIndex = idx >= 0 ? idx : 0
cursorActive = false
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() }) Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
} }
} }
@@ -638,11 +663,20 @@ fi
blocked: root.passwordSsid !== "" blocked: root.passwordSsid !== ""
onMoveRequested: function(dx, dy) { onMoveRequested: function(dx, dy) {
if (!root.cursorActive) {
root.cursorActive = true
if (dy >= 0) return
}
if (dy !== 0) { if (dy !== 0) {
if (root.focusSection === "dns") { if (root.focusSection === "header") {
// j from DNS drops into the wifi list if there's anywhere to if (dy > 0) root.focusSection = "dns"
// land; otherwise hold position so j isn't a no-op surprise. } else if (root.focusSection === "dns") {
if (dy > 0 && root.wifiNetworks.length > 0) { // k from DNS moves up into header actions; j drops into the
// wifi list if there's anywhere to land.
if (dy < 0) {
root.focusSection = "header"
root.headerIndex = root.headerActionCount - 1 // refresh by default
} else if (root.wifiNetworks.length > 0) {
root.focusSection = "wifi" root.focusSection = "wifi"
if (root.selectedIndex < 0) root.selectedIndex = 0 if (root.selectedIndex < 0) root.selectedIndex = 0
} }
@@ -653,15 +687,21 @@ fi
else root.selectByDelta(dy) else root.selectByDelta(dy)
} }
} }
if (dx !== 0 && root.focusSection === "dns") root.selectDnsByDelta(dx) if (dx !== 0) {
if (root.focusSection === "header") root.selectHeaderByDelta(dx)
else if (root.focusSection === "dns") root.selectDnsByDelta(dx)
}
} }
onActivateRequested: { onActivateRequested: {
if (root.focusSection === "dns") root.activateDns() if (root.cursorActive) {
else root.activateSelected() if (root.focusSection === "header") root.activateHeader()
else if (root.focusSection === "dns") root.activateDns()
else root.activateSelected()
}
} }
onCloseRequested: root.closePopout() onCloseRequested: root.closePopout()
onDeleteRequested: { onDeleteRequested: {
if (root.focusSection === "wifi") root.forgetSelected() if (root.cursorActive && root.focusSection === "wifi") root.forgetSelected()
} }
onTextKey: function(t) { onTextKey: function(t) {
if (t === "r" || t === "R") root.refresh() if (t === "r" || t === "R") root.refresh()
@@ -747,8 +787,9 @@ fi
PanelActionButton { PanelActionButton {
id: disconnectBtn id: disconnectBtn
visible: root.info.type === "wifi" && !!root.info.ssid visible: root.canDisconnect
enabled: !root.busy enabled: !root.busy
hasCursor: root.cursorActive && root.focusSection === "header" && root.headerIndex === 0
iconText: "󰅙" iconText: "󰅙"
tooltipText: "Disconnect" tooltipText: "Disconnect"
foreground: root.bar.foreground foreground: root.bar.foreground
@@ -756,6 +797,12 @@ fi
panelBackground: root.bar.background panelBackground: root.bar.background
fontFamily: root.bar.fontFamily fontFamily: root.bar.fontFamily
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
onHovered: function(h) {
if (!h) return
root.cursorActive = true
root.focusSection = "header"
root.headerIndex = 0
}
onClicked: root.disconnect(root.info.ssid) onClicked: root.disconnect(root.info.ssid)
} }
@@ -772,6 +819,13 @@ fi
verticalPadding: Style.spacing.labelGap verticalPadding: Style.spacing.labelGap
iconSize: Style.font.icon iconSize: Style.font.icon
active: root.scanning active: root.scanning
hasCursor: root.cursorActive && root.focusSection === "header" && root.headerIndex === (root.canDisconnect ? 1 : 0)
onHovered: function(h) {
if (!h) return
root.cursorActive = true
root.focusSection = "header"
root.headerIndex = root.canDisconnect ? 1 : 0
}
onClicked: root.refresh(true) onClicked: root.refresh(true)
} }
} }
@@ -950,10 +1004,11 @@ fi
// `current DNS` is the pill's `active` fill; the keyboard cursor lights // `current DNS` is the pill's `active` fill; the keyboard cursor lights
// up `hasCursor`. // up `hasCursor`.
active: root.dnsProvider === provider active: root.dnsProvider === provider
hasCursor: root.focusSection === "dns" && root.dnsIndex === index hasCursor: root.cursorActive && root.focusSection === "dns" && root.dnsIndex === index
onHovered: function(isHovered) { onHovered: function(isHovered) {
if (!isHovered) return if (!isHovered) return
root.cursorActive = true
root.focusSection = "dns" root.focusSection = "dns"
root.dnsIndex = pill.index root.dnsIndex = pill.index
} }
@@ -972,7 +1027,7 @@ fi
readonly property bool isProtected: root.isProtected(net ? net.security : "") readonly property bool isProtected: root.isProtected(net ? net.security : "")
readonly property bool isSelected: root.focusSection === "wifi" && root.selectedIndex === index readonly property bool isSelected: root.focusSection === "wifi" && root.selectedIndex === index
hasCursor: isSelected hasCursor: root.cursorActive && isSelected
current: isConnected current: isConnected
foreground: root.bar.foreground foreground: root.bar.foreground
fill: root.hoverFill fill: root.hoverFill
@@ -1017,12 +1072,13 @@ fi
// Move the cursor here when the mouse enters; mouse leaving doesn't // Move the cursor here when the mouse enters; mouse leaving doesn't
// clear it (so the cursor stays where the mouse last was and // clear it (so the cursor stays where the mouse last was and
// subsequent j/k pick up from this row). // subsequent j/k pick up from this row).
onContainsMouseChanged: if (containsMouse) { root.focusSection = "wifi"; root.selectedIndex = row.index } onContainsMouseChanged: if (containsMouse) { root.cursorActive = true; root.focusSection = "wifi"; root.selectedIndex = row.index }
onClicked: { onClicked: {
if (!row.net) return if (!row.net) return
// Resync cursor in case keyboard nav moved it away while the mouse // Resync cursor in case keyboard nav moved it away while the mouse
// stayed parked on this row — the click target is unambiguously here. // stayed parked on this row — the click target is unambiguously here.
root.cursorActive = true
root.focusSection = "wifi" root.focusSection = "wifi"
root.selectedIndex = row.index root.selectedIndex = row.index
if (row.isConnected) { if (row.isConnected) {
+9 -3
View File
@@ -18,6 +18,7 @@ Item {
property var profiles: [] property var profiles: []
property string activeProfile: "" property string activeProfile: ""
property int profileIndex: 0 property int profileIndex: 0
property bool cursorActive: false
function closePopout() { popupOpen = false } function closePopout() { popupOpen = false }
@@ -112,6 +113,7 @@ Item {
refresh() refresh()
var idx = profiles.indexOf(activeProfile) var idx = profiles.indexOf(activeProfile)
profileIndex = idx >= 0 ? idx : 0 profileIndex = idx >= 0 ? idx : 0
cursorActive = false
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() }) Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
} }
} }
@@ -187,10 +189,11 @@ printf 'time\t%s\n' "$($OMARCHY_PATH/bin/omarchy-battery-remaining-time 2>/dev/n
id: keyCatcher id: keyCatcher
anchors.fill: parent anchors.fill: parent
onMoveRequested: function(dx, dy) { onMoveRequested: function(dx, dy) {
if (!root.cursorActive) { root.cursorActive = true; return }
if (dx !== 0) root.selectProfileByDelta(dx) if (dx !== 0) root.selectProfileByDelta(dx)
else if (dy !== 0) root.selectProfileByDelta(dy) else if (dy !== 0) root.selectProfileByDelta(dy)
} }
onActivateRequested: root.activateSelectedProfile() onActivateRequested: if (root.cursorActive) root.activateSelectedProfile()
onCloseRequested: root.closePopout() onCloseRequested: root.closePopout()
Column { Column {
@@ -283,10 +286,13 @@ printf 'time\t%s\n' "$($OMARCHY_PATH/bin/omarchy-battery-remaining-time 2>/dev/n
horizontalPadding: Style.spacing.controlPaddingX horizontalPadding: Style.spacing.controlPaddingX
verticalPadding: Style.spacing.controlPaddingY verticalPadding: Style.spacing.controlPaddingY
active: root.activeProfile === modelData active: root.activeProfile === modelData
hasCursor: root.profileIndex === index hasCursor: root.cursorActive && root.profileIndex === index
onClicked: root.setProfile(modelData) onClicked: root.setProfile(modelData)
onHovered: function(h) { onHovered: function(h) {
if (h) root.profileIndex = index if (h) {
root.cursorActive = true
root.profileIndex = index
}
} }
} }
} }
@@ -15,6 +15,7 @@ Item {
property bool opened: false property bool opened: false
property string filterText: "" property string filterText: ""
property int selectedIndex: 0 property int selectedIndex: 0
property bool cursorActive: false
property var items: [] property var items: []
property color accent: Color.menu.selected property color accent: Color.menu.selected
@@ -34,6 +35,7 @@ Item {
root.opened = true root.opened = true
root.filterText = "" root.filterText = ""
root.selectedIndex = 0 root.selectedIndex = 0
root.cursorActive = false
// Trigger fetch // Trigger fetch
fetchProc.collected = "" fetchProc.collected = ""
@@ -98,13 +100,19 @@ Item {
function select(delta) { function select(delta) {
if (displayModel.count === 0) return if (displayModel.count === 0) return
selectedIndex = (selectedIndex + delta + displayModel.count) % displayModel.count if (!cursorActive) {
cursorActive = true
selectedIndex = delta < 0 ? displayModel.count - 1 : 0
} else {
selectedIndex = (selectedIndex + delta + displayModel.count) % displayModel.count
}
resultList.positionViewAtIndex(selectedIndex, ListView.Contain) resultList.positionViewAtIndex(selectedIndex, ListView.Contain)
} }
function setFilter(nextFilter) { function setFilter(nextFilter) {
root.filterText = nextFilter root.filterText = nextFilter
root.selectedIndex = 0 root.selectedIndex = 0
root.cursorActive = false
root.rebuildDisplay() root.rebuildDisplay()
} }
@@ -212,7 +220,8 @@ Item {
root.select(6) root.select(6)
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
root.activateIndex(root.selectedIndex) if (root.cursorActive) root.activateIndex(root.selectedIndex)
else if (displayModel.count > 0) root.cursorActive = true
event.accepted = true event.accepted = true
} else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127) { } else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127) {
root.setFilter(root.filterText + event.text) root.setFilter(root.filterText + event.text)
@@ -270,12 +279,14 @@ Item {
required property string previewType required property string previewType
required property bool isPassword required property bool isPassword
readonly property bool hasCursor: root.cursorActive && index === root.selectedIndex
width: ListView.view.width width: ListView.view.width
height: root.rowHeight height: root.rowHeight
radius: root.cornerRadius radius: root.cornerRadius
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent" color: hasCursor ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent" border.color: hasCursor ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0 border.width: hasCursor ? Style.hoverBorderWidth : 0
Rectangle { Rectangle {
visible: false visible: false
@@ -299,7 +310,7 @@ Item {
width: parent.width width: parent.width
height: parent.height height: parent.height
text: parent.parent.isPassword ? "••••••••" : (parent.parent.previewType === "text" ? parent.parent.previewText : "Image") text: parent.parent.isPassword ? "••••••••" : (parent.parent.previewType === "text" ? parent.parent.previewText : "Image")
color: index === root.selectedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground color: parent.parent.hasCursor ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: Style.font.title font.pixelSize: Style.font.title
font.italic: parent.parent.previewType === "file" || parent.parent.isPassword font.italic: parent.parent.previewType === "file" || parent.parent.isPassword
@@ -315,8 +326,12 @@ Item {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: if (containsMouse) root.selectedIndex = index onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.selectedIndex = index
}
onClicked: { onClicked: {
root.cursorActive = true
root.selectedIndex = index root.selectedIndex = index
root.activateIndex(index) root.activateIndex(index)
} }
@@ -333,7 +348,7 @@ Item {
border.width: Style.normalBorderWidth border.width: Style.normalBorderWidth
clip: true clip: true
property var activeRow: displayModel.count > 0 && root.selectedIndex >= 0 && root.selectedIndex < displayModel.count ? displayModel.get(root.selectedIndex) : null property var activeRow: root.cursorActive && displayModel.count > 0 && root.selectedIndex >= 0 && root.selectedIndex < displayModel.count ? displayModel.get(root.selectedIndex) : null
Text { Text {
visible: parent.activeRow && parent.activeRow.previewType === "text" visible: parent.activeRow && parent.activeRow.previewType === "text"
+1 -1
View File
@@ -410,7 +410,7 @@ Item {
color: Qt.darker(root.foreground, 1.4) color: Qt.darker(root.foreground, 1.4)
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: Style.font.bodySmall font.pixelSize: Style.font.bodySmall
text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns focusSection + selectedIndex; each element binds hasCursor: root.focusSection === 'X' && root.selectedIndex === N, and onHovered updates the same state. One highlight on screen, keyboard and mouse always agree. See plugins/bar/widgets/audioPanel.qml for the canonical recipe." text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns cursorActive + focusSection + selectedIndex; each element binds hasCursor: root.cursorActive && root.focusSection === 'X' && root.selectedIndex === N, and onHovered flips cursorActive on while updating the same state. No initial highlight, then one highlight on screen once the keyboard or mouse enters. See plugins/bar/widgets/audioPanel.qml for the canonical recipe."
} }
Text { Text {
width: parent.width width: parent.width
+33 -6
View File
@@ -15,6 +15,7 @@ Item {
property bool opened: false property bool opened: false
property string filterText: "" property string filterText: ""
property int selectedIndex: 0 property int selectedIndex: 0
property bool cursorActive: false
property var emojis: [] property var emojis: []
property var filteredEmojis: [] property var filteredEmojis: []
@@ -38,6 +39,7 @@ Item {
root.opened = true root.opened = true
root.filterText = "" root.filterText = ""
root.selectedIndex = 0 root.selectedIndex = 0
root.cursorActive = false
root.rebuildDisplay() root.rebuildDisplay()
Qt.callLater(function() { keyCatcher.forceActiveFocus() }) Qt.callLater(function() { keyCatcher.forceActiveFocus() })
} }
@@ -100,12 +102,23 @@ Item {
function select(delta) { function select(delta) {
if (displayModel.count === 0) return if (displayModel.count === 0) return
selectedIndex = (selectedIndex + delta + displayModel.count) % displayModel.count if (!cursorActive) {
cursorActive = true
selectedIndex = delta < 0 ? displayModel.count - 1 : 0
} else {
selectedIndex = (selectedIndex + delta + displayModel.count) % displayModel.count
}
resultGrid.positionViewAtIndex(selectedIndex, GridView.Contain) resultGrid.positionViewAtIndex(selectedIndex, GridView.Contain)
} }
function selectRow(delta) { function selectRow(delta) {
if (displayModel.count === 0) return if (displayModel.count === 0) return
if (!cursorActive) {
cursorActive = true
selectedIndex = delta < 0 ? displayModel.count - 1 : 0
resultGrid.positionViewAtIndex(selectedIndex, GridView.Contain)
return
}
var newIndex = selectedIndex + delta * columns var newIndex = selectedIndex + delta * columns
if (newIndex < 0) newIndex = 0 if (newIndex < 0) newIndex = 0
if (newIndex >= displayModel.count) newIndex = displayModel.count - 1 if (newIndex >= displayModel.count) newIndex = displayModel.count - 1
@@ -115,6 +128,12 @@ Item {
function selectPage(delta) { function selectPage(delta) {
if (displayModel.count === 0) return if (displayModel.count === 0) return
if (!cursorActive) {
cursorActive = true
selectedIndex = delta < 0 ? displayModel.count - 1 : 0
resultGrid.positionViewAtIndex(selectedIndex, GridView.Contain)
return
}
var visibleRows = Math.max(1, Math.floor(resultGrid.height / cellHeight)) var visibleRows = Math.max(1, Math.floor(resultGrid.height / cellHeight))
var newIndex = selectedIndex + delta * columns * visibleRows var newIndex = selectedIndex + delta * columns * visibleRows
if (newIndex < 0) newIndex = 0 if (newIndex < 0) newIndex = 0
@@ -126,6 +145,7 @@ Item {
function setFilter(nextFilter) { function setFilter(nextFilter) {
root.filterText = nextFilter root.filterText = nextFilter
root.selectedIndex = 0 root.selectedIndex = 0
root.cursorActive = false
root.rebuildDisplay() root.rebuildDisplay()
} }
@@ -220,7 +240,8 @@ Item {
root.selectPage(1) root.selectPage(1)
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
root.activateIndex(root.selectedIndex) if (root.cursorActive) root.activateIndex(root.selectedIndex)
else if (displayModel.count > 0) root.cursorActive = true
event.accepted = true event.accepted = true
} else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127) { } else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127) {
root.setFilter(root.filterText + event.text) root.setFilter(root.filterText + event.text)
@@ -271,12 +292,14 @@ Item {
required property int index required property int index
required property string emoji required property string emoji
readonly property bool hasCursor: root.cursorActive && index === root.selectedIndex
width: root.cellWidth width: root.cellWidth
height: root.cellHeight height: root.cellHeight
radius: root.cornerRadius radius: root.cornerRadius
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent" color: hasCursor ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent" border.color: hasCursor ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0 border.width: hasCursor ? Style.hoverBorderWidth : 0
Text { Text {
text: parent.emoji text: parent.emoji
@@ -292,8 +315,12 @@ Item {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: if (containsMouse) root.selectedIndex = index onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.selectedIndex = index
}
onClicked: { onClicked: {
root.cursorActive = true
root.selectedIndex = index root.selectedIndex = index
root.activateIndex(index) root.activateIndex(index)
} }
+24 -9
View File
@@ -44,6 +44,7 @@ Item {
property string activeMenu: "root" property string activeMenu: "root"
property string filterText: "" property string filterText: ""
property int selectedIndex: 0 property int selectedIndex: 0
property bool cursorActive: false
property int requestSerial: 0 property int requestSerial: 0
property int applySerial: 0 property int applySerial: 0
property var items: ({}) property var items: ({})
@@ -537,13 +538,19 @@ Item {
function select(delta) { function select(delta) {
if (displayModel.count === 0) return if (displayModel.count === 0) return
selectedIndex = (selectedIndex + delta + displayModel.count) % displayModel.count if (!cursorActive) {
cursorActive = true
selectedIndex = delta < 0 ? displayModel.count - 1 : 0
} else {
selectedIndex = (selectedIndex + delta + displayModel.count) % displayModel.count
}
resultList.positionViewAtIndex(selectedIndex, ListView.Contain) resultList.positionViewAtIndex(selectedIndex, ListView.Contain)
} }
function setFilter(nextFilter) { function setFilter(nextFilter) {
root.filterText = nextFilter root.filterText = nextFilter
root.selectedIndex = 0 root.selectedIndex = 0
root.cursorActive = false
if (root.filterText.trim()) root.loadProvidersForSearch() if (root.filterText.trim()) root.loadProvidersForSearch()
root.rebuildDisplay() root.rebuildDisplay()
} }
@@ -554,6 +561,7 @@ Item {
root.activeMenu = id root.activeMenu = id
root.filterText = "" root.filterText = ""
root.selectedIndex = 0 root.selectedIndex = 0
root.cursorActive = false
root.rebuildDisplay() root.rebuildDisplay()
root.loadProviderForMenu(id) root.loadProviderForMenu(id)
} }
@@ -605,6 +613,7 @@ Item {
navStack = [] navStack = []
filterText = "" filterText = ""
selectedIndex = 0 selectedIndex = 0
cursorActive = false
root.evaluateGuards() root.evaluateGuards()
opened = true opened = true
rebuildDisplay() rebuildDisplay()
@@ -842,7 +851,8 @@ Item {
if (!root.filterText) root.goBack() if (!root.filterText) root.goBack()
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_Right) { } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_Right) {
root.activateIndex(root.selectedIndex) if (root.cursorActive) root.activateIndex(root.selectedIndex)
else if (displayModel.count > 0) root.cursorActive = true
event.accepted = true event.accepted = true
} else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127 && (event.modifiers === Qt.NoModifier || event.modifiers === Qt.ShiftModifier)) { } else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127 && (event.modifiers === Qt.NoModifier || event.modifiers === Qt.ShiftModifier)) {
root.setFilter(root.filterText + event.text) root.setFilter(root.filterText + event.text)
@@ -922,12 +932,14 @@ Item {
required property string action required property string action
required property int childCount required property int childCount
readonly property bool hasCursor: root.cursorActive && row.index === root.selectedIndex
width: ListView.view.width width: ListView.view.width
height: root.rowHeightForDetail(row.detail) height: root.rowHeightForDetail(row.detail)
radius: root.cornerRadius radius: root.cornerRadius
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent" color: row.hasCursor ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent" border.color: row.hasCursor ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0 border.width: row.hasCursor ? Style.hoverBorderWidth : 0
Rectangle { Rectangle {
visible: false visible: false
@@ -943,7 +955,7 @@ Item {
Text { Text {
id: iconText id: iconText
text: row.icon text: row.icon
color: index === root.selectedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground color: row.hasCursor ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
opacity: row.kind === "back" ? 0.7 : 1 opacity: row.kind === "back" ? 0.7 : 1
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: Style.font.iconLarge font.pixelSize: Style.font.iconLarge
@@ -968,7 +980,7 @@ Item {
id: labelText id: labelText
width: parent.width width: parent.width
text: row.label text: row.label
color: index === root.selectedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground color: row.hasCursor ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: Style.font.heading font.pixelSize: Style.font.heading
font.weight: Font.Medium font.weight: Font.Medium
@@ -1007,7 +1019,7 @@ Item {
Text { Text {
text: row.kind === "menu" || row.kind === "link" ? "" : "" text: row.kind === "menu" || row.kind === "link" ? "" : ""
color: index === root.selectedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground color: row.hasCursor ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
opacity: row.kind === "menu" || row.kind === "link" ? 0.36 : 0 opacity: row.kind === "menu" || row.kind === "link" ? 0.36 : 0
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: Style.font.heading font.pixelSize: Style.font.heading
@@ -1021,7 +1033,10 @@ Item {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onContainsMouseChanged: if (containsMouse) root.selectedIndex = row.index onContainsMouseChanged: if (containsMouse) {
root.cursorActive = true
root.selectedIndex = row.index
}
onClicked: root.activateIndex(row.index) onClicked: root.activateIndex(row.index)
} }
} }