Files
Huỳnh Thiện Lộc 8ff7ce2cea feat(island): add system tray slot and overflow menu to compact home (#3780)
* feat(island): add system tray slot and overflow menu to compact home

- Add system tray slot and overflow expander menu in Dynamic Island
- Share tray icon path resolution and DBus context menu fallback in TrayMenuManager
- Support edge-anchored context menu positioning across top, bottom, left, and right
- Lock bottom and edge anchoring in DContextMenu to prevent animation jitter

* fix(island): only instantiate tray menus when tray slot is enabled

* feat(island): expand tray item and caret click area to slot height

* fix(island): use Theme.surfaceTextHover and restore DContextMenu height behavior

* refactor(tray): extract shared TrayMenuContent and TrayOverflowGrid, support tray order and IPC in island

* Revert "refactor(tray): extract shared TrayMenuContent and TrayOverflowGrid, support tray order and IPC in island"

This reverts commit 29888f89cd.

* refactor(tray): use shared TrayMenuManager helpers and TrayItemIcon, support tray order and IPC in island

* refactor(tray): extract shared TrayMenuContent and TrayOverflowGrid

* feat(island): support tray item drag and drop reordering

* fix(island): prevent re-entrant tray drag reordering on model destruction

* fix(tray): remove native submenu anchor hydration to prevent popup stutter

* fix(tray): decouple submenu hydrator from anchor window to avoid stutter

* fix(tray): restore anchor window on submenu hydrator and disable on island

* tray: fix bar overflow surface & reuse max visible items on the island
2026-10-14 16:55:26 -04:00

97 lines
2.6 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
import qs.Widgets
DContextMenu {
id: root
property var trayItem: null
property var bar: null
readonly property var menuHandle: trayItem?.menu ?? null
layerNamespace: "dms:tray-context-menu"
customContentWidth: Theme.fieldDefaultWidth
onOpenStateChanged: {
const screenName = root.targetScreen?.name;
if (!screenName)
return;
if (openState) {
TrayMenuManager.registerMenu(screenName, { close: () => root.hide() });
customContentItem?.clearState?.();
} else {
TrayMenuManager.unregisterMenu(screenName);
customContentItem?.clearState?.();
}
}
Component.onDestruction: {
if (openState && root.targetScreen?.name)
TrayMenuManager.unregisterMenu(root.targetScreen.name);
}
onBackdropRightClicked: root.hide()
function openFor(item, targetX, targetY, screen, edgeOrBottom) {
let edge = "top";
if (typeof edgeOrBottom === "string")
edge = edgeOrBottom;
else if (edgeOrBottom === true)
edge = "bottom";
root.menuEdge = edge;
trayItem = item;
const h = item?.menu ?? null;
if (h && typeof h.updateLayout === "function")
h.updateLayout();
let menuX = targetX;
let menuY = targetY;
switch (edge) {
case "bottom":
menuX = targetX - effectiveMenuWidth / 2;
menuY = targetY - Theme.spacingXS;
break;
case "left":
menuX = targetX + Theme.spacingXS;
menuY = targetY;
break;
case "right":
menuX = targetX - Theme.spacingXS;
menuY = targetY;
break;
case "top":
default:
menuX = targetX - effectiveMenuWidth / 2;
menuY = targetY + Theme.spacingXS;
break;
}
root.open(screen, menuX, menuY, false);
root.originX = targetX;
root.originY = targetY;
customContentItem?.clearState?.();
}
Timer {
id: pendingActionCloseTimer
interval: 80
repeat: false
onTriggered: root.hide()
}
customContent: Component {
TrayMenuContent {
id: contentRoot
width: root.customContentWidth
trayItem: root.trayItem
menuHandle: root.menuHandle
bar: root.bar
onCloseRequested: root.hide()
onActionTriggered: pendingActionCloseTimer.restart()
}
}
}