345 actionRects.resize(actions.size());
346 actionRects.fill(QRect());
350 QStyle *style = q->style();
353 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q),
354 vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q),
355 icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q);
356 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
357 const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q);
358 const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0;
359 const int base_y = vmargin + fw + topmargin + (scroll ? scroll->scrollOffset : 0) + tearoffHeight;
360 const int column_max_y = screen.height() - 2 * deskFw - (vmargin + bottommargin + fw);
361 int max_column_width = 0;
370 for (
int i = 0; i < actions.size(); ++i) {
371 QAction *action = actions.at(i);
372 if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action))
376 QIcon is = action->icon();
383 QFontMetrics qfm = q->fontMetrics();
384 bool previousWasSeparator =
true;
385#if QT_CONFIG(shortcut)
386 const bool contextMenu = isContextMenu();
388 const bool menuSupportsSections = q->style()->styleHint(QStyle::SH_Menu_SupportsSections,
nullptr, q);
389 for(
int i = 0; i <= lastVisibleAction; i++) {
390 QAction *action = actions.at(i);
391 const bool isSection = action->isSeparator() && (!action->text().isEmpty() || !action->icon().isNull());
392 const bool isPlainSeparator = (isSection && !menuSupportsSections)
393 || (action->isSeparator() && !isSection);
395 if (!action->isVisible() ||
399 previousWasSeparator = isPlainSeparator;
402 QStyleOptionMenuItem opt;
403 q->initStyleOption(&opt, action);
404 const QFontMetrics &fm = opt.fontMetrics;
407 if (QWidget *w = widgetItems.value(action)) {
408 sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
411 if (action->isSeparator() && action->text().isEmpty()) {
414 QString s = action->text();
415 qsizetype t = s.indexOf(u'\t');
419#if QT_CONFIG(shortcut)
420 }
else if (action->isShortcutVisibleInContextMenu() || !contextMenu) {
421 QKeySequence seq = action->shortcut();
423 tabWidth = qMax(
int(tabWidth), qfm.horizontalAdvance(seq.toString(QKeySequence::NativeText)));
426 sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width());
427 sz.setHeight(qMax(fm.height(), qfm.height()));
429 QIcon is = action->icon();
431 QSize is_sz = QSize(icone, icone);
432 if (is_sz.height() > sz.height())
433 sz.setHeight(is_sz.height());
436 sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
441 max_column_width = qMax(max_column_width, sz.width());
443 if (!scroll && y + sz.height() > column_max_y) {
450 actionRects[i] = QRect(0, 0, sz.width(), sz.height());
456 const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QSize(0, 0), q).width();
457 const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin));
458 max_column_width = qMax(min_column_width, max_column_width);
462 int x = hmargin + fw + leftmargin;
465 for(
int i = 0; i < actions.size(); i++) {
466 QRect &rect = actionRects[i];
469 if (!scroll && y + rect.height() > column_max_y) {
470 x += max_column_width + hmargin;
473 rect.translate(x, y);
474 rect.setWidth(max_column_width);
477 if (QWidget *widget = widgetItems.value(actions.at(i))) {
478 widget->setGeometry(rect);
479 widget->setVisible(actions.at(i)->isVisible());
1121 if (!scroll || !scroll->scrollFlags)
1127 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr, q);
1128 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr, q);
1131 for(
int i = 0, saccum = 0; i < actions.size(); i++) {
1132 if (actions.at(i) == action) {
1133 newOffset = topScroll - saccum;
1136 saccum += actionRects.at(i).height();
1139 for(
int i = 0, saccum = 0; i < actions.size(); i++) {
1140 saccum += actionRects.at(i).height();
1141 if (actions.at(i) == action) {
1142 if (location == QMenuScroller::ScrollCenter)
1143 newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
1145 newOffset = (q->height() - botScroll) - saccum;
1150 newOffset -= fw * 2;
1157 int saccum = newOffset;
1158 for(
int i = 0; i < actionRects.size(); i++) {
1159 saccum += actionRects.at(i).height();
1160 if (saccum > q->height()) {
1167 newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin - topmargin - bottommargin;
1169 newOffset -= q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr, q);
1177 newOffset -= vmargin;
1179 QRect screen = popupGeometry();
1180 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth,
nullptr, q);
1181 if (q->height() < screen.height()-(desktopFrame*2)-1) {
1182 QRect geom = q->geometry();
1184 const int newHeight = geom.height()-(newOffset-scroll
->scrollOffset);
1185 if (newHeight > geom.height())
1186 geom.setHeight(newHeight);
1189 if (newTop < desktopFrame+screen.top())
1190 newTop = desktopFrame+screen.top();
1191 if (newTop < geom.top()) {
1192 geom.setTop(newTop);
1197 if (geom.bottom() > screen.bottom() - desktopFrame)
1198 geom.setBottom(screen.bottom() - desktopFrame);
1199 if (geom.top() < desktopFrame+screen.top())
1200 geom.setTop(desktopFrame+screen.top());
1201 if (geom != q->geometry()) {
1203 if (newScrollFlags & QMenuScroller::ScrollDown &&
1204 q->geometry().top() - geom.top() >= -newOffset)
1205 newScrollFlags &= ~QMenuScroller::ScrollDown;
1207 q->setGeometry(geom);
1215 for (
int i = 0; i < actionRects.size(); ++i) {
1216 QRect ¤t = actionRects[i];
1217 current.moveTop(current.top() + delta);
1220 if (QWidget *w = widgetItems.value(actions.at(i)))
1221 w->setGeometry(current);
1225 scroll->scrollFlags = newScrollFlags;
1227 setCurrentAction(action);
1315 QPoint pos = q->mapFromGlobal(e->globalPosition().toPoint());
1317 QStyle *style = q->style();
1318 QStyleOption opt(0);
1320 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q);
1321 const int vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q);
1322 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
1324 if (scroll && !activeMenu) {
1325 bool isScroll =
false;
1326 if (pos.x() >= 0 && pos.x() < q->width()) {
1328 if (scroll->scrollFlags & dir) {
1329 if (dir == QMenuScroller::ScrollUp)
1330 isScroll = (pos.y() <= scrollerHeight() + fw + vmargin + topmargin);
1331 else if (dir == QMenuScroller::ScrollDown)
1332 isScroll = (pos.y() >= q->height() - scrollerHeight() - fw - vmargin - bottommargin);
1334 scroll->scrollDirection = dir;
1341 scroll->scrollTimer.start(50, q);
1344 scroll->scrollTimer.stop();
1349 QRect tearRect(leftmargin + hmargin + fw, topmargin + vmargin + fw, q->width() - fw * 2 - hmargin * 2 -leftmargin - rightmargin,
1350 q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q));
1353 q->update(tearRect);
1354 if (tearRect.contains(pos) && hasMouseMoved(e->globalPosition().toPoint())) {
1355 setCurrentAction(
nullptr);
1357 if (e->type() == QEvent::MouseButtonRelease) {
1359 tornPopup =
new QTornOffMenu(q);
1360 tornPopup->setGeometry(q->geometry());
1369 if (q->frameGeometry().contains(e->globalPosition().toPoint()))
1372 for(QWidget *caused = causedPopup.widget; caused;) {
1373 bool passOnEvent =
false;
1374 QWidget *next_widget =
nullptr;
1375 QPointF cpos = caused->mapFromGlobal(e->globalPosition());
1376#if QT_CONFIG(menubar)
1377 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
1378 passOnEvent = mb->rect().contains(cpos.toPoint());
1381 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
1382 passOnEvent = m->rect().contains(cpos.toPoint());
1383 next_widget = m->d_func()->causedPopup.widget;
1386 if (e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
1387 QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->globalPosition(),
1388 e->button(), e->buttons(), e->modifiers(),
1389 e->source(), e->pointingDevice());
1390 QCoreApplication::sendEvent(caused, &new_e);
1394 caused = next_widget;
1396 sloppyState.leave();
1442void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e,
bool self)
1445#if QT_CONFIG(whatsthis)
1446 bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
1448 if (!action || !q->isEnabled()
1449 || (action_e == QAction::Trigger
1450#if QT_CONFIG(whatsthis)
1453 && (action->isSeparator() ||!action->isEnabled())))
1457
1458
1459 const QList<QPointer<QWidget>> causedStack = calcCausedStack();
1460 if (action_e == QAction::Trigger) {
1461#if QT_CONFIG(whatsthis)
1462 if (!inWhatsThisMode)
1463 actionAboutToTrigger = action;
1466 if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
1469 for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
1470 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1473 widget = qmenu->d_func()->causedPopup.widget;
1480#if QT_CONFIG(whatsthis)
1481 if (inWhatsThisMode) {
1482 QString s = action->whatsThis();
1485 QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
1491 QPointer<QMenu> thisGuard(q);
1492 activateCausedStack(causedStack, action, action_e, self);
1496 if (action_e == QAction::Hover) {
1497#if QT_CONFIG(accessibility)
1498 if (QAccessible::isActive()) {
1499 int actionIndex = indexOf(action);
1500 QAccessibleEvent focusEvent(q, QAccessible::Focus);
1501 focusEvent.setChild(actionIndex);
1502 QAccessible::updateAccessibility(&focusEvent);
1505 action->showStatusText(topCausedWidget());
1507 actionAboutToTrigger =
nullptr;
1589void QMenu::initStyleOption(QStyleOptionMenuItem *option,
const QAction *action)
const
1591 if (!option || !action)
1595 option->initFrom(
this);
1596 option->palette = palette();
1597 option->state = QStyle::State_None;
1599 if (window()->isActiveWindow())
1600 option->state |= QStyle::State_Active;
1601 if (isEnabled() && action->isEnabled()
1602 && (!action->menu() || action->menu()->isEnabled()))
1603 option->state |= QStyle::State_Enabled;
1605 option->palette.setCurrentColorGroup(QPalette::Disabled);
1607 option->font = action->font().resolve(font());
1608 option->fontMetrics = QFontMetrics(option->font);
1610 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1611 option->state |= QStyle::State_Selected
1612 | (QMenuPrivate::mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1615 option->menuHasCheckableItems = d->hasCheckableItems;
1616 if (!action->isCheckable()) {
1617 option->checkType = QStyleOptionMenuItem::NotCheckable;
1619 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1620 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1621 option->checked = action->isChecked();
1624 option->menuItemType = QStyleOptionMenuItem::SubMenu;
1625 else if (action->isSeparator())
1626 option->menuItemType = QStyleOptionMenuItem::Separator;
1627 else if (d->defaultAction == action)
1628 option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1630 option->menuItemType = QStyleOptionMenuItem::Normal;
1631 if (action->isIconVisibleInMenu())
1632 option->icon = action->icon();
1633 QString textAndAccel = action->text();
1634#ifndef QT_NO_SHORTCUT
1635 if ((action->isShortcutVisibleInContextMenu() || !d->isContextMenu())
1636 && textAndAccel.indexOf(u'\t') == -1) {
1637 QKeySequence seq = action->shortcut();
1639 textAndAccel += u'\t' + seq.toString(QKeySequence::NativeText);
1642 option->text = textAndAccel;
1643 option->reservedShortcutWidth = d->tabWidth;
1644 option->maxIconWidth = d->maxIconWidth;
1645 option->menuRect = rect();
2306void QMenuPrivate::popup(
const QPoint &p, QAction *atAction, PositionFunction positionFunction)
2309 popupScreen = QGuiApplication::screenAt(p);
2310 QScopeGuard popupScreenGuard([
this](){ popupScreen.clear(); });
2323 q->ensurePolished();
2327 bool screenSet =
false;
2328 QScreen *screen = topData()->initialScreen;
2330 if (setScreen(screen))
2333 }
else if (QMenu *parentMenu = qobject_cast<QMenu *>(parent)) {
2336 if (setScreen(parentMenu->screen()))
2340 if (!screenSet && setScreenForPoint(p))
2355 if (!windowHandle()) {
2359#if QT_CONFIG(menubar)
2362 q->setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(topCausedWidget()) !=
nullptr);
2365 emit q->aboutToShow();
2369#if QT_CONFIG(graphicsview)
2370 bool isEmbedded = !bypassGraphicsProxyWidget(q) && QMenuPrivate::nearestGraphicsProxyWidget(q);
2372 screen = popupGeometry();
2375 screen = popupGeometry(QGuiApplication::screenAt(p));
2379 QPushButton *causedButton = qobject_cast<QPushButton*>(causedPopup.widget);
2380 if (actionListChanged && causedButton)
2381 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
2384 popupScreen = QGuiApplication::screenAt(pos);
2386 const QSize menuSizeHint(q->sizeHint());
2387 QSize size = menuSizeHint;
2389 if (positionFunction)
2390 pos = positionFunction(menuSizeHint);
2392 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth,
nullptr, q);
2393 bool adjustToDesktop = !q->window()->testAttribute(Qt::WA_DontShowOnScreen);
2396 if ((size.height() > screen.height() || size.width() > screen.width()) ||
2398 (ncols >1 && size.height() < screen.height())) {
2399 size.setWidth(qMin(menuSizeHint.width(), screen.width() - desktopFrame * 2));
2400 size.setHeight(qMin(menuSizeHint.height(), screen.height() - desktopFrame * 2));
2401 adjustToDesktop =
true;
2404#ifdef QT_KEYPAD_NAVIGATION
2405 if (!atAction && QApplicationPrivate::keypadNavigationEnabled()) {
2407 if (defaultAction && defaultAction->isEnabled()) {
2408 atAction = defaultAction;
2411 for (QAction *action : std::as_const(actions))
2412 if (action->isEnabled()) {
2417 currentAction = atAction;
2421 pos.setY(screen.top() + desktopFrame);
2422 }
else if (atAction) {
2423 for (
int i = 0, above_height = 0; i < actions.size(); i++) {
2424 QAction *action = actions.at(i);
2425 if (action == atAction) {
2426 int newY = pos.y() - above_height;
2427 if (scroll && newY < desktopFrame) {
2428 scroll->scrollFlags = scroll->scrollFlags
2431 newY = desktopFrame;
2435 if (scroll && scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone
2436 && !q->style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll,
nullptr, q)) {
2438 for (
int i2 = i; i2 < actionRects.size(); i2++)
2439 below_height += actionRects.at(i2).height();
2440 size.setHeight(below_height);
2444 above_height += actionRects.at(i).height();
2451 const auto rectIsNull = [](
const QRect &rect) {
return rect.isNull(); };
2452 if (q->childrenRect().isEmpty()
2453 && std::all_of(actionRects.cbegin(), actionRects.cend(), rectIsNull)) {
2455 syncAction =
nullptr;
2461 const QPoint mouse = QGuiApplicationPrivate::lastCursorPosition.toPoint();
2462 mousePopupPos = QGuiApplicationPrivate::lastCursorPosition;
2463 const bool snapToMouse = !causedPopup.widget && (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
2465 if (adjustToDesktop) {
2467 if (q->isRightToLeft()) {
2469 pos.setX(mouse.x() - size.width());
2471#if QT_CONFIG(menubar)
2473 if (qobject_cast<QMenuBar*>(causedPopup.widget) || qobject_cast<QMenu*>(causedPopup.widget))
2474 pos.rx() -= size.width();
2477 if (pos.x() < screen.left() + desktopFrame)
2478 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
2479 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2480 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
2482 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2483 pos.setX(screen.right() - desktopFrame - size.width() + 1);
2484 if (pos.x() < screen.left() + desktopFrame)
2485 pos.setX(screen.left() + desktopFrame);
2487 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
2489 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2491 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2494 if (pos.y() < screen.top() + desktopFrame)
2495 pos.setY(screen.top() + desktopFrame);
2496 if (pos.y() + menuSizeHint.height() - 1 > screen.bottom() - desktopFrame) {
2499 int y = qMax(screen.y(),pos.y());
2500 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
2503 pos.setY(screen.bottom() - size.height() + 1);
2508 const int subMenuOffset = q->style()->pixelMetric(QStyle::PM_SubMenuOverlap,
nullptr, q);
2509 QMenu *caused = qobject_cast<QMenu*>(causedPopup.widget);
2510 if (caused && caused->geometry().width() + menuSizeHint.width() + subMenuOffset < screen.width()) {
2511 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
2512 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
2513 parentActionRect.moveTopLeft(actionTopLeft);
2514 if (q->isRightToLeft()) {
2515 if ((pos.x() + menuSizeHint.width() > parentActionRect.left() - subMenuOffset)
2516 && (pos.x() < parentActionRect.right()))
2518 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2519 if (pos.x() < screen.x())
2520 pos.rx() = parentActionRect.right();
2521 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2522 pos.rx() = screen.x();
2525 if ((pos.x() < parentActionRect.right() + subMenuOffset)
2526 && (pos.x() + menuSizeHint.width() > parentActionRect.left()))
2528 pos.rx() = parentActionRect.right();
2529 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2530 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2531 if (pos.x() < screen.x())
2532 pos.rx() = screen.x() + screen.width() - menuSizeHint.width();
2536 popupScreen = QGuiApplication::screenAt(pos);
2537 q->setGeometry(QRect(pos, size));
2538#if QT_CONFIG(effects)
2539 int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
2540 int vGuess = QEffects::DownScroll;
2541 if (q->isRightToLeft()) {
2542 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
2543 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 > causedPopup.widget->x()))
2544 hGuess = QEffects::RightScroll;
2546 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
2547 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 < causedPopup.widget->x()))
2548 hGuess = QEffects::LeftScroll;
2551#if QT_CONFIG(menubar)
2552 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
2553 (qobject_cast<QMenuBar*>(causedPopup.widget) &&
2554 pos.y() + size.width() / 2 < causedPopup.widget->mapToGlobal(causedPopup.widget->pos()).y()))
2555 vGuess = QEffects::UpScroll;
2557 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
2558 bool doChildEffects =
true;
2559#if QT_CONFIG(menubar)
2560 if (QMenuBar *mb = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2561 doChildEffects = mb->d_func()->doChildEffects;
2562 mb->d_func()->doChildEffects =
false;
2565 if (QMenu *m = qobject_cast<QMenu*>(causedPopup.widget)) {
2566 doChildEffects = m->d_func()->doChildEffects;
2567 m->d_func()->doChildEffects =
false;
2570 if (doChildEffects) {
2571 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
2573 else if (causedPopup.widget)
2574 qScrollEffect(q, qobject_cast<QMenu*>(causedPopup.widget) ? hGuess : vGuess);
2576 qScrollEffect(q, hGuess | vGuess);
2579 qFadeEffect(
nullptr);
2580 qScrollEffect(
nullptr);
2590#if QT_CONFIG(accessibility)
2591 QAccessibleEvent event(q, QAccessible::PopupMenuStart);
2592 QAccessible::updateAccessibility(&event);
2743void QMenu::paintEvent(QPaintEvent *e)
2746 d->updateActionRects();
2747 QStylePainter p(
this);
2748 QRegion emptyArea = QRegion(rect());
2750 QStyleOptionMenuItem menuOpt;
2751 menuOpt.initFrom(
this);
2752 menuOpt.state = QStyle::State_None;
2753 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2754 menuOpt.maxIconWidth = 0;
2755 menuOpt.reservedShortcutWidth = 0;
2756 p.drawPrimitive(QStyle::PE_PanelMenu, menuOpt);
2759 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr,
this);
2760 const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin,
nullptr,
this);
2761 const int vmargin = style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr,
this);
2763 QRect scrollUpRect, scrollDownRect;
2764 const int leftmargin = fw + hmargin + d->leftmargin;
2765 const int topmargin = fw + vmargin + d->topmargin;
2766 const int bottommargin = fw + vmargin + d->bottommargin;
2767 const int contentWidth = width() - (fw + hmargin) * 2 - d->leftmargin - d->rightmargin;
2769 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2770 scrollUpRect.setRect(leftmargin, topmargin, contentWidth, d->scrollerHeight());
2772 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2773 scrollDownRect.setRect(leftmargin, height() - d->scrollerHeight() - bottommargin,
2774 contentWidth, d->scrollerHeight());
2780 tearOffRect.setRect(leftmargin, topmargin, contentWidth,
2781 style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this));
2782 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2783 tearOffRect.translate(0, d->scrollerHeight());
2787 QRect scrollUpTearOffRect = scrollUpRect.united(tearOffRect);
2788 for (
int i = 0; i < d->actions.size(); ++i) {
2789 QAction *action = d->actions.at(i);
2790 QRect actionRect = d->actionRects.at(i);
2791 if (!e->rect().intersects(actionRect)
2792 || d->widgetItems.value(action))
2795 emptyArea -= QRegion(actionRect);
2797 QRect adjustedActionRect = actionRect;
2798 if (!scrollUpTearOffRect.isEmpty() && adjustedActionRect.bottom() <= scrollUpTearOffRect.top())
2801 if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom())
2804 if (adjustedActionRect.intersects(scrollUpTearOffRect)) {
2805 if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom())
2808 adjustedActionRect.setTop(scrollUpTearOffRect.bottom()+1);
2811 if (adjustedActionRect.intersects(scrollDownRect)) {
2812 if (adjustedActionRect.top() >= scrollDownRect.top())
2815 adjustedActionRect.setBottom(scrollDownRect.top()-1);
2818 QRegion adjustedActionReg(adjustedActionRect);
2819 p.setClipRegion(adjustedActionReg);
2821 QStyleOptionMenuItem opt;
2822 initStyleOption(&opt, action);
2823 opt.rect = actionRect;
2824 p.drawControl(QStyle::CE_MenuItem, opt);
2827 emptyArea -= QRegion(scrollUpTearOffRect);
2828 emptyArea -= QRegion(scrollDownRect);
2830 if (d->scrollUpTearOffItem || d->scrollDownItem) {
2831 if (d->scrollUpTearOffItem)
2832 d->scrollUpTearOffItem->updateScrollerRects(scrollUpTearOffRect);
2833 if (d->scrollDownItem)
2834 d->scrollDownItem->updateScrollerRects(scrollDownRect);
2837 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollUp, scrollUpRect);
2838 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollDown, scrollDownRect);
2840 d->drawTearOff(&p, tearOffRect);
2846 borderReg += QRect(0, 0, fw, height());
2847 borderReg += QRect(width()-fw, 0, fw, height());
2848 borderReg += QRect(0, 0, width(), fw);
2849 borderReg += QRect(0, height()-fw, width(), fw);
2850 p.setClipRegion(borderReg);
2851 emptyArea -= borderReg;
2852 QStyleOptionFrame frame;
2853 frame.rect = rect();
2854 frame.palette = palette();
2855 frame.state = QStyle::State_None;
2856 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame,
this);
2857 frame.midLineWidth = 0;
2858 p.drawPrimitive(QStyle::PE_FrameMenu, frame);
2862 p.setClipRegion(emptyArea);
2863 menuOpt.state = QStyle::State_None;
2864 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2865 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2866 menuOpt.rect = rect();
2867 menuOpt.menuRect = rect();
2868 p.drawControl(QStyle::CE_MenuEmptyArea, menuOpt);
2993bool QMenu::event(QEvent *e)
2996 switch (e->type()) {
2997 case QEvent::Polish:
2998 d->updateLayoutDirection();
3000 case QEvent::ShortcutOverride: {
3001 QKeyEvent *kev =
static_cast<QKeyEvent *>(e);
3002 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
3003 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
3004 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
3005#ifndef QT_NO_SHORTCUT
3006 || kev->matches(QKeySequence::Cancel)
3014 case QEvent::KeyPress: {
3015 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
3016 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
3021 case QEvent::MouseButtonPress:
3022 case QEvent::ContextMenu: {
3023 bool canPopup =
true;
3024 if (e->type() == QEvent::MouseButtonPress)
3025 canPopup = (
static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton);
3026 if (canPopup && d->delayState.timer.isActive()) {
3027 d->delayState.stop();
3028 internalDelayedPopup();
3032 case QEvent::Resize: {
3033 QStyleHintReturnMask menuMask;
3034 QStyleOption option;
3035 option.initFrom(
this);
3036 if (style()->styleHint(QStyle::SH_Menu_Mask, &option,
this, &menuMask)) {
3037 setMask(menuMask.region);
3040 d->updateActionRects();
3043 QMenuPrivate::mouseDown =
nullptr;
3044 d->updateActionRects();
3045 d->sloppyState.reset();
3046 if (d->currentAction)
3047 d->popupAction(d->currentAction, 0,
false);
3048 if (isWindow() && window() && window()->windowHandle())
3049 window()->windowHandle()->setTransientParent(d->transientParentWindow());
3051#if QT_CONFIG(tooltip)
3052 case QEvent::ToolTip:
3053 if (d->toolTipsVisible) {
3054 const QHelpEvent *ev =
static_cast<
const QHelpEvent*>(e);
3055 if (
const QAction *action = actionAt(ev->pos())) {
3056 const QString toolTip = action->d_func()->tooltip;
3057 if (!toolTip.isEmpty())
3058 QToolTip::showText(ev->globalPos(), toolTip,
this);
3060 QToolTip::hideText();
3066#if QT_CONFIG(whatsthis)
3067 case QEvent::QueryWhatsThis:
3068 e->setAccepted(d->whatsThis.size());
3069 if (QAction *action = d->actionAt(
static_cast<QHelpEvent*>(e)->pos())) {
3070 if (action->whatsThis().size() || action->menu())
3078 return QWidget::event(e);
3095void QMenu::keyPressEvent(QKeyEvent *e)
3098 d->updateActionRects();
3100 if (isRightToLeft()) {
3101 if (key == Qt::Key_Left)
3102 key = Qt::Key_Right;
3103 else if (key == Qt::Key_Right)
3107 if (key == Qt::Key_Tab)
3109 if (key == Qt::Key_Backtab)
3113 bool key_consumed =
false;
3116 key_consumed =
true;
3118 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3121 key_consumed =
true;
3123 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3125 case Qt::Key_PageUp:
3126 key_consumed =
true;
3127 if (d->currentAction && d->scroll) {
3128 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3129 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp,
true,
true);
3131 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3134 case Qt::Key_PageDown:
3135 key_consumed =
true;
3136 if (d->currentAction && d->scroll) {
3137 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
3138 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown,
true,
true);
3140 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3144 case Qt::Key_Down: {
3145 key_consumed =
true;
3146 QAction *nextAction =
nullptr;
3147 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
3148 if (!d->currentAction) {
3149 if (key == Qt::Key_Down) {
3150 for(
int i = 0; i < d->actions.size(); ++i) {
3151 if (d->actionRects.at(i).isNull())
3153 QAction *act = d->actions.at(i);
3154 if (d->considerAction(act)) {
3160 for(
int i = d->actions.size()-1; i >= 0; --i) {
3161 if (d->actionRects.at(i).isNull())
3163 QAction *act = d->actions.at(i);
3164 if (d->considerAction(act)) {
3171 for(
int i = 0, y = 0; !nextAction && i < d->actions.size(); i++) {
3172 QAction *act = d->actions.at(i);
3173 if (act == d->currentAction) {
3174 if (key == Qt::Key_Up) {
3175 for(
int next_i = i-1;
true; next_i--) {
3177 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3180 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3181 next_i = d->actionRects.size()-1;
3183 QAction *next = d->actions.at(next_i);
3184 if (next == d->currentAction)
3186 if (d->actionRects.at(next_i).isNull())
3188 if (!d->considerAction(next))
3191 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
3192 int topVisible = d->scrollerHeight();
3194 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3195 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
3196 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3200 if (!nextAction && d->tearoff)
3201 d->tearoffHighlighted = 1;
3203 y += d->actionRects.at(i).height();
3204 for(
int next_i = i+1;
true; next_i++) {
3205 if (next_i == d->actionRects.size()) {
3206 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3209 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3212 QAction *next = d->actions.at(next_i);
3213 if (next == d->currentAction)
3215 if (d->actionRects.at(next_i).isNull())
3217 if (!d->considerAction(next))
3220 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
3221 int bottomVisible = height() - d->scrollerHeight();
3222 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3223 bottomVisible -= d->scrollerHeight();
3225 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3226 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
3227 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3234 y += d->actionRects.at(i).height();
3238 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
3239 d->scroll->scrollTimer.stop();
3240 d->scrollMenu(nextAction, scroll_loc);
3242 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, key == Qt::Key_Up ? QMenuPrivate::SelectionDirection::Up : QMenuPrivate::SelectionDirection::Down);
3247 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
3248 d->popupAction(d->currentAction, 0,
true);
3249 key_consumed =
true;
3253 case Qt::Key_Left: {
3254 if (d->currentAction && !d->scroll) {
3255 QAction *nextAction =
nullptr;
3256 if (key == Qt::Key_Left) {
3257 QRect actionR = d->actionRect(d->currentAction);
3258 for(
int x = actionR.left()-1; !nextAction && x >= 0; x--)
3259 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3261 QRect actionR = d->actionRect(d->currentAction);
3262 for(
int x = actionR.right()+1; !nextAction && x < width(); x++)
3263 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3266 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, QMenuPrivate::SelectionDirection::Up);
3267 key_consumed =
true;
3270 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
3271 QPointer<QWidget> caused = d->causedPopup.widget;
3275 key_consumed =
true;
3283 key_consumed =
true;
3284 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation,
nullptr,
this))
3287#if QT_CONFIG(menubar)
3288 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
3289 mb->d_func()->setKeyboardMode(
false);
3296 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem,
nullptr,
this))
3300#ifdef QT_KEYPAD_NAVIGATION
3301 case Qt::Key_Select:
3303 case Qt::Key_Return:
3304 case Qt::Key_Enter: {
3305 if (!d->currentAction) {
3306 d->setFirstActionActive();
3307 key_consumed =
true;
3313 if (d->currentAction->menu())
3314 d->popupAction(d->currentAction, 0,
true);
3316 d->activateAction(d->currentAction, QAction::Trigger);
3317 key_consumed =
true;
3320#if QT_CONFIG(whatsthis)
3322 if (!d->currentAction || d->currentAction->whatsThis().isNull())
3324 QWhatsThis::enterWhatsThisMode();
3325 d->activateAction(d->currentAction, QAction::Trigger);
3329 key_consumed =
false;
3332 if (!key_consumed && (
3334#ifndef QT_NO_SHORTCUT
3335 || e->matches(QKeySequence::Cancel)
3337#ifdef QT_KEYPAD_NAVIGATION
3338 || e->key() == Qt::Key_Back
3341 key_consumed =
true;
3347 QPointer<QWidget> caused = d->causedPopup.widget;
3349#if QT_CONFIG(menubar)
3350 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
3351 mb->d_func()->setCurrentAction(d->menuAction);
3352 mb->d_func()->setKeyboardMode(
true);
3358 if (!key_consumed) {
3359 const Qt::KeyboardModifiers modifiers = e->modifiers();
3360 if ((!modifiers || modifiers == Qt::AltModifier || modifiers == Qt::ShiftModifier
3361 || modifiers == Qt::KeypadModifier
3362 || modifiers == (Qt::KeypadModifier | Qt::AltModifier))
3363 && e->text().size() == 1) {
3364 bool activateAction =
false;
3365 QAction *nextAction =
nullptr;
3366 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch,
nullptr,
this) && !e->modifiers()) {
3367 int best_match_count = 0;
3368 d->searchBufferTimer.start(2000,
this);
3369 d->searchBuffer += e->text();
3370 for(
int i = 0; i < d->actions.size(); ++i) {
3371 int match_count = 0;
3372 if (d->actionRects.at(i).isNull())
3374 QAction *act = d->actions.at(i);
3375 const QString act_text = act->text();
3376 for(
int c = 0; c < d->searchBuffer.size(); ++c) {
3377 if (act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
3380 if (match_count > best_match_count) {
3381 best_match_count = match_count;
3386#ifndef QT_NO_SHORTCUT
3389 QAction *first =
nullptr, *currentSelected =
nullptr, *firstAfterCurrent =
nullptr;
3390 QChar c = e->text().at(0).toUpper();
3391 for(
int i = 0; i < d->actions.size(); ++i) {
3392 if (d->actionRects.at(i).isNull())
3394 QAction *act = d->actions.at(i);
3395 if (!act->isEnabled() || act->isSeparator())
3397 QKeySequence sequence = QKeySequence::mnemonic(act->text());
3398 int key = sequence[0].toCombined() & 0xffff;
3399 if (key == c.unicode()) {
3403 if (act == d->currentAction)
3404 currentSelected = act;
3405 else if (!firstAfterCurrent && currentSelected)
3406 firstAfterCurrent = act;
3409 if (clashCount == 1)
3410 activateAction =
true;
3411 if (clashCount >= 1) {
3412 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
3415 nextAction = firstAfterCurrent;
3420 key_consumed =
true;
3422 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter,
false);
3423 d->setCurrentAction(nextAction, 0, QMenuPrivate::SelectedFromElsewhere, QMenuPrivate::SelectionDirection::Down,
true);
3424 if (!nextAction->menu() && activateAction) {
3426 d->activateAction(nextAction, QAction::Trigger);
3430 if (!key_consumed) {
3431#if QT_CONFIG(menubar)
3432 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
3433 QAction *oldAct = mb->d_func()->currentAction;
3434 QCoreApplication::sendEvent(mb, e);
3435 if (mb->d_func()->currentAction != oldAct)
3436 key_consumed =
true;
3442 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
3443 QApplication::beep();
3540void QMenu::actionEvent(QActionEvent *e)
3544 setAttribute(Qt::WA_Resized,
false);
3546 d->tornPopup->syncWithMenu(
this, e);
3547 if (e->type() == QEvent::ActionAdded) {
3550#if QT_CONFIG(menubar)
3551 && !qobject_cast<QMenuBar*>(e->action()->parent())
3556 connect(e->action(), SIGNAL(triggered()),
this, SLOT(_q_actionTriggered()), Qt::UniqueConnection);
3557 connect(e->action(), SIGNAL(hovered()),
this, SLOT(_q_actionHovered()), Qt::UniqueConnection);
3559 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3560 QWidget *widget = wa->requestWidget(
this);
3562 d->widgetItems.insert(wa, widget);
3564 if (!d->scrollUpTearOffItem)
3565 d->scrollUpTearOffItem =
3566 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollUp, d,
this);
3567 if (!d->scrollDownItem)
3569 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollDown, d,
this);
3573 }
else if (e->type() == QEvent::ActionRemoved) {
3574 e->action()->disconnect(
this);
3575 if (e->action() == d->currentAction)
3576 d->currentAction =
nullptr;
3577 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3578 if (QWidget *widget = d->widgetItems.value(wa))
3579 wa->releaseWidget(widget);
3581 d->widgetItems.remove(
static_cast<QAction *>(e->action()));
3584 if (!d->platformMenu.isNull()) {
3585 auto action =
static_cast<QAction *>(e->action());
3586 if (e->type() == QEvent::ActionAdded) {
3587 QPlatformMenuItem *beforeItem = e->before()
3588 ? d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->before()))
3590 d->insertActionInPlatformMenu(action, beforeItem);
3591 }
else if (e->type() == QEvent::ActionRemoved) {
3592 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3593 d->platformMenu->removeMenuItem(menuItem);
3595 }
else if (e->type() == QEvent::ActionChanged) {
3596 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3598 d->copyActionToPlatformItem(action, menuItem);
3599 d->platformMenu->syncMenuItem(menuItem);
3603 d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators);