Launcher: Restored keyboard navigation with PageUp/PageDown/Home/End + Vim Keys
Oddly Ctrl+J does not work for me...
This commit is contained in:
parent
392f0e14b2
commit
65f73bb1ba
2 changed files with 95 additions and 56 deletions
|
|
@ -42,6 +42,9 @@ NPanel {
|
||||||
property var plugins: []
|
property var plugins: []
|
||||||
property var activePlugin: null
|
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
|
// Public API for plugins
|
||||||
function setSearchText(text) {
|
function setSearchText(text) {
|
||||||
searchText = 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
|
// Load plugins
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Load applications plugin
|
// Load applications plugin
|
||||||
|
|
@ -171,12 +150,56 @@ NPanel {
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
panelContent: Rectangle {
|
panelContent: Rectangle {
|
||||||
|
id: ui
|
||||||
color: Color.transparent
|
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
|
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
|
anchors.margins: Style.marginL * scaling
|
||||||
spacing: Style.marginM * scaling
|
spacing: Style.marginM * scaling
|
||||||
|
|
||||||
FocusScope {
|
Item {
|
||||||
id: searchInputWrap
|
id: searchInputWrap
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Math.round(Style.barHeight * scaling)
|
Layout.preferredHeight: Math.round(Style.barHeight * scaling)
|
||||||
|
|
||||||
// This FocusScope should get focus when panel opens
|
|
||||||
focus: true
|
|
||||||
|
|
||||||
NTextInput {
|
NTextInput {
|
||||||
id: searchInput
|
id: searchInput
|
||||||
anchors.fill: parent
|
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
|
inputMaxWidth: Number.MAX_SAFE_INTEGER
|
||||||
|
|
||||||
function forceActiveFocus() {
|
fontSize: Style.fontSizeL * scaling
|
||||||
// First ensure the scope has focus
|
fontWeight: Style.fontWeightSemiBold
|
||||||
searchInputWrap.forceActiveFocus()
|
|
||||||
// Then focus the actual input
|
text: searchText
|
||||||
if (inputItem && inputItem.visible) {
|
placeholderText: "Search entries... or use > for commands"
|
||||||
inputItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (inputItem) {
|
if (searchInput.inputItem && searchInput.inputItem.visible) {
|
||||||
inputItem.font.pointSize = Style.fontSizeL * scaling
|
searchInput.inputItem.forceActiveFocus()
|
||||||
inputItem.verticalAlignment = TextInput.AlignVCenter
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: searchText = text
|
onTextChanged: searchText = text
|
||||||
|
|
||||||
Keys.onDownPressed: root.selectNext()
|
|
||||||
Keys.onUpPressed: root.selectPrev()
|
|
||||||
Keys.onReturnPressed: root.activate()
|
|
||||||
Keys.onEscapePressed: root.close()
|
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
|
id: entry
|
||||||
|
|
||||||
property bool isSelected: mouseArea.containsMouse || (index === selectedIndex)
|
property bool isSelected: mouseArea.containsMouse || (index === selectedIndex)
|
||||||
property int badgeSize: Style.baseWidgetSize * 1.6 * scaling
|
|
||||||
|
|
||||||
// Property to reliably track the current item's ID.
|
// Property to reliably track the current item's ID.
|
||||||
// This changes whenever the delegate is recycled for a new item.
|
// This changes whenever the delegate is recycled for a new item.
|
||||||
|
|
@ -272,7 +308,7 @@ NPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
width: resultsList.width - Style.marginS * scaling
|
width: resultsList.width - Style.marginS * scaling
|
||||||
height: badgeSize + Style.marginM * 2 * scaling
|
height: entryHeight
|
||||||
radius: Style.radiusM * scaling
|
radius: Style.radiusM * scaling
|
||||||
color: entry.isSelected ? Color.mTertiary : Color.mSurface
|
color: entry.isSelected ? Color.mTertiary : Color.mSurface
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,12 @@ ColumnLayout {
|
||||||
property string description: ""
|
property string description: ""
|
||||||
property bool readOnly: false
|
property bool readOnly: false
|
||||||
property bool enabled: true
|
property bool enabled: true
|
||||||
property int inputMaxWidth: 420 * scaling
|
property int inputMaxWidth: Math.round(420 * scaling)
|
||||||
property color labelColor: Color.mOnSurface
|
property color labelColor: Color.mOnSurface
|
||||||
property color descriptionColor: Color.mOnSurfaceVariant
|
property color descriptionColor: Color.mOnSurfaceVariant
|
||||||
property string fontFamily: Settings.data.ui.fontDefault
|
property string fontFamily: Settings.data.ui.fontDefault
|
||||||
|
property real fontSize: Style.fontSizeS * scaling
|
||||||
|
property int fontWeight: Style.fontWeightRegular
|
||||||
|
|
||||||
property alias text: input.text
|
property alias text: input.text
|
||||||
property alias placeholderText: input.placeholderText
|
property alias placeholderText: input.placeholderText
|
||||||
|
|
@ -77,7 +79,8 @@ ColumnLayout {
|
||||||
placeholderTextColor: Color.mOnSurfaceVariant
|
placeholderTextColor: Color.mOnSurfaceVariant
|
||||||
background: null
|
background: null
|
||||||
font.family: fontFamily
|
font.family: fontFamily
|
||||||
font.pointSize: Style.fontSizeS * scaling
|
font.pointSize: fontSize
|
||||||
|
font.weight: fontWeight
|
||||||
onEditingFinished: root.editingFinished()
|
onEditingFinished: root.editingFinished()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue