feat(quickshell): keep only one popout open across all screens
Update PKGBUILD version / update-pkgver (push) Successful in 6s
Update PKGBUILD version / update-pkgver (push) Successful in 6s
Each bar owns a per-screen PopoutState, so on multi-monitor a popout could stay open on one screen while another opened on the next — and per-screen `shown` drives global services (bluetooth discovery, wifi scan), so closing one screen's popout could kill the service under the other's still-open popout. A small PopoutBus singleton holds an `owner` token: open() claims it, and any state that loses ownership folds up. Per-screen state (and thus per-screen anchoring/rendering) stays; only cross-screen exclusivity is added.
This commit is contained in:
@@ -200,3 +200,9 @@
|
||||
- **Why**: The composition switch left `Popouts.qml` a hardcoded `switch` — a user-composed widget could join the bar but couldn't open a popout in the shared animated overlay, so it was second-class (a widget without a popout is useless for most cases). quickshell's native `PopupWindow` would be composable by construction but drops moonarch's unfold/morph animation (one shared overlay) — decided: **keep the animation**, make the registry composable instead. caelestia's central `Panels.qml` + `DrawerVisibilities` was considered and rejected: it fits a handful of author-owned edge drawers, not many user-composable per-widget popouts (it reintroduces central registration).
|
||||
- **Tradeoffs**: `open()` takes a `Component` instead of a name — less conventional, but it removes the central registry entirely: a widget carries its own popout and renders it in the shared morphing frame. Churn across the 10 trigger widgets + `open()` signature; each widget file gains `pragma ComponentBehavior: Bound` so the inline popout `Component` resolves `root` when the shared host instantiates it.
|
||||
- **How**: `PopoutState.currentName` → `currentComponent`; `open(component, rect, payload)`. `PopoutHost` renders `Loader { sourceComponent: popouts.currentComponent }`. `Popouts.qml` deleted, `qmldir` regenerated. Each of the 10 widgets declares an inline `Component { XxxPopout { popouts: root.popouts } }` (Tray also wires `menuHandle: popouts.payload`) and calls `open(thatComponent, …)`. Verified: `qmllint` clean, and an end-to-end throwaway config where a user widget opens its **own** popout on startup instantiates in the shared host with no errors/binding loops. Visual/animation unverified (needs a live eyeball).
|
||||
|
||||
## 2026-07-06 – Quickshell bar: one popout system-wide (cross-screen exclusivity)
|
||||
- **Who**: Dominik, ClaudeCode
|
||||
- **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. Cross-screen fold + visuals unverified (needs a live multi-monitor eyeball).
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// ABOUTME: System-wide coordinator so only one bar popout is open at a time across all screens.
|
||||
// ABOUTME: Each per-screen PopoutState claims `owner` on open; the others fold up when ownership changes.
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
// The PopoutState that currently owns the single, system-wide open popout (null = none).
|
||||
property var owner: null
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// ABOUTME: Shared state for a bar's popout host — which popout is open, where, and its payload.
|
||||
// ABOUTME: One instance per bar (per screen); bar widgets call open()/close().
|
||||
// ABOUTME: One instance per bar (per screen); bar widgets call open()/close(). Only one may be open
|
||||
// ABOUTME: system-wide — cross-screen exclusivity is coordinated through the PopoutBus singleton.
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
@@ -15,9 +16,21 @@ QtObject {
|
||||
root.anchorRect = rect;
|
||||
root.currentComponent = component;
|
||||
root.hasCurrent = true;
|
||||
// Claim the system-wide slot; any popout open on another screen folds up.
|
||||
PopoutBus.owner = root;
|
||||
}
|
||||
|
||||
function close() {
|
||||
// Deliberately leaves currentComponent set: PopoutHost's Loader keeps that
|
||||
// instance alive and sized through the fold-up animation. Nulling it here would
|
||||
// unload the content instantly and collapse the slide-out to zero size.
|
||||
root.hasCurrent = false;
|
||||
if (PopoutBus.owner === root)
|
||||
PopoutBus.owner = null;
|
||||
}
|
||||
|
||||
// Fold up when another screen's popout claims the system-wide slot.
|
||||
readonly property bool ownsSlot: PopoutBus.owner === root
|
||||
onOwnsSlotChanged: if (!root.ownsSlot && root.hasCurrent)
|
||||
root.close()
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ singleton IdleInhibit IdleInhibit.qml
|
||||
singleton NightlightService NightlightService.qml
|
||||
singleton Niri Niri.qml
|
||||
singleton NotifService NotifService.qml
|
||||
singleton PopoutBus PopoutBus.qml
|
||||
singleton Theme Theme.qml
|
||||
Toggle Toggle.qml
|
||||
Tooltip Tooltip.qml
|
||||
|
||||
Reference in New Issue
Block a user