* Navigate Elsewhen from the keyboard and summon it with Super+Ctrl+Alt+E Up and down walk home and the cities, lighting the picked row and turning the globe to it. Left and right move the clocks an hour, held until Escape or close. Escape now clears a shifted time before leaving the globe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Reach Add a city from the keyboard and shift time past a day Down past the last city selects Add a city, where Return or Space opens the search. The arrow keys no longer stop at twelve hours, and shifts past a day read in days. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Delete the picked Elsewhen city from the keyboard Delete (or x) removes the picked city and moves the cursor to the one that takes its place. The picked row shows its × so the target is plain. Delete now reaches every panel's deleteRequested, as it already does in the clipboard and the menu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Center bar glyphs on the open-panel underline The 16px icon canvas in the 27px slot was snapped from 5.5 to 6, and the glyph's fractional centering correction was snapped too, so glyphs sat up to 0.9px right of the underline, which centers on the slot. Both are now placed exactly, and the geometry test measures against the slot to within 0.05px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
2.8 KiB
QML
86 lines
2.8 KiB
QML
import QtQuick
|
|
|
|
// Drop-in key dispatcher for keyboard-driven panels. Wraps panel content
|
|
// and emits semantic signals so each panel keeps its own state machine
|
|
// (focusSection, selectedIndex, action rules) while the boilerplate
|
|
// key handling lives here.
|
|
//
|
|
// Usage:
|
|
// Common.KeyboardPanel {
|
|
// ...
|
|
// PanelKeyCatcher {
|
|
// anchors.fill: parent
|
|
// onMoveRequested: function(dx, dy) { root.moveCursor(dx, dy) }
|
|
// onActivateRequested: root.activateCursor()
|
|
// onCloseRequested: root.close()
|
|
// onDeleteRequested: root.deleteSelected()
|
|
// onTextKey: function(t) { if (t === "r") root.refresh() }
|
|
//
|
|
// Column { ... panel content ... }
|
|
// }
|
|
// }
|
|
//
|
|
// Keys.priority: Keys.BeforeItem means this handler gets keys first,
|
|
// even when a descendant has activeFocus. That's what lets Up/Down
|
|
// arrows drive the cursor instead of being consumed by an inner
|
|
// Flickable's built-in scroll handling. When a panel has an inline
|
|
// editor (wifi passphrase, gallery TextField demo) the panel must
|
|
// set `blocked: editor.activeFocus` so this handler short-circuits
|
|
// and the editor receives keys normally.
|
|
//
|
|
// blocked: when true, ALL keys are forwarded to descendants without
|
|
// triggering signals.
|
|
Item {
|
|
id: root
|
|
|
|
property bool blocked: false
|
|
|
|
signal moveRequested(int dx, int dy)
|
|
signal activateRequested()
|
|
signal returnRequested()
|
|
signal closeRequested()
|
|
signal deleteRequested()
|
|
signal tabRequested(int direction)
|
|
signal textKey(string text)
|
|
|
|
focus: true
|
|
Keys.priority: Keys.BeforeItem
|
|
Keys.onPressed: function(event) {
|
|
if (blocked) return
|
|
|
|
if (event.key === Qt.Key_Escape) {
|
|
closeRequested(); event.accepted = true; return
|
|
}
|
|
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) {
|
|
tabRequested((event.modifiers & Qt.ShiftModifier) || event.key === Qt.Key_Backtab ? -1 : 1)
|
|
event.accepted = true
|
|
return
|
|
}
|
|
if (event.key === Qt.Key_Down || event.text === "j") {
|
|
moveRequested(0, 1); event.accepted = true; return
|
|
}
|
|
if (event.key === Qt.Key_Up || event.text === "k") {
|
|
moveRequested(0, -1); event.accepted = true; return
|
|
}
|
|
if (event.key === Qt.Key_Right || event.text === "l") {
|
|
moveRequested(1, 0); event.accepted = true; return
|
|
}
|
|
if (event.key === Qt.Key_Left || event.text === "h") {
|
|
moveRequested(-1, 0); event.accepted = true; return
|
|
}
|
|
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
|
returnRequested()
|
|
activateRequested(); event.accepted = true; return
|
|
}
|
|
if (event.key === Qt.Key_Space) {
|
|
activateRequested(); event.accepted = true; return
|
|
}
|
|
if (event.key === Qt.Key_Delete || event.text === "x" || event.text === "X") {
|
|
deleteRequested(); event.accepted = true; return
|
|
}
|
|
if (event.text && event.text.length === 1) {
|
|
textKey(event.text)
|
|
}
|
|
}
|
|
}
|