Skip to content

Commit

Permalink
Fix menu size in multiscreen setups
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
FriedemannKleint authored and vohi committed Mar 7, 2024
1 parent 9069b7f commit 8cd7a3d
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 4 deletions.
12 changes: 12 additions & 0 deletions src/widgets/widgets/qmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/widgets/widgets/qmenu_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,11 @@ class QMenuPrivate : public QWidgetPrivate

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;
Expand Down
12 changes: 8 additions & 4 deletions src/widgets/widgets/qmenubar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down

0 comments on commit 8cd7a3d

Please sign in to comment.