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 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().toPoint())) {
1358 setCurrentAction(
nullptr);
1360 if (e->type() == QEvent::MouseButtonRelease) {
1362 tornPopup =
new QTornOffMenu(q);
1363 tornPopup->setGeometry(q->geometry());
1372 if (q->frameGeometry().contains(e->globalPosition().toPoint()))
1375 for(QWidget *caused = causedPopup.widget; caused;) {
1376 bool passOnEvent =
false;
1377 QWidget *next_widget =
nullptr;
1378 QPointF cpos = caused->mapFromGlobal(e->globalPosition());
1379#if QT_CONFIG(menubar)
1380 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
1381 passOnEvent = mb->rect().contains(cpos.toPoint());
1384 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
1385 passOnEvent = m->rect().contains(cpos.toPoint());
1386 next_widget = m->d_func()->causedPopup.widget;
1389 if (e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
1390 QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->globalPosition(),
1391 e->button(), e->buttons(), e->modifiers(),
1392 e->source(), e->pointingDevice());
1393 QCoreApplication::sendEvent(caused, &new_e);
1397 caused = next_widget;
1399 sloppyState.leave();
1445void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e,
bool self)
1448#if QT_CONFIG(whatsthis)
1449 bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
1451 if (!action || !q->isEnabled()
1452 || (action_e == QAction::Trigger
1453#if QT_CONFIG(whatsthis)
1456 && (action->isSeparator() ||!action->isEnabled())))
1460
1461
1462 const QList<QPointer<QWidget>> causedStack = calcCausedStack();
1463 if (action_e == QAction::Trigger) {
1464#if QT_CONFIG(whatsthis)
1465 if (!inWhatsThisMode)
1466 actionAboutToTrigger = action;
1469 if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
1472 for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
1473 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1476 widget = qmenu->d_func()->causedPopup.widget;
1483#if QT_CONFIG(whatsthis)
1484 if (inWhatsThisMode) {
1485 QString s = action->whatsThis();
1488 QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
1494 QPointer<QMenu> thisGuard(q);
1495 activateCausedStack(causedStack, action, action_e, self);
1499 if (action_e == QAction::Hover) {
1500#if QT_CONFIG(accessibility)
1501 if (QAccessible::isActive()) {
1502 int actionIndex = indexOf(action);
1503 QAccessibleEvent focusEvent(q, QAccessible::Focus);
1504 focusEvent.setChild(actionIndex);
1505 QAccessible::updateAccessibility(&focusEvent);
1508 action->showStatusText(topCausedWidget());
1510 actionAboutToTrigger =
nullptr;
1596void QMenu::initStyleOption(QStyleOptionMenuItem *option,
const QAction *action)
const
1598 if (!option || !action)
1602 option->initFrom(
this);
1603 option->palette = palette();
1604 option->state = QStyle::State_None;
1606 if (window()->isActiveWindow())
1607 option->state |= QStyle::State_Active;
1608 if (isEnabled() && action->isEnabled()
1609 && (!action->menu() || action->menu()->isEnabled()))
1610 option->state |= QStyle::State_Enabled;
1612 option->palette.setCurrentColorGroup(QPalette::Disabled);
1614 option->font = action->font().resolve(font());
1615 option->fontMetrics = QFontMetrics(option->font);
1617 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1618 option->state |= QStyle::State_Selected
1619 | (QMenuPrivate::mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1622 option->menuHasCheckableItems = d->hasCheckableItems;
1623 if (!action->isCheckable()) {
1624 option->checkType = QStyleOptionMenuItem::NotCheckable;
1626 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1627 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1628 option->checked = action->isChecked();
1631 option->menuItemType = QStyleOptionMenuItem::SubMenu;
1632 else if (action->isSeparator())
1633 option->menuItemType = QStyleOptionMenuItem::Separator;
1634 else if (d->defaultAction == action)
1635 option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1637 option->menuItemType = QStyleOptionMenuItem::Normal;
1638 if (action->isIconVisibleInMenu())
1639 option->icon = action->icon();
1640 QString textAndAccel = action->text();
1641#ifndef QT_NO_SHORTCUT
1642 if ((action->isShortcutVisibleInContextMenu() || !d->isContextMenu())
1643 && textAndAccel.indexOf(u'\t') == -1) {
1644 QKeySequence seq = action->shortcut();
1646 textAndAccel += u'\t' + seq.toString(QKeySequence::NativeText);
1649 option->text = textAndAccel;
1650 option->reservedShortcutWidth = d->tabWidth;
1651 option->maxIconWidth = d->maxIconWidth;
1652 option->menuRect = rect();
2313void QMenuPrivate::popup(
const QPoint &p, QAction *atAction, PositionFunction positionFunction)
2316 popupScreen = QGuiApplication::screenAt(p);
2317 QScopeGuard popupScreenGuard([
this](){ popupScreen.clear(); });
2330 q->ensurePolished();
2334 bool screenSet =
false;
2335 QScreen *screen = topData()->initialScreen;
2337 if (setScreen(screen))
2340 }
else if (QMenu *parentMenu = qobject_cast<QMenu *>(parent)) {
2343 if (setScreen(parentMenu->screen()))
2347 if (!screenSet && setScreenForPoint(p))
2362 if (!windowHandle()) {
2366#if QT_CONFIG(menubar)
2369 q->setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(topCausedWidget()) !=
nullptr);
2372 emit q->aboutToShow();
2376#if QT_CONFIG(graphicsview)
2377 bool isEmbedded = !bypassGraphicsProxyWidget(q) && QMenuPrivate::nearestGraphicsProxyWidget(q);
2379 screen = popupGeometry();
2382 screen = popupGeometry(QGuiApplication::screenAt(p));
2386 QPushButton *causedButton = qobject_cast<QPushButton*>(causedPopup.widget);
2387 if (actionListChanged && causedButton)
2388 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
2391 popupScreen = QGuiApplication::screenAt(pos);
2393 const QSize menuSizeHint(q->sizeHint());
2394 QSize size = menuSizeHint;
2396 if (positionFunction)
2397 pos = positionFunction(menuSizeHint);
2399 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth,
nullptr, q);
2400 bool adjustToDesktop = !q->window()->testAttribute(Qt::WA_DontShowOnScreen);
2403 if ((size.height() > screen.height() || size.width() > screen.width()) ||
2405 (ncols >1 && size.height() < screen.height())) {
2406 size.setWidth(qMin(menuSizeHint.width(), screen.width() - desktopFrame * 2));
2407 size.setHeight(qMin(menuSizeHint.height(), screen.height() - desktopFrame * 2));
2408 adjustToDesktop =
true;
2411#ifdef QT_KEYPAD_NAVIGATION
2412 if (!atAction && QApplicationPrivate::keypadNavigationEnabled()) {
2414 if (defaultAction && defaultAction->isEnabled()) {
2415 atAction = defaultAction;
2418 for (QAction *action : std::as_const(actions))
2419 if (action->isEnabled()) {
2424 currentAction = atAction;
2428 pos.setY(screen.top() + desktopFrame);
2429 }
else if (atAction) {
2430 for (
int i = 0, above_height = 0; i < actions.size(); i++) {
2431 QAction *action = actions.at(i);
2432 if (action == atAction) {
2433 int newY = pos.y() - above_height;
2434 if (scroll && newY < desktopFrame) {
2435 scroll->scrollFlags = scroll->scrollFlags
2438 newY = desktopFrame;
2443 && !q->style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll,
nullptr, q)) {
2445 for (
int i2 = i; i2 < actionRects.size(); i2++)
2446 below_height += actionRects.at(i2).height();
2447 size.setHeight(below_height);
2451 above_height += actionRects.at(i).height();
2458 const auto rectIsNull = [](
const QRect &rect) {
return rect.isNull(); };
2459 if (q->childrenRect().isEmpty()
2460 && std::all_of(actionRects.cbegin(), actionRects.cend(), rectIsNull)) {
2462 syncAction =
nullptr;
2468 const QPoint mouse = QGuiApplicationPrivate::lastCursorPosition.toPoint();
2469 mousePopupPos = QGuiApplicationPrivate::lastCursorPosition;
2470 const bool snapToMouse = !causedPopup.widget && (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
2472 if (adjustToDesktop) {
2474 if (q->isRightToLeft()) {
2476 pos.setX(mouse.x() - size.width());
2478#if QT_CONFIG(menubar)
2480 if (qobject_cast<QMenuBar*>(causedPopup.widget) || qobject_cast<QMenu*>(causedPopup.widget))
2481 pos.rx() -= size.width();
2484 if (pos.x() < screen.left() + desktopFrame)
2485 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
2486 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2487 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
2489 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2490 pos.setX(screen.right() - desktopFrame - size.width() + 1);
2491 if (pos.x() < screen.left() + desktopFrame)
2492 pos.setX(screen.left() + desktopFrame);
2494 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
2496 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2498 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2501 if (pos.y() < screen.top() + desktopFrame)
2502 pos.setY(screen.top() + desktopFrame);
2503 if (pos.y() + menuSizeHint.height() - 1 > screen.bottom() - desktopFrame) {
2506 int y = qMax(screen.y(),pos.y());
2507 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
2510 pos.setY(screen.bottom() - size.height() + 1);
2515 const int subMenuOffset = q->style()->pixelMetric(QStyle::PM_SubMenuOverlap,
nullptr, q);
2516 QMenu *caused = qobject_cast<QMenu*>(causedPopup.widget);
2517 if (caused && caused->geometry().width() + menuSizeHint.width() + subMenuOffset < screen.width()) {
2518 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
2519 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
2520 parentActionRect.moveTopLeft(actionTopLeft);
2521 if (q->isRightToLeft()) {
2522 if ((pos.x() + menuSizeHint.width() > parentActionRect.left() - subMenuOffset)
2523 && (pos.x() < parentActionRect.right()))
2525 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2526 if (pos.x() < screen.x())
2527 pos.rx() = parentActionRect.right();
2528 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2529 pos.rx() = screen.x();
2532 if ((pos.x() < parentActionRect.right() + subMenuOffset)
2533 && (pos.x() + menuSizeHint.width() > parentActionRect.left()))
2535 pos.rx() = parentActionRect.right();
2536 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2537 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2538 if (pos.x() < screen.x())
2539 pos.rx() = screen.x() + screen.width() - menuSizeHint.width();
2543 popupScreen = QGuiApplication::screenAt(pos);
2544 q->setGeometry(QRect(pos, size));
2546#if QT_CONFIG(wayland)
2548 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(q->windowHandle()->handle())) {
2550 const QRect controlGeometry(causedButton->mapTo(causedButton->window(), QPoint(0, 0)), causedButton->size());
2551 waylandWindow->setParentControlGeometry(controlGeometry);
2552 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2553 }
else if (caused) {
2554 waylandWindow->setParentControlGeometry(caused->d_func()->actionRect(caused->d_func()->currentAction));
2555 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu);
2556 }
else if (
auto menubar = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2557 QPoint menuBarWindowPosition = menubar->mapTo(menubar->window(), QPoint(0, 0));
2558 waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action).translated(menuBarWindowPosition));
2559 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2564#if QT_CONFIG(effects)
2565 int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
2566 int vGuess = QEffects::DownScroll;
2567 if (q->isRightToLeft()) {
2568 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
2569 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 > causedPopup.widget->x()))
2570 hGuess = QEffects::RightScroll;
2572 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
2573 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 < causedPopup.widget->x()))
2574 hGuess = QEffects::LeftScroll;
2577#if QT_CONFIG(menubar)
2578 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
2579 (qobject_cast<QMenuBar*>(causedPopup.widget) &&
2580 pos.y() + size.width() / 2 < causedPopup.widget->mapToGlobal(causedPopup.widget->pos()).y()))
2581 vGuess = QEffects::UpScroll;
2583 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
2584 bool doChildEffects =
true;
2585#if QT_CONFIG(menubar)
2586 if (QMenuBar *mb = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2587 doChildEffects = mb->d_func()->doChildEffects;
2588 mb->d_func()->doChildEffects =
false;
2591 if (QMenu *m = qobject_cast<QMenu*>(causedPopup.widget)) {
2592 doChildEffects = m->d_func()->doChildEffects;
2593 m->d_func()->doChildEffects =
false;
2596 if (doChildEffects) {
2597 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
2599 else if (causedPopup.widget)
2600 qScrollEffect(q, qobject_cast<QMenu*>(causedPopup.widget) ? hGuess : vGuess);
2602 qScrollEffect(q, hGuess | vGuess);
2605 qFadeEffect(
nullptr);
2606 qScrollEffect(
nullptr);
2616#if QT_CONFIG(accessibility)
2617 QAccessibleEvent event(q, QAccessible::PopupMenuStart);
2618 QAccessible::updateAccessibility(&event);
2769void QMenu::paintEvent(QPaintEvent *e)
2772 d->updateActionRects();
2773 QStylePainter p(
this);
2774 QRegion emptyArea = QRegion(rect());
2776 QStyleOptionMenuItem menuOpt;
2777 menuOpt.initFrom(
this);
2778 menuOpt.state = QStyle::State_None;
2779 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2780 menuOpt.maxIconWidth = 0;
2781 menuOpt.reservedShortcutWidth = 0;
2782 p.drawPrimitive(QStyle::PE_PanelMenu, menuOpt);
2785 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr,
this);
2786 const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin,
nullptr,
this);
2787 const int vmargin = style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr,
this);
2789 QRect scrollUpRect, scrollDownRect;
2790 const int leftmargin = fw + hmargin + d->leftmargin;
2791 const int topmargin = fw + vmargin + d->topmargin;
2792 const int bottommargin = fw + vmargin + d->bottommargin;
2793 const int contentWidth = width() - (fw + hmargin) * 2 - d->leftmargin - d->rightmargin;
2795 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2796 scrollUpRect.setRect(leftmargin, topmargin, contentWidth, d->scrollerHeight());
2798 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2799 scrollDownRect.setRect(leftmargin, height() - d->scrollerHeight() - bottommargin,
2800 contentWidth, d->scrollerHeight());
2806 tearOffRect.setRect(leftmargin, topmargin, contentWidth,
2807 style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this));
2808 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2809 tearOffRect.translate(0, d->scrollerHeight());
2813 QRect scrollUpTearOffRect = scrollUpRect.united(tearOffRect);
2814 for (
int i = 0; i < d->actions.size(); ++i) {
2815 QAction *action = d->actions.at(i);
2816 QRect actionRect = d->actionRects.at(i);
2817 if (!e->rect().intersects(actionRect)
2818 || d->widgetItems.value(action))
2821 emptyArea -= QRegion(actionRect);
2823 QRect adjustedActionRect = actionRect;
2824 if (!scrollUpTearOffRect.isEmpty() && adjustedActionRect.bottom() <= scrollUpTearOffRect.top())
2827 if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom())
2830 if (adjustedActionRect.intersects(scrollUpTearOffRect)) {
2831 if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom())
2834 adjustedActionRect.setTop(scrollUpTearOffRect.bottom()+1);
2837 if (adjustedActionRect.intersects(scrollDownRect)) {
2838 if (adjustedActionRect.top() >= scrollDownRect.top())
2841 adjustedActionRect.setBottom(scrollDownRect.top()-1);
2844 QRegion adjustedActionReg(adjustedActionRect);
2845 p.setClipRegion(adjustedActionReg);
2847 QStyleOptionMenuItem opt;
2848 initStyleOption(&opt, action);
2849 opt.rect = actionRect;
2850 p.drawControl(QStyle::CE_MenuItem, opt);
2853 emptyArea -= QRegion(scrollUpTearOffRect);
2854 emptyArea -= QRegion(scrollDownRect);
2856 if (d->scrollUpTearOffItem || d->scrollDownItem) {
2857 if (d->scrollUpTearOffItem)
2858 d->scrollUpTearOffItem->updateScrollerRects(scrollUpTearOffRect);
2859 if (d->scrollDownItem)
2860 d->scrollDownItem->updateScrollerRects(scrollDownRect);
2863 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollUp, scrollUpRect);
2864 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollDown, scrollDownRect);
2866 d->drawTearOff(&p, tearOffRect);
2872 borderReg += QRect(0, 0, fw, height());
2873 borderReg += QRect(width()-fw, 0, fw, height());
2874 borderReg += QRect(0, 0, width(), fw);
2875 borderReg += QRect(0, height()-fw, width(), fw);
2876 p.setClipRegion(borderReg);
2877 emptyArea -= borderReg;
2878 QStyleOptionFrame frame;
2879 frame.rect = rect();
2880 frame.palette = palette();
2881 frame.state = QStyle::State_None;
2882 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame,
this);
2883 frame.midLineWidth = 0;
2884 p.drawPrimitive(QStyle::PE_FrameMenu, frame);
2888 p.setClipRegion(emptyArea);
2889 menuOpt.state = QStyle::State_None;
2890 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2891 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2892 menuOpt.rect = rect();
2893 menuOpt.menuRect = rect();
2894 p.drawControl(QStyle::CE_MenuEmptyArea, menuOpt);
3022bool QMenu::event(QEvent *e)
3025 switch (e->type()) {
3026 case QEvent::Polish:
3027 d->updateLayoutDirection();
3029 case QEvent::ShortcutOverride: {
3030 QKeyEvent *kev =
static_cast<QKeyEvent *>(e);
3031 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
3032 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
3033 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
3034#ifndef QT_NO_SHORTCUT
3035 || kev->matches(QKeySequence::Cancel)
3043 case QEvent::KeyPress: {
3044 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
3045 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
3050 case QEvent::MouseButtonPress:
3051 case QEvent::ContextMenu: {
3052 bool canPopup =
true;
3053 if (e->type() == QEvent::MouseButtonPress)
3054 canPopup = (
static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton);
3055 if (canPopup && d->delayState.timer.isActive()) {
3056 d->delayState.stop();
3057 internalDelayedPopup();
3061 case QEvent::Resize: {
3062 QStyleHintReturnMask menuMask;
3063 QStyleOption option;
3064 option.initFrom(
this);
3065 if (style()->styleHint(QStyle::SH_Menu_Mask, &option,
this, &menuMask)) {
3066 setMask(menuMask.region);
3069 d->updateActionRects();
3072 QMenuPrivate::mouseDown =
nullptr;
3073 d->updateActionRects();
3074 d->sloppyState.reset();
3075 if (d->currentAction)
3076 d->popupAction(d->currentAction, 0,
false);
3077 if (isWindow() && window() && window()->windowHandle())
3078 window()->windowHandle()->setTransientParent(d->transientParentWindow());
3080#if QT_CONFIG(tooltip)
3081 case QEvent::ToolTip:
3082 if (d->toolTipsVisible) {
3083 const QHelpEvent *ev =
static_cast<
const QHelpEvent*>(e);
3084 if (
const QAction *action = actionAt(ev->pos())) {
3085 const QString toolTip = action->d_func()->tooltip;
3086 if (!toolTip.isEmpty())
3087 QToolTip::showText(ev->globalPos(), toolTip,
this);
3089 QToolTip::hideText();
3095#if QT_CONFIG(whatsthis)
3096 case QEvent::QueryWhatsThis:
3097 e->setAccepted(d->whatsThis.size());
3098 if (QAction *action = d->actionAt(
static_cast<QHelpEvent*>(e)->pos())) {
3099 if (action->whatsThis().size() || action->menu())
3107 return QWidget::event(e);
3124void QMenu::keyPressEvent(QKeyEvent *e)
3127 d->updateActionRects();
3129 if (isRightToLeft()) {
3130 if (key == Qt::Key_Left)
3131 key = Qt::Key_Right;
3132 else if (key == Qt::Key_Right)
3136 if (key == Qt::Key_Tab)
3138 if (key == Qt::Key_Backtab)
3142 bool key_consumed =
false;
3145 key_consumed =
true;
3147 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3150 key_consumed =
true;
3152 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3154 case Qt::Key_PageUp:
3155 key_consumed =
true;
3156 if (d->currentAction && d->scroll) {
3157 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3158 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp,
true,
true);
3160 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3163 case Qt::Key_PageDown:
3164 key_consumed =
true;
3165 if (d->currentAction && d->scroll) {
3166 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
3167 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown,
true,
true);
3169 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3173 case Qt::Key_Down: {
3174 key_consumed =
true;
3175 QAction *nextAction =
nullptr;
3176 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
3177 if (!d->currentAction) {
3178 if (key == Qt::Key_Down) {
3179 for(
int i = 0; i < d->actions.size(); ++i) {
3180 if (d->actionRects.at(i).isNull())
3182 QAction *act = d->actions.at(i);
3183 if (d->considerAction(act)) {
3189 for(
int i = d->actions.size()-1; i >= 0; --i) {
3190 if (d->actionRects.at(i).isNull())
3192 QAction *act = d->actions.at(i);
3193 if (d->considerAction(act)) {
3200 for(
int i = 0, y = 0; !nextAction && i < d->actions.size(); i++) {
3201 QAction *act = d->actions.at(i);
3202 if (act == d->currentAction) {
3203 if (key == Qt::Key_Up) {
3204 for(
int next_i = i-1;
true; next_i--) {
3206 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3209 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3210 next_i = d->actionRects.size()-1;
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::ScrollUp)) {
3221 int topVisible = d->scrollerHeight();
3223 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3224 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
3225 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3229 if (!nextAction && d->tearoff)
3230 d->tearoffHighlighted = 1;
3232 y += d->actionRects.at(i).height();
3233 for(
int next_i = i+1;
true; next_i++) {
3234 if (next_i == d->actionRects.size()) {
3235 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3238 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3241 QAction *next = d->actions.at(next_i);
3242 if (next == d->currentAction)
3244 if (d->actionRects.at(next_i).isNull())
3246 if (!d->considerAction(next))
3249 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
3250 int bottomVisible = height() - d->scrollerHeight();
3251 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3252 bottomVisible -= d->scrollerHeight();
3254 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3255 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
3256 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3263 y += d->actionRects.at(i).height();
3267 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
3268 d->scroll->scrollTimer.stop();
3269 d->scrollMenu(nextAction, scroll_loc);
3271 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, key == Qt::Key_Up ? QMenuPrivate::SelectionDirection::Up : QMenuPrivate::SelectionDirection::Down);
3276 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
3277 d->popupAction(d->currentAction, 0,
true);
3278 key_consumed =
true;
3282 case Qt::Key_Left: {
3283 if (d->currentAction && !d->scroll) {
3284 QAction *nextAction =
nullptr;
3285 if (key == Qt::Key_Left) {
3286 QRect actionR = d->actionRect(d->currentAction);
3287 for(
int x = actionR.left()-1; !nextAction && x >= 0; x--)
3288 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3290 QRect actionR = d->actionRect(d->currentAction);
3291 for(
int x = actionR.right()+1; !nextAction && x < width(); x++)
3292 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3295 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, QMenuPrivate::SelectionDirection::Up);
3296 key_consumed =
true;
3299 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
3300 QPointer<QWidget> caused = d->causedPopup.widget;
3304 key_consumed =
true;
3312 key_consumed =
true;
3313 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation,
nullptr,
this))
3316#if QT_CONFIG(menubar)
3317 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
3318 mb->d_func()->setKeyboardMode(
false);
3325 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem,
nullptr,
this))
3329#ifdef QT_KEYPAD_NAVIGATION
3330 case Qt::Key_Select:
3332 case Qt::Key_Return:
3333 case Qt::Key_Enter: {
3334 if (!d->currentAction) {
3335 d->setFirstActionActive();
3336 key_consumed =
true;
3342 if (d->currentAction->menu())
3343 d->popupAction(d->currentAction, 0,
true);
3345 d->activateAction(d->currentAction, QAction::Trigger);
3346 key_consumed =
true;
3349#if QT_CONFIG(whatsthis)
3351 if (!d->currentAction || d->currentAction->whatsThis().isNull())
3353 QWhatsThis::enterWhatsThisMode();
3354 d->activateAction(d->currentAction, QAction::Trigger);
3358 key_consumed =
false;
3361 if (!key_consumed && (
3363#ifndef QT_NO_SHORTCUT
3364 || e->matches(QKeySequence::Cancel)
3366#ifdef QT_KEYPAD_NAVIGATION
3367 || e->key() == Qt::Key_Back
3370 key_consumed =
true;
3376 QPointer<QWidget> caused = d->causedPopup.widget;
3378#if QT_CONFIG(menubar)
3379 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
3380 mb->d_func()->setCurrentAction(d->menuAction);
3381 mb->d_func()->setKeyboardMode(
true);
3387 if (!key_consumed) {
3388 const Qt::KeyboardModifiers modifiers = e->modifiers();
3389 if ((!modifiers || modifiers == Qt::AltModifier || modifiers == Qt::ShiftModifier
3390 || modifiers == Qt::KeypadModifier
3391 || modifiers == (Qt::KeypadModifier | Qt::AltModifier))
3392 && e->text().size() == 1) {
3393 bool activateAction =
false;
3394 QAction *nextAction =
nullptr;
3395 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch,
nullptr,
this) && !e->modifiers()) {
3396 int best_match_count = 0;
3397 d->searchBufferTimer.start(2000,
this);
3398 d->searchBuffer += e->text();
3399 for(
int i = 0; i < d->actions.size(); ++i) {
3400 int match_count = 0;
3401 if (d->actionRects.at(i).isNull())
3403 QAction *act = d->actions.at(i);
3404 const QString act_text = act->text();
3405 for(
int c = 0; c < d->searchBuffer.size(); ++c) {
3406 if (act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
3409 if (match_count > best_match_count) {
3410 best_match_count = match_count;
3415#ifndef QT_NO_SHORTCUT
3418 QAction *first =
nullptr, *currentSelected =
nullptr, *firstAfterCurrent =
nullptr;
3419 QChar c = e->text().at(0).toUpper();
3420 for(
int i = 0; i < d->actions.size(); ++i) {
3421 if (d->actionRects.at(i).isNull())
3423 QAction *act = d->actions.at(i);
3424 if (!act->isEnabled() || act->isSeparator())
3426 QKeySequence sequence = QKeySequence::mnemonic(act->text());
3427 int key = sequence[0].toCombined() & 0xffff;
3428 if (key == c.unicode()) {
3432 if (act == d->currentAction)
3433 currentSelected = act;
3434 else if (!firstAfterCurrent && currentSelected)
3435 firstAfterCurrent = act;
3438 if (clashCount == 1)
3439 activateAction =
true;
3440 if (clashCount >= 1) {
3441 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
3444 nextAction = firstAfterCurrent;
3449 key_consumed =
true;
3451 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter,
false);
3452 d->setCurrentAction(nextAction, 0, QMenuPrivate::SelectedFromElsewhere, QMenuPrivate::SelectionDirection::Down,
true);
3453 if (!nextAction->menu() && activateAction) {
3455 d->activateAction(nextAction, QAction::Trigger);
3459 if (!key_consumed) {
3460#if QT_CONFIG(menubar)
3461 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
3462 QAction *oldAct = mb->d_func()->currentAction;
3463 QCoreApplication::sendEvent(mb, e);
3464 if (mb->d_func()->currentAction != oldAct)
3465 key_consumed =
true;
3471 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
3472 QApplication::beep();
3569void QMenu::actionEvent(QActionEvent *e)
3573 setAttribute(Qt::WA_Resized,
false);
3575 d->tornPopup->syncWithMenu(
this, e);
3576 if (e->type() == QEvent::ActionAdded) {
3579#if QT_CONFIG(menubar)
3580 && !qobject_cast<QMenuBar*>(e->action()->parent())
3585 connect(e->action(), SIGNAL(triggered()),
this, SLOT(_q_actionTriggered()), Qt::UniqueConnection);
3586 connect(e->action(), SIGNAL(hovered()),
this, SLOT(_q_actionHovered()), Qt::UniqueConnection);
3588 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3589 QWidget *widget = wa->requestWidget(
this);
3591 d->widgetItems.insert(wa, widget);
3593 if (!d->scrollUpTearOffItem)
3594 d->scrollUpTearOffItem =
3595 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollUp, d,
this);
3596 if (!d->scrollDownItem)
3598 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollDown, d,
this);
3602 }
else if (e->type() == QEvent::ActionRemoved) {
3603 e->action()->disconnect(
this);
3604 if (e->action() == d->currentAction)
3605 d->currentAction =
nullptr;
3606 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3607 if (QWidget *widget = d->widgetItems.value(wa))
3608 wa->releaseWidget(widget);
3610 d->widgetItems.remove(
static_cast<QAction *>(e->action()));
3613 if (!d->platformMenu.isNull()) {
3614 auto action =
static_cast<QAction *>(e->action());
3615 if (e->type() == QEvent::ActionAdded) {
3616 QPlatformMenuItem *beforeItem = e->before()
3617 ? d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->before()))
3619 d->insertActionInPlatformMenu(action, beforeItem);
3620 }
else if (e->type() == QEvent::ActionRemoved) {
3621 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3622 d->platformMenu->removeMenuItem(menuItem);
3624 }
else if (e->type() == QEvent::ActionChanged) {
3625 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3627 d->copyActionToPlatformItem(action, menuItem);
3628 d->platformMenu->syncMenuItem(menuItem);
3632 d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators);