diff --git a/quickshell/Modules/DankBar/Widgets/KeyboardLayoutName.qml b/quickshell/Modules/DankBar/Widgets/KeyboardLayoutName.qml index f7c0aa59a..9f7cb2f48 100644 --- a/quickshell/Modules/DankBar/Widgets/KeyboardLayoutName.qml +++ b/quickshell/Modules/DankBar/Widgets/KeyboardLayoutName.qml @@ -1,6 +1,7 @@ import QtQuick import Quickshell import Quickshell.Hyprland +import Quickshell.I3 import qs.Common import qs.Modules.Plugins import qs.Services @@ -110,6 +111,27 @@ BasePill { Quickshell.execDetached(["hyprctl", "switchxkblayout", root.hyprlandKeyboard, "next"]); } else if (CompositorService.isMango) { MangoService.cycleKeyboardLayout(); + } else if (CompositorService.isSway) { + I3.dispatch("input type:keyboard xkb_switch_layout next"); + } + } + } + + Loader { + active: CompositorService.isSway + sourceComponent: I3IpcListener { + subscriptions: ["input"] + onIpcEvent: event => { + if (event.type !== "input") + return; + try { + const payload = JSON.parse(event.data); + if (payload.change !== "xkb_layout") + return; + const name = payload.input?.xkb_active_layout_name; + if (name) + root.currentLayout = name; + } catch (e) {} } } } @@ -126,12 +148,25 @@ BasePill { } Component.onCompleted: { - if (CompositorService.isHyprland) { + if (CompositorService.isHyprland || CompositorService.isSway) { updateLayout(); } } function updateLayout() { + if (CompositorService.isSway) { + Proc.runCommand(null, ["swaymsg", "-t", "get_inputs", "-r"], (output, exitCode) => { + if (exitCode !== 0) + return; + try { + const inputs = JSON.parse(output); + const kb = inputs.find(i => i.type === "keyboard" && i.xkb_active_layout_name); + if (kb) + root.currentLayout = kb.xkb_active_layout_name; + } catch (e) {} + }); + return; + } if (CompositorService.isHyprland) { Proc.runCommand(null, ["hyprctl", "-j", "devices"], (output, exitCode) => { if (exitCode !== 0) { diff --git a/quickshell/translations/check_term_freeze.py b/quickshell/translations/check_term_freeze.py index dcddaeb68..7b070eb7d 100644 --- a/quickshell/translations/check_term_freeze.py +++ b/quickshell/translations/check_term_freeze.py @@ -18,9 +18,18 @@ ROOT_DIR = SCRIPT_DIR.parent FREEZE_FILE = SCRIPT_DIR / 'term_freeze.json' + +# dms-plugins is a separate checkout with its own release cadence; the freeze +# only guards this repo's terms. Catalog extraction still includes plugins. +def shell_terms(translations): + return { + term: info for term, info in translations.items() + if any(not occ['file'].startswith('dms-plugins/') for occ in info['occurrences']) + } + def main(): if '--update' in sys.argv: - translations = extract_qstr_strings(ROOT_DIR) + translations = shell_terms(extract_qstr_strings(ROOT_DIR)) FREEZE_FILE.write_text(json.dumps(sorted(translations), indent=2, ensure_ascii=False) + '\n', encoding='utf-8') print(f"Froze {len(translations)} terms to {FREEZE_FILE}") return 0 @@ -30,7 +39,7 @@ def main(): return 0 frozen = set(json.loads(FREEZE_FILE.read_text(encoding='utf-8'))) - translations = extract_qstr_strings(ROOT_DIR) + translations = shell_terms(extract_qstr_strings(ROOT_DIR)) new_terms = sorted(term for term in translations if term not in frozen) if not new_terms: return 0