From 34a6bbfb32b549e5142b1ca5606491b119e7c986 Mon Sep 17 00:00:00 2001 From: Jonas Bloch <128738169+Silzinc@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:42:45 +0100 Subject: [PATCH] feat: Keybinds cheatsheet search (#1706) * feat(wip): add fuzzy finder in keybinds cheatsheet * fix: replace GridLayout with RowLayout and don't use anchors in KeybindsModal * fix: replace fuzzyfinder with simple inclusion criterion for keybind search * fix: bring back categoryKeys (there was no reason to remove it) --- quickshell/Modals/KeybindsModal.qml | 52 ++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/quickshell/Modals/KeybindsModal.qml b/quickshell/Modals/KeybindsModal.qml index 8caf3bd5..5359ff96 100644 --- a/quickshell/Modals/KeybindsModal.qml +++ b/quickshell/Modals/KeybindsModal.qml @@ -1,4 +1,6 @@ +import QtQml import QtQuick +import QtQuick.Layouts import Quickshell.Hyprland import qs.Common import qs.Modals.Common @@ -69,11 +71,32 @@ DankModal { anchors.margins: Theme.spacingL spacing: Theme.spacingL - StyledText { - text: KeybindsService.cheatsheet.title || "Keybinds" - font.pixelSize: Theme.fontSizeLarge - font.weight: Font.Bold - color: Theme.primary + RowLayout { + width: parent.width + + StyledText { + Layout.alignment: Qt.AlignLeft + text: KeybindsService.cheatsheet.title || "Keybinds" + font.pixelSize: Theme.fontSizeLarge + font.weight: Font.Bold + color: Theme.primary + } + + DankTextField { + id: searchField + Layout.alignment: Qt.AlignRight + leftIconName: "search" + onTextEdited: searchDebounce.restart() + } + } + + Timer { + id: searchDebounce + interval: 50 + repeat: false + onTriggered: { + mainFlickable.categories = mainFlickable.generateCategories(searchField.text); + } } DankFlickable { @@ -87,17 +110,26 @@ DankModal { Component.onCompleted: root.activeFlickable = mainFlickable property var rawBinds: KeybindsService.cheatsheet.binds || {} - property var categories: { + + function generateCategories(query) { + const lowerQuery = query ? query.toLowerCase().trim() : ""; const processed = {}; + for (const cat in rawBinds) { const binds = rawBinds[cat]; + const catLower = cat.toLowerCase(); const subcats = {}; let hasSubcats = false; - for (let i = 0; i < binds.length; i++) { const bind = binds[i]; + const keyLower = bind.key.toLowerCase(); + const descLower = bind.desc.toLowerCase(); + const actionLower = bind.action.toLowerCase(); + if (!(lowerQuery.length === 0 || keyLower.includes(lowerQuery) || descLower.includes(lowerQuery) || catLower.includes(lowerQuery) || actionLower.includes(lowerQuery))) + continue; if (bind.hideOnOverlay) continue; + if (bind.subcat) { hasSubcats = true; if (!subcats[bind.subcat]) @@ -119,9 +151,11 @@ DankModal { subcatKeys: Object.keys(subcats) }; } + return processed; } - property var categoryKeys: Object.keys(categories) + + property var categories: generateCategories(""); function estimateCategoryHeight(catName) { const catData = categories[catName]; @@ -136,6 +170,8 @@ DankModal { return 40 + bindCount * 28; } + property var categoryKeys: Object.keys(categories); + function distributeCategories(cols) { const columns = []; const heights = [];