mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
PopupContextMenu: fixed popup positionning on wlroots and hyprland
This commit is contained in:
@@ -450,8 +450,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,8 +154,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
} else {
|
||||
const types = ["linear", "mirrored", "wave"];
|
||||
|
||||
@@ -248,8 +248,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(pill, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(pill, pos.x, pos.y);
|
||||
contextMenu.openAtItem(pill, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,8 +93,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(pill, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(pill, pos.x, pos.y);
|
||||
contextMenu.openAtItem(pill, screen);
|
||||
}
|
||||
}
|
||||
tooltipText: {
|
||||
|
||||
@@ -151,8 +151,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(pill, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(pill, pos.x, pos.y);
|
||||
contextMenu.openAtItem(pill, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +169,7 @@ Rectangle {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
} else {
|
||||
PanelService.getPanel("clockPanel", screen)?.toggle(this);
|
||||
|
||||
@@ -149,8 +149,7 @@ NIconButton {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
}
|
||||
onMiddleClicked: PanelService.getPanel("launcherPanel", screen)?.toggle()
|
||||
|
||||
@@ -83,8 +83,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(pill, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(pill, pos.x, pos.y);
|
||||
contextMenu.openAtItem(pill, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +83,7 @@ Rectangle {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,8 +366,7 @@ Item {
|
||||
var popupWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupWindow) {
|
||||
popupWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(container, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(container, pos.x, pos.y);
|
||||
contextMenu.openAtItem(container, screen);
|
||||
}
|
||||
} else if (mouse.button === Qt.MiddleButton && hasPlayer && MediaService.canGoPrevious) {
|
||||
MediaService.previous();
|
||||
|
||||
@@ -170,8 +170,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(pill, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(pill, pos.x, pos.y);
|
||||
contextMenu.openAtItem(pill, screen);
|
||||
}
|
||||
}
|
||||
onMiddleClicked: root.openExternalMixer()
|
||||
|
||||
@@ -106,8 +106,7 @@ NIconButton {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +89,7 @@ NIconButton {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,7 @@ Rectangle {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,7 +687,7 @@ Rectangle {
|
||||
menuY = globalPos.y + (item.height / 2) - (contextMenu.implicitHeight / 2);
|
||||
}
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
contextMenu.openAtItem(root, menuX, menuY);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,8 +125,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(pill, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(pill, pos.x, pos.y);
|
||||
contextMenu.openAtItem(pill, screen);
|
||||
}
|
||||
}
|
||||
tooltipText: {
|
||||
|
||||
@@ -154,8 +154,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(pill, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(pill, pos.x, pos.y);
|
||||
contextMenu.openAtItem(pill, screen);
|
||||
}
|
||||
}
|
||||
onMiddleClicked: root.openExternalMixer()
|
||||
|
||||
@@ -61,8 +61,7 @@ NIconButton {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(root, pos.x, pos.y);
|
||||
contextMenu.openAtItem(root, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,8 +115,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(pill, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(pill, pos.x, pos.y);
|
||||
contextMenu.openAtItem(pill, screen);
|
||||
}
|
||||
}
|
||||
tooltipText: {
|
||||
|
||||
@@ -333,8 +333,7 @@ Item {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (popupMenuWindow) {
|
||||
popupMenuWindow.showContextMenu(contextMenu);
|
||||
const pos = BarService.getContextMenuPosition(workspaceBackground, contextMenu.implicitWidth, contextMenu.implicitHeight);
|
||||
contextMenu.openAtItem(workspaceBackground, pos.x, pos.y);
|
||||
contextMenu.openAtItem(workspaceBackground, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,56 +286,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate context menu position based on bar position
|
||||
// Parameters:
|
||||
// anchorItem: The widget item to anchor the menu to
|
||||
// menuWidth: Width of the context menu (optional, defaults to 180)
|
||||
// menuHeight: Height of the context menu (optional, defaults to 100)
|
||||
// Returns: { x: number, y: number }
|
||||
// Note: Anchor position is top-left corner, so we calculate from center
|
||||
function getContextMenuPosition(anchorItem, menuWidth, menuHeight) {
|
||||
if (!anchorItem) {
|
||||
Logger.w("BarService", "getContextMenuPosition: anchorItem is null");
|
||||
return {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
};
|
||||
}
|
||||
|
||||
const mWidth = menuWidth || 180;
|
||||
const mHeight = menuHeight || 100;
|
||||
const barPosition = Settings.data.bar.position;
|
||||
let menuX = 0;
|
||||
let menuY = 0;
|
||||
|
||||
// Calculate center-based positioning for consistent spacing
|
||||
const anchorCenterX = anchorItem.width / 2;
|
||||
const anchorCenterY = anchorItem.height / 2;
|
||||
|
||||
if (barPosition === "left") {
|
||||
// For left bar: position menu to the right of anchor, vertically centered
|
||||
menuX = anchorItem.width + Style.marginM;
|
||||
menuY = anchorCenterY - (mHeight / 2);
|
||||
} else if (barPosition === "right") {
|
||||
// For right bar: position menu to the left of anchor, vertically centered
|
||||
menuX = -mWidth - Style.marginM;
|
||||
menuY = anchorCenterY - (mHeight / 2);
|
||||
} else if (barPosition === "top") {
|
||||
// For top bar: position menu below bar, horizontally centered
|
||||
menuX = anchorCenterX - (mWidth / 2);
|
||||
menuY = Style.barHeight;
|
||||
} else {
|
||||
// For bottom bar: position menu above, horizontally centered
|
||||
menuX = anchorCenterX - (mWidth / 2);
|
||||
menuY = -mHeight - Style.marginM;
|
||||
}
|
||||
|
||||
return {
|
||||
"x": menuX,
|
||||
"y": menuY
|
||||
};
|
||||
}
|
||||
|
||||
// Open widget settings dialog for a bar widget
|
||||
// Parameters:
|
||||
// screen: The screen to show the dialog on
|
||||
|
||||
@@ -3,6 +3,9 @@ import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
|
||||
/*
|
||||
This component could probably be deleted and replaced by NPopupContextMenu
|
||||
*/
|
||||
Popup {
|
||||
id: root
|
||||
|
||||
|
||||
@@ -6,20 +6,22 @@ import qs.Commons
|
||||
|
||||
// Simple context menu PopupWindow (similar to TrayMenu)
|
||||
// Designed to be rendered inside a PopupMenuWindow for click-outside-to-close
|
||||
// Automatically positions itself to respect screen boundaries
|
||||
PopupWindow {
|
||||
id: root
|
||||
|
||||
property alias model: repeater.model
|
||||
property real itemHeight: 28 // Match TrayMenu
|
||||
property real itemHeight: 28 // Match TrayMenu
|
||||
property real itemPadding: Style.marginM
|
||||
property int verticalPolicy: ScrollBar.AsNeeded
|
||||
property int horizontalPolicy: ScrollBar.AsNeeded
|
||||
|
||||
property var anchorItem: null
|
||||
property real anchorX: 0
|
||||
property real anchorY: 0
|
||||
property ShellScreen screen: null
|
||||
property real calculatedWidth: 180
|
||||
|
||||
readonly property string barPosition: Settings.data.bar.position
|
||||
|
||||
signal triggered(string action, var item)
|
||||
|
||||
implicitWidth: calculatedWidth
|
||||
@@ -76,8 +78,82 @@ PopupWindow {
|
||||
}
|
||||
|
||||
anchor.item: anchorItem
|
||||
anchor.rect.x: anchorX
|
||||
anchor.rect.y: anchorY
|
||||
|
||||
anchor.rect.x: {
|
||||
if (anchorItem && screen) {
|
||||
const anchorGlobalPos = anchorItem.mapToItem(null, 0, 0);
|
||||
|
||||
// For right bar: position menu to the left of anchor
|
||||
if (root.barPosition === "right") {
|
||||
let baseX = -implicitWidth - Style.marginM;
|
||||
return baseX;
|
||||
}
|
||||
|
||||
// For left bar: position menu to the right of anchor
|
||||
if (root.barPosition === "left") {
|
||||
let baseX = anchorItem.width + Style.marginM;
|
||||
return baseX;
|
||||
}
|
||||
|
||||
// For top/bottom bar: center horizontally on anchor
|
||||
const anchorCenterX = anchorItem.width / 2;
|
||||
let baseX = anchorCenterX - (implicitWidth / 2);
|
||||
|
||||
// Calculate menu position on screen
|
||||
const menuScreenX = anchorGlobalPos.x + baseX;
|
||||
const menuRight = menuScreenX + implicitWidth;
|
||||
|
||||
// Adjust if menu would clip on the right
|
||||
if (menuRight > screen.width - Style.marginM) {
|
||||
const overflow = menuRight - (screen.width - Style.marginM);
|
||||
return baseX - overflow;
|
||||
}
|
||||
// Adjust if menu would clip on the left
|
||||
if (menuScreenX < Style.marginM) {
|
||||
return baseX + (Style.marginM - menuScreenX);
|
||||
}
|
||||
return baseX;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
anchor.rect.y: {
|
||||
if (anchorItem && screen) {
|
||||
const anchorCenterY = anchorItem.height / 2;
|
||||
|
||||
// Calculate base Y position based on bar orientation
|
||||
let baseY;
|
||||
if (root.barPosition === "bottom") {
|
||||
// For bottom bar: position menu above the bar
|
||||
baseY = -(implicitHeight + Style.marginM);
|
||||
} else if (root.barPosition === "top") {
|
||||
// For top bar: position menu below bar
|
||||
baseY = Style.barHeight + Style.marginM;
|
||||
} else {
|
||||
// For left/right bar: vertically center on anchor
|
||||
baseY = anchorCenterY - (implicitHeight / 2);
|
||||
}
|
||||
|
||||
// Calculate menu position on screen
|
||||
const anchorGlobalPos = anchorItem.mapToItem(null, 0, 0);
|
||||
const menuScreenY = anchorGlobalPos.y + baseY;
|
||||
|
||||
const menuBottom = menuScreenY + implicitHeight;
|
||||
|
||||
// Adjust if menu would clip at bottom
|
||||
if (menuBottom > screen.height - Style.marginM) {
|
||||
const overflow = menuBottom - (screen.height - Style.marginM);
|
||||
return baseY - overflow;
|
||||
}
|
||||
|
||||
return baseY;
|
||||
}
|
||||
|
||||
// Fallback if no screen
|
||||
if (root.barPosition === "bottom") {
|
||||
return -implicitHeight - Style.marginM;
|
||||
}
|
||||
return Style.barHeight;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Qt.callLater(calculateWidth);
|
||||
@@ -208,8 +284,9 @@ PopupWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to open at specific position relative to anchor item
|
||||
function openAt(x, y, item) {
|
||||
// Helper function to open context menu anchored to an item
|
||||
// Position is calculated automatically based on bar position and screen boundaries
|
||||
function openAtItem(item, itemScreen) {
|
||||
if (!item) {
|
||||
Logger.w("NPopupContextMenu", "anchorItem is undefined, won't show menu.");
|
||||
return;
|
||||
@@ -218,19 +295,8 @@ PopupWindow {
|
||||
calculateWidth();
|
||||
|
||||
anchorItem = item;
|
||||
anchorX = x;
|
||||
anchorY = y;
|
||||
screen = itemScreen || null;
|
||||
visible = true;
|
||||
|
||||
Qt.callLater(() => {
|
||||
if (root.anchor) {
|
||||
root.anchor.updateAnchor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openAtItem(item, mouseX, mouseY) {
|
||||
openAt(mouseX || 0, mouseY || 0, item);
|
||||
}
|
||||
|
||||
function close() {
|
||||
|
||||
Reference in New Issue
Block a user