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;
2412 pos.setY(screen.top() + desktopFrame);
2413 }
else if (atAction) {
2414 for (
int i = 0, above_height = 0; i < actions.size(); i++) {
2415 QAction *action = actions.at(i);
2416 if (action == atAction) {
2417 int newY = pos.y() - above_height;
2418 if (scroll && newY < desktopFrame) {
2419 scroll->scrollFlags = scroll->scrollFlags
2422 newY = desktopFrame;
2427 && !q->style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll,
nullptr, q)) {
2429 for (
int i2 = i; i2 < actionRects.size(); i2++)
2430 below_height += actionRects.at(i2).height();
2431 size.setHeight(below_height);
2435 above_height += actionRects.at(i).height();
2442 const auto rectIsNull = [](
const QRect &rect) {
return rect.isNull(); };
2443 if (q->childrenRect().isEmpty()
2444 && std::all_of(actionRects.cbegin(), actionRects.cend(), rectIsNull)) {
2446 syncAction =
nullptr;
2452 const QPoint mouse = QGuiApplicationPrivate::lastCursorPosition.toPoint();
2453 mousePopupPos = QGuiApplicationPrivate::lastCursorPosition;
2454 const bool snapToMouse = !causedPopup.widget && (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
2456 if (adjustToDesktop) {
2458 if (q->isRightToLeft()) {
2460 pos.setX(mouse.x() - size.width());
2462#if QT_CONFIG(menubar)
2464 if (qobject_cast<QMenuBar*>(causedPopup.widget) || qobject_cast<QMenu*>(causedPopup.widget))
2465 pos.rx() -= size.width();
2468 if (pos.x() < screen.left() + desktopFrame)
2469 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
2470 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2471 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
2473 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2474 pos.setX(screen.right() - desktopFrame - size.width() + 1);
2475 if (pos.x() < screen.left() + desktopFrame)
2476 pos.setX(screen.left() + desktopFrame);
2478 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
2480 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2482 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2485 if (pos.y() < screen.top() + desktopFrame)
2486 pos.setY(screen.top() + desktopFrame);
2487 if (pos.y() + menuSizeHint.height() - 1 > screen.bottom() - desktopFrame) {
2490 int y = qMax(screen.y(),pos.y());
2491 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
2494 pos.setY(screen.bottom() - size.height() + 1);
2499 const int subMenuOffset = q->style()->pixelMetric(QStyle::PM_SubMenuOverlap,
nullptr, q);
2500 QMenu *caused = qobject_cast<QMenu*>(causedPopup.widget);
2501 if (caused && caused->geometry().width() + menuSizeHint.width() + subMenuOffset < screen.width()) {
2502 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
2503 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
2504 parentActionRect.moveTopLeft(actionTopLeft);
2505 if (q->isRightToLeft()) {
2506 if ((pos.x() + menuSizeHint.width() > parentActionRect.left() - subMenuOffset)
2507 && (pos.x() < parentActionRect.right()))
2509 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2510 if (pos.x() < screen.x())
2511 pos.rx() = parentActionRect.right();
2512 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2513 pos.rx() = screen.x();
2516 if ((pos.x() < parentActionRect.right() + subMenuOffset)
2517 && (pos.x() + menuSizeHint.width() > parentActionRect.left()))
2519 pos.rx() = parentActionRect.right();
2520 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2521 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2522 if (pos.x() < screen.x())
2523 pos.rx() = screen.x() + screen.width() - menuSizeHint.width();
2527 popupScreen = QGuiApplication::screenAt(pos);
2528 q->setGeometry(QRect(pos, size));
2530#if QT_CONFIG(wayland)
2532 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(q->windowHandle()->handle())) {
2534 const QRect controlGeometry(causedButton->mapTo(causedButton->window(), QPoint(0, 0)), causedButton->size());
2535 waylandWindow->setParentControlGeometry(controlGeometry);
2536 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2537 }
else if (caused) {
2538 waylandWindow->setParentControlGeometry(caused->d_func()->actionRect(caused->d_func()->currentAction));
2539 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu);
2540 }
else if (
auto menubar = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2541 QPoint menuBarWindowPosition = menubar->mapTo(menubar->window(), QPoint(0, 0));
2542 waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action).translated(menuBarWindowPosition));
2543 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2548#if QT_CONFIG(effects)
2549 int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
2550 int vGuess = QEffects::DownScroll;
2551 if (q->isRightToLeft()) {
2552 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
2553 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 > causedPopup.widget->x()))
2554 hGuess = QEffects::RightScroll;
2556 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
2557 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 < causedPopup.widget->x()))
2558 hGuess = QEffects::LeftScroll;
2561#if QT_CONFIG(menubar)
2562 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
2563 (qobject_cast<QMenuBar*>(causedPopup.widget) &&
2564 pos.y() + size.width() / 2 < causedPopup.widget->mapToGlobal(causedPopup.widget->pos()).y()))
2565 vGuess = QEffects::UpScroll;
2567 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
2568 bool doChildEffects =
true;
2569#if QT_CONFIG(menubar)
2570 if (QMenuBar *mb = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2571 doChildEffects = mb->d_func()->doChildEffects;
2572 mb->d_func()->doChildEffects =
false;
2575 if (QMenu *m = qobject_cast<QMenu*>(causedPopup.widget)) {
2576 doChildEffects = m->d_func()->doChildEffects;
2577 m->d_func()->doChildEffects =
false;
2580 if (doChildEffects) {
2581 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
2583 else if (causedPopup.widget)
2584 qScrollEffect(q, qobject_cast<QMenu*>(causedPopup.widget) ? hGuess : vGuess);
2586 qScrollEffect(q, hGuess | vGuess);
2589 qFadeEffect(
nullptr);
2590 qScrollEffect(
nullptr);
2600#if QT_CONFIG(accessibility)
2601 QAccessibleEvent event(q, QAccessible::PopupMenuStart);
2602 QAccessible::updateAccessibility(&event);
2753void QMenu::paintEvent(QPaintEvent *e)
2756 d->updateActionRects();
2757 QStylePainter p(
this);
2758 QRegion emptyArea = QRegion(rect());
2760 QStyleOptionMenuItem menuOpt;
2761 menuOpt.initFrom(
this);
2762 menuOpt.state = QStyle::State_None;
2763 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2764 menuOpt.maxIconWidth = 0;
2765 menuOpt.reservedShortcutWidth = 0;
2766 p.drawPrimitive(QStyle::PE_PanelMenu, menuOpt);
2769 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr,
this);
2770 const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin,
nullptr,
this);
2771 const int vmargin = style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr,
this);
2773 QRect scrollUpRect, scrollDownRect;
2774 const int leftmargin = fw + hmargin + d->leftmargin;
2775 const int topmargin = fw + vmargin + d->topmargin;
2776 const int bottommargin = fw + vmargin + d->bottommargin;
2777 const int contentWidth = width() - (fw + hmargin) * 2 - d->leftmargin - d->rightmargin;
2779 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2780 scrollUpRect.setRect(leftmargin, topmargin, contentWidth, d->scrollerHeight());
2782 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2783 scrollDownRect.setRect(leftmargin, height() - d->scrollerHeight() - bottommargin,
2784 contentWidth, d->scrollerHeight());
2790 tearOffRect.setRect(leftmargin, topmargin, contentWidth,
2791 style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this));
2792 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2793 tearOffRect.translate(0, d->scrollerHeight());
2797 QRect scrollUpTearOffRect = scrollUpRect.united(tearOffRect);
2798 for (
int i = 0; i < d->actions.size(); ++i) {
2799 QAction *action = d->actions.at(i);
2800 QRect actionRect = d->actionRects.at(i);
2801 if (!e->rect().intersects(actionRect)
2802 || d->widgetItems.value(action))
2805 emptyArea -= QRegion(actionRect);
2807 QRect adjustedActionRect = actionRect;
2808 if (!scrollUpTearOffRect.isEmpty() && adjustedActionRect.bottom() <= scrollUpTearOffRect.top())
2811 if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom())
2814 if (adjustedActionRect.intersects(scrollUpTearOffRect)) {
2815 if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom())
2818 adjustedActionRect.setTop(scrollUpTearOffRect.bottom()+1);
2821 if (adjustedActionRect.intersects(scrollDownRect)) {
2822 if (adjustedActionRect.top() >= scrollDownRect.top())
2825 adjustedActionRect.setBottom(scrollDownRect.top()-1);
2828 QRegion adjustedActionReg(adjustedActionRect);
2829 p.setClipRegion(adjustedActionReg);
2831 QStyleOptionMenuItem opt;
2832 initStyleOption(&opt, action);
2833 opt.rect = actionRect;
2834 p.drawControl(QStyle::CE_MenuItem, opt);
2837 emptyArea -= QRegion(scrollUpTearOffRect);
2838 emptyArea -= QRegion(scrollDownRect);
2840 if (d->scrollUpTearOffItem || d->scrollDownItem) {
2841 if (d->scrollUpTearOffItem)
2842 d->scrollUpTearOffItem->updateScrollerRects(scrollUpTearOffRect);
2843 if (d->scrollDownItem)
2844 d->scrollDownItem->updateScrollerRects(scrollDownRect);
2847 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollUp, scrollUpRect);
2848 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollDown, scrollDownRect);
2850 d->drawTearOff(&p, tearOffRect);
2856 borderReg += QRect(0, 0, fw, height());
2857 borderReg += QRect(width()-fw, 0, fw, height());
2858 borderReg += QRect(0, 0, width(), fw);
2859 borderReg += QRect(0, height()-fw, width(), fw);
2860 p.setClipRegion(borderReg);
2861 emptyArea -= borderReg;
2862 QStyleOptionFrame frame;
2863 frame.rect = rect();
2864 frame.palette = palette();
2865 frame.state = QStyle::State_None;
2866 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame,
this);
2867 frame.midLineWidth = 0;
2868 p.drawPrimitive(QStyle::PE_FrameMenu, frame);
2872 p.setClipRegion(emptyArea);
2873 menuOpt.state = QStyle::State_None;
2874 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2875 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2876 menuOpt.rect = rect();
2877 menuOpt.menuRect = rect();
2878 p.drawControl(QStyle::CE_MenuEmptyArea, menuOpt);
3009bool QMenu::event(QEvent *e)
3012 switch (e->type()) {
3013 case QEvent::Polish:
3014 d->updateLayoutDirection();
3016 case QEvent::ShortcutOverride: {
3017 QKeyEvent *kev =
static_cast<QKeyEvent *>(e);
3018 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
3019 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
3020 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
3021#ifndef QT_NO_SHORTCUT
3022 || kev->matches(QKeySequence::Cancel)
3030 case QEvent::KeyPress: {
3031 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
3032 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
3037 case QEvent::MouseButtonPress:
3038 case QEvent::ContextMenu: {
3039 bool canPopup =
true;
3040 if (e->type() == QEvent::MouseButtonPress)
3041 canPopup = (
static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton);
3042 if (canPopup && d->delayState.timer.isActive()) {
3043 d->delayState.stop();
3044 internalDelayedPopup();
3048 case QEvent::Resize: {
3049 QStyleHintReturnMask menuMask;
3050 QStyleOption option;
3051 option.initFrom(
this);
3052 if (style()->styleHint(QStyle::SH_Menu_Mask, &option,
this, &menuMask)) {
3053 setMask(menuMask.region);
3056 d->updateActionRects();
3059 QMenuPrivate::mouseDown =
nullptr;
3060 d->updateActionRects();
3061 d->sloppyState.reset();
3062 if (d->currentAction)
3063 d->popupAction(d->currentAction, 0,
false);
3064 if (isWindow() && window() && window()->windowHandle())
3065 window()->windowHandle()->setTransientParent(d->transientParentWindow());
3067#if QT_CONFIG(tooltip)
3068 case QEvent::ToolTip:
3069 if (d->toolTipsVisible) {
3070 const QHelpEvent *ev =
static_cast<
const QHelpEvent*>(e);
3071 if (QAction *action = actionAt(ev->pos())) {
3072 const QString toolTip = action->d_func()->tooltip;
3073 if (!toolTip.isEmpty())
3074 QToolTip::showText(ev->globalPos(), toolTip,
this, actionGeometry(action));
3076 QToolTip::hideText();
3082#if QT_CONFIG(whatsthis)
3083 case QEvent::QueryWhatsThis:
3084 e->setAccepted(d->whatsThis.size());
3085 if (QAction *action = d->actionAt(
static_cast<QHelpEvent*>(e)->pos())) {
3086 if (action->whatsThis().size() || action->menu())
3094 return QWidget::event(e);
3111void QMenu::keyPressEvent(QKeyEvent *e)
3114 d->updateActionRects();
3116 if (isRightToLeft()) {
3117 if (key == Qt::Key_Left)
3118 key = Qt::Key_Right;
3119 else if (key == Qt::Key_Right)
3123 if (key == Qt::Key_Tab)
3125 if (key == Qt::Key_Backtab)
3129 bool key_consumed =
false;
3132 key_consumed =
true;
3134 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3137 key_consumed =
true;
3139 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3141 case Qt::Key_PageUp:
3142 key_consumed =
true;
3143 if (d->currentAction && d->scroll) {
3144 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3145 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp,
true,
true);
3147 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3150 case Qt::Key_PageDown:
3151 key_consumed =
true;
3152 if (d->currentAction && d->scroll) {
3153 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
3154 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown,
true,
true);
3156 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3160 case Qt::Key_Down: {
3161 key_consumed =
true;
3162 QAction *nextAction =
nullptr;
3163 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
3164 if (!d->currentAction) {
3165 if (key == Qt::Key_Down) {
3166 for(
int i = 0; i < d->actions.size(); ++i) {
3167 if (d->actionRects.at(i).isNull())
3169 QAction *act = d->actions.at(i);
3170 if (d->considerAction(act)) {
3176 for(
int i = d->actions.size()-1; i >= 0; --i) {
3177 if (d->actionRects.at(i).isNull())
3179 QAction *act = d->actions.at(i);
3180 if (d->considerAction(act)) {
3187 for(
int i = 0, y = 0; !nextAction && i < d->actions.size(); i++) {
3188 QAction *act = d->actions.at(i);
3189 if (act == d->currentAction) {
3190 if (key == Qt::Key_Up) {
3191 for(
int next_i = i-1;
true; next_i--) {
3193 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3196 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3197 next_i = d->actionRects.size()-1;
3199 QAction *next = d->actions.at(next_i);
3200 if (next == d->currentAction)
3202 if (d->actionRects.at(next_i).isNull())
3204 if (!d->considerAction(next))
3207 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
3208 int topVisible = d->scrollerHeight();
3210 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3211 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
3212 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3216 if (!nextAction && d->tearoff)
3217 d->tearoffHighlighted = 1;
3219 y += d->actionRects.at(i).height();
3220 for(
int next_i = i+1;
true; next_i++) {
3221 if (next_i == d->actionRects.size()) {
3222 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3225 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3228 QAction *next = d->actions.at(next_i);
3229 if (next == d->currentAction)
3231 if (d->actionRects.at(next_i).isNull())
3233 if (!d->considerAction(next))
3236 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
3237 int bottomVisible = height() - d->scrollerHeight();
3238 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3239 bottomVisible -= d->scrollerHeight();
3241 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3242 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
3243 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3250 y += d->actionRects.at(i).height();
3254 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
3255 d->scroll->scrollTimer.stop();
3256 d->scrollMenu(nextAction, scroll_loc);
3258 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, key == Qt::Key_Up ? QMenuPrivate::SelectionDirection::Up : QMenuPrivate::SelectionDirection::Down);
3263 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
3264 d->popupAction(d->currentAction, 0,
true);
3265 key_consumed =
true;
3269 case Qt::Key_Left: {
3270 if (d->currentAction && !d->scroll) {
3271 QAction *nextAction =
nullptr;
3272 if (key == Qt::Key_Left) {
3273 QRect actionR = d->actionRect(d->currentAction);
3274 for(
int x = actionR.left()-1; !nextAction && x >= 0; x--)
3275 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3277 QRect actionR = d->actionRect(d->currentAction);
3278 for(
int x = actionR.right()+1; !nextAction && x < width(); x++)
3279 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3282 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, QMenuPrivate::SelectionDirection::Up);
3283 key_consumed =
true;
3286 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
3287 QPointer<QWidget> caused = d->causedPopup.widget;
3291 key_consumed =
true;
3299 key_consumed =
true;
3300 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation,
nullptr,
this))
3303#if QT_CONFIG(menubar)
3304 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
3305 mb->d_func()->setKeyboardMode(
false);
3312 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem,
nullptr,
this))
3316 case Qt::Key_Return:
3317 case Qt::Key_Enter: {
3318 if (!d->currentAction) {
3319 d->setFirstActionActive();
3320 key_consumed =
true;
3326 if (d->currentAction->menu())
3327 d->popupAction(d->currentAction, 0,
true);
3329 d->activateAction(d->currentAction, QAction::Trigger);
3330 key_consumed =
true;
3333#if QT_CONFIG(whatsthis)
3335 if (!d->currentAction || d->currentAction->whatsThis().isNull())
3337 QWhatsThis::enterWhatsThisMode();
3338 d->activateAction(d->currentAction, QAction::Trigger);
3342 key_consumed =
false;
3345 if (!key_consumed && (
3347#ifndef QT_NO_SHORTCUT
3348 || e->matches(QKeySequence::Cancel)
3351 key_consumed =
true;
3357 QPointer<QWidget> caused = d->causedPopup.widget;
3359#if QT_CONFIG(menubar)
3360 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
3361 mb->d_func()->setCurrentAction(d->menuAction);
3362 mb->d_func()->setKeyboardMode(
true);
3368 if (!key_consumed) {
3369 const Qt::KeyboardModifiers modifiers = e->modifiers();
3370 if ((!modifiers || modifiers == Qt::AltModifier || modifiers == Qt::ShiftModifier
3371 || modifiers == Qt::KeypadModifier
3372 || modifiers == (Qt::KeypadModifier | Qt::AltModifier))
3373 && e->text().size() == 1) {
3374 bool activateAction =
false;
3375 QAction *nextAction =
nullptr;
3376 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch,
nullptr,
this) && !e->modifiers()) {
3377 int best_match_count = 0;
3378 d->searchBufferTimer.start(2000,
this);
3379 d->searchBuffer += e->text();
3380 for(
int i = 0; i < d->actions.size(); ++i) {
3381 int match_count = 0;
3382 if (d->actionRects.at(i).isNull())
3384 QAction *act = d->actions.at(i);
3385 const QString act_text = act->text();
3386 for(
int c = 0; c < d->searchBuffer.size(); ++c) {
3387 if (act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
3390 if (match_count > best_match_count) {
3391 best_match_count = match_count;
3396#ifndef QT_NO_SHORTCUT
3399 QAction *first =
nullptr, *currentSelected =
nullptr, *firstAfterCurrent =
nullptr;
3400 QChar c = e->text().at(0).toUpper();
3401 for(
int i = 0; i < d->actions.size(); ++i) {
3402 if (d->actionRects.at(i).isNull())
3404 QAction *act = d->actions.at(i);
3405 if (!act->isEnabled() || act->isSeparator())
3407 QKeySequence sequence = QKeySequence::mnemonic(act->text());
3408 int key = sequence[0].toCombined() & 0xffff;
3409 if (key == c.unicode()) {
3413 if (act == d->currentAction)
3414 currentSelected = act;
3415 else if (!firstAfterCurrent && currentSelected)
3416 firstAfterCurrent = act;
3419 if (clashCount == 1)
3420 activateAction =
true;
3421 if (clashCount >= 1) {
3422 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
3425 nextAction = firstAfterCurrent;
3430 key_consumed =
true;
3432 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter,
false);
3433 d->setCurrentAction(nextAction, 0, QMenuPrivate::SelectedFromElsewhere, QMenuPrivate::SelectionDirection::Down,
true);
3434 if (!nextAction->menu() && activateAction) {
3436 d->activateAction(nextAction, QAction::Trigger);
3440 if (!key_consumed) {
3441#if QT_CONFIG(menubar)
3442 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
3443 QAction *oldAct = mb->d_func()->currentAction;
3444 QCoreApplication::sendEvent(mb, e);
3445 if (mb->d_func()->currentAction != oldAct)
3446 key_consumed =
true;
3452 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
3453 QApplication::beep();
3550void QMenu::actionEvent(QActionEvent *e)
3554 setAttribute(Qt::WA_Resized,
false);
3556 d->tornPopup->syncWithMenu(
this, e);
3557 if (e->type() == QEvent::ActionAdded) {
3560#if QT_CONFIG(menubar)
3561 && !qobject_cast<QMenuBar*>(e->action()->parent())
3566 connect(e->action(), SIGNAL(triggered()),
this, SLOT(_q_actionTriggered()), Qt::UniqueConnection);
3567 connect(e->action(), SIGNAL(hovered()),
this, SLOT(_q_actionHovered()), Qt::UniqueConnection);
3569 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3570 QWidget *widget = wa->requestWidget(
this);
3572 d->widgetItems.insert(wa, widget);
3574 if (!d->scrollUpTearOffItem)
3575 d->scrollUpTearOffItem =
3576 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollUp, d,
this);
3577 if (!d->scrollDownItem)
3579 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollDown, d,
this);
3583 }
else if (e->type() == QEvent::ActionRemoved) {
3584 e->action()->disconnect(
this);
3585 if (e->action() == d->currentAction)
3586 d->currentAction =
nullptr;
3587 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3588 if (QWidget *widget = d->widgetItems.value(wa))
3589 wa->releaseWidget(widget);
3591 d->widgetItems.remove(
static_cast<QAction *>(e->action()));
3594 if (!d->platformMenu.isNull()) {
3595 auto action =
static_cast<QAction *>(e->action());
3596 if (e->type() == QEvent::ActionAdded) {
3597 QPlatformMenuItem *beforeItem = e->before()
3598 ? d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->before()))
3600 d->insertActionInPlatformMenu(action, beforeItem);
3601 }
else if (e->type() == QEvent::ActionRemoved) {
3602 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3603 d->platformMenu->removeMenuItem(menuItem);
3605 }
else if (e->type() == QEvent::ActionChanged) {
3606 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3608 d->copyActionToPlatformItem(action, menuItem);
3609 d->platformMenu->syncMenuItem(menuItem);
3613 d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators);