diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-24 09:44:39 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-24 09:44:39 +0200 |
| commit | db144ea05a98051662b4d77a2a0dfd950d670295 (patch) | |
| tree | a7a38704c421b9eb983d48f32345c7c923771b07 | |
| parent | 0fa4736470a39b6abbb0e70d3eb5b7636fb61549 (diff) | |
| download | quickshell-db144ea05a98051662b4d77a2a0dfd950d670295.tar.gz quickshell-db144ea05a98051662b4d77a2a0dfd950d670295.zip | |
island: release the tray-menu hold when the menu closes
A tray menu un-hovers the pills, so the island holds itself shown while
one is open. display() reports no close, so the hold ended only when
the pointer came back to the island. Quitting an app from its menu
left the pointer on the vanished menu, below the pills, and the island
stayed open until the next hover.
Menus now open through a QsMenuAnchor. Its closed signal fires on any
dismissal, an entry picked, Escape or a click elsewhere, and clears the
hold and restarts the hide timer. Checked by eye: menu placement
unchanged, Quit and a dismissal both retract the island, hovering back
keeps it shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | AGENTS.md | 9 | ||||
| -rw-r--r-- | desktop/island/Island.qml | 11 | ||||
| -rw-r--r-- | desktop/island/README.md | 4 | ||||
| -rw-r--r-- | desktop/island/StatusPill.qml | 17 |
4 files changed, 30 insertions, 11 deletions
@@ -143,14 +143,17 @@ changing that component. The ones that generalise: dominfo <vm>` grepped for `Managed save: yes`, polled per VM the way the agent rows are. It measured 6ms. - **Tray menus need `//@ pragma UseQApplication`** at the top of the root QML - file. Without it `SystemTrayItem.display()` only logs a qCritical ("not + file. Without it `SystemTrayItem.display()` and `QsMenuAnchor.open()` only log a qCritical ("not started in QApplication mode") and nothing opens. Pragmas are read at launch, before the first import, so adding or changing one needs a restart, not a hot reload. `desktop/shell.qml` carries it. - **A tray menu is a separate popup, and hovering it un-hovers the island.** The pills lose the pointer, so the hide timer would close the island under - the open menu. `display()` reports no close, so the island holds itself shown - after it until the pointer returns (a ponytail ceiling in `Island.qml`). + the open menu, so the island holds itself shown while one is open. + `display()` reports no close, and holding until the pointer returned left + the island stuck open after a menu's Quit, with the pointer on the menu. + Menus open through a `QsMenuAnchor` instead, whose `closed` signal fires on + any dismissal and ends the hold. - **A mouse button nothing accepts does not reliably fall through to a catcher underneath.** The drawer's back button (`BTN_SIDE`, `Qt.BackButton`) never reached a full-surface `MouseArea` below the panel while a page was diff --git a/desktop/island/Island.qml b/desktop/island/Island.qml index 12f49ea..ee03ca1 100644 --- a/desktop/island/Island.qml +++ b/desktop/island/Island.qml @@ -52,10 +52,9 @@ PanelWindow { // A tray menu is a separate popup: moving onto it un-hovers the pills, // and the hide timer would then hide the island under the open menu. The - // menu cannot report its own close through display(), so the hold ends - // the next time the pointer comes back to the island. - // ponytail: if the menu is dismissed elsewhere, the island may stay shown - // until the next hover. + // hold ends when the menu closes, or the next time the pointer comes back. + // It used to wait for the pointer alone, which left the island stuck open + // after a menu's Quit, since the pointer was on the menu, not the pills. property bool menuHeld: false onHoveredChanged: { @@ -319,6 +318,10 @@ PanelWindow { window: win panel: win.panel onMenuShown: win.menuHeld = true + onMenuClosed: { + win.menuHeld = false; + if (!win.hovered && win.panel === "") hideTimer.restart(); + } onOpenSound: win.panel = win.panel === "sound" ? "" : "sound" onOpenHeadphones: win.panel = win.panel === "headphones" ? "" : "headphones" onClosePanel: win.panel = "" diff --git a/desktop/island/README.md b/desktop/island/README.md index 9353f7e..01751c1 100644 --- a/desktop/island/README.md +++ b/desktop/island/README.md @@ -67,8 +67,8 @@ page now (`desktop/modules/perf/`). opens the sound panel. - **Tray** shows `SystemTray` items. Left click activates, right click opens the item's menu. The island is the session's StatusNotifier host. A tray - menu is a separate popup, so the island holds itself shown after `display()` - until the pointer returns. + menu is a separate popup, opened through a `QsMenuAnchor`, so the island + holds itself shown until the anchor reports the menu closed. - **Layout flag** shows the current keyboard layout, driven by Hyprland's `activelayout` event. Scroll switches layout with `hyprctl switchxkblayout current next|prev`. diff --git a/desktop/island/StatusPill.qml b/desktop/island/StatusPill.qml index 915807b..99f1512 100644 --- a/desktop/island/StatusPill.qml +++ b/desktop/island/StatusPill.qml @@ -38,8 +38,17 @@ Capsule { signal openHeadphones() signal closePanel() signal openDrawer() - // Emitted just before a tray menu opens; the menu cannot report its close. + // A tray menu opening and closing. display() reports no close, so menus + // open through a QsMenuAnchor, whose closed fires on any dismissal: an + // entry picked (Quit included), Escape or a click elsewhere. signal menuShown() + signal menuClosed() + + QsMenuAnchor { + id: menuAnchor + anchor.window: pill.window + onClosed: pill.menuClosed() + } expanded: panel === "sound" || panel === "headphones" restWidth: restRow.implicitWidth + 24 @@ -100,8 +109,12 @@ Capsule { if (!wantsMenu) { item.activate(); return; } if (!item.hasMenu) return; const p = trayIcon.mapToItem(null, 0, trayIcon.height + 6); + if (menuAnchor.visible) menuAnchor.close(); + menuAnchor.menu = item.menu; + menuAnchor.anchor.rect.x = p.x; + menuAnchor.anchor.rect.y = p.y; pill.menuShown(); - item.display(pill.window, p.x, p.y); + menuAnchor.open(); } } } |
