diff --git a/Modules/Bar/Extras/TrayMenu.qml b/Modules/Bar/Extras/TrayMenu.qml index f8d5840b..53c5cd37 100644 --- a/Modules/Bar/Extras/TrayMenu.qml +++ b/Modules/Bar/Extras/TrayMenu.qml @@ -19,136 +19,8 @@ PopupWindow { property string widgetSection: "" property int widgetIndex: -1 - // Menu can be set directly (for submenus) or derived from trayItem - property var menu: null - property var stableMenu: null // Debounced menu for opener binding - property var menuItems: [] - property int loadRetryCount: 0 - property bool isLoadingMenu: false // Mutex to prevent concurrent access - - // Debounce timer to prevent rapid menu rebinding - Timer { - id: menuDebounceTimer - interval: 50 - repeat: false - onTriggered: { - // Update stable menu only after debounce period - if (root.visible && root.menu) { - root.stableMenu = root.menu - } else { - root.stableMenu = null - } - } - } - - // Timer to defer menu loading to avoid freezing on opener.children access - // Uses retry mechanism with exponential backoff for reliability - Timer { - id: menuLoadTimer - interval: 50 - repeat: false - onTriggered: { - // Early exit if not visible, no stable menu, or already loading - if (!root.visible || !root.stableMenu || root.isLoadingMenu) { - root.menuItems = [] - root.loadRetryCount = 0 - root.isLoadingMenu = false - return - } - - // Set loading flag to prevent concurrent access - root.isLoadingMenu = true - - try { - if (opener && opener.children && 'values' in opener.children) { - const values = opener.children.values - if (values && values.length > 0) { - root.menuItems = [...values] - root.loadRetryCount = 0 // Success, reset retry count - root.isLoadingMenu = false - } else { - // Empty menu - retry if we haven't exceeded max attempts - if (root.loadRetryCount < 3) { - root.loadRetryCount++ - menuLoadTimer.interval = 150 * Math.pow(2, root.loadRetryCount - 1) // Exponential backoff: 150, 300, 600 - root.isLoadingMenu = false // Release lock before retry - menuLoadTimer.running = true - } else { - // Max retries exceeded, give up gracefully - root.menuItems = [] - root.loadRetryCount = 0 - root.isLoadingMenu = false - } - } - } else { - // opener.children not ready - retry if we haven't exceeded max attempts - if (root.loadRetryCount < 3) { - root.loadRetryCount++ - menuLoadTimer.interval = 150 * Math.pow(2, root.loadRetryCount - 1) // Exponential backoff - root.isLoadingMenu = false // Release lock before retry - menuLoadTimer.running = true - } else { - // Max retries exceeded, give up gracefully - root.menuItems = [] - root.loadRetryCount = 0 - root.isLoadingMenu = false - } - } - } catch (e) { - Logger.w("TrayMenu", "Error accessing menu items:", e) - root.menuItems = [] - root.loadRetryCount = 0 - root.isLoadingMenu = false - } - } - } - - onVisibleChanged: { - if (visible && menu) { - // Cancel all pending operations - menuDebounceTimer.running = false - menuLoadTimer.running = false - root.isLoadingMenu = false - root.loadRetryCount = 0 - - // Start debounce for stable menu - menuDebounceTimer.running = true - } else if (!visible) { - // Clean up when hiding - menuDebounceTimer.running = false - menuLoadTimer.running = false - root.isLoadingMenu = false - root.loadRetryCount = 0 - root.stableMenu = null - root.menuItems = [] - } - } - - onMenuChanged: { - // Cancel pending load whenever menu changes - menuLoadTimer.running = false - root.isLoadingMenu = false - root.loadRetryCount = 0 - root.menuItems = [] - - if (visible && menu) { - // Restart debounce timer - menuDebounceTimer.running = false - menuDebounceTimer.running = true - } else { - menuDebounceTimer.running = false - root.stableMenu = null - } - } - - onStableMenuChanged: { - if (visible && stableMenu) { - // Stable menu is ready, start loading - menuLoadTimer.interval = 150 // Reset to initial interval - menuLoadTimer.running = false - menuLoadTimer.running = true - } - } + // Derive menu from trayItem (only used for non-submenus) + readonly property QsMenuHandle menu: isSubMenu ? null : (trayItem ? trayItem.menu : null) // Compute if current tray item is pinned readonly property bool isPinned: { @@ -193,8 +65,10 @@ PopupWindow { return } - if (!isSubMenu && trayItem && trayItem.menu) { - menu = trayItem.menu + if (!opener.children || opener.children.values.length === 0) { + //Logger.w("TrayMenu", "Menu not ready, delaying show") + Qt.callLater(() => showAt(item, x, y)) + return } anchorItem = item @@ -210,7 +84,7 @@ PopupWindow { }) } - function hideMenu(closeWindow = true) { + function hideMenu() { visible = false // Clean up all submenus recursively @@ -222,20 +96,23 @@ PopupWindow { child.subMenu = null } } + } - // Close the parent TrayMenuWindow if this is not a submenu - // closeWindow parameter prevents infinite recursion - if (closeWindow && !isSubMenu && screen) { - const trayMenuWindow = PanelService.getTrayMenuWindow(screen) - if (trayMenuWindow) { - trayMenuWindow.close() - } - } + // Full-sized, transparent MouseArea to track the mouse. + MouseArea { + id: rootMouseArea + anchors.fill: parent + hoverEnabled: true + } + + Item { + anchors.fill: parent + Keys.onEscapePressed: root.hideMenu() } QsMenuOpener { id: opener - menu: root.stableMenu // Bind to debounced stable menu instead of raw menu + menu: root.menu } Rectangle { @@ -280,7 +157,7 @@ PopupWindow { spacing: 0 Repeater { - model: root.menuItems + model: opener.children ? [...opener.children.values] : [] delegate: Rectangle { id: entry @@ -399,7 +276,6 @@ PopupWindow { entry.subMenu.anchorItem = entry entry.subMenu.anchorX = openLeft ? -overlap : overlap entry.subMenu.anchorY = 0 - entry.subMenu.menuLoadTrigger++ entry.subMenu.visible = true // Force anchor update with new position Qt.callLater(() => { diff --git a/Modules/MainScreen/TrayMenuWindow.qml b/Modules/MainScreen/TrayMenuWindow.qml index 32c33013..4625ff7c 100644 --- a/Modules/MainScreen/TrayMenuWindow.qml +++ b/Modules/MainScreen/TrayMenuWindow.qml @@ -41,7 +41,7 @@ PanelWindow { function close() { visible = false if (trayMenu.item) { - trayMenu.item.hideMenu(false) // Don't close window again (prevents recursion) + trayMenu.item.hideMenu() } }