Launcher: Restored keyboard navigation with PageUp/PageDown/Home/End + Vim Keys

Oddly Ctrl+J does not work for me...
This commit is contained in:
LemmyCook 2025-09-03 17:02:05 -04:00
parent 392f0e14b2
commit 65f73bb1ba
2 changed files with 95 additions and 56 deletions

View file

@ -42,6 +42,9 @@ NPanel {
property var plugins: []
property var activePlugin: null
readonly property int badgeSize: Math.round(Style.baseWidgetSize * 1.6 * scaling)
readonly property int entryHeight: Math.round(badgeSize + Style.marginM * 2 * scaling)
// Public API for plugins
function setSearchText(text) {
searchText = text
@ -115,30 +118,6 @@ NPanel {
}
}
// Navigation
function selectNext() {
if (results.length > 0) {
// Clamp the index to not exceed the last item
selectedIndex = Math.min(selectedIndex + 1, results.length - 1)
}
}
function selectPrev() {
if (results.length > 0) {
// Clamp the index to not go below the first item (0)
selectedIndex = Math.max(selectedIndex - 1, 0)
}
}
function activate() {
if (results.length > 0 && results[selectedIndex]) {
const item = results[selectedIndex]
if (item.onActivate) {
item.onActivate()
}
}
}
// Load plugins
Component.onCompleted: {
// Load applications plugin
@ -171,12 +150,56 @@ NPanel {
// UI
panelContent: Rectangle {
id: ui
color: Color.transparent
Component.onCompleted: {
// ---------------------
// Navigation
function selectNext() {
if (results.length > 0) {
// Clamp the index to not exceed the last item
selectedIndex = Math.min(selectedIndex + 1, results.length - 1)
}
}
function selectPrevious() {
if (results.length > 0) {
// Clamp the index to not go below the first item (0)
selectedIndex = Math.max(selectedIndex - 1, 0)
}
}
function selectFirst() {
selectedIndex = 0
if (searchInput?.forceActiveFocus) {
searchInput.forceActiveFocus()
}
function selectLast() {
if (results.length > 0) {
selectedIndex = results.length - 1
} else {
selectedIndex = 0
}
}
function selectNextPage() {
if (results.length > 0) {
const page = Math.max(1, Math.floor(resultsList.height / entryHeight))
selectedIndex = Math.min(selectedIndex + page, results.length - 1)
}
}
function selectPreviousPage() {
if (results.length > 0) {
const page = Math.max(1, Math.floor(resultsList.height / entryHeight))
selectedIndex = Math.max(selectedIndex - page, 0)
}
}
function activate() {
if (results.length > 0 && results[selectedIndex]) {
const item = results[selectedIndex]
if (item.onActivate) {
item.onActivate()
}
}
}
@ -185,47 +208,61 @@ NPanel {
anchors.margins: Style.marginL * scaling
spacing: Style.marginM * scaling
FocusScope {
Item {
id: searchInputWrap
Layout.fillWidth: true
Layout.preferredHeight: Math.round(Style.barHeight * scaling)
// This FocusScope should get focus when panel opens
focus: true
NTextInput {
id: searchInput
anchors.fill: parent
// The input should have focus within the scope
focus: true
placeholderText: "Search entries... or use > for commands"
text: searchText
inputMaxWidth: Number.MAX_SAFE_INTEGER
function forceActiveFocus() {
// First ensure the scope has focus
searchInputWrap.forceActiveFocus()
// Then focus the actual input
if (inputItem && inputItem.visible) {
inputItem.forceActiveFocus()
}
}
fontSize: Style.fontSizeL * scaling
fontWeight: Style.fontWeightSemiBold
text: searchText
placeholderText: "Search entries... or use > for commands"
Component.onCompleted: {
if (inputItem) {
inputItem.font.pointSize = Style.fontSizeL * scaling
inputItem.verticalAlignment = TextInput.AlignVCenter
if (searchInput.inputItem && searchInput.inputItem.visible) {
searchInput.inputItem.forceActiveFocus()
}
}
onTextChanged: searchText = text
Keys.onDownPressed: root.selectNext()
Keys.onUpPressed: root.selectPrev()
Keys.onReturnPressed: root.activate()
Keys.onEscapePressed: root.close()
Keys.onReturnPressed: ui.activate()
Keys.onDownPressed: ui.selectNext()
Keys.onUpPressed: ui.selectPrevious()
Keys.onPressed: event => {
if (event.key === Qt.Key_PageDown) {
ui.selectNextPage()
event.accepted = true
} else if (event.key === Qt.Key_PageUp) {
ui.selectPreviousPage()
event.accepted = true
} else if (event.key === Qt.Key_Home) {
ui.selectFirst()
event.accepted = true
} else if (event.key === Qt.Key_End) {
ui.selectLast()
event.accepted = true
}
if (event.modifiers & Qt.ControlModifier) {
switch (event.key) {
case Qt.Key_K:
ui.selectPrevious()
event.accepted = true
break
case Qt.Key_J:
ui.selectNext()
event.accepted = true
break
}
}
}
}
}
@ -257,7 +294,6 @@ NPanel {
id: entry
property bool isSelected: mouseArea.containsMouse || (index === selectedIndex)
property int badgeSize: Style.baseWidgetSize * 1.6 * scaling
// Property to reliably track the current item's ID.
// This changes whenever the delegate is recycled for a new item.
@ -272,7 +308,7 @@ NPanel {
}
width: resultsList.width - Style.marginS * scaling
height: badgeSize + Style.marginM * 2 * scaling
height: entryHeight
radius: Style.radiusM * scaling
color: entry.isSelected ? Color.mTertiary : Color.mSurface