diff options
Diffstat (limited to 'src/widgets/widgets/qmenu.cpp')
-rw-r--r-- | src/widgets/widgets/qmenu.cpp | 64 |
1 files changed, 52 insertions, 12 deletions
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index e789c1bef2..db00f8a650 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -93,7 +93,9 @@ public: Q_D(QTornOffMenu); // make the torn-off menu a sibling of p (instead of a child) QWidget *parentWidget = d->causedStack.isEmpty() ? p : d->causedStack.constLast(); - if (parentWidget->parentWidget()) + if (!parentWidget && p) + parentWidget = p; + if (parentWidget && parentWidget->parentWidget()) parentWidget = parentWidget->parentWidget(); setParent(parentWidget, Qt::Window | Qt::Tool); setAttribute(Qt::WA_DeleteOnClose, true); @@ -163,7 +165,7 @@ void QMenuPrivate::init() defaultMenuAction = menuAction = new QAction(q); menuAction->setMenu(q); // this calls setOverrideMenuAction setOverrideMenuAction(nullptr); - QObject::connect(menuAction, &QAction::changed, [this] { + QObject::connect(menuAction, &QAction::changed, q, [this] { if (!tornPopup.isNull()) tornPopup->updateWindowTitle(); }); @@ -290,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); @@ -567,10 +576,16 @@ void QMenuPrivate::hideMenu(QMenu *menu) }; #if QT_CONFIG(effects) - QSignalBlocker blocker(menu); + // If deleteLater has been called and the event loop spins, while waiting + // for visual effects to happen, menu might become stale. + // To prevent a QSignalBlocker from restoring a stale object, block and restore signals manually. + QPointer<QMenu> stillAlive(menu); + const bool signalsBlocked = menu->signalsBlocked(); + menu->blockSignals(true); + aboutToHide = true; // Flash item which is about to trigger (if any). - if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem) + if (menu && menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem) && currentAction && currentAction == actionAboutToTrigger && menu->actions().contains(currentAction)) { QEventLoop eventLoop; @@ -581,6 +596,9 @@ void QMenuPrivate::hideMenu(QMenu *menu) QTimer::singleShot(60, &eventLoop, SLOT(quit())); eventLoop.exec(); + if (!stillAlive) + return; + // Select and wait 20 ms. menu->setActiveAction(activeAction); QTimer::singleShot(20, &eventLoop, SLOT(quit())); @@ -588,10 +606,16 @@ void QMenuPrivate::hideMenu(QMenu *menu) } aboutToHide = false; - blocker.unblock(); + + if (stillAlive) + menu->blockSignals(signalsBlocked); + else + return; + #endif // QT_CONFIG(effects) if (activeMenu == menu) activeMenu = nullptr; + menu->d_func()->causedPopup.action = nullptr; menu->close(); menu->d_func()->causedPopup.widget = nullptr; @@ -1387,9 +1411,18 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e) void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget>> &causedStack, QAction *action, QAction::ActionEvent action_e, bool self) { - QBoolBlocker guard(activationRecursionGuard); + Q_Q(QMenu); + // can't use QBoolBlocker here + const bool activationRecursionGuardReset = activationRecursionGuard; + activationRecursionGuard = true; + QPointer<QMenu> guard(q); if (self) action->activate(action_e); + if (!guard) + return; + auto boolBlocker = qScopeGuard([this, activationRecursionGuardReset]{ + activationRecursionGuard = activationRecursionGuardReset; + }); for(int i = 0; i < causedStack.size(); ++i) { QPointer<QWidget> widget = causedStack.at(i); @@ -1465,9 +1498,10 @@ void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e #endif } - + QPointer<QMenu> thisGuard(q); activateCausedStack(causedStack, action, action_e, self); - + if (!thisGuard) + return; if (action_e == QAction::Hover) { #if QT_CONFIG(accessibility) @@ -1698,7 +1732,7 @@ void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) \b{Important inherited functions:} addAction(), removeAction(), clear(), addSeparator(), and addMenu(). - \sa QMenuBar, {Qt Widgets - Application Example}, {Menus Example} + \sa QMenuBar, {Menus Example} */ @@ -2282,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 @@ -2357,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; @@ -2495,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; @@ -2635,6 +2674,7 @@ QAction *QMenuPrivate::exec(const QPoint &p, QAction *action, PositionFunction p action = syncAction; syncAction = nullptr; eventLoop = nullptr; + popupScreen.clear(); return action; } @@ -2812,7 +2852,7 @@ void QMenu::paintEvent(QPaintEvent *e) frame.rect = rect(); frame.palette = palette(); frame.state = QStyle::State_None; - frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame); + frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame, this); frame.midLineWidth = 0; p.drawPrimitive(QStyle::PE_FrameMenu, frame); } @@ -2939,7 +2979,7 @@ bool QMenu::event(QEvent *e) d->updateLayoutDirection(); break; case QEvent::ShortcutOverride: { - QKeyEvent *kev = static_cast<QKeyEvent*>(e); + QKeyEvent *kev = static_cast<QKeyEvent *>(e); if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return @@ -2953,7 +2993,7 @@ bool QMenu::event(QEvent *e) } break; case QEvent::KeyPress: { - QKeyEvent *ke = (QKeyEvent*)e; + QKeyEvent *ke = static_cast<QKeyEvent *>(e); if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) { keyPressEvent(ke); return true; |