347 actionRects.resize(actions.size());
348 actionRects.fill(QRect());
352 QStyle *style = q->style();
355 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q),
356 vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q),
357 icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q);
358 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
359 const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q);
360 const int tearoffHeight =
tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0;
361 const int base_y = vmargin + fw + topmargin + (scroll ? scroll->scrollOffset : 0) + tearoffHeight;
362 const int column_max_y = screen.height() - 2 * deskFw - (vmargin + bottommargin + fw);
363 int max_column_width = 0;
372 for (
int i = 0; i < actions.size(); ++i) {
373 QAction *action = actions.at(i);
374 if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action))
378 QIcon is = action->icon();
385 QFontMetrics qfm = q->fontMetrics();
386 bool previousWasSeparator =
true;
387#if QT_CONFIG(shortcut)
388 const bool contextMenu = isContextMenu();
390 const bool menuSupportsSections = q->style()->styleHint(QStyle::SH_Menu_SupportsSections,
nullptr, q);
391 for(
int i = 0; i <= lastVisibleAction; i++) {
392 QAction *action = actions.at(i);
393 const bool isSection = action->isSeparator() && (!action->text().isEmpty() || !action->icon().isNull());
394 const bool isPlainSeparator = (isSection && !menuSupportsSections)
395 || (action->isSeparator() && !isSection);
397 if (!action->isVisible() ||
401 previousWasSeparator = isPlainSeparator;
404 QStyleOptionMenuItem opt;
405 q->initStyleOption(&opt, action);
406 const QFontMetrics &fm = opt.fontMetrics;
409 if (QWidget *w = widgetItems.value(action)) {
410 sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
413 if (action->isSeparator() && action->text().isEmpty()) {
416 QString s = action->text();
417 qsizetype t = s.indexOf(u'\t');
421#if QT_CONFIG(shortcut)
422 }
else if (action->isShortcutVisibleInContextMenu() || !contextMenu) {
423 QKeySequence seq = action->shortcut();
425 tabWidth = qMax(
int(tabWidth), qfm.horizontalAdvance(seq.toString(QKeySequence::NativeText)));
428 sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width());
429 sz.setHeight(qMax(fm.height(), qfm.height()));
431 QIcon is = action->icon();
433 QSize is_sz = QSize(icone, icone);
434 if (is_sz.height() > sz.height())
435 sz.setHeight(is_sz.height());
438 sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
443 max_column_width = qMax(max_column_width, sz.width());
445 if (!scroll && y + sz.height() > column_max_y) {
452 actionRects[i] = QRect(0, 0, sz.width(), sz.height());
458 const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QSize(0, 0), q).width();
459 const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin));
460 max_column_width = qMax(min_column_width, max_column_width);
464 int x = hmargin + fw + leftmargin;
467 for(
int i = 0; i < actions.size(); i++) {
468 QRect &rect = actionRects[i];
471 if (!scroll && y + rect.height() > column_max_y) {
472 x += max_column_width + hmargin;
475 rect.translate(x, y);
476 rect.setWidth(max_column_width);
479 if (QWidget *widget = widgetItems.value(actions.at(i))) {
480 widget->setGeometry(rect);
481 widget->setVisible(actions.at(i)->isVisible());
1124 if (!scroll || !scroll->scrollFlags)
1130 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr, q);
1131 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr, q);
1134 for(
int i = 0, saccum = 0; i < actions.size(); i++) {
1135 if (actions.at(i) == action) {
1136 newOffset = topScroll - saccum;
1139 saccum += actionRects.at(i).height();
1142 for(
int i = 0, saccum = 0; i < actions.size(); i++) {
1143 saccum += actionRects.at(i).height();
1144 if (actions.at(i) == action) {
1145 if (location == QMenuScroller::ScrollCenter)
1146 newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
1148 newOffset = (q->height() - botScroll) - saccum;
1153 newOffset -= fw * 2;
1160 int saccum = newOffset;
1161 for(
int i = 0; i < actionRects.size(); i++) {
1162 saccum += actionRects.at(i).height();
1163 if (saccum > q->height()) {
1170 newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin - topmargin - bottommargin;
1172 newOffset -= q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr, q);
1180 newOffset -= vmargin;
1182 QRect screen = popupGeometry();
1183 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth,
nullptr, q);
1184 if (q->height() < screen.height()-(desktopFrame*2)-1) {
1185 QRect geom = q->geometry();
1187 const int newHeight = geom.height()-(newOffset-scroll
->scrollOffset);
1188 if (newHeight > geom.height())
1189 geom.setHeight(newHeight);
1192 if (newTop < desktopFrame+screen.top())
1193 newTop = desktopFrame+screen.top();
1194 if (newTop < geom.top()) {
1195 geom.setTop(newTop);
1200 if (geom.bottom() > screen.bottom() - desktopFrame)
1201 geom.setBottom(screen.bottom() - desktopFrame);
1202 if (geom.top() < desktopFrame+screen.top())
1203 geom.setTop(desktopFrame+screen.top());
1204 if (geom != q->geometry()) {
1206 if (newScrollFlags & QMenuScroller::ScrollDown &&
1207 q->geometry().top() - geom.top() >= -newOffset)
1208 newScrollFlags &= ~QMenuScroller::ScrollDown;
1210 q->setGeometry(geom);
1218 for (
int i = 0; i < actionRects.size(); ++i) {
1219 QRect ¤t = actionRects[i];
1220 current.moveTop(current.top() + delta);
1223 if (QWidget *w = widgetItems.value(actions.at(i)))
1224 w->setGeometry(current);
1228 scroll->scrollFlags = newScrollFlags;
1230 setCurrentAction(action);
1318 const QPoint pos = q->mapFromGlobal(e->globalPosition()).toPoint();
1320 QStyle *style = q->style();
1321 QStyleOption opt(0);
1323 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q);
1324 const int vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q);
1325 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
1327 if (scroll && !activeMenu) {
1328 bool isScroll =
false;
1329 if (pos.x() >= 0 && pos.x() < q->width()) {
1331 if (scroll->scrollFlags & dir) {
1332 if (dir == QMenuScroller::ScrollUp)
1333 isScroll = (pos.y() <= scrollerHeight() + fw + vmargin + topmargin);
1334 else if (dir == QMenuScroller::ScrollDown)
1335 isScroll = (pos.y() >= q->height() - scrollerHeight() - fw - vmargin - bottommargin);
1337 scroll->scrollDirection = dir;
1344 scroll->scrollTimer.start(50, q);
1347 scroll->scrollTimer.stop();
1352 QRect tearRect(leftmargin + hmargin + fw, topmargin + vmargin + fw, q->width() - fw * 2 - hmargin * 2 -leftmargin - rightmargin,
1353 q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q));
1356 q->update(tearRect);
1357 if (tearRect.contains(pos) && hasMouseMoved(e->globalPosition())) {
1358 setCurrentAction(
nullptr);
1360 if (e->type() == QEvent::MouseButtonRelease) {
1362 tornPopup =
new QTornOffMenu(q);
1363 tornPopup->setGeometry(q->geometry());
1372 if (q->frameGeometry().contains(e->globalPosition().toPoint()))
1375 for(QWidget *caused = causedPopup.widget; caused;) {
1376 bool passOnEvent =
false;
1377 QWidget *next_widget =
nullptr;
1378 QPointF cpos = caused->mapFromGlobal(e->globalPosition());
1379#if QT_CONFIG(menubar)
1380 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
1381 passOnEvent = mb->rect().contains(cpos.toPoint());
1384 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
1385 passOnEvent = m->rect().contains(cpos.toPoint());
1386 next_widget = m->d_func()->causedPopup.widget;
1389 if (e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
1390 QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->globalPosition(),
1391 e->button(), e->buttons(), e->modifiers(),
1392 e->source(), e->pointingDevice());
1393 QCoreApplication::sendEvent(caused, &new_e);
1397 caused = next_widget;
1399 sloppyState.leave();
1445void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e,
bool self)
1448#if QT_CONFIG(whatsthis)
1449 bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
1451 if (!action || !q->isEnabled()
1452 || (action_e == QAction::Trigger
1453#if QT_CONFIG(whatsthis)
1456 && (action->isSeparator() ||!action->isEnabled())))
1460
1461
1462 const QList<QPointer<QWidget>> causedStack = calcCausedStack();
1463 if (action_e == QAction::Trigger) {
1464#if QT_CONFIG(whatsthis)
1465 if (!inWhatsThisMode)
1466 actionAboutToTrigger = action;
1469 if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
1472 for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
1473 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1476 widget = qmenu->d_func()->causedPopup.widget;
1483#if QT_CONFIG(whatsthis)
1484 if (inWhatsThisMode) {
1485 QString s = action->whatsThis();
1488 QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
1494 QPointer<QMenu> thisGuard(q);
1495 activateCausedStack(causedStack, action, action_e, self);
1499 if (action_e == QAction::Hover) {
1500#if QT_CONFIG(accessibility)
1501 if (QAccessible::isActive()) {
1502 int actionIndex = indexOf(action);
1503 QAccessibleEvent focusEvent(q, QAccessible::Focus);
1504 focusEvent.setChild(actionIndex);
1505 QAccessible::updateAccessibility(&focusEvent);
1508 action->showStatusText(topCausedWidget());
1510 actionAboutToTrigger =
nullptr;
1596void QMenu::initStyleOption(QStyleOptionMenuItem *option,
const QAction *action)
const
1598 if (!option || !action)
1602 option->initFrom(
this);
1603 option->palette = palette();
1604 option->state = QStyle::State_None;
1606 if (window()->isActiveWindow())
1607 option->state |= QStyle::State_Active;
1608 if (isEnabled() && action->isEnabled()
1609 && (!action->menu() || action->menu()->isEnabled()))
1610 option->state |= QStyle::State_Enabled;
1612 option->palette.setCurrentColorGroup(QPalette::Disabled);
1614 option->font = action->font().resolve(font());
1615 option->fontMetrics = QFontMetrics(option->font);
1617 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1618 option->state |= QStyle::State_Selected
1619 | (QMenuPrivate::mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1622 option->menuHasCheckableItems = d->hasCheckableItems;
1623 if (!action->isCheckable()) {
1624 option->checkType = QStyleOptionMenuItem::NotCheckable;
1626 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1627 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1628 option->checked = action->isChecked();
1631 option->menuItemType = QStyleOptionMenuItem::SubMenu;
1632 else if (action->isSeparator())
1633 option->menuItemType = QStyleOptionMenuItem::Separator;
1634 else if (d->defaultAction == action)
1635 option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1637 option->menuItemType = QStyleOptionMenuItem::Normal;
1638 if (action->isIconVisibleInMenu())
1639 option->icon = action->icon();
1640 QString textAndAccel = action->text();
1641#ifndef QT_NO_SHORTCUT
1642 if ((action->isShortcutVisibleInContextMenu() || !d->isContextMenu())
1643 && textAndAccel.indexOf(u'\t') == -1) {
1644 QKeySequence seq = action->shortcut();
1646 textAndAccel += u'\t' + seq.toString(QKeySequence::NativeText);
1649 option->text = textAndAccel;
1650 option->reservedShortcutWidth = d->tabWidth;
1651 option->maxIconWidth = d->maxIconWidth;
1652 option->menuRect = rect();
2313void QMenuPrivate::popup(
const QPoint &p, QAction *atAction, PositionFunction positionFunction)
2316 popupScreen = QGuiApplication::screenAt(p);
2317 QScopeGuard popupScreenGuard([
this](){ popupScreen.clear(); });
2330 q->ensurePolished();
2334 bool screenSet =
false;
2335 QScreen *screen = topData()->initialScreen;
2337 if (setScreen(screen))
2340 }
else if (QMenu *parentMenu = qobject_cast<QMenu *>(parent)) {
2343 if (setScreen(parentMenu->screen()))
2347 if (!screenSet && setScreenForPoint(p))
2362 if (!windowHandle()) {
2366#if QT_CONFIG(menubar)
2369 q->setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(topCausedWidget()) !=
nullptr);
2372 emit q->aboutToShow();
2376#if QT_CONFIG(graphicsview)
2377 bool isEmbedded = !bypassGraphicsProxyWidget(q) && QMenuPrivate::nearestGraphicsProxyWidget(q);
2379 screen = popupGeometry();
2382 screen = popupGeometry(QGuiApplication::screenAt(p));
2386 QPushButton *causedButton = qobject_cast<QPushButton*>(causedPopup.widget);
2387 if (actionListChanged && causedButton)
2388 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
2391 popupScreen = QGuiApplication::screenAt(pos);
2393 const QSize menuSizeHint(q->sizeHint());
2394 QSize size = menuSizeHint;
2396 if (positionFunction)
2397 pos = positionFunction(menuSizeHint);
2399 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth,
nullptr, q);
2400 bool adjustToDesktop = !q->window()->testAttribute(Qt::WA_DontShowOnScreen);
2403 if ((size.height() > screen.height() || size.width() > screen.width()) ||
2405 (ncols >1 && size.height() < screen.height())) {
2406 size.setWidth(qMin(menuSizeHint.width(), screen.width() - desktopFrame * 2));
2407 size.setHeight(qMin(menuSizeHint.height(), screen.height() - desktopFrame * 2));
2408 adjustToDesktop =
true;
2411#ifdef QT_KEYPAD_NAVIGATION
2412 if (!atAction && QApplicationPrivate::keypadNavigationEnabled()) {
2414 if (defaultAction && defaultAction->isEnabled()) {
2415 atAction = defaultAction;
2418 for (QAction *action : std::as_const(actions))
2419 if (action->isEnabled()) {
2424 currentAction = atAction;
2428 pos.setY(screen.top() + desktopFrame);
2429 }
else if (atAction) {
2430 for (
int i = 0, above_height = 0; i < actions.size(); i++) {
2431 QAction *action = actions.at(i);
2432 if (action == atAction) {
2433 int newY = pos.y() - above_height;
2434 if (scroll && newY < desktopFrame) {
2435 scroll->scrollFlags = scroll->scrollFlags
2438 newY = desktopFrame;
2443 && !q->style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll,
nullptr, q)) {
2445 for (
int i2 = i; i2 < actionRects.size(); i2++)
2446 below_height += actionRects.at(i2).height();
2447 size.setHeight(below_height);
2451 above_height += actionRects.at(i).height();
2458 const auto rectIsNull = [](
const QRect &rect) {
return rect.isNull(); };
2459 if (q->childrenRect().isEmpty()
2460 && std::all_of(actionRects.cbegin(), actionRects.cend(), rectIsNull)) {
2462 syncAction =
nullptr;
2468 const QPoint mouse = QGuiApplicationPrivate::lastCursorPosition.toPoint();
2469 mousePopupPos = QGuiApplicationPrivate::lastCursorPosition;
2470 const bool snapToMouse = !causedPopup.widget && (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
2472 if (adjustToDesktop) {
2474 if (q->isRightToLeft()) {
2476 pos.setX(mouse.x() - size.width());
2478#if QT_CONFIG(menubar)
2480 if (qobject_cast<QMenuBar*>(causedPopup.widget) || qobject_cast<QMenu*>(causedPopup.widget))
2481 pos.rx() -= size.width();
2484 if (pos.x() < screen.left() + desktopFrame)
2485 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
2486 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2487 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
2489 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2490 pos.setX(screen.right() - desktopFrame - size.width() + 1);
2491 if (pos.x() < screen.left() + desktopFrame)
2492 pos.setX(screen.left() + desktopFrame);
2494 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
2496 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2498 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2501 if (pos.y() < screen.top() + desktopFrame)
2502 pos.setY(screen.top() + desktopFrame);
2503 if (pos.y() + menuSizeHint.height() - 1 > screen.bottom() - desktopFrame) {
2506 int y = qMax(screen.y(),pos.y());
2507 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
2510 pos.setY(screen.bottom() - size.height() + 1);
2515 const int subMenuOffset = q->style()->pixelMetric(QStyle::PM_SubMenuOverlap,
nullptr, q);
2516 QMenu *caused = qobject_cast<QMenu*>(causedPopup.widget);
2517 if (caused && caused->geometry().width() + menuSizeHint.width() + subMenuOffset < screen.width()) {
2518 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
2519 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
2520 parentActionRect.moveTopLeft(actionTopLeft);
2521 if (q->isRightToLeft()) {
2522 if ((pos.x() + menuSizeHint.width() > parentActionRect.left() - subMenuOffset)
2523 && (pos.x() < parentActionRect.right()))
2525 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2526 if (pos.x() < screen.x())
2527 pos.rx() = parentActionRect.right();
2528 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2529 pos.rx() = screen.x();
2532 if ((pos.x() < parentActionRect.right() + subMenuOffset)
2533 && (pos.x() + menuSizeHint.width() > parentActionRect.left()))
2535 pos.rx() = parentActionRect.right();
2536 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2537 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2538 if (pos.x() < screen.x())
2539 pos.rx() = screen.x() + screen.width() - menuSizeHint.width();
2543 popupScreen = QGuiApplication::screenAt(pos);
2544 q->setGeometry(QRect(pos, size));
2546#if QT_CONFIG(wayland)
2548 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(q->windowHandle()->handle())) {
2550 const QRect controlGeometry(causedButton->mapTo(causedButton->window(), QPoint(0, 0)), causedButton->size());
2551 waylandWindow->setParentControlGeometry(controlGeometry);
2552 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2553 }
else if (caused) {
2554 waylandWindow->setParentControlGeometry(caused->d_func()->actionRect(caused->d_func()->currentAction));
2555 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu);
2556 }
else if (
auto menubar = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2557 QPoint menuBarWindowPosition = menubar->mapTo(menubar->window(), QPoint(0, 0));
2558 waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action).translated(menuBarWindowPosition));
2559 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2564#if QT_CONFIG(effects)
2565 int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
2566 int vGuess = QEffects::DownScroll;
2567 if (q->isRightToLeft()) {
2568 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
2569 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 > causedPopup.widget->x()))
2570 hGuess = QEffects::RightScroll;
2572 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
2573 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 < causedPopup.widget->x()))
2574 hGuess = QEffects::LeftScroll;
2577#if QT_CONFIG(menubar)
2578 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
2579 (qobject_cast<QMenuBar*>(causedPopup.widget) &&
2580 pos.y() + size.width() / 2 < causedPopup.widget->mapToGlobal(causedPopup.widget->pos()).y()))
2581 vGuess = QEffects::UpScroll;
2583 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
2584 bool doChildEffects =
true;
2585#if QT_CONFIG(menubar)
2586 if (QMenuBar *mb = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2587 doChildEffects = mb->d_func()->doChildEffects;
2588 mb->d_func()->doChildEffects =
false;
2591 if (QMenu *m = qobject_cast<QMenu*>(causedPopup.widget)) {
2592 doChildEffects = m->d_func()->doChildEffects;
2593 m->d_func()->doChildEffects =
false;
2596 if (doChildEffects) {
2597 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
2599 else if (causedPopup.widget)
2600 qScrollEffect(q, qobject_cast<QMenu*>(causedPopup.widget) ? hGuess : vGuess);
2602 qScrollEffect(q, hGuess | vGuess);
2605 qFadeEffect(
nullptr);
2606 qScrollEffect(
nullptr);
2616#if QT_CONFIG(accessibility)
2617 QAccessibleEvent event(q, QAccessible::PopupMenuStart);
2618 QAccessible::updateAccessibility(&event);
2769void QMenu::paintEvent(QPaintEvent *e)
2772 d->updateActionRects();
2773 QStylePainter p(
this);
2774 QRegion emptyArea = QRegion(rect());
2776 QStyleOptionMenuItem menuOpt;
2777 menuOpt.initFrom(
this);
2778 menuOpt.state = QStyle::State_None;
2779 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2780 menuOpt.maxIconWidth = 0;
2781 menuOpt.reservedShortcutWidth = 0;
2782 p.drawPrimitive(QStyle::PE_PanelMenu, menuOpt);
2785 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr,
this);
2786 const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin,
nullptr,
this);
2787 const int vmargin = style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr,
this);
2789 QRect scrollUpRect, scrollDownRect;
2790 const int leftmargin = fw + hmargin + d->leftmargin;
2791 const int topmargin = fw + vmargin + d->topmargin;
2792 const int bottommargin = fw + vmargin + d->bottommargin;
2793 const int contentWidth = width() - (fw + hmargin) * 2 - d->leftmargin - d->rightmargin;
2795 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2796 scrollUpRect.setRect(leftmargin, topmargin, contentWidth, d->scrollerHeight());
2798 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2799 scrollDownRect.setRect(leftmargin, height() - d->scrollerHeight() - bottommargin,
2800 contentWidth, d->scrollerHeight());
2806 tearOffRect.setRect(leftmargin, topmargin, contentWidth,
2807 style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this));
2808 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2809 tearOffRect.translate(0, d->scrollerHeight());
2813 QRect scrollUpTearOffRect = scrollUpRect.united(tearOffRect);
2814 for (
int i = 0; i < d->actions.size(); ++i) {
2815 QAction *action = d->actions.at(i);
2816 QRect actionRect = d->actionRects.at(i);
2817 if (!e->rect().intersects(actionRect)
2818 || d->widgetItems.value(action))
2821 emptyArea -= QRegion(actionRect);
2823 QRect adjustedActionRect = actionRect;
2824 if (!scrollUpTearOffRect.isEmpty() && adjustedActionRect.bottom() <= scrollUpTearOffRect.top())
2827 if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom())
2830 if (adjustedActionRect.intersects(scrollUpTearOffRect)) {
2831 if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom())
2834 adjustedActionRect.setTop(scrollUpTearOffRect.bottom()+1);
2837 if (adjustedActionRect.intersects(scrollDownRect)) {
2838 if (adjustedActionRect.top() >= scrollDownRect.top())
2841 adjustedActionRect.setBottom(scrollDownRect.top()-1);
2844 QRegion adjustedActionReg(adjustedActionRect);
2845 p.setClipRegion(adjustedActionReg);
2847 QStyleOptionMenuItem opt;
2848 initStyleOption(&opt, action);
2849 opt.rect = actionRect;
2850 p.drawControl(QStyle::CE_MenuItem, opt);
2853 emptyArea -= QRegion(scrollUpTearOffRect);
2854 emptyArea -= QRegion(scrollDownRect);
2856 if (d->scrollUpTearOffItem || d->scrollDownItem) {
2857 if (d->scrollUpTearOffItem)
2858 d->scrollUpTearOffItem->updateScrollerRects(scrollUpTearOffRect);
2859 if (d->scrollDownItem)
2860 d->scrollDownItem->updateScrollerRects(scrollDownRect);
2863 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollUp, scrollUpRect);
2864 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollDown, scrollDownRect);
2866 d->drawTearOff(&p, tearOffRect);
2872 borderReg += QRect(0, 0, fw, height());
2873 borderReg += QRect(width()-fw, 0, fw, height());
2874 borderReg += QRect(0, 0, width(), fw);
2875 borderReg += QRect(0, height()-fw, width(), fw);
2876 p.setClipRegion(borderReg);
2877 emptyArea -= borderReg;
2878 QStyleOptionFrame frame;
2879 frame.rect = rect();
2880 frame.palette = palette();
2881 frame.state = QStyle::State_None;
2882 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame,
this);
2883 frame.midLineWidth = 0;
2884 p.drawPrimitive(QStyle::PE_FrameMenu, frame);
2888 p.setClipRegion(emptyArea);
2889 menuOpt.state = QStyle::State_None;
2890 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2891 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2892 menuOpt.rect = rect();
2893 menuOpt.menuRect = rect();
2894 p.drawControl(QStyle::CE_MenuEmptyArea, menuOpt);
3025bool QMenu::event(QEvent *e)
3028 switch (e->type()) {
3029 case QEvent::Polish:
3030 d->updateLayoutDirection();
3032 case QEvent::ShortcutOverride: {
3033 QKeyEvent *kev =
static_cast<QKeyEvent *>(e);
3034 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
3035 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
3036 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
3037#ifndef QT_NO_SHORTCUT
3038 || kev->matches(QKeySequence::Cancel)
3046 case QEvent::KeyPress: {
3047 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
3048 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
3053 case QEvent::MouseButtonPress:
3054 case QEvent::ContextMenu: {
3055 bool canPopup =
true;
3056 if (e->type() == QEvent::MouseButtonPress)
3057 canPopup = (
static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton);
3058 if (canPopup && d->delayState.timer.isActive()) {
3059 d->delayState.stop();
3060 internalDelayedPopup();
3064 case QEvent::Resize: {
3065 QStyleHintReturnMask menuMask;
3066 QStyleOption option;
3067 option.initFrom(
this);
3068 if (style()->styleHint(QStyle::SH_Menu_Mask, &option,
this, &menuMask)) {
3069 setMask(menuMask.region);
3072 d->updateActionRects();
3075 QMenuPrivate::mouseDown =
nullptr;
3076 d->updateActionRects();
3077 d->sloppyState.reset();
3078 if (d->currentAction)
3079 d->popupAction(d->currentAction, 0,
false);
3080 if (isWindow() && window() && window()->windowHandle())
3081 window()->windowHandle()->setTransientParent(d->transientParentWindow());
3083#if QT_CONFIG(tooltip)
3084 case QEvent::ToolTip:
3085 if (d->toolTipsVisible) {
3086 const QHelpEvent *ev =
static_cast<
const QHelpEvent*>(e);
3087 if (QAction *action = actionAt(ev->pos())) {
3088 const QString toolTip = action->d_func()->tooltip;
3089 if (!toolTip.isEmpty())
3090 QToolTip::showText(ev->globalPos(), toolTip,
this, actionGeometry(action));
3092 QToolTip::hideText();
3098#if QT_CONFIG(whatsthis)
3099 case QEvent::QueryWhatsThis:
3100 e->setAccepted(d->whatsThis.size());
3101 if (QAction *action = d->actionAt(
static_cast<QHelpEvent*>(e)->pos())) {
3102 if (action->whatsThis().size() || action->menu())
3110 return QWidget::event(e);
3127void QMenu::keyPressEvent(QKeyEvent *e)
3130 d->updateActionRects();
3132 if (isRightToLeft()) {
3133 if (key == Qt::Key_Left)
3134 key = Qt::Key_Right;
3135 else if (key == Qt::Key_Right)
3139 if (key == Qt::Key_Tab)
3141 if (key == Qt::Key_Backtab)
3145 bool key_consumed =
false;
3148 key_consumed =
true;
3150 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3153 key_consumed =
true;
3155 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3157 case Qt::Key_PageUp:
3158 key_consumed =
true;
3159 if (d->currentAction && d->scroll) {
3160 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3161 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp,
true,
true);
3163 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3166 case Qt::Key_PageDown:
3167 key_consumed =
true;
3168 if (d->currentAction && d->scroll) {
3169 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
3170 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown,
true,
true);
3172 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3176 case Qt::Key_Down: {
3177 key_consumed =
true;
3178 QAction *nextAction =
nullptr;
3179 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
3180 if (!d->currentAction) {
3181 if (key == Qt::Key_Down) {
3182 for(
int i = 0; i < d->actions.size(); ++i) {
3183 if (d->actionRects.at(i).isNull())
3185 QAction *act = d->actions.at(i);
3186 if (d->considerAction(act)) {
3192 for(
int i = d->actions.size()-1; i >= 0; --i) {
3193 if (d->actionRects.at(i).isNull())
3195 QAction *act = d->actions.at(i);
3196 if (d->considerAction(act)) {
3203 for(
int i = 0, y = 0; !nextAction && i < d->actions.size(); i++) {
3204 QAction *act = d->actions.at(i);
3205 if (act == d->currentAction) {
3206 if (key == Qt::Key_Up) {
3207 for(
int next_i = i-1;
true; next_i--) {
3209 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3212 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3213 next_i = d->actionRects.size()-1;
3215 QAction *next = d->actions.at(next_i);
3216 if (next == d->currentAction)
3218 if (d->actionRects.at(next_i).isNull())
3220 if (!d->considerAction(next))
3223 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
3224 int topVisible = d->scrollerHeight();
3226 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3227 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
3228 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3232 if (!nextAction && d->tearoff)
3233 d->tearoffHighlighted = 1;
3235 y += d->actionRects.at(i).height();
3236 for(
int next_i = i+1;
true; next_i++) {
3237 if (next_i == d->actionRects.size()) {
3238 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3241 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3244 QAction *next = d->actions.at(next_i);
3245 if (next == d->currentAction)
3247 if (d->actionRects.at(next_i).isNull())
3249 if (!d->considerAction(next))
3252 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
3253 int bottomVisible = height() - d->scrollerHeight();
3254 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3255 bottomVisible -= d->scrollerHeight();
3257 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3258 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
3259 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3266 y += d->actionRects.at(i).height();
3270 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
3271 d->scroll->scrollTimer.stop();
3272 d->scrollMenu(nextAction, scroll_loc);
3274 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, key == Qt::Key_Up ? QMenuPrivate::SelectionDirection::Up : QMenuPrivate::SelectionDirection::Down);
3279 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
3280 d->popupAction(d->currentAction, 0,
true);
3281 key_consumed =
true;
3285 case Qt::Key_Left: {
3286 if (d->currentAction && !d->scroll) {
3287 QAction *nextAction =
nullptr;
3288 if (key == Qt::Key_Left) {
3289 QRect actionR = d->actionRect(d->currentAction);
3290 for(
int x = actionR.left()-1; !nextAction && x >= 0; x--)
3291 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3293 QRect actionR = d->actionRect(d->currentAction);
3294 for(
int x = actionR.right()+1; !nextAction && x < width(); x++)
3295 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3298 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, QMenuPrivate::SelectionDirection::Up);
3299 key_consumed =
true;
3302 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
3303 QPointer<QWidget> caused = d->causedPopup.widget;
3307 key_consumed =
true;
3315 key_consumed =
true;
3316 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation,
nullptr,
this))
3319#if QT_CONFIG(menubar)
3320 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
3321 mb->d_func()->setKeyboardMode(
false);
3328 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem,
nullptr,
this))
3332#ifdef QT_KEYPAD_NAVIGATION
3333 case Qt::Key_Select:
3335 case Qt::Key_Return:
3336 case Qt::Key_Enter: {
3337 if (!d->currentAction) {
3338 d->setFirstActionActive();
3339 key_consumed =
true;
3345 if (d->currentAction->menu())
3346 d->popupAction(d->currentAction, 0,
true);
3348 d->activateAction(d->currentAction, QAction::Trigger);
3349 key_consumed =
true;
3352#if QT_CONFIG(whatsthis)
3354 if (!d->currentAction || d->currentAction->whatsThis().isNull())
3356 QWhatsThis::enterWhatsThisMode();
3357 d->activateAction(d->currentAction, QAction::Trigger);
3361 key_consumed =
false;
3364 if (!key_consumed && (
3366#ifndef QT_NO_SHORTCUT
3367 || e->matches(QKeySequence::Cancel)
3369#ifdef QT_KEYPAD_NAVIGATION
3370 || e->key() == Qt::Key_Back
3373 key_consumed =
true;
3379 QPointer<QWidget> caused = d->causedPopup.widget;
3381#if QT_CONFIG(menubar)
3382 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
3383 mb->d_func()->setCurrentAction(d->menuAction);
3384 mb->d_func()->setKeyboardMode(
true);
3390 if (!key_consumed) {
3391 const Qt::KeyboardModifiers modifiers = e->modifiers();
3392 if ((!modifiers || modifiers == Qt::AltModifier || modifiers == Qt::ShiftModifier
3393 || modifiers == Qt::KeypadModifier
3394 || modifiers == (Qt::KeypadModifier | Qt::AltModifier))
3395 && e->text().size() == 1) {
3396 bool activateAction =
false;
3397 QAction *nextAction =
nullptr;
3398 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch,
nullptr,
this) && !e->modifiers()) {
3399 int best_match_count = 0;
3400 d->searchBufferTimer.start(2000,
this);
3401 d->searchBuffer += e->text();
3402 for(
int i = 0; i < d->actions.size(); ++i) {
3403 int match_count = 0;
3404 if (d->actionRects.at(i).isNull())
3406 QAction *act = d->actions.at(i);
3407 const QString act_text = act->text();
3408 for(
int c = 0; c < d->searchBuffer.size(); ++c) {
3409 if (act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
3412 if (match_count > best_match_count) {
3413 best_match_count = match_count;
3418#ifndef QT_NO_SHORTCUT
3421 QAction *first =
nullptr, *currentSelected =
nullptr, *firstAfterCurrent =
nullptr;
3422 QChar c = e->text().at(0).toUpper();
3423 for(
int i = 0; i < d->actions.size(); ++i) {
3424 if (d->actionRects.at(i).isNull())
3426 QAction *act = d->actions.at(i);
3427 if (!act->isEnabled() || act->isSeparator())
3429 QKeySequence sequence = QKeySequence::mnemonic(act->text());
3430 int key = sequence[0].toCombined() & 0xffff;
3431 if (key == c.unicode()) {
3435 if (act == d->currentAction)
3436 currentSelected = act;
3437 else if (!firstAfterCurrent && currentSelected)
3438 firstAfterCurrent = act;
3441 if (clashCount == 1)
3442 activateAction =
true;
3443 if (clashCount >= 1) {
3444 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
3447 nextAction = firstAfterCurrent;
3452 key_consumed =
true;
3454 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter,
false);
3455 d->setCurrentAction(nextAction, 0, QMenuPrivate::SelectedFromElsewhere, QMenuPrivate::SelectionDirection::Down,
true);
3456 if (!nextAction->menu() && activateAction) {
3458 d->activateAction(nextAction, QAction::Trigger);
3462 if (!key_consumed) {
3463#if QT_CONFIG(menubar)
3464 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
3465 QAction *oldAct = mb->d_func()->currentAction;
3466 QCoreApplication::sendEvent(mb, e);
3467 if (mb->d_func()->currentAction != oldAct)
3468 key_consumed =
true;
3474 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
3475 QApplication::beep();
3572void QMenu::actionEvent(QActionEvent *e)
3576 setAttribute(Qt::WA_Resized,
false);
3578 d->tornPopup->syncWithMenu(
this, e);
3579 if (e->type() == QEvent::ActionAdded) {
3582#if QT_CONFIG(menubar)
3583 && !qobject_cast<QMenuBar*>(e->action()->parent())
3588 connect(e->action(), SIGNAL(triggered()),
this, SLOT(_q_actionTriggered()), Qt::UniqueConnection);
3589 connect(e->action(), SIGNAL(hovered()),
this, SLOT(_q_actionHovered()), Qt::UniqueConnection);
3591 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3592 QWidget *widget = wa->requestWidget(
this);
3594 d->widgetItems.insert(wa, widget);
3596 if (!d->scrollUpTearOffItem)
3597 d->scrollUpTearOffItem =
3598 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollUp, d,
this);
3599 if (!d->scrollDownItem)
3601 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollDown, d,
this);
3605 }
else if (e->type() == QEvent::ActionRemoved) {
3606 e->action()->disconnect(
this);
3607 if (e->action() == d->currentAction)
3608 d->currentAction =
nullptr;
3609 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3610 if (QWidget *widget = d->widgetItems.value(wa))
3611 wa->releaseWidget(widget);
3613 d->widgetItems.remove(
static_cast<QAction *>(e->action()));
3616 if (!d->platformMenu.isNull()) {
3617 auto action =
static_cast<QAction *>(e->action());
3618 if (e->type() == QEvent::ActionAdded) {
3619 QPlatformMenuItem *beforeItem = e->before()
3620 ? d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->before()))
3622 d->insertActionInPlatformMenu(action, beforeItem);
3623 }
else if (e->type() == QEvent::ActionRemoved) {
3624 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3625 d->platformMenu->removeMenuItem(menuItem);
3627 }
else if (e->type() == QEvent::ActionChanged) {
3628 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3630 d->copyActionToPlatformItem(action, menuItem);
3631 d->platformMenu->syncMenuItem(menuItem);
3635 d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators);