aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md9
-rw-r--r--desktop/island/Island.qml11
-rw-r--r--desktop/island/README.md4
-rw-r--r--desktop/island/StatusPill.qml17
4 files changed, 30 insertions, 11 deletions
diff --git a/AGENTS.md b/AGENTS.md
index cefaf5b..ddcf76d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -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();
}
}
}