From b81dc1ef2b5b29c9e9e8d2d3fa6fb1a7ecfc2110 Mon Sep 17 00:00:00 2001 From: WojciechSulocki-Gif Date: Tue, 4 Aug 2026 22:06:06 +0200 Subject: [PATCH] fix(keymap): make modifier layers discoverable --- keymap/README.md | 2 + keymap/panel.luau | 75 +++++++++++++++++++++++++++++++++++-- keymap/plugin.toml | 2 +- keymap/translations/en.json | 2 + 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/keymap/README.md b/keymap/README.md index 6861cec8..40585417 100644 --- a/keymap/README.md +++ b/keymap/README.md @@ -102,6 +102,8 @@ In keyboard view, enable an exact Super, Ctrl, Shift, and Alt layer. Occupied keys open the shortcuts assigned to that combination; unoccupied keys can be sent directly to the creator. Change the physical layout from the keyboard size selector while editing shortcuts, or set its default in plugin settings. +When a key is occupied in another modifier layer, select it and use the layer +buttons in the details card to jump directly to the matching combination. In list view, type into the search box to filter the complete category tree. Sequential shortcuts such as workspaces 1 through 9 can optionally be folded diff --git a/keymap/panel.luau b/keymap/panel.luau index 78b95943..28989a17 100644 --- a/keymap/panel.luau +++ b/keymap/panel.luau @@ -286,6 +286,33 @@ local function activeModifierSignature() return table.concat(ordered, "+") end +local function modifierSignatureLabel(signature) + if signature == "" then return tr("panel.keyboard.no_modifiers") end + local labels = {} + for modifier in tostring(signature):gmatch("[^+]+") do + labels[#labels + 1] = modifier == "CTRL" and "Ctrl" + or (modifier == "SUPER" and "Super" or (modifier == "SHIFT" and "Shift" or ( + modifier == "ALT" and "Alt" or modifier + ))) + end + return table.concat(labels, " + ") +end + +local function modifierSignatureSelectable(signature) + for modifier in tostring(signature):gmatch("[^+]+") do + if activeModifiers[modifier] == nil then return false end + end + return true +end + +local function selectModifierSignature(signature) + local selected = {} + for modifier in tostring(signature):gmatch("[^+]+") do selected[modifier] = true end + for _, modifier in ipairs(MODIFIER_ORDER) do + activeModifiers[modifier] = selected[modifier] == true + end +end + local function expandedKeys(value) local canonical = canonicalKey(value) local first, last = canonical:match("^(%d)%-(%d)$") @@ -341,6 +368,19 @@ local function keyCallback(id, code) return callback end +local layerCallbackCache = {} +local function layerCallback(signature) + local callback = layerCallbackCache[signature] + if callback == nil then + callback = function() + selectModifierSignature(signature) + render() + end + layerCallbackCache[signature] = callback + end + return callback +end + local function contains(haystack, needle) return string.find(normalized(haystack), needle, 1, true) ~= nil end @@ -1059,6 +1099,17 @@ local function selectedKeyDetails(index) local signature = activeModifierSignature() local entries = asArray(index.exact[signature .. "|" .. selectedKeyboardKey]) + local otherSignatures = {} + local seenSignatures = {} + for _, entry in ipairs(asArray(index.any[selectedKeyboardKey])) do + local entrySignature = modifierSignature(entry.bind.modifiers) + if entrySignature ~= signature and not seenSignatures[entrySignature] then + seenSignatures[entrySignature] = true + otherSignatures[#otherSignatures + 1] = entrySignature + end + end + table.sort(otherSignatures) + local usedOnAnotherLayer = #entries == 0 and #otherSignatures > 0 local chordParts = {} for _, modifier in ipairs(MODIFIER_ORDER) do if activeModifiers[modifier] then @@ -1073,8 +1124,11 @@ local function selectedKeyDetails(index) ui.row({ gap = 8, align = "center" }, { ui.label({ text = chord, color = "on_surface", fontSize = 13, fontWeight = "bold", flexGrow = 1 }), ui.label({ - text = #entries > 0 and tr("panel.keyboard.occupied") or tr("panel.keyboard.free"), - color = #entries > 0 and asString(cfg("category_color"), "primary") or "on_surface_variant", + text = #entries > 0 and tr("panel.keyboard.occupied") or ( + usedOnAnotherLayer and tr("panel.keyboard.other_layer") or tr("panel.keyboard.free") + ), + color = (#entries > 0 or usedOnAnotherLayer) + and asString(cfg("category_color"), "primary") or "on_surface_variant", fontSize = 11, fontWeight = "bold", }), @@ -1082,11 +1136,26 @@ local function selectedKeyDetails(index) } if #entries == 0 then children[#children + 1] = ui.label({ - text = tr("panel.keyboard.free_hint"), + text = tr(usedOnAnotherLayer and "panel.keyboard.other_layer_hint" or "panel.keyboard.free_hint"), color = "on_surface_variant", fontSize = 11, maxLines = 2, }) + if usedOnAnotherLayer then + local layerButtons = { + ui.label({ text = tr("panel.keyboard.available_layers"), color = "on_surface_variant", fontSize = 10 }), + } + for _, otherSignature in ipairs(otherSignatures) do + layerButtons[#layerButtons + 1] = ui.button({ + text = modifierSignatureLabel(otherSignature), + variant = "outline", + controlSize = "sm", + enabled = modifierSignatureSelectable(otherSignature), + onClick = layerCallback(otherSignature), + }) + end + children[#children + 1] = ui.row({ gap = 6, align = "center" }, layerButtons) + end else for entryIndex, entry in ipairs(entries) do if entryIndex > 5 then diff --git a/keymap/plugin.toml b/keymap/plugin.toml index 97dd27da..f83d1617 100644 --- a/keymap/plugin.toml +++ b/keymap/plugin.toml @@ -1,6 +1,6 @@ id = "blackbartblues/keymap" name = "Keymap" -version = "1.3.5" +version = "1.3.6" plugin_api = 9 author = "blackbartblues" license = "MIT" diff --git a/keymap/translations/en.json b/keymap/translations/en.json index 197b0086..055b8559 100644 --- a/keymap/translations/en.json +++ b/keymap/translations/en.json @@ -289,6 +289,7 @@ "unknown": "The service returned an unknown error." }, "keyboard": { + "available_layers": "Used with", "clear_modifiers": "Clear modifiers", "free": "Free", "free_hint": "This combination is not used in the currently selected layer.", @@ -307,6 +308,7 @@ "no_modifiers": "No modifiers", "occupied": "Occupied", "other_layer": "Used on another layer", + "other_layer_hint": "This key is assigned with different modifiers. Choose a layer below to view its shortcuts.", "outside": "{count} shortcuts outside the keyboard view", "select_hint": "Select modifiers, then click a key to inspect that combination.", "summary": "{layer}: {count} occupied keys",