diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2023-11-08 22:23:00 +0100 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2024-03-07 15:09:58 +0000 |
commit | 8cd7a3d4723ca414f3fe544704a0ccb752da94b8 (patch) | |
tree | 3239ca5990f23cd8c9564d9053f4b398c6a7ed87 /src/widgets/widgets | |
parent | 9069b7fb20fe46f6e7b74c23d052274a8c5cf8dc (diff) |
Fix menu size in multiscreen setups
During QMenuPrivate::popup() and QMenuBarPrivate::popupAction(),
QMenuPrivate::popupGeometry() is called from updateActionRects() with
screen = 0 several times (from sizeHint() and various event handlers
triggered), which causes it to return the primary screen geometry
always.
To fix this for the non-QGraphicsView case, use the screen of
the menu when it is visible or the screen stored in a newly introduced
popupScreen member variable, which is set from a few places
in QMenuPrivate::popup().
Fixes: QTBUG-118434
Pick-to: 6.7 6.6 6.5
Change-Id: I6b18593d313719d628b0856004197ac59f46c270
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/widgets/widgets')
-rw-r--r-- | src/widgets/widgets/qmenu.cpp | 12 | ||||
-rw-r--r-- | src/widgets/widgets/qmenu_p.h | 5 | ||||
-rw-r--r-- | src/widgets/widgets/qmenubar.cpp | 12 |
3 files changed, 25 insertions, 4 deletions
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 0e5e85ca48..120820dea1 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -292,6 +292,13 @@ inline bool QMenuPrivate::useFullScreenForPopup() const QRect QMenuPrivate::popupGeometry(QScreen *screen) const { Q_Q(const QMenu); + if (screen == nullptr +#if QT_CONFIG(graphicsview) + && q->graphicsProxyWidget() == nullptr +#endif + ) { + screen = q->isVisible() ? q->screen() : popupScreen.data(); + } if (useFullScreenForPopup()) return screen ? screen->geometry() : QWidgetPrivate::screenGeometry(q); @@ -2309,6 +2316,9 @@ void QMenu::popup(const QPoint &p, QAction *atAction) void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction positionFunction) { Q_Q(QMenu); + popupScreen = QGuiApplication::screenAt(p); + QScopeGuard popupScreenGuard([this](){ popupScreen.clear(); }); + if (scroll) { // reset scroll state from last popup if (scroll->scrollOffset) itemsDirty = 1; // sizeHint will be incorrect if there is previous scroll @@ -2384,6 +2394,7 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition(); else pos = p; + popupScreen = QGuiApplication::screenAt(pos); const QSize menuSizeHint(q->sizeHint()); QSize size = menuSizeHint; @@ -2522,6 +2533,7 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po } } } + popupScreen = QGuiApplication::screenAt(pos); q->setGeometry(QRect(pos, size)); #if QT_CONFIG(effects) int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll; diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h index a009262128..1e32fbcdfa 100644 --- a/src/widgets/widgets/qmenu_p.h +++ b/src/widgets/widgets/qmenu_p.h @@ -473,6 +473,11 @@ public: mutable quint8 ncols = 0; // "255cols ought to be enough for anybody." + // Contains the screen of the popup point during popup(QPoint). + // This is to make sure the screen is remembered, + // when the menu contains many items on multiple screens + QPointer<QScreen> popupScreen; + mutable bool itemsDirty : 1; mutable bool hasCheckableItems : 1; bool lastContextMenu : 1; diff --git a/src/widgets/widgets/qmenubar.cpp b/src/widgets/widgets/qmenubar.cpp index 9c44f005ea..1ffe82835d 100644 --- a/src/widgets/widgets/qmenubar.cpp +++ b/src/widgets/widgets/qmenubar.cpp @@ -281,17 +281,21 @@ void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst) if (action->isEnabled() && action->menu()->isEnabled()) { closePopupMode = 0; activeMenu = action->menu(); - activeMenu->d_func()->causedPopup.widget = q; - activeMenu->d_func()->causedPopup.action = action; + auto *activeMenuPriv = activeMenu->d_func(); + activeMenuPriv->causedPopup.widget = q; + activeMenuPriv->causedPopup.action = action; QRect adjustedActionRect = actionRect(action); QPoint pos(q->mapToGlobal(QPoint(adjustedActionRect.left(), adjustedActionRect.bottom() + 1))); - QSize popup_size = activeMenu->sizeHint(); //we put the popup menu on the screen containing the bottom-center of the action rect QScreen *menubarScreen = q->window()->windowHandle()->screen(); - QScreen *popupScreen = menubarScreen->virtualSiblingAt(pos + QPoint(adjustedActionRect.width() / 2, 0)); + QPointer<QScreen> popupScreen = menubarScreen->virtualSiblingAt(pos + QPoint(adjustedActionRect.width() / 2, 0)); if (!popupScreen) popupScreen = menubarScreen; + std::swap(popupScreen, activeMenuPriv->popupScreen); + const QSize popup_size = activeMenu->sizeHint(); + std::swap(popupScreen, activeMenuPriv->popupScreen); + QRect screenRect = popupScreen->geometry(); pos = QPoint(qMax(pos.x(), screenRect.x()), qMax(pos.y(), screenRect.y())); const bool fitUp = (pos.y() - popup_size.height() >= screenRect.top()); |