Krolyxon
Commit c1f15d7

Branches

Refs →

aurorac1f15d7

aurora: vim keys and /-search in the wallpaper picker

Author
krolyxon
Date
2026-10-17 15:58 +0530
Parent
e3b4e57
Commit
c1f15d78aa9eea5950c09efebc96f0ca2d417f71

+62 −15

1 file changed

  1. M .config/quickshell/aurora/WallPicker.qml +62 −15
M .config/quickshell/aurora/WallPicker.qml View
@@ -4,8 +4,10 @@ import Quickshell
44 import Quickshell.Wayland
55 import Quickshell.Hyprland
66
7−// Wallpaper picker: folder chips, filter, thumbnail grid. Enter / double-click applies (stays open
8−// so you can try a few), Esc or click outside closes. Tab / Shift+Tab cycles folders.
7+// Wallpaper picker: folder chips, filter, thumbnail grid. Vim-style: the grid has the keyboard
8+// (hjkl/arrows move, g/G first/last, ctrl+d/u page), `/` starts searching (enter keeps the filter,
9+// esc clears it). Enter / double-click applies (stays open so you can try a few), Esc / q or click
10+// outside closes. Tab / Shift+Tab cycles folders.
911 PanelWindow {
1012 id: win
1113 property bool opened: false
@@ -30,7 +32,19 @@ PanelWindow {
3032 if (!opened) return
3133 filter.text = ""
3234 Wall.refresh()
33− filter.forceActiveFocus()
35+ grid.forceActiveFocus()
36+ }
37+
38+ // search mode = the filter box has the keyboard (entered with `/` or by clicking it)
39+ readonly property bool searching: filter.activeFocus
40+ function search() { filter.forceActiveFocus() }
41+ function endSearch(clear) {
42+ if (clear) filter.text = ""
43+ grid.forceActiveFocus()
44+ }
45+ function applyCurrent() { if (grid.currentIndex >= 0 && grid.currentIndex < shown.length) Wall.apply(shown[grid.currentIndex]) }
46+ function page(d) {
47+ grid.currentIndex = Math.max(0, Math.min(shown.length - 1, grid.currentIndex + d * grid.perPage))
3448 }
3549
3650 readonly property var shown: {
@@ -107,7 +121,18 @@ PanelWindow {
107121 anchors.left: parent.left
108122 anchors.leftMargin: 10
109123 anchors.verticalCenter: parent.verticalCenter
110− text: Theme.iSearch
124+ text: win.searching ? "/" : Theme.iSearch
125+ color: win.searching ? Theme.accent : Theme.dim
126+ font.family: Theme.font
127+ font.pixelSize: Theme.fontSize
128+ font.bold: win.searching
129+ }
130+ Text {
131+ anchors.left: parent.left
132+ anchors.leftMargin: 32
133+ anchors.verticalCenter: parent.verticalCenter
134+ visible: filter.text === "" && !win.searching
135+ text: "press / to search"
111136 color: Theme.dim
112137 font.family: Theme.font
113138 font.pixelSize: Theme.fontSize
@@ -125,19 +150,14 @@ PanelWindow {
125150 font.pixelSize: Theme.fontSize
126151 clip: true
127152 onTextChanged: grid.currentIndex = 0
128− Keys.onEscapePressed: win.closeRequested()
129− Keys.onReturnPressed: if (grid.currentIndex >= 0) Wall.apply(win.shown[grid.currentIndex])
130− Keys.onEnterPressed: if (grid.currentIndex >= 0) Wall.apply(win.shown[grid.currentIndex])
131− Keys.onLeftPressed: event => { if (text === "") grid.moveCurrentIndexLeft(); else event.accepted = false }
132− Keys.onRightPressed: event => { if (text === "") grid.moveCurrentIndexRight(); else event.accepted = false }
153+ // like vim's `/`: enter keeps the filter and goes back to the grid, esc drops it
154+ Keys.onEscapePressed: win.endSearch(true)
155+ Keys.onReturnPressed: win.endSearch(false)
156+ Keys.onEnterPressed: win.endSearch(false)
133157 Keys.onUpPressed: grid.moveCurrentIndexUp()
134158 Keys.onDownPressed: grid.moveCurrentIndexDown()
135159 Keys.onTabPressed: win.cycleFolder(1)
136160 Keys.onBacktabPressed: win.cycleFolder(-1)
137− Keys.onPressed: event => {
138− if (event.key === Qt.Key_PageDown) { grid.currentIndex = Math.min(win.shown.length - 1, grid.currentIndex + grid.perPage); event.accepted = true }
139− else if (event.key === Qt.Key_PageUp) { grid.currentIndex = Math.max(0, grid.currentIndex - grid.perPage); event.accepted = true }
140− }
141161 }
142162 }
143163 Text {
@@ -165,6 +185,31 @@ PanelWindow {
165185 highlightMoveDuration: 0
166186 cacheBuffer: cellHeight * 2
167187
188+ // normal mode (arrows are handled by GridView itself)
189+ Keys.onTabPressed: win.cycleFolder(1)
190+ Keys.onBacktabPressed: win.cycleFolder(-1)
191+ Keys.onPressed: event => {
192+ var ctrl = event.modifiers & Qt.ControlModifier
193+ event.accepted = true
194+ if (event.key === Qt.Key_Escape) {
195+ if (filter.text !== "") filter.text = "" // first esc drops an active filter
196+ else win.closeRequested()
197+ }
198+ else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) win.applyCurrent()
199+ else if (event.key === Qt.Key_PageDown || (ctrl && event.key === Qt.Key_D)) win.page(1)
200+ else if (event.key === Qt.Key_PageUp || (ctrl && event.key === Qt.Key_U)) win.page(-1)
201+ else if (ctrl) event.accepted = false
202+ else if (event.text === "/") win.search()
203+ else if (event.text === "h") grid.moveCurrentIndexLeft()
204+ else if (event.text === "j") grid.moveCurrentIndexDown()
205+ else if (event.text === "k") grid.moveCurrentIndexUp()
206+ else if (event.text === "l") grid.moveCurrentIndexRight()
207+ else if (event.text === "g") grid.currentIndex = 0
208+ else if (event.text === "G") grid.currentIndex = win.shown.length - 1
209+ else if (event.text === "q") win.closeRequested()
210+ else event.accepted = false
211+ }
212+
168213 delegate: Item {
169214 id: cell
170215 required property var modelData
@@ -218,7 +263,7 @@ PanelWindow {
218263 MouseArea {
219264 anchors.fill: parent
220265 cursorShape: Qt.PointingHandCursor
221− onClicked: grid.currentIndex = cell.index
266+ onClicked: { grid.currentIndex = cell.index; grid.forceActiveFocus() }
222267 onDoubleClicked: Wall.apply(cell.modelData)
223268 }
224269 }
@@ -226,7 +271,9 @@ PanelWindow {
226271
227272 Text {
228273 Layout.fillWidth: true
229− text: "enter apply tab folder arrows move esc close " + Theme.iImage + " " + win.name(Wall.current)
274+ text: (win.searching ? "-- SEARCH -- enter done esc clear ↑↓ move "
275+ : "hjkl move / search enter apply tab folder g/G first/last esc close ")
276+ + Theme.iImage + " " + win.name(Wall.current)
230277 elide: Text.ElideRight
231278 color: Theme.dim
232279 font.family: Theme.font