Refine shell cursor highlights
This commit is contained in:
@@ -105,6 +105,7 @@ Item {
|
||||
// keyboard + mouse like wifi does.
|
||||
property string focusSection: "output"
|
||||
property int selectedIndex: -1
|
||||
property bool cursorActive: false
|
||||
|
||||
readonly property color hoverFill: bar
|
||||
? Style.hoverFillFor(bar.foreground, Color.accent)
|
||||
@@ -216,7 +217,8 @@ Item {
|
||||
onPopupOpenChanged: {
|
||||
if (popupOpen) {
|
||||
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() })
|
||||
}
|
||||
}
|
||||
@@ -488,15 +490,17 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
id: keyCatcher
|
||||
anchors.fill: parent
|
||||
onMoveRequested: function(dx, dy) {
|
||||
if (!root.cursorActive) { root.cursorActive = true; return }
|
||||
if (dy !== 0) root.moveCursor(dy)
|
||||
else if (dx !== 0) root.adjustVolume(dx * 0.05)
|
||||
}
|
||||
onActivateRequested: root.activateCursor()
|
||||
onActivateRequested: if (root.cursorActive) root.activateCursor()
|
||||
onCloseRequested: root.closePopout()
|
||||
onTextKey: function(t) {
|
||||
// 'm' mutes whatever the cursor is on: focused section's slider
|
||||
// for output/input, the focused stream for streams.
|
||||
if (t === "m" || t === "M") {
|
||||
if (!root.cursorActive) return
|
||||
if (root.focusSection === "streams" && root.selectedIndex >= 0
|
||||
&& root.selectedIndex < root.audioStreams.length) {
|
||||
var s = root.audioStreams[root.selectedIndex]
|
||||
@@ -556,7 +560,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
id: outputSliderRow
|
||||
width: parent.width
|
||||
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)
|
||||
foreground: root.bar.foreground
|
||||
outline: true
|
||||
@@ -620,6 +624,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = "output"
|
||||
root.selectedIndex = -1
|
||||
}
|
||||
@@ -673,7 +678,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
visible: !!root.source
|
||||
width: parent.width
|
||||
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)
|
||||
foreground: root.bar.foreground
|
||||
outline: true
|
||||
@@ -737,6 +742,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = "input"
|
||||
root.selectedIndex = -1
|
||||
}
|
||||
@@ -797,7 +803,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
required property int rowIndex
|
||||
|
||||
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)
|
||||
current: isActive
|
||||
foreground: root.bar.foreground
|
||||
@@ -850,6 +856,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = "output"
|
||||
root.selectedIndex = sinkRow.rowIndex
|
||||
}
|
||||
@@ -864,7 +871,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
required property int rowIndex
|
||||
|
||||
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)
|
||||
current: isActive
|
||||
foreground: root.bar.foreground
|
||||
@@ -917,6 +924,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = "input"
|
||||
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 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)
|
||||
foreground: root.bar.foreground
|
||||
fill: root.hoverFill
|
||||
@@ -1020,6 +1028,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = "streams"
|
||||
root.selectedIndex = streamRow.rowIndex
|
||||
}
|
||||
|
||||
@@ -100,7 +100,8 @@ Item {
|
||||
// never from containsMouse. Mouse hover updates root cursor state too,
|
||||
// guaranteeing one highlight on screen.
|
||||
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
|
||||
|
||||
// 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 (knownDevices.length > 0) { focusSection = "known"; selectedIndex = 0 }
|
||||
else { focusSection = "header"; selectedIndex = 1 }
|
||||
cursorActive = false
|
||||
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
|
||||
}
|
||||
}
|
||||
@@ -369,12 +371,13 @@ Item {
|
||||
id: keyCatcher
|
||||
anchors.fill: parent
|
||||
onMoveRequested: function(dx, dy) {
|
||||
if (!root.cursorActive) { root.cursorActive = true; return }
|
||||
if (dy !== 0) root.moveCursor(dy)
|
||||
else if (dx !== 0) root.moveCursorH(dx)
|
||||
}
|
||||
onActivateRequested: root.activateCursor()
|
||||
onActivateRequested: if (root.cursorActive) root.activateCursor()
|
||||
onCloseRequested: root.closePopout()
|
||||
onDeleteRequested: root.deleteSelected()
|
||||
onDeleteRequested: if (root.cursorActive) root.deleteSelected()
|
||||
|
||||
Column {
|
||||
id: column
|
||||
@@ -523,11 +526,12 @@ Item {
|
||||
enabled: pillEnabled
|
||||
opacity: pillEnabled ? 1 : 0.4
|
||||
|
||||
hasCursor: root.focusSection === "header" && root.selectedIndex === pillIndex
|
||||
hasCursor: root.cursorActive && root.focusSection === "header" && root.selectedIndex === pillIndex
|
||||
|
||||
onClicked: pill.activated()
|
||||
onHovered: function(isHovered) {
|
||||
if (!isHovered) return
|
||||
root.cursorActive = true
|
||||
root.focusSection = "header"
|
||||
root.selectedIndex = pill.pillIndex
|
||||
}
|
||||
@@ -548,7 +552,7 @@ Item {
|
||||
readonly property int devState: dev && dev.state !== undefined ? dev.state : -1
|
||||
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)
|
||||
current: isConnected
|
||||
foreground: root.bar.foreground
|
||||
@@ -623,6 +627,7 @@ Item {
|
||||
cursorShape: row.dev ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = row.sectionName
|
||||
root.selectedIndex = row.rowIndex
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ Item {
|
||||
readonly property var scaleValues: ["1", "1.25", "1.6", "2", "3", "4"]
|
||||
property string focusSection: "scale"
|
||||
property int selectedIndex: 0
|
||||
property bool cursorActive: false
|
||||
|
||||
readonly property var visibleSections: {
|
||||
var list = []
|
||||
@@ -271,7 +272,8 @@ Item {
|
||||
Component.onCompleted: refresh()
|
||||
|
||||
// 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: {
|
||||
if (popupOpen) {
|
||||
refresh()
|
||||
@@ -282,6 +284,7 @@ Item {
|
||||
focusSection = "scale"
|
||||
selectedIndex = 0
|
||||
}
|
||||
cursorActive = false
|
||||
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
|
||||
}
|
||||
}
|
||||
@@ -374,13 +377,14 @@ Item {
|
||||
id: keyCatcher
|
||||
anchors.fill: parent
|
||||
onMoveRequested: function(dx, dy) {
|
||||
if (!root.cursorActive) { root.cursorActive = true; return }
|
||||
if (dy !== 0) root.moveCursor(dy)
|
||||
else if (dx !== 0) {
|
||||
if (root.focusSection === "brightness") root.adjustBrightness(dx * 5)
|
||||
else if (root.focusSection === "scale") root.moveCursorH(dx)
|
||||
}
|
||||
}
|
||||
onActivateRequested: root.activateCursor()
|
||||
onActivateRequested: if (root.cursorActive) root.activateCursor()
|
||||
onCloseRequested: root.closePopout()
|
||||
|
||||
ScrollView {
|
||||
@@ -412,7 +416,7 @@ Item {
|
||||
visible: root.brightnessAvailable
|
||||
width: parent.width
|
||||
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)
|
||||
foreground: root.bar.foreground
|
||||
outline: true
|
||||
@@ -465,6 +469,7 @@ Item {
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: if (hovered) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = "brightness"
|
||||
root.selectedIndex = -1
|
||||
}
|
||||
@@ -514,10 +519,11 @@ Item {
|
||||
horizontalPadding: 0
|
||||
verticalPadding: Style.spacing.controlPaddingY
|
||||
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)
|
||||
onHovered: function(h) {
|
||||
if (h) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = "scale"
|
||||
root.selectedIndex = index
|
||||
}
|
||||
@@ -555,10 +561,11 @@ Item {
|
||||
foreground: root.bar.foreground
|
||||
accent: root.bar.foreground
|
||||
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)
|
||||
onHovered: function(h) {
|
||||
if (h) {
|
||||
root.cursorActive = true
|
||||
root.focusSection = "monitors"
|
||||
root.selectedIndex = index
|
||||
}
|
||||
|
||||
@@ -47,14 +47,38 @@ Item {
|
||||
|
||||
// Index into `wifiNetworks` for keyboard navigation. -1 = no selection.
|
||||
property int selectedIndex: -1
|
||||
property bool cursorActive: false
|
||||
|
||||
// Keyboard focus zone for the panel. j/k crosses the boundary: from the
|
||||
// top of the wifi list a k goes back up to the DNS row, j from DNS drops
|
||||
// into the wifi list. h/l only mean something while focused on DNS.
|
||||
property string focusSection: "dns" // "dns" | "wifi"
|
||||
// Keyboard focus zone for the panel. j/k crosses row boundaries:
|
||||
// header actions ⇄ DNS row ⇄ Wi-Fi networks. h/l move within header
|
||||
// actions or DNS providers.
|
||||
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"]
|
||||
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) {
|
||||
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
|
||||
// panel, located via `focusSection` + (`selectedIndex` | `dnsIndex`).
|
||||
// Mouse hover and keyboard nav both mutate this state at the root; items
|
||||
// never read containsMouse for visuals. See CursorSurface for the
|
||||
// shared chrome (fill / border) shared by NetworkRow and DnsProviderPill.
|
||||
// panel, located via `focusSection` + (`headerIndex` | `dnsIndex` |
|
||||
// `selectedIndex`). Mouse hover and keyboard nav both mutate this state
|
||||
// at the root; items never read containsMouse for visuals. See
|
||||
// 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 selectedFill: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "transparent"
|
||||
|
||||
@@ -83,6 +107,7 @@ Item {
|
||||
focusSection = wifiNetworks.length > 0 ? "wifi" : "dns"
|
||||
var idx = dnsProviders.indexOf(dnsProvider)
|
||||
dnsIndex = idx >= 0 ? idx : 0
|
||||
cursorActive = false
|
||||
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
|
||||
}
|
||||
}
|
||||
@@ -638,11 +663,20 @@ fi
|
||||
blocked: root.passwordSsid !== ""
|
||||
|
||||
onMoveRequested: function(dx, dy) {
|
||||
if (!root.cursorActive) {
|
||||
root.cursorActive = true
|
||||
if (dy >= 0) return
|
||||
}
|
||||
if (dy !== 0) {
|
||||
if (root.focusSection === "dns") {
|
||||
// j from DNS drops into the wifi list if there's anywhere to
|
||||
// land; otherwise hold position so j isn't a no-op surprise.
|
||||
if (dy > 0 && root.wifiNetworks.length > 0) {
|
||||
if (root.focusSection === "header") {
|
||||
if (dy > 0) root.focusSection = "dns"
|
||||
} else if (root.focusSection === "dns") {
|
||||
// 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"
|
||||
if (root.selectedIndex < 0) root.selectedIndex = 0
|
||||
}
|
||||
@@ -653,15 +687,21 @@ fi
|
||||
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: {
|
||||
if (root.focusSection === "dns") root.activateDns()
|
||||
else root.activateSelected()
|
||||
if (root.cursorActive) {
|
||||
if (root.focusSection === "header") root.activateHeader()
|
||||
else if (root.focusSection === "dns") root.activateDns()
|
||||
else root.activateSelected()
|
||||
}
|
||||
}
|
||||
onCloseRequested: root.closePopout()
|
||||
onDeleteRequested: {
|
||||
if (root.focusSection === "wifi") root.forgetSelected()
|
||||
if (root.cursorActive && root.focusSection === "wifi") root.forgetSelected()
|
||||
}
|
||||
onTextKey: function(t) {
|
||||
if (t === "r" || t === "R") root.refresh()
|
||||
@@ -747,8 +787,9 @@ fi
|
||||
|
||||
PanelActionButton {
|
||||
id: disconnectBtn
|
||||
visible: root.info.type === "wifi" && !!root.info.ssid
|
||||
visible: root.canDisconnect
|
||||
enabled: !root.busy
|
||||
hasCursor: root.cursorActive && root.focusSection === "header" && root.headerIndex === 0
|
||||
iconText: ""
|
||||
tooltipText: "Disconnect"
|
||||
foreground: root.bar.foreground
|
||||
@@ -756,6 +797,12 @@ fi
|
||||
panelBackground: root.bar.background
|
||||
fontFamily: root.bar.fontFamily
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -772,6 +819,13 @@ fi
|
||||
verticalPadding: Style.spacing.labelGap
|
||||
iconSize: Style.font.icon
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -950,10 +1004,11 @@ fi
|
||||
// `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
|
||||
hasCursor: root.cursorActive && root.focusSection === "dns" && root.dnsIndex === index
|
||||
|
||||
onHovered: function(isHovered) {
|
||||
if (!isHovered) return
|
||||
root.cursorActive = true
|
||||
root.focusSection = "dns"
|
||||
root.dnsIndex = pill.index
|
||||
}
|
||||
@@ -972,7 +1027,7 @@ fi
|
||||
readonly property bool isProtected: root.isProtected(net ? net.security : "")
|
||||
readonly property bool isSelected: root.focusSection === "wifi" && root.selectedIndex === index
|
||||
|
||||
hasCursor: isSelected
|
||||
hasCursor: root.cursorActive && isSelected
|
||||
current: isConnected
|
||||
foreground: root.bar.foreground
|
||||
fill: root.hoverFill
|
||||
@@ -1017,12 +1072,13 @@ fi
|
||||
// Move the cursor here when the mouse enters; mouse leaving doesn't
|
||||
// clear it (so the cursor stays where the mouse last was and
|
||||
// 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: {
|
||||
if (!row.net) return
|
||||
// Resync cursor in case keyboard nav moved it away while the mouse
|
||||
// stayed parked on this row — the click target is unambiguously here.
|
||||
root.cursorActive = true
|
||||
root.focusSection = "wifi"
|
||||
root.selectedIndex = row.index
|
||||
if (row.isConnected) {
|
||||
|
||||
@@ -18,6 +18,7 @@ Item {
|
||||
property var profiles: []
|
||||
property string activeProfile: ""
|
||||
property int profileIndex: 0
|
||||
property bool cursorActive: false
|
||||
|
||||
function closePopout() { popupOpen = false }
|
||||
|
||||
@@ -112,6 +113,7 @@ Item {
|
||||
refresh()
|
||||
var idx = profiles.indexOf(activeProfile)
|
||||
profileIndex = idx >= 0 ? idx : 0
|
||||
cursorActive = false
|
||||
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
|
||||
anchors.fill: parent
|
||||
onMoveRequested: function(dx, dy) {
|
||||
if (!root.cursorActive) { root.cursorActive = true; return }
|
||||
if (dx !== 0) root.selectProfileByDelta(dx)
|
||||
else if (dy !== 0) root.selectProfileByDelta(dy)
|
||||
}
|
||||
onActivateRequested: root.activateSelectedProfile()
|
||||
onActivateRequested: if (root.cursorActive) root.activateSelectedProfile()
|
||||
onCloseRequested: root.closePopout()
|
||||
|
||||
Column {
|
||||
@@ -283,10 +286,13 @@ printf 'time\t%s\n' "$($OMARCHY_PATH/bin/omarchy-battery-remaining-time 2>/dev/n
|
||||
horizontalPadding: Style.spacing.controlPaddingX
|
||||
verticalPadding: Style.spacing.controlPaddingY
|
||||
active: root.activeProfile === modelData
|
||||
hasCursor: root.profileIndex === index
|
||||
hasCursor: root.cursorActive && root.profileIndex === index
|
||||
onClicked: root.setProfile(modelData)
|
||||
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 string filterText: ""
|
||||
property int selectedIndex: 0
|
||||
property bool cursorActive: false
|
||||
property var items: []
|
||||
|
||||
property color accent: Color.menu.selected
|
||||
@@ -34,6 +35,7 @@ Item {
|
||||
root.opened = true
|
||||
root.filterText = ""
|
||||
root.selectedIndex = 0
|
||||
root.cursorActive = false
|
||||
|
||||
// Trigger fetch
|
||||
fetchProc.collected = ""
|
||||
@@ -98,13 +100,19 @@ Item {
|
||||
|
||||
function select(delta) {
|
||||
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)
|
||||
}
|
||||
|
||||
function setFilter(nextFilter) {
|
||||
root.filterText = nextFilter
|
||||
root.selectedIndex = 0
|
||||
root.cursorActive = false
|
||||
root.rebuildDisplay()
|
||||
}
|
||||
|
||||
@@ -212,7 +220,8 @@ Item {
|
||||
root.select(6)
|
||||
event.accepted = true
|
||||
} 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
|
||||
} else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127) {
|
||||
root.setFilter(root.filterText + event.text)
|
||||
@@ -270,12 +279,14 @@ Item {
|
||||
required property string previewType
|
||||
required property bool isPassword
|
||||
|
||||
readonly property bool hasCursor: root.cursorActive && index === root.selectedIndex
|
||||
|
||||
width: ListView.view.width
|
||||
height: root.rowHeight
|
||||
radius: root.cornerRadius
|
||||
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0
|
||||
color: hasCursor ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: hasCursor ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: hasCursor ? Style.hoverBorderWidth : 0
|
||||
|
||||
Rectangle {
|
||||
visible: false
|
||||
@@ -299,7 +310,7 @@ Item {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
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.pixelSize: Style.font.title
|
||||
font.italic: parent.parent.previewType === "file" || parent.parent.isPassword
|
||||
@@ -315,8 +326,12 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onContainsMouseChanged: if (containsMouse) root.selectedIndex = index
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.cursorActive = true
|
||||
root.selectedIndex = index
|
||||
}
|
||||
onClicked: {
|
||||
root.cursorActive = true
|
||||
root.selectedIndex = index
|
||||
root.activateIndex(index)
|
||||
}
|
||||
@@ -333,7 +348,7 @@ Item {
|
||||
border.width: Style.normalBorderWidth
|
||||
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 {
|
||||
visible: parent.activeRow && parent.activeRow.previewType === "text"
|
||||
|
||||
@@ -410,7 +410,7 @@ Item {
|
||||
color: Qt.darker(root.foreground, 1.4)
|
||||
font.family: root.fontFamily
|
||||
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 {
|
||||
width: parent.width
|
||||
|
||||
@@ -15,6 +15,7 @@ Item {
|
||||
property bool opened: false
|
||||
property string filterText: ""
|
||||
property int selectedIndex: 0
|
||||
property bool cursorActive: false
|
||||
property var emojis: []
|
||||
property var filteredEmojis: []
|
||||
|
||||
@@ -38,6 +39,7 @@ Item {
|
||||
root.opened = true
|
||||
root.filterText = ""
|
||||
root.selectedIndex = 0
|
||||
root.cursorActive = false
|
||||
root.rebuildDisplay()
|
||||
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
|
||||
}
|
||||
@@ -100,12 +102,23 @@ Item {
|
||||
|
||||
function select(delta) {
|
||||
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)
|
||||
}
|
||||
|
||||
function selectRow(delta) {
|
||||
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
|
||||
if (newIndex < 0) newIndex = 0
|
||||
if (newIndex >= displayModel.count) newIndex = displayModel.count - 1
|
||||
@@ -115,6 +128,12 @@ Item {
|
||||
|
||||
function selectPage(delta) {
|
||||
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 newIndex = selectedIndex + delta * columns * visibleRows
|
||||
if (newIndex < 0) newIndex = 0
|
||||
@@ -126,6 +145,7 @@ Item {
|
||||
function setFilter(nextFilter) {
|
||||
root.filterText = nextFilter
|
||||
root.selectedIndex = 0
|
||||
root.cursorActive = false
|
||||
root.rebuildDisplay()
|
||||
}
|
||||
|
||||
@@ -220,7 +240,8 @@ Item {
|
||||
root.selectPage(1)
|
||||
event.accepted = true
|
||||
} 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
|
||||
} else if (event.text && event.text.length === 1 && event.text.charCodeAt(0) >= 32 && event.text.charCodeAt(0) !== 127) {
|
||||
root.setFilter(root.filterText + event.text)
|
||||
@@ -271,12 +292,14 @@ Item {
|
||||
required property int index
|
||||
required property string emoji
|
||||
|
||||
readonly property bool hasCursor: root.cursorActive && index === root.selectedIndex
|
||||
|
||||
width: root.cellWidth
|
||||
height: root.cellHeight
|
||||
radius: root.cornerRadius
|
||||
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0
|
||||
color: hasCursor ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: hasCursor ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: hasCursor ? Style.hoverBorderWidth : 0
|
||||
|
||||
Text {
|
||||
text: parent.emoji
|
||||
@@ -292,8 +315,12 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onContainsMouseChanged: if (containsMouse) root.selectedIndex = index
|
||||
onContainsMouseChanged: if (containsMouse) {
|
||||
root.cursorActive = true
|
||||
root.selectedIndex = index
|
||||
}
|
||||
onClicked: {
|
||||
root.cursorActive = true
|
||||
root.selectedIndex = index
|
||||
root.activateIndex(index)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ Item {
|
||||
property string activeMenu: "root"
|
||||
property string filterText: ""
|
||||
property int selectedIndex: 0
|
||||
property bool cursorActive: false
|
||||
property int requestSerial: 0
|
||||
property int applySerial: 0
|
||||
property var items: ({})
|
||||
@@ -537,13 +538,19 @@ Item {
|
||||
function select(delta) {
|
||||
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)
|
||||
}
|
||||
|
||||
function setFilter(nextFilter) {
|
||||
root.filterText = nextFilter
|
||||
root.selectedIndex = 0
|
||||
root.cursorActive = false
|
||||
if (root.filterText.trim()) root.loadProvidersForSearch()
|
||||
root.rebuildDisplay()
|
||||
}
|
||||
@@ -554,6 +561,7 @@ Item {
|
||||
root.activeMenu = id
|
||||
root.filterText = ""
|
||||
root.selectedIndex = 0
|
||||
root.cursorActive = false
|
||||
root.rebuildDisplay()
|
||||
root.loadProviderForMenu(id)
|
||||
}
|
||||
@@ -605,6 +613,7 @@ Item {
|
||||
navStack = []
|
||||
filterText = ""
|
||||
selectedIndex = 0
|
||||
cursorActive = false
|
||||
root.evaluateGuards()
|
||||
opened = true
|
||||
rebuildDisplay()
|
||||
@@ -842,7 +851,8 @@ Item {
|
||||
if (!root.filterText) root.goBack()
|
||||
event.accepted = true
|
||||
} 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
|
||||
} 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)
|
||||
@@ -922,12 +932,14 @@ Item {
|
||||
required property string action
|
||||
required property int childCount
|
||||
|
||||
readonly property bool hasCursor: root.cursorActive && row.index === root.selectedIndex
|
||||
|
||||
width: ListView.view.width
|
||||
height: root.rowHeightForDetail(row.detail)
|
||||
radius: root.cornerRadius
|
||||
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0
|
||||
color: row.hasCursor ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: row.hasCursor ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: row.hasCursor ? Style.hoverBorderWidth : 0
|
||||
|
||||
Rectangle {
|
||||
visible: false
|
||||
@@ -943,7 +955,7 @@ Item {
|
||||
Text {
|
||||
id: iconText
|
||||
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
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.iconLarge
|
||||
@@ -968,7 +980,7 @@ Item {
|
||||
id: labelText
|
||||
width: parent.width
|
||||
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.pixelSize: Style.font.heading
|
||||
font.weight: Font.Medium
|
||||
@@ -1007,7 +1019,7 @@ Item {
|
||||
|
||||
Text {
|
||||
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
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
@@ -1021,7 +1033,10 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user