diff --git a/Modules/Bar/Widgets/Taskbar.qml b/Modules/Bar/Widgets/Taskbar.qml index 434a01ea..6378d99c 100644 --- a/Modules/Bar/Widgets/Taskbar.qml +++ b/Modules/Bar/Widgets/Taskbar.qml @@ -43,22 +43,26 @@ Rectangle { // Context menu state property var selectedWindow: null property string selectedAppName: "" + property int modelUpdateTrigger: 0 // Dummy property to force model re-evaluation NPopupContextMenu { id: contextMenu model: { + // Reference modelUpdateTrigger to make binding reactive + const _ = root.modelUpdateTrigger; + var items = []; - if (selectedWindow) { + if (root.selectedWindow) { items.push({ "label": I18n.tr("context-menu.activate-app", { - "app": selectedAppName + "app": root.selectedAppName }), "action": "activate", "icon": "focus" }); items.push({ "label": I18n.tr("context-menu.close-app", { - "app": selectedAppName + "app": root.selectedAppName }), "action": "close", "icon": "x" @@ -203,6 +207,7 @@ Rectangle { hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton + preventStealing: true onPressed: function (mouse) { if (!taskbarItem.modelData) @@ -213,16 +218,25 @@ Rectangle { } catch (error) { Logger.e("Taskbar", "Failed to activate toplevel: " + error); } - } else if (mouse.button === Qt.RightButton) { + } + } + + onReleased: function (mouse) { + if (!taskbarItem.modelData) + return; + if (mouse.button === Qt.RightButton) { + mouse.accepted = true; TooltipService.hide(); root.selectedWindow = taskbarItem.modelData; root.selectedAppName = CompositorService.getCleanAppName(taskbarItem.modelData.appId, taskbarItem.modelData.title); - var popupMenuWindow = PanelService.getPopupMenuWindow(screen); - if (popupMenuWindow) { - const pos = BarService.getContextMenuPosition(taskbarItem, contextMenu.implicitWidth, contextMenu.implicitHeight); - contextMenu.openAtItem(taskbarItem, pos.x, pos.y); - popupMenuWindow.showContextMenu(contextMenu); - } + + // Store position and size for timer callback + const globalPos = taskbarItem.mapToItem(root, 0, 0); + contextMenuOpenTimer.globalX = globalPos.x; + contextMenuOpenTimer.globalY = globalPos.y; + contextMenuOpenTimer.itemWidth = taskbarItem.width; + contextMenuOpenTimer.itemHeight = taskbarItem.height; + contextMenuOpenTimer.restart(); } } onEntered: TooltipService.show(taskbarItem, taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app.", BarService.getTooltipDirection()) @@ -231,4 +245,71 @@ Rectangle { } } } + + Timer { + id: contextMenuOpenTimer + interval: 10 + repeat: false + property real globalX: 0 + property real globalY: 0 + property real itemWidth: 0 + property real itemHeight: 0 + + onTriggered: { + // Directly build and set model as a new array (bypass binding issues) + var items = []; + if (root.selectedWindow) { + items.push({ + "label": I18n.tr("context-menu.activate-app", { + "app": root.selectedAppName + }), + "action": "activate", + "icon": "focus" + }); + items.push({ + "label": I18n.tr("context-menu.close-app", { + "app": root.selectedAppName + }), + "action": "close", + "icon": "x" + }); + } + items.push({ + "label": I18n.tr("context-menu.widget-settings"), + "action": "widget-settings", + "icon": "settings" + }); + + // Set the model directly + contextMenu.model = items; + + var popupMenuWindow = PanelService.getPopupMenuWindow(screen); + if (popupMenuWindow) { + popupMenuWindow.open(); + + // Calculate menu position relative to the clicked item with consistent spacing + const barPosition = Settings.data.bar.position; + const spacing = Style.barHeight * 0.5; + let menuX, menuY; + + if (barPosition === "top") { + menuX = globalX + (itemWidth / 2) - (contextMenu.implicitWidth / 2); + menuY = globalY + itemHeight + spacing; + } else if (barPosition === "bottom") { + menuX = globalX + (itemWidth / 2) - (contextMenu.implicitWidth / 2); + menuY = globalY - contextMenu.implicitHeight - (Style.barHeight * 2); + } else if (barPosition === "left") { + menuX = globalX + itemWidth + spacing; + menuY = globalY + (itemHeight / 2) - (contextMenu.implicitHeight / 2); + } else { + // right + menuX = globalX - contextMenu.implicitWidth - spacing; + menuY = globalY + (itemHeight / 2) - (contextMenu.implicitHeight / 2); + } + + contextMenu.openAtItem(root, menuX, menuY); + popupMenuWindow.contentItem = contextMenu; + } + } + } } diff --git a/Modules/Bar/Widgets/TaskbarGrouped.qml b/Modules/Bar/Widgets/TaskbarGrouped.qml index dad1b05f..15afa90b 100644 --- a/Modules/Bar/Widgets/TaskbarGrouped.qml +++ b/Modules/Bar/Widgets/TaskbarGrouped.qml @@ -53,6 +53,7 @@ Item { // Context menu state property var selectedWindow: null property string selectedAppName: "" + property int modelUpdateTrigger: 0 // Dummy property to force model re-evaluation function refreshWorkspaces() { localWorkspaces.clear(); @@ -167,18 +168,21 @@ Item { id: contextMenu model: { + // Reference modelUpdateTrigger to make binding reactive + const _ = root.modelUpdateTrigger; + var items = []; - if (selectedWindow) { + if (root.selectedWindow) { items.push({ "label": I18n.tr("context-menu.activate-app", { - "app": selectedAppName + "app": root.selectedAppName }), "action": "activate", "icon": "focus" }); items.push({ "label": I18n.tr("context-menu.close-app", { - "app": selectedAppName + "app": root.selectedAppName }), "action": "close", "icon": "x" @@ -273,21 +277,28 @@ Item { enabled: !hasWindows cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor acceptedButtons: Qt.LeftButton | Qt.RightButton + preventStealing: true onPressed: mouse => { if (mouse.button === Qt.LeftButton) { CompositorService.switchToWorkspace(workspaceModel); - } else if (mouse.button === Qt.RightButton) { - TooltipService.hide(); - root.selectedWindow = ""; - root.selectedAppName = ""; - var popupMenuWindow = PanelService.getPopupMenuWindow(screen); - if (popupMenuWindow) { - const pos = BarService.getContextMenuPosition(container, contextMenu.implicitWidth, contextMenu.implicitHeight); - contextMenu.openAtItem(container, pos.x, pos.y); - popupMenuWindow.showContextMenu(contextMenu); - } } } + onReleased: mouse => { + if (mouse.button === Qt.RightButton) { + mouse.accepted = true; + TooltipService.hide(); + root.selectedWindow = ""; + root.selectedAppName = ""; + + // Store position and size for timer callback + const globalPos = container.mapToItem(root, 0, 0); + contextMenuOpenTimer1.globalX = globalPos.x; + contextMenuOpenTimer1.globalY = globalPos.y; + contextMenuOpenTimer1.itemWidth = container.width; + contextMenuOpenTimer1.itemHeight = container.height; + contextMenuOpenTimer1.restart(); + } + } } Flow { @@ -359,6 +370,7 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton + preventStealing: true onPressed: mouse => { if (!model) { @@ -367,18 +379,29 @@ Item { if (mouse.button === Qt.LeftButton) { CompositorService.focusWindow(model); - } else if (mouse.button === Qt.RightButton) { - TooltipService.hide(); - root.selectedWindow = model; - root.selectedAppName = CompositorService.getCleanAppName(model.appId, model.title); - var popupMenuWindow = PanelService.getPopupMenuWindow(screen); - if (popupMenuWindow) { - const pos = BarService.getContextMenuPosition(taskbarItem, contextMenu.implicitWidth, contextMenu.implicitHeight); - contextMenu.openAtItem(taskbarItem, pos.x, pos.y); - popupMenuWindow.showContextMenu(contextMenu); - } } } + + onReleased: mouse => { + if (!model) { + return; + } + + if (mouse.button === Qt.RightButton) { + mouse.accepted = true; + TooltipService.hide(); + root.selectedWindow = model; + root.selectedAppName = CompositorService.getCleanAppName(model.appId, model.title); + + // Store position and size for timer callback + const globalPos = taskbarItem.mapToItem(root, 0, 0); + contextMenuOpenTimer2.globalX = globalPos.x; + contextMenuOpenTimer2.globalY = globalPos.y; + contextMenuOpenTimer2.itemWidth = taskbarItem.width; + contextMenuOpenTimer2.itemHeight = taskbarItem.height; + contextMenuOpenTimer2.restart(); + } + } onEntered: { taskbarItem.itemHovered = true; TooltipService.show(taskbarItem, model.title || model.appId || "Unknown app.", BarService.getTooltipDirection()); @@ -533,4 +556,138 @@ Item { delegate: workspaceRepeaterDelegate } } + + Timer { + id: contextMenuOpenTimer1 + interval: 10 + repeat: false + property real globalX: 0 + property real globalY: 0 + property real itemWidth: 0 + property real itemHeight: 0 + + onTriggered: { + // Directly build and set model as a new array (bypass binding issues) + var items = []; + if (root.selectedWindow) { + items.push({ + "label": I18n.tr("context-menu.activate-app", { + "app": root.selectedAppName + }), + "action": "activate", + "icon": "focus" + }); + items.push({ + "label": I18n.tr("context-menu.close-app", { + "app": root.selectedAppName + }), + "action": "close", + "icon": "x" + }); + } + items.push({ + "label": I18n.tr("context-menu.widget-settings"), + "action": "widget-settings", + "icon": "settings" + }); + + // Set the model directly + contextMenu.model = items; + + var popupMenuWindow = PanelService.getPopupMenuWindow(screen); + if (popupMenuWindow) { + popupMenuWindow.open(); + + // Calculate menu position relative to the clicked item with consistent spacing + const barPosition = Settings.data.bar.position; + const spacing = Style.barHeight * 0.5; + let menuX, menuY; + + if (barPosition === "top") { + menuX = globalX + (itemWidth / 2) - (contextMenu.implicitWidth / 2); + menuY = globalY + itemHeight + spacing; + } else if (barPosition === "bottom") { + menuX = globalX + (itemWidth / 2) - (contextMenu.implicitWidth / 2); + menuY = globalY - contextMenu.implicitHeight - (Style.barHeight * 2); + } else if (barPosition === "left") { + menuX = globalX + itemWidth + spacing; + menuY = globalY + (itemHeight / 2) - (contextMenu.implicitHeight / 2); + } else { + // right + menuX = globalX - contextMenu.implicitWidth - spacing; + menuY = globalY + (itemHeight / 2) - (contextMenu.implicitHeight / 2); + } + + contextMenu.openAtItem(root, menuX, menuY); + popupMenuWindow.contentItem = contextMenu; + } + } + } + + Timer { + id: contextMenuOpenTimer2 + interval: 10 + repeat: false + property real globalX: 0 + property real globalY: 0 + property real itemWidth: 0 + property real itemHeight: 0 + + onTriggered: { + // Directly build and set model as a new array (bypass binding issues) + var items = []; + if (root.selectedWindow) { + items.push({ + "label": I18n.tr("context-menu.activate-app", { + "app": root.selectedAppName + }), + "action": "activate", + "icon": "focus" + }); + items.push({ + "label": I18n.tr("context-menu.close-app", { + "app": root.selectedAppName + }), + "action": "close", + "icon": "x" + }); + } + items.push({ + "label": I18n.tr("context-menu.widget-settings"), + "action": "widget-settings", + "icon": "settings" + }); + + // Set the model directly + contextMenu.model = items; + + var popupMenuWindow = PanelService.getPopupMenuWindow(screen); + if (popupMenuWindow) { + popupMenuWindow.open(); + + // Calculate menu position relative to the clicked item with consistent spacing + const barPosition = Settings.data.bar.position; + const spacing = Style.barHeight * 0.5; + let menuX, menuY; + + if (barPosition === "top") { + menuX = globalX + (itemWidth / 2) - (contextMenu.implicitWidth / 2); + menuY = globalY + itemHeight + spacing; + } else if (barPosition === "bottom") { + menuX = globalX + (itemWidth / 2) - (contextMenu.implicitWidth / 2); + menuY = globalY - contextMenu.implicitHeight - (Style.barHeight * 2); + } else if (barPosition === "left") { + menuX = globalX + itemWidth + spacing; + menuY = globalY + (itemHeight / 2) - (contextMenu.implicitHeight / 2); + } else { + // right + menuX = globalX - contextMenu.implicitWidth - spacing; + menuY = globalY + (itemHeight / 2) - (contextMenu.implicitHeight / 2); + } + + contextMenu.openAtItem(root, menuX, menuY); + popupMenuWindow.contentItem = contextMenu; + } + } + } }