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;
1592void QMenu::initStyleOption(QStyleOptionMenuItem *option,
const QAction *action)
const
1594 if (!option || !action)
1598 option->initFrom(
this);
1599 option->palette = palette();
1600 option->state = QStyle::State_None;
1602 if (window()->isActiveWindow())
1603 option->state |= QStyle::State_Active;
1604 if (isEnabled() && action->isEnabled()
1605 && (!action->menu() || action->menu()->isEnabled()))
1606 option->state |= QStyle::State_Enabled;
1608 option->palette.setCurrentColorGroup(QPalette::Disabled);
1610 option->font = action->font().resolve(font());
1611 option->fontMetrics = QFontMetrics(option->font);
1613 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1614 option->state |= QStyle::State_Selected
1615 | (QMenuPrivate::mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1618 option->menuHasCheckableItems = d->hasCheckableItems;
1619 if (!action->isCheckable()) {
1620 option->checkType = QStyleOptionMenuItem::NotCheckable;
1622 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1623 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1624 option->checked = action->isChecked();
1627 option->menuItemType = QStyleOptionMenuItem::SubMenu;
1628 else if (action->isSeparator())
1629 option->menuItemType = QStyleOptionMenuItem::Separator;
1630 else if (d->defaultAction == action)
1631 option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1633 option->menuItemType = QStyleOptionMenuItem::Normal;
1634 if (action->isIconVisibleInMenu())
1635 option->icon = action->icon();
1636 QString textAndAccel = action->text();
1637#ifndef QT_NO_SHORTCUT
1638 if ((action->isShortcutVisibleInContextMenu() || !d->isContextMenu())
1639 && textAndAccel.indexOf(u'\t') == -1) {
1640 QKeySequence seq = action->shortcut();
1642 textAndAccel += u'\t' + seq.toString(QKeySequence::NativeText);
1645 option->text = textAndAccel;
1646 option->reservedShortcutWidth = d->tabWidth;
1647 option->maxIconWidth = d->maxIconWidth;
1648 option->menuRect = rect();
2309void QMenuPrivate::popup(
const QPoint &p, QAction *atAction, PositionFunction positionFunction)
2312 popupScreen = QGuiApplication::screenAt(p);
2313 QScopeGuard popupScreenGuard([
this](){ popupScreen.clear(); });
2326 q->ensurePolished();
2330 bool screenSet =
false;
2331 QScreen *screen = topData()->initialScreen;
2333 if (setScreen(screen))
2336 }
else if (QMenu *parentMenu = qobject_cast<QMenu *>(parent)) {
2339 if (setScreen(parentMenu->screen()))
2343 if (!screenSet && setScreenForPoint(p))
2358 if (!windowHandle()) {
2362#if QT_CONFIG(menubar)
2365 q->setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(topCausedWidget()) !=
nullptr);
2368 emit q->aboutToShow();
2372#if QT_CONFIG(graphicsview)
2373 bool isEmbedded = !bypassGraphicsProxyWidget(q) && QMenuPrivate::nearestGraphicsProxyWidget(q);
2375 screen = popupGeometry();
2378 screen = popupGeometry(QGuiApplication::screenAt(p));
2382 QPushButton *causedButton = qobject_cast<QPushButton*>(causedPopup.widget);
2383 if (actionListChanged && causedButton)
2384 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
2387 popupScreen = QGuiApplication::screenAt(pos);
2389 const QSize menuSizeHint(q->sizeHint());
2390 QSize size = menuSizeHint;
2392 if (positionFunction)
2393 pos = positionFunction(menuSizeHint);
2395 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth,
nullptr, q);
2396 bool adjustToDesktop = !q->window()->testAttribute(Qt::WA_DontShowOnScreen);
2399 if ((size.height() > screen.height() || size.width() > screen.width()) ||
2401 (ncols >1 && size.height() < screen.height())) {
2402 size.setWidth(qMin(menuSizeHint.width(), screen.width() - desktopFrame * 2));
2403 size.setHeight(qMin(menuSizeHint.height(), screen.height() - desktopFrame * 2));
2404 adjustToDesktop =
true;
2407#ifdef QT_KEYPAD_NAVIGATION
2408 if (!atAction && QApplicationPrivate::keypadNavigationEnabled()) {
2410 if (defaultAction && defaultAction->isEnabled()) {
2411 atAction = defaultAction;
2414 for (QAction *action : std::as_const(actions))
2415 if (action->isEnabled()) {
2420 currentAction = atAction;
2424 pos.setY(screen.top() + desktopFrame);
2425 }
else if (atAction) {
2426 for (
int i = 0, above_height = 0; i < actions.size(); i++) {
2427 QAction *action = actions.at(i);
2428 if (action == atAction) {
2429 int newY = pos.y() - above_height;
2430 if (scroll && newY < desktopFrame) {
2431 scroll->scrollFlags = scroll->scrollFlags
2434 newY = desktopFrame;
2438 if (scroll && scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone
2439 && !q->style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll,
nullptr, q)) {
2441 for (
int i2 = i; i2 < actionRects.size(); i2++)
2442 below_height += actionRects.at(i2).height();
2443 size.setHeight(below_height);
2447 above_height += actionRects.at(i).height();
2454 const auto rectIsNull = [](
const QRect &rect) {
return rect.isNull(); };
2455 if (q->childrenRect().isEmpty()
2456 && std::all_of(actionRects.cbegin(), actionRects.cend(), rectIsNull)) {
2458 syncAction =
nullptr;
2464 const QPoint mouse = QGuiApplicationPrivate::lastCursorPosition.toPoint();
2465 mousePopupPos = QGuiApplicationPrivate::lastCursorPosition;
2466 const bool snapToMouse = !causedPopup.widget && (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
2468 if (adjustToDesktop) {
2470 if (q->isRightToLeft()) {
2472 pos.setX(mouse.x() - size.width());
2474#if QT_CONFIG(menubar)
2476 if (qobject_cast<QMenuBar*>(causedPopup.widget) || qobject_cast<QMenu*>(causedPopup.widget))
2477 pos.rx() -= size.width();
2480 if (pos.x() < screen.left() + desktopFrame)
2481 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
2482 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2483 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
2485 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
2486 pos.setX(screen.right() - desktopFrame - size.width() + 1);
2487 if (pos.x() < screen.left() + desktopFrame)
2488 pos.setX(screen.left() + desktopFrame);
2490 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
2492 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2494 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
2497 if (pos.y() < screen.top() + desktopFrame)
2498 pos.setY(screen.top() + desktopFrame);
2499 if (pos.y() + menuSizeHint.height() - 1 > screen.bottom() - desktopFrame) {
2502 int y = qMax(screen.y(),pos.y());
2503 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
2506 pos.setY(screen.bottom() - size.height() + 1);
2511 const int subMenuOffset = q->style()->pixelMetric(QStyle::PM_SubMenuOverlap,
nullptr, q);
2512 QMenu *caused = qobject_cast<QMenu*>(causedPopup.widget);
2513 if (caused && caused->geometry().width() + menuSizeHint.width() + subMenuOffset < screen.width()) {
2514 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
2515 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
2516 parentActionRect.moveTopLeft(actionTopLeft);
2517 if (q->isRightToLeft()) {
2518 if ((pos.x() + menuSizeHint.width() > parentActionRect.left() - subMenuOffset)
2519 && (pos.x() < parentActionRect.right()))
2521 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2522 if (pos.x() < screen.x())
2523 pos.rx() = parentActionRect.right();
2524 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2525 pos.rx() = screen.x();
2528 if ((pos.x() < parentActionRect.right() + subMenuOffset)
2529 && (pos.x() + menuSizeHint.width() > parentActionRect.left()))
2531 pos.rx() = parentActionRect.right();
2532 if (pos.x() + menuSizeHint.width() > screen.x() + screen.width())
2533 pos.rx() = parentActionRect.left() - menuSizeHint.width();
2534 if (pos.x() < screen.x())
2535 pos.rx() = screen.x() + screen.width() - menuSizeHint.width();
2539 popupScreen = QGuiApplication::screenAt(pos);
2540 q->setGeometry(QRect(pos, size));
2542#if QT_CONFIG(wayland)
2544 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(q->windowHandle()->handle())) {
2546 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2547 waylandWindow->setParentControlGeometry(causedButton->geometry());
2548 }
else if (caused) {
2549 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu);
2550 waylandWindow->setParentControlGeometry(caused->d_func()->actionRect(caused->d_func()->currentAction));
2551 }
else if (
auto menubar = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2552 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
2553 waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action));
2558#if QT_CONFIG(effects)
2559 int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
2560 int vGuess = QEffects::DownScroll;
2561 if (q->isRightToLeft()) {
2562 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
2563 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 > causedPopup.widget->x()))
2564 hGuess = QEffects::RightScroll;
2566 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
2567 (qobject_cast<QMenu*>(causedPopup.widget) && pos.x() + size.width() / 2 < causedPopup.widget->x()))
2568 hGuess = QEffects::LeftScroll;
2571#if QT_CONFIG(menubar)
2572 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
2573 (qobject_cast<QMenuBar*>(causedPopup.widget) &&
2574 pos.y() + size.width() / 2 < causedPopup.widget->mapToGlobal(causedPopup.widget->pos()).y()))
2575 vGuess = QEffects::UpScroll;
2577 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
2578 bool doChildEffects =
true;
2579#if QT_CONFIG(menubar)
2580 if (QMenuBar *mb = qobject_cast<QMenuBar*>(causedPopup.widget)) {
2581 doChildEffects = mb->d_func()->doChildEffects;
2582 mb->d_func()->doChildEffects =
false;
2585 if (QMenu *m = qobject_cast<QMenu*>(causedPopup.widget)) {
2586 doChildEffects = m->d_func()->doChildEffects;
2587 m->d_func()->doChildEffects =
false;
2590 if (doChildEffects) {
2591 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
2593 else if (causedPopup.widget)
2594 qScrollEffect(q, qobject_cast<QMenu*>(causedPopup.widget) ? hGuess : vGuess);
2596 qScrollEffect(q, hGuess | vGuess);
2599 qFadeEffect(
nullptr);
2600 qScrollEffect(
nullptr);
2610#if QT_CONFIG(accessibility)
2611 QAccessibleEvent event(q, QAccessible::PopupMenuStart);
2612 QAccessible::updateAccessibility(&event);
2763void QMenu::paintEvent(QPaintEvent *e)
2766 d->updateActionRects();
2767 QStylePainter p(
this);
2768 QRegion emptyArea = QRegion(rect());
2770 QStyleOptionMenuItem menuOpt;
2771 menuOpt.initFrom(
this);
2772 menuOpt.state = QStyle::State_None;
2773 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2774 menuOpt.maxIconWidth = 0;
2775 menuOpt.reservedShortcutWidth = 0;
2776 p.drawPrimitive(QStyle::PE_PanelMenu, menuOpt);
2779 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth,
nullptr,
this);
2780 const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin,
nullptr,
this);
2781 const int vmargin = style()->pixelMetric(QStyle::PM_MenuVMargin,
nullptr,
this);
2783 QRect scrollUpRect, scrollDownRect;
2784 const int leftmargin = fw + hmargin + d->leftmargin;
2785 const int topmargin = fw + vmargin + d->topmargin;
2786 const int bottommargin = fw + vmargin + d->bottommargin;
2787 const int contentWidth = width() - (fw + hmargin) * 2 - d->leftmargin - d->rightmargin;
2789 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2790 scrollUpRect.setRect(leftmargin, topmargin, contentWidth, d->scrollerHeight());
2792 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2793 scrollDownRect.setRect(leftmargin, height() - d->scrollerHeight() - bottommargin,
2794 contentWidth, d->scrollerHeight());
2800 tearOffRect.setRect(leftmargin, topmargin, contentWidth,
2801 style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this));
2802 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2803 tearOffRect.translate(0, d->scrollerHeight());
2807 QRect scrollUpTearOffRect = scrollUpRect.united(tearOffRect);
2808 for (
int i = 0; i < d->actions.size(); ++i) {
2809 QAction *action = d->actions.at(i);
2810 QRect actionRect = d->actionRects.at(i);
2811 if (!e->rect().intersects(actionRect)
2812 || d->widgetItems.value(action))
2815 emptyArea -= QRegion(actionRect);
2817 QRect adjustedActionRect = actionRect;
2818 if (!scrollUpTearOffRect.isEmpty() && adjustedActionRect.bottom() <= scrollUpTearOffRect.top())
2821 if (!scrollDownRect.isEmpty() && adjustedActionRect.top() >= scrollDownRect.bottom())
2824 if (adjustedActionRect.intersects(scrollUpTearOffRect)) {
2825 if (adjustedActionRect.bottom() <= scrollUpTearOffRect.bottom())
2828 adjustedActionRect.setTop(scrollUpTearOffRect.bottom()+1);
2831 if (adjustedActionRect.intersects(scrollDownRect)) {
2832 if (adjustedActionRect.top() >= scrollDownRect.top())
2835 adjustedActionRect.setBottom(scrollDownRect.top()-1);
2838 QRegion adjustedActionReg(adjustedActionRect);
2839 p.setClipRegion(adjustedActionReg);
2841 QStyleOptionMenuItem opt;
2842 initStyleOption(&opt, action);
2843 opt.rect = actionRect;
2844 p.drawControl(QStyle::CE_MenuItem, opt);
2847 emptyArea -= QRegion(scrollUpTearOffRect);
2848 emptyArea -= QRegion(scrollDownRect);
2850 if (d->scrollUpTearOffItem || d->scrollDownItem) {
2851 if (d->scrollUpTearOffItem)
2852 d->scrollUpTearOffItem->updateScrollerRects(scrollUpTearOffRect);
2853 if (d->scrollDownItem)
2854 d->scrollDownItem->updateScrollerRects(scrollDownRect);
2857 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollUp, scrollUpRect);
2858 d->drawScroller(&p, QMenuPrivate::ScrollerTearOffItem::ScrollDown, scrollDownRect);
2860 d->drawTearOff(&p, tearOffRect);
2866 borderReg += QRect(0, 0, fw, height());
2867 borderReg += QRect(width()-fw, 0, fw, height());
2868 borderReg += QRect(0, 0, width(), fw);
2869 borderReg += QRect(0, height()-fw, width(), fw);
2870 p.setClipRegion(borderReg);
2871 emptyArea -= borderReg;
2872 QStyleOptionFrame frame;
2873 frame.rect = rect();
2874 frame.palette = palette();
2875 frame.state = QStyle::State_None;
2876 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame,
this);
2877 frame.midLineWidth = 0;
2878 p.drawPrimitive(QStyle::PE_FrameMenu, frame);
2882 p.setClipRegion(emptyArea);
2883 menuOpt.state = QStyle::State_None;
2884 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2885 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2886 menuOpt.rect = rect();
2887 menuOpt.menuRect = rect();
2888 p.drawControl(QStyle::CE_MenuEmptyArea, menuOpt);
3013bool QMenu::event(QEvent *e)
3016 switch (e->type()) {
3017 case QEvent::Polish:
3018 d->updateLayoutDirection();
3020 case QEvent::ShortcutOverride: {
3021 QKeyEvent *kev =
static_cast<QKeyEvent *>(e);
3022 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
3023 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
3024 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
3025#ifndef QT_NO_SHORTCUT
3026 || kev->matches(QKeySequence::Cancel)
3034 case QEvent::KeyPress: {
3035 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
3036 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
3041 case QEvent::MouseButtonPress:
3042 case QEvent::ContextMenu: {
3043 bool canPopup =
true;
3044 if (e->type() == QEvent::MouseButtonPress)
3045 canPopup = (
static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton);
3046 if (canPopup && d->delayState.timer.isActive()) {
3047 d->delayState.stop();
3048 internalDelayedPopup();
3052 case QEvent::Resize: {
3053 QStyleHintReturnMask menuMask;
3054 QStyleOption option;
3055 option.initFrom(
this);
3056 if (style()->styleHint(QStyle::SH_Menu_Mask, &option,
this, &menuMask)) {
3057 setMask(menuMask.region);
3060 d->updateActionRects();
3063 QMenuPrivate::mouseDown =
nullptr;
3064 d->updateActionRects();
3065 d->sloppyState.reset();
3066 if (d->currentAction)
3067 d->popupAction(d->currentAction, 0,
false);
3068 if (isWindow() && window() && window()->windowHandle())
3069 window()->windowHandle()->setTransientParent(d->transientParentWindow());
3071#if QT_CONFIG(tooltip)
3072 case QEvent::ToolTip:
3073 if (d->toolTipsVisible) {
3074 const QHelpEvent *ev =
static_cast<
const QHelpEvent*>(e);
3075 if (
const QAction *action = actionAt(ev->pos())) {
3076 const QString toolTip = action->d_func()->tooltip;
3077 if (!toolTip.isEmpty())
3078 QToolTip::showText(ev->globalPos(), toolTip,
this);
3080 QToolTip::hideText();
3086#if QT_CONFIG(whatsthis)
3087 case QEvent::QueryWhatsThis:
3088 e->setAccepted(d->whatsThis.size());
3089 if (QAction *action = d->actionAt(
static_cast<QHelpEvent*>(e)->pos())) {
3090 if (action->whatsThis().size() || action->menu())
3098 return QWidget::event(e);
3115void QMenu::keyPressEvent(QKeyEvent *e)
3118 d->updateActionRects();
3120 if (isRightToLeft()) {
3121 if (key == Qt::Key_Left)
3122 key = Qt::Key_Right;
3123 else if (key == Qt::Key_Right)
3127 if (key == Qt::Key_Tab)
3129 if (key == Qt::Key_Backtab)
3133 bool key_consumed =
false;
3136 key_consumed =
true;
3138 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3141 key_consumed =
true;
3143 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3145 case Qt::Key_PageUp:
3146 key_consumed =
true;
3147 if (d->currentAction && d->scroll) {
3148 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3149 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp,
true,
true);
3151 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop,
true);
3154 case Qt::Key_PageDown:
3155 key_consumed =
true;
3156 if (d->currentAction && d->scroll) {
3157 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
3158 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown,
true,
true);
3160 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom,
true);
3164 case Qt::Key_Down: {
3165 key_consumed =
true;
3166 QAction *nextAction =
nullptr;
3167 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
3168 if (!d->currentAction) {
3169 if (key == Qt::Key_Down) {
3170 for(
int i = 0; i < d->actions.size(); ++i) {
3171 if (d->actionRects.at(i).isNull())
3173 QAction *act = d->actions.at(i);
3174 if (d->considerAction(act)) {
3180 for(
int i = d->actions.size()-1; i >= 0; --i) {
3181 if (d->actionRects.at(i).isNull())
3183 QAction *act = d->actions.at(i);
3184 if (d->considerAction(act)) {
3191 for(
int i = 0, y = 0; !nextAction && i < d->actions.size(); i++) {
3192 QAction *act = d->actions.at(i);
3193 if (act == d->currentAction) {
3194 if (key == Qt::Key_Up) {
3195 for(
int next_i = i-1;
true; next_i--) {
3197 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3200 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3201 next_i = d->actionRects.size()-1;
3203 QAction *next = d->actions.at(next_i);
3204 if (next == d->currentAction)
3206 if (d->actionRects.at(next_i).isNull())
3208 if (!d->considerAction(next))
3211 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
3212 int topVisible = d->scrollerHeight();
3214 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3215 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
3216 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3220 if (!nextAction && d->tearoff)
3221 d->tearoffHighlighted = 1;
3223 y += d->actionRects.at(i).height();
3224 for(
int next_i = i+1;
true; next_i++) {
3225 if (next_i == d->actionRects.size()) {
3226 if (!style()->styleHint(QStyle::SH_Menu_SelectionWrap,
nullptr,
this))
3229 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
3232 QAction *next = d->actions.at(next_i);
3233 if (next == d->currentAction)
3235 if (d->actionRects.at(next_i).isNull())
3237 if (!d->considerAction(next))
3240 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
3241 int bottomVisible = height() - d->scrollerHeight();
3242 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
3243 bottomVisible -= d->scrollerHeight();
3245 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight,
nullptr,
this);
3246 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
3247 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
3254 y += d->actionRects.at(i).height();
3258 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
3259 d->scroll->scrollTimer.stop();
3260 d->scrollMenu(nextAction, scroll_loc);
3262 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, key == Qt::Key_Up ? QMenuPrivate::SelectionDirection::Up : QMenuPrivate::SelectionDirection::Down);
3267 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
3268 d->popupAction(d->currentAction, 0,
true);
3269 key_consumed =
true;
3273 case Qt::Key_Left: {
3274 if (d->currentAction && !d->scroll) {
3275 QAction *nextAction =
nullptr;
3276 if (key == Qt::Key_Left) {
3277 QRect actionR = d->actionRect(d->currentAction);
3278 for(
int x = actionR.left()-1; !nextAction && x >= 0; x--)
3279 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3281 QRect actionR = d->actionRect(d->currentAction);
3282 for(
int x = actionR.right()+1; !nextAction && x < width(); x++)
3283 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
3286 d->setCurrentAction(nextAction, -1, QMenuPrivate::SelectedFromKeyboard, QMenuPrivate::SelectionDirection::Up);
3287 key_consumed =
true;
3290 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
3291 QPointer<QWidget> caused = d->causedPopup.widget;
3295 key_consumed =
true;
3303 key_consumed =
true;
3304 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation,
nullptr,
this))
3307#if QT_CONFIG(menubar)
3308 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
3309 mb->d_func()->setKeyboardMode(
false);
3316 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem,
nullptr,
this))
3320#ifdef QT_KEYPAD_NAVIGATION
3321 case Qt::Key_Select:
3323 case Qt::Key_Return:
3324 case Qt::Key_Enter: {
3325 if (!d->currentAction) {
3326 d->setFirstActionActive();
3327 key_consumed =
true;
3333 if (d->currentAction->menu())
3334 d->popupAction(d->currentAction, 0,
true);
3336 d->activateAction(d->currentAction, QAction::Trigger);
3337 key_consumed =
true;
3340#if QT_CONFIG(whatsthis)
3342 if (!d->currentAction || d->currentAction->whatsThis().isNull())
3344 QWhatsThis::enterWhatsThisMode();
3345 d->activateAction(d->currentAction, QAction::Trigger);
3349 key_consumed =
false;
3352 if (!key_consumed && (
3354#ifndef QT_NO_SHORTCUT
3355 || e->matches(QKeySequence::Cancel)
3357#ifdef QT_KEYPAD_NAVIGATION
3358 || e->key() == Qt::Key_Back
3361 key_consumed =
true;
3367 QPointer<QWidget> caused = d->causedPopup.widget;
3369#if QT_CONFIG(menubar)
3370 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
3371 mb->d_func()->setCurrentAction(d->menuAction);
3372 mb->d_func()->setKeyboardMode(
true);
3378 if (!key_consumed) {
3379 const Qt::KeyboardModifiers modifiers = e->modifiers();
3380 if ((!modifiers || modifiers == Qt::AltModifier || modifiers == Qt::ShiftModifier
3381 || modifiers == Qt::KeypadModifier
3382 || modifiers == (Qt::KeypadModifier | Qt::AltModifier))
3383 && e->text().size() == 1) {
3384 bool activateAction =
false;
3385 QAction *nextAction =
nullptr;
3386 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch,
nullptr,
this) && !e->modifiers()) {
3387 int best_match_count = 0;
3388 d->searchBufferTimer.start(2000,
this);
3389 d->searchBuffer += e->text();
3390 for(
int i = 0; i < d->actions.size(); ++i) {
3391 int match_count = 0;
3392 if (d->actionRects.at(i).isNull())
3394 QAction *act = d->actions.at(i);
3395 const QString act_text = act->text();
3396 for(
int c = 0; c < d->searchBuffer.size(); ++c) {
3397 if (act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
3400 if (match_count > best_match_count) {
3401 best_match_count = match_count;
3406#ifndef QT_NO_SHORTCUT
3409 QAction *first =
nullptr, *currentSelected =
nullptr, *firstAfterCurrent =
nullptr;
3410 QChar c = e->text().at(0).toUpper();
3411 for(
int i = 0; i < d->actions.size(); ++i) {
3412 if (d->actionRects.at(i).isNull())
3414 QAction *act = d->actions.at(i);
3415 if (!act->isEnabled() || act->isSeparator())
3417 QKeySequence sequence = QKeySequence::mnemonic(act->text());
3418 int key = sequence[0].toCombined() & 0xffff;
3419 if (key == c.unicode()) {
3423 if (act == d->currentAction)
3424 currentSelected = act;
3425 else if (!firstAfterCurrent && currentSelected)
3426 firstAfterCurrent = act;
3429 if (clashCount == 1)
3430 activateAction =
true;
3431 if (clashCount >= 1) {
3432 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
3435 nextAction = firstAfterCurrent;
3440 key_consumed =
true;
3442 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter,
false);
3443 d->setCurrentAction(nextAction, 0, QMenuPrivate::SelectedFromElsewhere, QMenuPrivate::SelectionDirection::Down,
true);
3444 if (!nextAction->menu() && activateAction) {
3446 d->activateAction(nextAction, QAction::Trigger);
3450 if (!key_consumed) {
3451#if QT_CONFIG(menubar)
3452 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
3453 QAction *oldAct = mb->d_func()->currentAction;
3454 QCoreApplication::sendEvent(mb, e);
3455 if (mb->d_func()->currentAction != oldAct)
3456 key_consumed =
true;
3462 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
3463 QApplication::beep();
3560void QMenu::actionEvent(QActionEvent *e)
3564 setAttribute(Qt::WA_Resized,
false);
3566 d->tornPopup->syncWithMenu(
this, e);
3567 if (e->type() == QEvent::ActionAdded) {
3570#if QT_CONFIG(menubar)
3571 && !qobject_cast<QMenuBar*>(e->action()->parent())
3576 connect(e->action(), SIGNAL(triggered()),
this, SLOT(_q_actionTriggered()), Qt::UniqueConnection);
3577 connect(e->action(), SIGNAL(hovered()),
this, SLOT(_q_actionHovered()), Qt::UniqueConnection);
3579 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3580 QWidget *widget = wa->requestWidget(
this);
3582 d->widgetItems.insert(wa, widget);
3584 if (!d->scrollUpTearOffItem)
3585 d->scrollUpTearOffItem =
3586 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollUp, d,
this);
3587 if (!d->scrollDownItem)
3589 new QMenuPrivate::ScrollerTearOffItem(QMenuPrivate::ScrollerTearOffItem::ScrollDown, d,
this);
3593 }
else if (e->type() == QEvent::ActionRemoved) {
3594 e->action()->disconnect(
this);
3595 if (e->action() == d->currentAction)
3596 d->currentAction =
nullptr;
3597 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
3598 if (QWidget *widget = d->widgetItems.value(wa))
3599 wa->releaseWidget(widget);
3601 d->widgetItems.remove(
static_cast<QAction *>(e->action()));
3604 if (!d->platformMenu.isNull()) {
3605 auto action =
static_cast<QAction *>(e->action());
3606 if (e->type() == QEvent::ActionAdded) {
3607 QPlatformMenuItem *beforeItem = e->before()
3608 ? d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->before()))
3610 d->insertActionInPlatformMenu(action, beforeItem);
3611 }
else if (e->type() == QEvent::ActionRemoved) {
3612 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3613 d->platformMenu->removeMenuItem(menuItem);
3615 }
else if (e->type() == QEvent::ActionChanged) {
3616 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(
reinterpret_cast<quintptr>(e->action()));
3618 d->copyActionToPlatformItem(action, menuItem);
3619 d->platformMenu->syncMenuItem(menuItem);
3623 d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators);