diff --git a/default/input-methods/indicator.py b/default/input-methods/indicator.py index be6451ac..bdd12c05 100644 --- a/default/input-methods/indicator.py +++ b/default/input-methods/indicator.py @@ -114,7 +114,8 @@ class Indicator: def select_next(self): state = snapshot(self.bus) methods = state["methods"] - state["current"] = self.pending or state["current"] or self.last or next(iter(methods), "") + current = self.pending or state["current"] or self.last + state["current"] = current if current in methods else next(iter(methods), "") devices = subprocess.run(["hyprctl", "-j", "devices"], check=True, text=True, capture_output=True) keyboards = json.loads(devices.stdout).get("keyboards", []) following, index = cycle_choice(state, keyboards) @@ -187,7 +188,7 @@ def watch(bus): if line == "cycle": try: indicator.select_next() - except GLib.Error: + except (GLib.Error, OSError, ValueError, subprocess.CalledProcessError): indicator.pending = "" refresh() GLib.source_remove(timer) diff --git a/shell/plugins/bar/widgets/KeyboardLayout.qml b/shell/plugins/bar/widgets/KeyboardLayout.qml index f6db826d..f9fe0781 100644 --- a/shell/plugins/bar/widgets/KeyboardLayout.qml +++ b/shell/plugins/bar/widgets/KeyboardLayout.qml @@ -244,7 +244,7 @@ BarWidget { horizontalMargin: 6 tooltipText: KeyboardLayoutModel.inputTooltip(root.inputState, root.layoutFull, root.multipleLayouts) onPressed: function(button) { - if ((root.multipleInputs || root.multipleLayouts) && button === Qt.LeftButton) { + if (root.multipleInputs && button === Qt.LeftButton) { InputMethodState.cycle() } else if (root.multipleLayouts) { root.cycleLayout() diff --git a/test/shell.d/input-indicator-test.sh b/test/shell.d/input-indicator-test.sh index dfadff2f..8e91a79c 100755 --- a/test/shell.d/input-indicator-test.sh +++ b/test/shell.d/input-indicator-test.sh @@ -140,6 +140,7 @@ def fake_process(command, **kwargs): return subprocess.CompletedProcess(command, 0, stdout="") with patch.object(indicator, "snapshot", side_effect=lambda bus: dict(live)), patch.object(indicator, "call", side_effect=fake_call), patch.object(indicator.subprocess, "run", side_effect=fake_process): reader = indicator.Indicator(None) + reader.last = "removed-engine" reader.select_next() assert keyboards[0]["active_layout_index"] == 1 reader.select_next() @@ -153,4 +154,20 @@ with patch.object(indicator, "snapshot", side_effect=lambda bus: dict(live)), pa assert selected[-1] == "keyboard-us" print("ok - mixed-layout bar clicks queue an engine without focus and apply it when a text field gains focus") +from unittest.mock import Mock +callback = {} +def register(source, flags, fn): + callback["read"] = fn + return 1 +def exercise(): + assert callback["read"](None, None) == indicator.GLib.SOURCE_CONTINUE + assert callback["read"](None, None) == indicator.GLib.SOURCE_CONTINUE +state = {"methods": ["keyboard-us", "pinyin"], "current": "keyboard-us", "name": "English", "label": "en", "language": "en"} +loop = Mock() +loop.run.side_effect = exercise +with patch.object(indicator.GLib, "MainLoop", return_value=loop), patch.object(indicator.GLib, "io_add_watch", side_effect=register), patch.object(indicator.GLib, "timeout_add", return_value=1), patch.object(indicator.GLib, "source_remove"), patch.object(indicator.os, "read", return_value=b"cycle\n"), patch.object(indicator, "snapshot", return_value=state), patch.object(indicator.subprocess, "run", side_effect=[subprocess.CalledProcessError(1, "hyprctl"), subprocess.CompletedProcess([], 0, stdout='{"keyboards": []}')]), patch.object(indicator, "call") as call, patch("builtins.print"): + indicator.watch(None) + assert call.call_args.args[1] == "SetCurrentIM" +print("ok - a failed compositor query leaves the bar click reader alive for the next request") + PY