diff --git a/DECISIONS.md b/DECISIONS.md index 887834e..a18d343 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -236,3 +236,9 @@ - **Why**: Each `Bar` owns a per-screen `PopoutState`, so on a multi-monitor setup a popout could stay open on screen 1 while another opened on screen 2. Worse, popout `shown` drives *global* singleton services (`adapter.discovering`, `wifiDev.scannerEnabled`), so closing one screen's popout could stop the service under another screen's still-open popout. A popout is a single focused interaction — only one should exist at a time. - **Tradeoffs**: Kept the per-screen `PopoutState` (rendering + anchor coordinates are screen-local; a single global state would either mirror the popout onto every monitor or need a screen field threaded through `open()`/`PopoutHost`). Added only the minimal cross-screen coordination — one `owner` token. Rejected a central registry (the composable-popouts decision just removed one). Not a compositor grab: pure QML state, so it never preempts other windows. - **How**: New `PopoutBus.qml` singleton with `property var owner`. `PopoutState.open()` sets `PopoutBus.owner = root`; `close()` clears it if still owned. A `readonly property bool ownsSlot: PopoutBus.owner === root` + `onOwnsSlotChanged` folds up any state that loses the slot (no loop: a foreign-close leaves `owner` untouched). `qmldir` regenerated (adds `singleton PopoutBus`). Verified: throwaway `quickshell -p …/moonarch` → `Configuration Loaded`, no binding loop/error; after deploy (`moonarch-git-r142.c34d1d2`) the running instance's log stayed error/warning-free through popout interaction, and the cross-screen fold was confirmed live on a multi-monitor setup. + +## 2026-07-09 – Quickshell popout: OnDemand keyboard focus (prompts spawned by a popout action can steal focus) +- **Who**: Dominik, ClaudeCode +- **Why**: A popout action that triggers an external auth toplevel left focus stuck on quickshell — reproduced with the network popout: select a VPN → Enter → `nmcli connection up` fires nm-applet's secret agent → gnome-keyring/gcr shows a GTK unlock prompt, but keyboard focus stayed on the popout, so the prompt was untypeable. Same class for Wi-Fi PSK, polkit, any dialog spawned by any popout. Not VPN-specific — the cause is the popout's keyboard-focus grab. +- **Tradeoffs**: Root cause is `PopoutHost`'s `WlrKeyboardFocus.Exclusive` while open. Per wlr-layer-shell, Exclusive on the overlay layer sits above all normal windows, so the compositor never focuses a newly-mapped toplevel while it holds. Empirically verified on niri 26.04 (`niri msg focused-window`): under **Exclusive**, a prompt maps with `is_focused=false` and `focused-window` stays `null` — no `WindowFocusChanged` fires, so a listen-and-close approach is impossible (there is no focus event to react to). Under **OnDemand**, a keybind-opened popout *still* grabs the keyboard on open (`focused-window: null` → Escape/Tab/arrow nav preserved) **and** an externally-mapped toplevel takes focus (`nm-connection-editor is_focused=true`). So OnDemand fixes it with no loss of the keyboard-nav feature and no per-action handler code. Rejected: closing the popout on the VPN toggle (symptom fix, per-action, doesn't generalise). +- **How**: `PopoutHost.qml` — `keyboardFocus: root.wantOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None` (was `Exclusive`), comment updated with the verified niri behaviour. One line; covers every popout and every prompt type. diff --git a/defaults/xdg/quickshell/moonarch/PopoutHost.qml b/defaults/xdg/quickshell/moonarch/PopoutHost.qml index 16d8db9..fac6cda 100644 --- a/defaults/xdg/quickshell/moonarch/PopoutHost.qml +++ b/defaults/xdg/quickshell/moonarch/PopoutHost.qml @@ -50,9 +50,12 @@ PanelWindow { visible: root.rendered WlrLayershell.layer: WlrLayer.Overlay - // Exclusive while open so a keybind-opened popout gets the keyboard (Escape, navigation) with no - // click; None while closed to release it. Compositor keybinds still fire (niri sees keys first). - WlrLayershell.keyboardFocus: root.wantOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + // OnDemand while open so a keybind-opened popout still grabs the keyboard (Escape, navigation) with + // no click, yet an externally-mapped toplevel — a keyring/PSK/polkit prompt spawned by a popout + // action — can steal focus and become typeable. Exclusive would sit above all normal windows and + // block that prompt from ever getting the keyboard (verified on niri 26.04: under Exclusive the + // prompt maps with is_focused=false; under OnDemand it takes focus). None while closed to release it. + WlrLayershell.keyboardFocus: root.wantOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None WlrLayershell.namespace: "moonarch-popout" // Backdrop — click outside the menu closes it. Our own MouseArea (not a compositor grab).