From d0d878dec69f6e6f99562601b1d6db1c152f724c Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 11 Sep 2026 17:00:29 +0200 Subject: fix(vm-manager): close on Escape, and stay running Two bugs that both looked like the panel ignoring input. Escape did nothing despite the panel saying it closes. Keys.onEscapePressed was on the PanelWindow, and key events go to a focused item rather than to a window, so it never fired. A filled Item with focus: true now catches it and reclaims focus when a delete confirmation's TextInput releases it. The shell also exited whenever the drawer was closed, which is most of the time: with the LazyLoader inactive there is no visible window, and quickshell quits at that point. Since the keybind reaches the panel over IPC, that left the key talking to nothing. Same 1x1 transparent keepalive window as volume-osd, for the same reason. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01G7NRsGyF9jGfPYS4zPqpN7 --- vm-manager/README.md | 14 ++++++++++++++ vm-manager/VmPanel.qml | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/vm-manager/README.md b/vm-manager/README.md index 1bc20ef..8d817f9 100644 --- a/vm-manager/README.md +++ b/vm-manager/README.md @@ -69,6 +69,20 @@ Statistics do need sampling, on a 2 second timer, but only while the drawer is open: `Virsh.sampling` follows the panel's `open`. A closed panel costs one idle process waiting on an event socket. +## Two things that bite + +**A config with no visible window exits.** The drawer is closed most of the +time, so the shell holds itself open with a 1x1 transparent window with an +empty mask, which is click-through and draws nothing. Without it the shell +loads, reports no error and quits, and since `SUPER+v` reaches it over IPC, +the key then has nothing to talk to and silently does nothing. + +**Key events reach an item, not a window.** Setting `keyboardFocus` on the +layer shell is necessary but not sufficient: `Keys.onEscapePressed` on the +PanelWindow itself never fires, because no item inside holds focus. A filled +`Item` with `focus: true` catches it, and takes focus back when the TextInput +in a delete confirmation gives it up. + ## Destructive actions `Reset`, `Force stop`, snapshot `Revert` and snapshot `Delete` each take one diff --git a/vm-manager/VmPanel.qml b/vm-manager/VmPanel.qml index 7aecaab..cf224d6 100644 --- a/vm-manager/VmPanel.qml +++ b/vm-manager/VmPanel.qml @@ -78,6 +78,20 @@ Scope { else Virsh.act(vm, action); } + // Quickshell exits once no window is visible, and the drawer is closed + // most of the time, so a 1x1 transparent window holds the process open. + // Without it the shell quits and the keybind, which reaches it over IPC, + // has nothing left to talk to. + PanelWindow { + visible: true + implicitWidth: 1 + implicitHeight: 1 + color: "transparent" + exclusionMode: ExclusionMode.Ignore + mask: Region {} + WlrLayershell.keyboardFocus: WlrKeyboardFocus.None + } + LazyLoader { active: root.open @@ -102,7 +116,26 @@ Scope { MouseArea { anchors.fill: parent; onClicked: root.close() } } - Keys.onEscapePressed: root.confirming ? root.confirming = null : root.close() + // Keys reach an item, not the window, so something inside has to + // hold focus. The TextInput in a delete confirmation takes it when + // it appears; this takes it back whenever that is gone. + Item { + id: keyCatcher + anchors.fill: parent + focus: true + + Keys.onEscapePressed: { + if (root.confirming) root.confirming = null; + else root.close(); + } + + Connections { + target: root + function onConfirmingChanged() { + if (!root.confirming) keyCatcher.forceActiveFocus(); + } + } + } Rectangle { id: drawer -- cgit v1.2.3