348 actionRects.resize(actions.size());
349 actionRects.fill(QRect());
353 QStyle *style = q->style();
356 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q),
357 vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q),
358 icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q);
359 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
360 const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q);
361 const int tearoffHeight =
tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0;
362 const int base_y = vmargin + fw + topmargin + (scroll ? scroll->scrollOffset : 0) + tearoffHeight;
363 const int column_max_y = screen.height() - 2 * deskFw - (vmargin + bottommargin + fw);
364 int max_column_width = 0;
373 for (
int i = 0; i < actions.size(); ++i) {
374 QAction *action = actions.at(i);
375 if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action))
379 QIcon is = action->icon();
386 QFontMetrics qfm = q->fontMetrics();
387 bool previousWasSeparator =
true;
388#if QT_CONFIG(shortcut)
389 const bool contextMenu = isContextMenu();
391 const bool menuSupportsSections = q->style()->styleHint(QStyle::SH_Menu_SupportsSections,
nullptr, q);
392 for(
int i = 0; i <= lastVisibleAction; i++) {
393 QAction *action = actions.at(i);
394 const bool isSection = action->isSeparator() && (!action->text().isEmpty() || !action->icon().isNull());
395 const bool isPlainSeparator = (isSection && !menuSupportsSections)
396 || (action->isSeparator() && !isSection);
398 if (!action->isVisible() ||
402 previousWasSeparator = isPlainSeparator;
405 QStyleOptionMenuItem opt;
406 q->initStyleOption(&opt, action);
407 const QFontMetrics &fm = opt.fontMetrics;
410 if (QWidget *w = widgetItems.value(action)) {
411 sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
414 if (action->isSeparator() && action->text().isEmpty()) {
417 QString s = action->text();
418 qsizetype t = s.indexOf(u'\t');
422#if QT_CONFIG(shortcut)
423 }
else if (action->isShortcutVisibleInContextMenu() || !contextMenu) {
424 QKeySequence seq = action->shortcut();
426 tabWidth = qMax(
int(tabWidth), qfm.horizontalAdvance(seq.toString(QKeySequence::NativeText)));
429 sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width());
430 sz.setHeight(qMax(fm.height(), qfm.height()));
432 QIcon is = action->icon();
434 QSize is_sz = QSize(icone, icone);
435 if (is_sz.height() > sz.height())
436 sz.setHeight(is_sz.height());
439 sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
444 max_column_width = qMax(max_column_width, sz.width());
446 if (!scroll && y + sz.height() > column_max_y) {
453 actionRects[i] = QRect(0, 0, sz.width(), sz.height());
459 const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QSize(0, 0), q).width();
460 const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin));
461 max_column_width = qMax(min_column_width, max_column_width);
465 int x = hmargin + fw + leftmargin;
468 for(
int i = 0; i < actions.size(); i++) {
469 QRect &rect = actionRects[i];
472 if (!scroll && y + rect.height() > column_max_y) {
473 x += max_column_width + hmargin;
476 rect.translate(x, y);
477 rect.setWidth(max_column_width);
480 if (QWidget *widget = widgetItems.value(actions.at(i))) {
481 widget->setGeometry(rect);
482 widget->setVisible(actions.at(i)->isVisible());
1125 if (!scroll || !scroll->scrollFlags)
1131 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr, q);
1132 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr, q);
1135 for(
int i = 0, saccum = 0; i < actions.size(); i++) {
1136 if (actions.at(i) == action) {
1137 newOffset = topScroll - saccum;
1140 saccum += actionRects.at(i).height();
1143 for(
int i = 0, saccum = 0; i < actions.size(); i++) {
1144 saccum += actionRects.at(i).height();
1145 if (actions.at(i) == action) {
1146 if (location == QMenuScroller::ScrollCenter)
1147 newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
1149 newOffset = (q->height() - botScroll) - saccum;
1154 newOffset -= fw * 2;
1161 int saccum = newOffset;
1162 for(
int i = 0; i < actionRects.size(); i++) {
1163 saccum += actionRects.at(i).height();
1164 if (saccum > q->height()) {
1171 newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin - topmargin - bottommargin;
1173 newOffset -= q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr, q);
1181 newOffset -= vmargin;
1183 QRect screen = popupGeometry();
1184 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth,
nullptr, q);
1185 if (q->height() < screen.height()-(desktopFrame*2)-1) {
1186 QRect geom = q->geometry();
1188 const int newHeight = geom.height()-(newOffset-scroll
->scrollOffset);
1189 if (newHeight > geom.height())
1190 geom.setHeight(newHeight);
1193 if (newTop < desktopFrame+screen.top())
1194 newTop = desktopFrame+screen.top();
1195 if (newTop < geom.top()) {
1196 geom.setTop(newTop);
1201 if (geom.bottom() > screen.bottom() - desktopFrame)
1202 geom.setBottom(screen.bottom() - desktopFrame);
1203 if (geom.top() < desktopFrame+screen.top())
1204 geom.setTop(desktopFrame+screen.top());
1205 if (geom != q->geometry()) {
1207 if (newScrollFlags & QMenuScroller::ScrollDown &&
1208 q->geometry().top() - geom.top() >= -newOffset)
1209 newScrollFlags &= ~QMenuScroller::ScrollDown;
1211 q->setGeometry(geom);
1219 for (
int i = 0; i < actionRects.size(); ++i) {
1220 QRect ¤t = actionRects[i];
1221 current.moveTop(current.top() + delta);
1224 if (QWidget *w = widgetItems.value(actions.at(i)))
1225 w->setGeometry(current);
1229 scroll->scrollFlags = newScrollFlags;
1231 setCurrentAction(action);
1319 const QPoint pos = q->mapFromGlobal(e->globalPosition()).toPoint();
1321 QStyle *style = q->style();
1322 QStyleOption opt(0);
1324 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q);
1325 const int vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q);
1326 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
1328 if (scroll && !activeMenu) {
1329 bool isScroll =
false;
1330 if (pos.x() >= 0 && pos.x() < q->width()) {
1332 if (scroll->scrollFlags & dir) {
1333 if (dir == QMenuScroller::ScrollUp)
1334 isScroll = (pos.y() <= scrollerHeight() + fw + vmargin + topmargin);
1335 else if (dir == QMenuScroller::ScrollDown)
1336 isScroll = (pos.y() >= q->height() - scrollerHeight() - fw - vmargin - bottommargin);
1338 scroll->scrollDirection = dir;
1345 scroll->scrollTimer.start(50, q);
1348 scroll->scrollTimer.stop();
1353 QRect tearRect(leftmargin + hmargin + fw, topmargin + vmargin + fw, q->width() - fw * 2 - hmargin * 2 -leftmargin - rightmargin,
1354 q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q));
1357 q->update(tearRect);
1358 if (tearRect.contains(pos) && hasMouseMoved(e->globalPosition())) {
1359 setCurrentAction(
nullptr);
1361 if (e->type() == QEvent::MouseButtonRelease) {
1363 tornPopup =
new QTornOffMenu(q);
1364 tornPopup->setGeometry(q->geometry());
1373 if (q->frameGeometry().contains(e->globalPosition().toPoint()))
1376 for(QWidget *caused = causedPopup.widget; caused;) {
1377 bool passOnEvent =
false;
1378 QWidget *next_widget =
nullptr;
1379 QPointF cpos = caused->mapFromGlobal(e->globalPosition());
1380#if QT_CONFIG(menubar)
1381 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
1382 passOnEvent = mb->rect().contains(cpos.toPoint());
1385 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
1386 passOnEvent = m->rect().contains(cpos.toPoint());
1387 next_widget = m->d_func()->causedPopup.widget;
1390 if (e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
1391 QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->globalPosition(),
1392 e->button(), e->buttons(), e->modifiers(),
1393 e->source(), e->pointingDevice());
1394 QCoreApplication::sendEvent(caused, &new_e);
1398 caused = next_widget;
1400 sloppyState.leave();
1446void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e,
bool self)
1449#if QT_CONFIG(whatsthis)
1450 bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
1452 if (!action || !q->isEnabled()
1453 || (action_e == QAction::Trigger
1454#if QT_CONFIG(whatsthis)
1457 && (action->isSeparator() ||!action->isEnabled())))
1461
1462
1463 const QList<QPointer<QWidget>> causedStack = calcCausedStack();
1464 if (action_e == QAction::Trigger) {
1465#if QT_CONFIG(whatsthis)
1466 if (!inWhatsThisMode)
1467 actionAboutToTrigger = action;
1470 if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
1473 for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
1474 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1477 widget = qmenu->d_func()->causedPopup.widget;
1484#if QT_CONFIG(whatsthis)
1485 if (inWhatsThisMode) {
1486 QString s = action->whatsThis();
1489 QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
1495 QPointer<QMenu> thisGuard(q);
1496 activateCausedStack(causedStack, action, action_e, self);
1500 if (action_e == QAction::Hover) {
1501#if QT_CONFIG(accessibility)
1502 if (QAccessible::isActive()) {
1503 int actionIndex = indexOf(action);
1504 QAccessibleEvent focusEvent(q, QAccessible::Focus);
1505 focusEvent.setChild(actionIndex);
1506 QAccessible::updateAccessibility(&focusEvent);
1509 action->showStatusText(topCausedWidget());
1511 actionAboutToTrigger =
nullptr;
1597void QMenu::initStyleOption(QStyleOptionMenuItem *option,
const QAction *action)
const
1599 if (!option || !action)
1603 option->initFrom(
this);
1604 option->palette = palette();
1605 option->state = QStyle::State_None;
1607 if (window()->isActiveWindow())
1608 option->state |= QStyle::State_Active;
1609 if (isEnabled() && action->isEnabled()
1610 && (!action->menu() || action->menu()->isEnabled()))
1611 option->state |= QStyle::State_Enabled;
1613 option->palette.setCurrentColorGroup(QPalette::Disabled);
1615 option->font = action->font().resolve(font());
1616 option->fontMetrics = QFontMetrics(option->font);
1618 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1619 option->state |= QStyle::State_Selected
1620 | (QMenuPrivate::mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1623 option->menuHasCheckableItems = d->hasCheckableItems;
1624 if (!action->isCheckable()) {
1625 option->checkType = QStyleOptionMenuItem::NotCheckable;
1627 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1628 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1629 option->checked = action->isChecked();
1632 option->menuItemType = QStyleOptionMenuItem::SubMenu;
1633 else if (action->isSeparator())
1634 option->menuItemType = QStyleOptionMenuItem::Separator;
1635 else if (d->defaultAction == action)
1636 option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1638 option->menuItemType = QStyleOptionMenuItem::Normal;
1639 if (action->isIconVisibleInMenu())
1640 option->icon = action->icon();
1641 QString textAndAccel = action->text();
1642#ifndef QT_NO_SHORTCUT
1643 if ((action->isShortcutVisibleInContextMenu() || !d->isContextMenu())
1644 && textAndAccel.indexOf(u'\t') == -1) {
1645 QKeySequence seq = action->shortcut();
1647 textAndAccel += u'\t' + seq.toString(QKeySequence::NativeText);
1650 option->text = textAndAccel;
1651 option->reservedShortcutWidth = d->tabWidth;
1652 option->maxIconWidth = d->maxIconWidth;
1653 option->menuRect = rect();
2314void QMenuPrivate::popup(
const QPoint &p, QAction *atAction, PositionFunction positionFunction)
2317 popupScreen = QGuiApplication::screenAt(p);
2318 QScopeGuard popupScreenGuard([
this](){ popupScreen.clear(); });
2331 q->ensurePolished();
2335 bool screenSet =
false;
2336 QScreen *screen = topData()->initialScreen;
2338 if (setScreen(screen))
2341 }
else if (QMenu *parentMenu = qobject_cast<QMenu *>(parent)) {
2344 if (setScreen(parentMenu->screen()))
2348 if (!screenSet && setScreenForPoint(p))
2363 if (!windowHandle()) {
2367#if QT_CONFIG(menubar)
2370 q->setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(topCausedWidget()) !=
nullptr);
2373 emit q->aboutToShow();
2377#if QT_CONFIG(graphicsview)
2378 bool isEmbedded = !bypassGraphicsProxyWidget(q) && QMenuPrivate::nearestGraphicsProxyWidget(q);
2380 screen = popupGeometry();
2383 screen = popupGeometry(QGuiApplication::screenAt(p));
2387 QPushButton *causedButton = qobject_cast<QPushButton*>(causedPopup.widget);
2388 if (actionListChanged && causedButton)
2389 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
2392 popupScreen = QGuiApplication::screenAt(pos);
2394 const QSize menuSizeHint(q->sizeHint());
2395 QSize size = menuSizeHint;
2397 if (positionFunction)
2398 pos = positionFunction(menuSizeHint);
2400 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth,
nullptr, q);
2401 bool adjustToDesktop = !q->window()->testAttribute(Qt::WA_DontShowOnScreen);
2404 if ((size.height() > screen.height() || size.width() > screen.width()) ||
2406 (ncols >1 && size.height() < screen.height())) {
2407 size.setWidth(qMin(menuSizeHint.width(), screen.width() - desktopFrame * 2));
2408 size.setHeight(qMin(menuSizeHint.height(), screen.height() - desktopFrame * 2));
2409 adjustToDesktop =
true;
2413 pos.setY(screen.top() + desktopFrame);
2414 }
else if (atAction) {
2415 for (
int i = 0, above_height = 0; i < actions.size(); i++) {
2416 QAction *action = actions.at(i);
2417 if (action == atAction) {
2418 int newY = pos.y() - above_height;
2419 if (scroll && newY < desktopFrame) {
2420 scroll->scrollFlags = scroll->scrollFlags
2423 newY = desktopFrame;
2428 && !q->style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll,
nullptr, q)) {
2430 for (
int i2 = i; i2 < actionRects.size(); i2++)
2431 below_height += actionRects.at(i2).height();
2432 size.setHeight(below_height);
2436 above_height += actionRects.at(i).height();
2443 const auto rectIsNull = [](
const QRect &rect) {
return rect.isNull(); };
2444 if (q->childrenRect().isEmpty()
2445 && std::all_of(actionRects.cbegin(), actionRects.cend(), rectIsNull)) {
2447 syncAction =
nullptr;
2453 const QPoint mouse = QGuiApplicationPrivate::lastCursorPosition.toPoint();
2454 mousePopupPos = QGuiApplicationPrivate::lastCursorPosition;
2455 const bool snapToMouse = !causedPopup.widget && (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
2457 if (adjustToDesktop) {
2459 if (q->isRightToLeft()) {
2461 pos.setX(mouse.x() - size.width());
2463#if QT_CONFIG(menubar)
2465 if (qobject_cast<QMenuBar*>(causedPopup.widget) || qobject_cast<QMenu*>(causedPopup.widget))
2466 pos.rx() -= size.width();
2469 if (pos.x() < screen.left() + desktopFrame)
2470 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
2471 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2472 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
2474 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2475 pos.setX(screen.right() - desktopFrame - size.width() + 1);
2476 if (pos.x() < screen.left() + desktopFrame)
2477 pos.setX(screen.left() + desktopFrame);
2479 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
2481 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2483 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2486 if (pos.y() < screen.top() + desktopFrame)
2487 pos.setY(screen.top() + desktopFrame);
2488 if (pos.y() + menuSizeHint.height() - 1 > screen.bottom() - desktopFrame) {
2491 int y = qMax(screen.y(),pos.y());
2492 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
2495 pos.setY(screen.bottom() - size.height() + 1);
2500 const int subMenuOffset = q->style()->pixelMetric(QStyle::PM_SubMenuOverlap,
nullptr, q);
2501 QMenu *caused = qobject_cast<QMenu*>(causedPopup.widget);
2502 if (caused && caused->geometry().width() + menuSizeHint.width() + subMenuOffset < screen.width()) {
2503 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
2504 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
2505 parentActionRect.moveTopLeft(actionTopLeft);
2506 if (q->isRightToLeft()) {
2507 if ((pos.x() + menuSizeHint.width() > parentActionRect.left() - subMenuOffset)
2508 && (pos.x() < parentActionRect.right()))
2510 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2511 if (pos.x() < screen.x())
2512 pos.rx() = parentActionRect.right();
2513 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2514 pos.rx() = screen.x();
2517 if ((pos.x() < parentActionRect.right() + subMenuOffset)
2518 && (pos.x() + menuSizeHint.width() > parentActionRect.left()))
2520 pos.rx() = parentActionRect.right();
2521 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2522 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2523 if (pos.x() < screen.x())
2524 pos.rx() = screen.x() + screen.width() - menuSizeHint.width();
2528 popupScreen = QGuiApplication::screenAt(pos);
2529 q->setGeometry(QRect(pos, size));
2531#if QT_CONFIG(wayland)
2533 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(q->windowHandle()->handle())) {
2535 const QRect controlGeometry(causedButton->mapTo(causedButton->window(), QPoint(0, 0)), causedButton->size());
2536 waylandWindow->setParentControlGeometry(controlGeometry);
2537 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2538 }
else if (caused) {
2539 waylandWindow->setParentControlGeometry(caused->d_func()->actionRect(caused->d_func()->currentAction));
2540 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu);
2541 }
else if (
auto menubar = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2542 QPoint menuBarWindowPosition = menubar->mapTo(menubar->window(), QPoint(0, 0));
2543 waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action).translated(menuBarWindowPosition));
2544 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2549#if QT_CONFIG(effects)
2550 int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
2551 int vGuess = QEffects::DownScroll;
2552 if (q->isRightToLeft()) {
2553 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
2554 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 > causedPopup.widget->x()))
2555 hGuess = QEffects::RightScroll;
2557 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
2558 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 < causedPopup.widget->x()))
2559 hGuess = QEffects::LeftScroll;
2562#if QT_CONFIG(menubar)
2563 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
2564 (qobject_cast<QMenuBar*>(causedPopup.widget) &&
2565 pos.y() + size.width() / 2 < causedPopup.widget->mapToGlobal(causedPopup.widget->pos()).y()))
2566 vGuess = QEffects::UpScroll;
2568 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
2569 bool doChildEffects =
true;
2570#if QT_CONFIG(menubar)
2571 if (QMenuBar *mb = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2572 doChildEffects = mb->d_func()->doChildEffects;
2573 mb->d_func()->doChildEffects =
false;
2576 if (QMenu *m = qobject_cast<QMenu*>(causedPopup.widget)) {
2577 doChildEffects = m->d_func()->doChildEffects;
2578 m->d_func()->doChildEffects =
false;
2581 if (doChildEffects) {
2582 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
2584 else if (causedPopup.widget)
2585 qScrollEffect(q, qobject_cast<QMenu*>(causedPopup.widget) ? hGuess : vGuess);
2587 qScrollEffect(q, hGuess | vGuess);
2590 qFadeEffect(
nullptr);
2591 qScrollEffect(
nullptr);
2601#if QT_CONFIG(accessibility)
2602 QAccessibleEvent event(q, QAccessible::PopupMenuStart);
2603 QAccessible::updateAccessibility(&event);
2754void QMenu::paintEvent(QPaintEvent *e)
2757 d->updateActionRects();
2758 QStylePainter p(
this);
2759 QRegion emptyArea = QRegion(rect());
2761 QStyleOptionMenuItem menuOpt;
2762 menuOpt.initFrom(
this);
2763 menuOpt.state = QStyle::State_None;
2764 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2765 menuOpt.maxIconWidth = 0;
2766 menuOpt.reservedShortcutWidth = 0;
2767 p.drawPrimitive(QStyle::PE_PanelMenu, menuOpt);
2770 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr,
this);
2771 const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin,
nullptr,
this);
2772 const int vmargin = style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr,
this);
2774 QRect scrollUpRect, scrollDownRect;
2775 const int leftmargin = fw + hmargin + d->leftmargin;
2776 const int topmargin = fw + vmargin + d->topmargin;
2777 const int bottommargin = fw + vmargin + d->bottommargin;
2778 const int contentWidth = width() - (fw + hmargin) * 2 - d->leftmargin - d->rightmargin;
2780 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2781 scrollUpRect.setRect(leftmargin, topmargin, contentWidth, d->scrollerHeight());
2783 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2784 scrollDownRect.setRect(leftmargin, height() - d->scrollerHeight() - bottommargin,
2785 contentWidth, d->scrollerHeight());
2791 tearOffRect.setRect(leftmargin, topmargin, contentWidth,
2792 style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this));
2793 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2794 tearOffRect.translate(0, d->scrollerHeight());
2798 QRect scrollUpTearOffRect = scrollUpRect.united(tearOffRect);
2799 for (
int i = 0; i < d->actions.size(); ++i) {
2800 QAction *action = d->actions.at(i);
2801 QRect actionRect = d->actionRects.at(i);
2802 if (!e->rect().intersects(actionRect)
2803 || d->widgetItems.value(action))
2806 emptyArea -= QRegion(actionRect);
2808 QRect adjustedActionRect = actionRect;
2809 if (!scrollUpTearOffRect.isEmpty() && adjustedActionRect.bottom() <= scrollUpTearOffRect.top())
2812 if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom())
2815 if (adjustedActionRect.intersects(scrollUpTearOffRect)) {
2816 if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom())
2819 adjustedActionRect.setTop(scrollUpTearOffRect.bottom()+1);
2822 if (adjustedActionRect.intersects(scrollDownRect)) {
2823 if (adjustedActionRect.top() >= scrollDownRect.top())
2826 adjustedActionRect.setBottom(scrollDownRect.top()-1);
2829 QRegion adjustedActionReg(adjustedActionRect);
2830 p.setClipRegion(adjustedActionReg);
2832 QStyleOptionMenuItem opt;
2833 initStyleOption(&opt, action);
2834 opt.rect = actionRect;
2835 p.drawControl(QStyle::CE_MenuItem, opt);
2838 emptyArea -= QRegion(scrollUpTearOffRect);
2839 emptyArea -= QRegion(scrollDownRect);
2841 if (d->scrollUpTearOffItem || d->scrollDownItem) {
2842 if (d->scrollUpTearOffItem)
2843 d->scrollUpTearOffItem->updateScrollerRects(scrollUpTearOffRect);
2844 if (d->scrollDownItem)
2845 d->scrollDownItem->updateScrollerRects(scrollDownRect);
2848 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollUp, scrollUpRect);
2849 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollDown, scrollDownRect);
2851 d->drawTearOff(&p, tearOffRect);
2857 borderReg += QRect(0, 0, fw, height());
2858 borderReg += QRect(width()-fw, 0, fw, height());
2859 borderReg += QRect(0, 0, width(), fw);
2860 borderReg += QRect(0, height()-fw, width(), fw);
2861 p.setClipRegion(borderReg);
2862 emptyArea -= borderReg;
2863 QStyleOptionFrame frame;
2864 frame.rect = rect();
2865 frame.palette = palette();
2866 frame.state = QStyle::State_None;
2867 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame,
this);
2868 frame.midLineWidth = 0;
2869 p.drawPrimitive(QStyle::PE_FrameMenu, frame);
2873 p.setClipRegion(emptyArea);
2874 menuOpt.state = QStyle::State_None;
2875 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2876 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2877 menuOpt.rect = rect();
2878 menuOpt.menuRect = rect();
2879 p.drawControl(QStyle::CE_MenuEmptyArea, menuOpt);
3035bool QMenu::event(QEvent *e)
3038 switch (e->type()) {
3039 case QEvent::Polish:
3040 d->updateLayoutDirection();
3042 case QEvent::ShortcutOverride: {
3043 QKeyEvent *kev =
static_cast<QKeyEvent *>(e);
3044 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
3045 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
3046 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
3047#ifndef QT_NO_SHORTCUT
3048 || kev->matches(QKeySequence::Cancel)
3056 case QEvent::KeyPress: {
3057 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
3058 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
3063 case QEvent::MouseButtonPress:
3064 case QEvent::ContextMenu: {
3065 bool canPopup =
true;
3066 if (e->type() == QEvent::MouseButtonPress)
3067 canPopup = (
static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton);
3068 if (canPopup && d->delayState.timer.isActive()) {
3069 d->delayState.stop();
3070 internalDelayedPopup();
3074 case QEvent::Resize: {
3075 QStyleHintReturnMask menuMask;
3076 QStyleOption option;
3077 option.initFrom(
this);
3078 if (style()->styleHint(QStyle::SH_Menu_Mask, &option,
this, &menuMask)) {
3079 setMask(menuMask.region);
3082 d->updateActionRects();
3085 QMenuPrivate::mouseDown =
nullptr;
3086 d->updateActionRects();
3087 d->sloppyState.reset();
3088 if (d->currentAction)
3089 d->popupAction(d->currentAction, 0,
false);
3090 if (isWindow() && window() && window()->windowHandle())
3091 window()->windowHandle()->setTransientParent(d->transientParentWindow());
3093#if QT_CONFIG(tooltip)
3094 case QEvent::ToolTip:
3095 if (d->toolTipsVisible) {
3096 const QHelpEvent *ev =
static_cast<
const QHelpEvent*>(e);
3097 if (QAction *action = actionAt(ev->pos())) {
3098 const QString toolTip = action->d_func()->tooltip;
3099 if (!toolTip.isEmpty())
3100 QToolTip::showText(ev->globalPos(), toolTip,
this, actionGeometry(action));
3102 QToolTip::hideText();
3108#if QT_CONFIG(whatsthis)
3109 case QEvent::QueryWhatsThis:
3110 e->setAccepted(d->whatsThis.size());
3111 if (QAction *action = d->actionAt(
static_cast<QHelpEvent*>(e)->pos())) {
3112 if (action->whatsThis().size() || action->menu())
3120 return QWidget::event(e);
3137void QMenu::keyPressEvent(QKeyEvent *e)
3140 d->updateActionRects();
3142 if (isRightToLeft()) {
3143 if (key == Qt::Key_Left)
3144 key = Qt::Key_Right;
3145 else if (key == Qt::Key_Right)
3149 if (key == Qt::Key_Tab)
3151 if (key == Qt::Key_Backtab)
3155 bool key_consumed =
false;
3158 key_consumed =
true;
3160 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3163 key_consumed =
true;
3165 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3167 case Qt::Key_PageUp:
3168 key_consumed =
true;
3169 if (d->currentAction && d->scroll) {
3170 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3171 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp,
true,
true);
3173 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3176 case Qt::Key_PageDown:
3177 key_consumed =
true;
3178 if (d->currentAction && d->scroll) {
3179 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
3180 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown,
true,
true);
3182 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3186 case Qt::Key_Down: {
3187 key_consumed =
true;
3188 QAction *nextAction =
nullptr;
3189 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
3190 if (!d->currentAction) {
3191 if (key == Qt::Key_Down) {
3192 for(
int i = 0; i < d->actions.size(); ++i) {
3193 if (d->actionRects.at(i).isNull())
3195 QAction *act = d->actions.at(i);
3196 if (d->considerAction(act)) {
3202 for(
int i = d->actions.size()-1; i >= 0; --i) {
3203 if (d->actionRects.at(i).isNull())
3205 QAction *act = d->actions.at(i);
3206 if (d->considerAction(act)) {
3213 for(
int i = 0, y = 0; !nextAction && i < d->actions.size(); i++) {
3214 QAction *act = d->actions.at(i);
3215 if (act == d->currentAction) {
3216 if (key == Qt::Key_Up) {
3217 for(
int next_i = i-1;
true; next_i--) {
3219 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3222 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3223 next_i = d->actionRects.size()-1;
3225 QAction *next = d->actions.at(next_i);
3226 if (next == d->currentAction)
3228 if (d->actionRects.at(next_i).isNull())
3230 if (!d->considerAction(next))
3233 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
3234 int topVisible = d->scrollerHeight();
3236 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3237 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
3238 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3242 if (!nextAction && d->tearoff)
3243 d->tearoffHighlighted = 1;
3245 y += d->actionRects.at(i).height();
3246 for(
int next_i = i+1;
true; next_i++) {
3247 if (next_i == d->actionRects.size()) {
3248 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3251 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3254 QAction *next = d->actions.at(next_i);
3255 if (next == d->currentAction)
3257 if (d->actionRects.at(next_i).isNull())
3259 if (!d->considerAction(next))
3262 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
3263 int bottomVisible = height() - d->scrollerHeight();
3264 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3265 bottomVisible -= d->scrollerHeight();
3267 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3268 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
3269 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3276 y += d->actionRects.at(i).height();
3280 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
3281 d->scroll->scrollTimer.stop();
3282 d->scrollMenu(nextAction, scroll_loc);
3284 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, key == Qt::Key_Up ? QMenuPrivate::SelectionDirection::Up : QMenuPrivate::SelectionDirection::Down);
3289 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
3290 d->popupAction(d->currentAction, 0,
true);
3291 key_consumed =
true;
3295 case Qt::Key_Left: {
3296 if (d->currentAction && !d->scroll) {
3297 QAction *nextAction =
nullptr;
3298 if (key == Qt::Key_Left) {
3299 QRect actionR = d->actionRect(d->currentAction);
3300 for(
int x = actionR.left()-1; !nextAction && x >= 0; x--)
3301 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3303 QRect actionR = d->actionRect(d->currentAction);
3304 for(
int x = actionR.right()+1; !nextAction && x < width(); x++)
3305 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3308 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, QMenuPrivate::SelectionDirection::Up);
3309 key_consumed =
true;
3312 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
3313 QPointer<QWidget> caused = d->causedPopup.widget;
3317 key_consumed =
true;
3325 key_consumed =
true;
3326 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation,
nullptr,
this))
3329#if QT_CONFIG(menubar)
3330 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
3331 mb->d_func()->setKeyboardMode(
false);
3338 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem,
nullptr,
this))
3342 case Qt::Key_Return:
3343 case Qt::Key_Enter: {
3344 if (!d->currentAction) {
3345 d->setFirstActionActive();
3346 key_consumed =
true;
3352 if (d->currentAction->menu())
3353 d->popupAction(d->currentAction, 0,
true);
3355 d->activateAction(d->currentAction, QAction::Trigger);
3356 key_consumed =
true;
3359#if QT_CONFIG(whatsthis)
3361 if (!d->currentAction || d->currentAction->whatsThis().isNull())
3363 QWhatsThis::enterWhatsThisMode();
3364 d->activateAction(d->currentAction, QAction::Trigger);
3368 key_consumed =
false;
3371 if (!key_consumed && (
3373#ifndef QT_NO_SHORTCUT
3374 || e->matches(QKeySequence::Cancel)
3377 key_consumed =
true;
3383 QPointer<QWidget> caused = d->causedPopup.widget;
3385#if QT_CONFIG(menubar)
3386 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
3387 mb->d_func()->setCurrentAction(d->menuAction);
3388 mb->d_func()->setKeyboardMode(
true);
3394 if (!key_consumed) {
3395 const Qt::KeyboardModifiers modifiers = e->modifiers();
3396 if ((!modifiers || modifiers == Qt::AltModifier || modifiers == Qt::ShiftModifier
3397 || modifiers == Qt::KeypadModifier
3398 || modifiers == (Qt::KeypadModifier | Qt::AltModifier))
3399 && e->text().size() == 1) {
3400 bool activateAction =
false;
3401 QAction *nextAction =
nullptr;
3402 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch,
nullptr,
this) && !e->modifiers()) {
3403 int best_match_count = 0;
3404 d->searchBufferTimer.start(2000,
this);
3405 d->searchBuffer += e->text();
3406 for(
int i = 0; i < d->actions.size(); ++i) {
3407 int match_count = 0;
3408 if (d->actionRects.at(i).isNull())
3410 QAction *act = d->actions.at(i);
3411 const QString act_text = act->text();
3412 for(
int c = 0; c < d->searchBuffer.size(); ++c) {
3413 if (act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
3416 if (match_count > best_match_count) {
3417 best_match_count = match_count;
3422#ifndef QT_NO_SHORTCUT
3425 QAction *first =
nullptr, *currentSelected =
nullptr, *firstAfterCurrent =
nullptr;
3426 QChar c = e->text().at(0).toUpper();
3427 for(
int i = 0; i < d->actions.size(); ++i) {
3428 if (d->actionRects.at(i).isNull())
3430 QAction *act = d->actions.at(i);
3431 if (!act->isEnabled() || act->isSeparator())
3433 QKeySequence sequence = QKeySequence::mnemonic(act->text());
3434 int key = sequence[0].toCombined() & 0xffff;
3435 if (key == c.unicode()) {
3439 if (act == d->currentAction)
3440 currentSelected = act;
3441 else if (!firstAfterCurrent && currentSelected)
3442 firstAfterCurrent = act;
3445 if (clashCount == 1)
3446 activateAction =
true;
3447 if (clashCount >= 1) {
3448 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
3451 nextAction = firstAfterCurrent;
3456 key_consumed =
true;
3458 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter,
false);
3459 d->setCurrentAction(nextAction, 0, QMenuPrivate::SelectedFromElsewhere, QMenuPrivate::SelectionDirection::Down,
true);
3460 if (!nextAction->menu() && activateAction) {
3462 d->activateAction(nextAction, QAction::Trigger);
3466 if (!key_consumed) {
3467#if QT_CONFIG(menubar)
3468 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
3469 QAction *oldAct = mb->d_func()->currentAction;
3470 QCoreApplication::sendEvent(mb, e);
3471 if (mb->d_func()->currentAction != oldAct)
3472 key_consumed =
true;
3478 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
3479 QApplication::beep();
3576void QMenu::actionEvent(QActionEvent *e)
3580 setAttribute(Qt::WA_Resized,
false);
3582 d->tornPopup->syncWithMenu(
this, e);
3583 if (e->type() == QEvent::ActionAdded) {
3586#if QT_CONFIG(menubar)
3587 && !qobject_cast<QMenuBar*>(e->action()->parent())
3592 connect(e->action(), SIGNAL(triggered()),
this, SLOT(_q_actionTriggered()), Qt::UniqueConnection);
3593 connect(e->action(), SIGNAL(hovered()),
this, SLOT(_q_actionHovered()), Qt::UniqueConnection);
3595 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3596 QWidget *widget = wa->requestWidget(
this);
3598 d->widgetItems.insert(wa, widget);
3600 if (!d->scrollUpTearOffItem)
3601 d->scrollUpTearOffItem =
3602 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollUp, d,
this);
3603 if (!d->scrollDownItem)
3605 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollDown, d,
this);
3609 }
else if (e->type() == QEvent::ActionRemoved) {
3610 e->action()->disconnect(
this);
3611 if (e->action() == d->currentAction)
3612 d->currentAction =
nullptr;
3613 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3614 if (QWidget *widget = d->widgetItems.value(wa))
3615 wa->releaseWidget(widget);
3617 d->widgetItems.remove(
static_cast<QAction *>(e->action()));
3620 if (!d->platformMenu.isNull()) {
3621 auto action =
static_cast<QAction *>(e->action());
3622 if (e->type() == QEvent::ActionAdded) {
3623 QPlatformMenuItem *beforeItem = e->before()
3624 ? d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->before()))
3626 d->insertActionInPlatformMenu(action, beforeItem);
3627 }
else if (e->type() == QEvent::ActionRemoved) {
3628 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3629 d->platformMenu->removeMenuItem(menuItem);
3631 }
else if (e->type() == QEvent::ActionChanged) {
3632 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3634 d->copyActionToPlatformItem(action, menuItem);
3635 d->platformMenu->syncMenuItem(menuItem);
3639 d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators);