Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qmenubar.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <qmenubar.h>
6
7#include <qstyle.h>
8#include <qlayout.h>
9#include <qapplication.h>
10#if QT_CONFIG(accessibility)
11# include <qaccessible.h>
12#endif
13#include <qpainter.h>
14#include <qstylepainter.h>
15#include <qevent.h>
16#if QT_CONFIG(mainwindow)
17#include <qmainwindow.h>
18#endif
19#if QT_CONFIG(toolbutton)
20#include <qtoolbutton.h>
21#endif
22#if QT_CONFIG(whatsthis)
23#include <qwhatsthis.h>
24#endif
25#include <qpa/qplatformtheme.h>
26#include "private/qguiapplication_p.h"
27#include "qpa/qplatformintegration.h"
28
29#include "qmenu_p.h"
30#include "qmenubar_p.h"
31#include <private/qscreen_p.h>
32#include "qdebug.h"
33
35
36using namespace Qt::StringLiterals;
37
39{
40public:
41 explicit QMenuBarExtension(QWidget *parent);
42
43 QSize sizeHint() const override;
44 void paintEvent(QPaintEvent *) override;
45};
46
49{
50 setObjectName("qt_menubar_ext_button"_L1);
51 setAutoRaise(true);
52#if QT_CONFIG(menu)
53 setPopupMode(QToolButton::InstantPopup);
54#endif
55 setIcon(style()->standardIcon(QStyle::SP_ToolBarHorizontalExtensionButton, nullptr, parentWidget()));
56}
57
58void QMenuBarExtension::paintEvent(QPaintEvent *)
59{
60 QStylePainter p(this);
61 QStyleOptionToolButton opt;
62 initStyleOption(&opt);
63 // We do not need to draw both extension arrows
64 opt.features &= ~QStyleOptionToolButton::HasMenu;
65 p.drawComplexControl(QStyle::CC_ToolButton, opt);
66}
67
68
70{
71 int ext = style()->pixelMetric(QStyle::PM_ToolBarExtensionExtent, nullptr, parentWidget());
72 return QSize(ext, ext);
73}
74
75
76/*!
77 \internal
78*/
79QAction *QMenuBarPrivate::actionAt(QPoint p) const
80{
81 for (auto *action : actions) {
82 if (actionRect(action).contains(p))
83 return action;
84 }
85 return nullptr;
86}
87
88QRect QMenuBarPrivate::menuRect(bool extVisible) const
89{
90 Q_Q(const QMenuBar);
91
92 int hmargin = q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, q);
93 QRect result = q->rect();
94 result.adjust(hmargin, 0, -hmargin, 0);
95
96 if (extVisible) {
97 if (q->isRightToLeft())
98 result.setLeft(result.left() + extension->sizeHint().width());
99 else
100 result.setWidth(result.width() - extension->sizeHint().width());
101 }
102
103 if (leftWidget && leftWidget->isVisible()) {
104 QSize sz = leftWidget->sizeHint();
105 if (q->isRightToLeft())
106 result.setRight(result.right() - sz.width());
107 else
108 result.setLeft(result.left() + sz.width());
109 }
110
111 if (rightWidget && rightWidget->isVisible()) {
112 QSize sz = rightWidget->sizeHint();
113 if (q->isRightToLeft())
114 result.setLeft(result.left() + sz.width());
115 else
116 result.setRight(result.right() - sz.width());
117 }
118
119 return result;
120}
121
122bool QMenuBarPrivate::isVisible(QAction *action)
123{
124 return !hiddenActions.contains(action);
125}
126
128{
129 Q_Q(QMenuBar);
130 if (!itemsDirty)
131 return;
132 int q_width = q->width()-(q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, q)*2);
133 int q_start = -1;
134 if (leftWidget || rightWidget) {
135 int vmargin = q->style()->pixelMetric(QStyle::PM_MenuBarVMargin, nullptr, q)
136 + q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, q);
137 int hmargin = q->style()->pixelMetric(QStyle::PM_MenuBarHMargin, nullptr, q)
138 + q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, q);
139 if (leftWidget && leftWidget->isVisible()) {
140 QSize sz = leftWidget->sizeHint();
141 q_width -= sz.width();
142 q_start = sz.width();
143 QPoint pos(hmargin, (q->height() - leftWidget->height()) / 2);
144 QRect vRect = QStyle::visualRect(q->layoutDirection(), q->rect(), QRect(pos, sz));
145 leftWidget->setGeometry(vRect);
146 }
147 if (rightWidget && rightWidget->isVisible()) {
148 QSize sz = rightWidget->sizeHint();
149 q_width -= sz.width();
150 QPoint pos(q->width() - sz.width() - hmargin, vmargin);
151 QRect vRect = QStyle::visualRect(q->layoutDirection(), q->rect(), QRect(pos, sz));
152 rightWidget->setGeometry(vRect);
153 }
154 }
155
156#ifdef Q_OS_MAC
157 if (q->isNativeMenuBar()) {//nothing to see here folks, move along..
158 itemsDirty = false;
159 return;
160 }
161#endif
162 calcActionRects(q_width, q_start);
163 currentAction = nullptr;
164#ifndef QT_NO_SHORTCUT
165 if (itemsDirty) {
166 for (const auto shortcutId : std::as_const(shortcutIndexMap))
167 q->releaseShortcut(shortcutId);
168 shortcutIndexMap.clear();
169 shortcutIndexMap.reserve(actions.size());
170 for (const auto *action : std::as_const(actions))
171 shortcutIndexMap.append(q->grabShortcut(QKeySequence::mnemonic(action->text())));
172 }
173#endif
174 itemsDirty = false;
175
176 hiddenActions.clear();
177 //this is the menu rectangle without any extension
178 QRect menuRect = this->menuRect(false);
179
180 //we try to see if the actions will fit there
181 bool hasHiddenActions = false;
182 for (const auto &rect : std::as_const(actionRects)) {
183 if (rect.isValid() && !menuRect.contains(rect)) {
184 hasHiddenActions = true;
185 break;
186 }
187 }
188
189 //...and if not, determine the ones that fit on the menu with the extension visible
190 if (hasHiddenActions) {
191 menuRect = this->menuRect(true);
192 for (qsizetype i = 0; i < actions.size(); ++i) {
193 const QRect &rect = actionRects.at(i);
194 if (rect.isValid() && !menuRect.contains(rect)) {
195 hiddenActions.append(actions.at(i));
196 }
197 }
198 }
199
200 if (hiddenActions.size() > 0) {
201 QMenu *pop = extension->menu();
202 if (!pop) {
203 pop = new QMenu(q);
204 extension->setMenu(pop);
205 }
206 pop->clear();
207 pop->addActions(hiddenActions);
208
209 int vmargin = q->style()->pixelMetric(QStyle::PM_MenuBarVMargin, nullptr, q);
210 int x = q->isRightToLeft()
211 ? menuRect.left() - extension->sizeHint().width() + 1
212 : menuRect.right();
213 extension->setGeometry(x, vmargin, extension->sizeHint().width(), menuRect.height() - vmargin*2);
214 extension->show();
215 } else {
216 extension->hide();
217 }
218 q->updateGeometry();
219}
220
221QRect QMenuBarPrivate::actionRect(QAction *act) const
222{
223 const qsizetype index = actions.indexOf(act);
224
225 //makes sure the geometries are up-to-date
226 const_cast<QMenuBarPrivate*>(this)->updateGeometries();
227
228 if (index < 0 || index >= actionRects.size())
229 return QRect(); // that can happen in case of native menubar
230
231 return actionRects.at(index);
232}
233
235{
236 if (!currentAction) {
238 qsizetype index = 0;
239 while (index < actions.size() && actionRects.at(index).isNull()) ++index;
240 if (index < actions.size())
241 setCurrentAction(actions.at(index));
242 }
243}
244
246{
247 Q_Q(QMenuBar);
248 if (b && !q->style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, nullptr, q)) {
249 setCurrentAction(nullptr);
250 return;
251 }
252 keyboardState = b;
253 if (b) {
254 QWidget *fw = QApplication::focusWidget();
255 if (fw && fw != q && fw->window() != QApplication::activePopupWidget())
256 keyboardFocusWidget = fw;
258 q->setFocus(Qt::MenuBarFocusReason);
259 } else {
260 if (!popupState)
261 setCurrentAction(nullptr);
262 if (keyboardFocusWidget) {
263 if (QApplication::focusWidget() == q)
264 keyboardFocusWidget->setFocus(Qt::MenuBarFocusReason);
265 keyboardFocusWidget = nullptr;
266 }
267 }
268 q->update();
269}
270
271void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst)
272{
273 Q_Q(QMenuBar);
274 if (!action || !action->menu() || closePopupMode)
275 return;
276 popupState = true;
277 if (action->isEnabled() && action->menu()->isEnabled()) {
278 closePopupMode = 0;
279 activeMenu = action->menu();
280 auto *activeMenuPriv = activeMenu->d_func();
281 activeMenuPriv->causedPopup.widget = q;
282 activeMenuPriv->causedPopup.action = action;
283
284 QRect adjustedActionRect = actionRect(action);
285 QPoint popupPos = adjustedActionRect.bottomLeft() + QPoint(0, 1);
286
287 //we put the popup menu on the screen containing the bottom-center of the action rect
288 QScreen *menubarScreen = q->window()->windowHandle()->screen();
289 QPoint screenTestPos = q->mapToGlobal(popupPos + QPoint(adjustedActionRect.width() / 2, 0));
290 QPointer<QScreen> popupScreen = menubarScreen->virtualSiblingAt(screenTestPos);
291 if (!popupScreen)
292 popupScreen = menubarScreen;
293 std::swap(popupScreen, activeMenuPriv->popupScreen);
294 const QSize popup_size = activeMenu->sizeHint();
295 std::swap(popupScreen, activeMenuPriv->popupScreen);
296
297 // Use screenTestPos.y() for the popup y position. This is the correct global y
298 // consistent with the selected screen in cases where the action rect spans
299 // multiple screens with different scale factors.
300 QPoint pos(q->mapToGlobal(popupPos).x(), screenTestPos.y());
301
302 QRect screenRect = popupScreen->geometry();
303 pos = QPoint(qMax(pos.x(), screenRect.x()), qMax(pos.y(), screenRect.y()));
304 const bool fitUp = (pos.y() - popup_size.height() >= screenRect.top());
305 const bool fitDown = (pos.y() + popup_size.height() <= screenRect.bottom());
306 const bool rtl = q->isRightToLeft();
307 const int actionWidth = adjustedActionRect.width();
308
309 if (!fitUp && !fitDown) { //we should shift the menu
310 bool shouldShiftToRight = !rtl;
311 if (rtl && popup_size.width() > pos.x())
312 shouldShiftToRight = true;
313 else if (actionWidth + popup_size.width() + pos.x() > screenRect.right())
314 shouldShiftToRight = false;
315
316 if (shouldShiftToRight) {
317 pos.rx() += actionWidth + (rtl ? popup_size.width() : 0);
318 } else {
319 //shift to left
320 if (!rtl)
321 pos.rx() -= popup_size.width();
322 }
323 } else if (rtl) {
324 pos.rx() += actionWidth;
325 }
326
327 if (!defaultPopDown || (fitUp && !fitDown))
328 pos.setY(qMax(screenRect.y(), q->mapToGlobal(QPoint(0, adjustedActionRect.top()-popup_size.height())).y()));
329 QMenuPrivate::get(activeMenu)->topData()->initialScreen = popupScreen;
330 activeMenu->popup(pos);
331 if (activateFirst)
332 activeMenu->d_func()->setFirstActionActive();
333 }
334 q->update(actionRect(action));
335}
336
337void QMenuBarPrivate::setCurrentAction(QAction *action, bool popup, bool activateFirst)
338{
339 if (currentAction == action && popup == popupState)
340 return;
341
342 autoReleaseTimer.stop();
343
344 doChildEffects = (popup && !activeMenu);
345 Q_Q(QMenuBar);
346 QWidget *fw = nullptr;
347 if (QMenu *menu = activeMenu) {
348 activeMenu = nullptr;
349 hoverAction = nullptr;
350 if (popup) {
351 fw = q->window()->focusWidget();
352 q->setFocus(Qt::NoFocusReason);
353 }
354 menu->hide();
355 }
356
357 if (currentAction)
358 q->update(actionRect(currentAction));
359
360 popupState = popup;
361#if QT_CONFIG(statustip)
362 QAction *previousAction = currentAction;
363#endif
364 currentAction = action;
365 if (action && action->isEnabled()) {
366 activateAction(action, QAction::Hover);
367 if (popup)
368 popupAction(action, activateFirst);
369 q->update(actionRect(action));
370#if QT_CONFIG(statustip)
371 } else if (previousAction) {
372 QString empty;
373 QStatusTipEvent tip(empty);
374 QCoreApplication::sendEvent(q, &tip);
375#endif
376 }
377 if (fw)
378 fw->setFocus(Qt::NoFocusReason);
379}
380
381void QMenuBarPrivate::calcActionRects(int max_width, int start) const
382{
383 Q_Q(const QMenuBar);
384
385 if (!itemsDirty)
386 return;
387
388 //let's reinitialize the buffer
389 actionRects.resize(actions.size());
390 actionRects.fill(QRect());
391
392 const QStyle *style = q->style();
393
394 const int itemSpacing = style->pixelMetric(QStyle::PM_MenuBarItemSpacing, nullptr, q);
395 int max_item_height = 0, separator = -1, separator_start = 0, separator_len = 0;
396
397 //calculate size
398 const QFontMetrics fm = q->fontMetrics();
399 const int hmargin = style->pixelMetric(QStyle::PM_MenuBarHMargin, nullptr, q),
400 vmargin = style->pixelMetric(QStyle::PM_MenuBarVMargin, nullptr, q),
401 icone = style->pixelMetric(QStyle::PM_SmallIconSize, nullptr, q);
402 for (qsizetype i = 0; i < actions.size(); ++i) {
403 const QAction *action = actions.at(i);
404 if (!action->isVisible())
405 continue;
406
407 QSize sz;
408
409 //calc what I think the size is..
410 if (action->isSeparator()) {
411 if (style->styleHint(QStyle::SH_DrawMenuBarSeparator, nullptr, q))
412 separator = i;
413 continue; //we don't really position these!
414 } else {
415 const QString s = action->text();
416 QIcon is = action->icon();
417 // If an icon is set, only the icon is visible
418 if (!is.isNull())
419 sz = sz.expandedTo(QSize(icone, icone));
420 else if (!s.isEmpty())
421 sz = fm.size(Qt::TextShowMnemonic, s);
422 }
423
424 //let the style modify the above size..
425 QStyleOptionMenuItemV2 opt;
426 q->initStyleOption(&opt, action);
427 sz = q->style()->sizeFromContents(QStyle::CT_MenuBarItem, &opt, sz, q);
428
429 if (!sz.isEmpty()) {
430 { //update the separator state
431 int iWidth = sz.width() + itemSpacing;
432 if (separator == -1)
433 separator_start += iWidth;
434 else
435 separator_len += iWidth;
436 }
437 //maximum height
438 max_item_height = qMax(max_item_height, sz.height());
439 //append
440 actionRects[i] = QRect(0, 0, sz.width(), sz.height());
441 }
442 }
443
444 //calculate position
445 const int fw = q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, q);
446 int x = fw + ((start == -1) ? hmargin : start) + itemSpacing;
447 int y = fw + vmargin;
448 for (qsizetype i = 0; i < actionRects.size(); ++i) {
449 QRect &rect = actionRects[i];
450 if (rect.isNull())
451 continue;
452
453 //resize
454 rect.setHeight(max_item_height);
455
456 //move
457 if (separator != -1 && i >= separator) { //after the separator
458 int left = (max_width - separator_len - hmargin - itemSpacing) + (x - separator_start - hmargin);
459 if (left < separator_start) { //wrap
460 separator_start = x = hmargin;
461 y += max_item_height;
462 }
463 rect.moveLeft(left);
464 } else {
465 rect.moveLeft(x);
466 }
467 rect.moveTop(y);
468
469 //keep moving along..
470 x += rect.width() + itemSpacing;
471
472 //make sure we follow the layout direction
473 rect = QStyle::visualRect(q->layoutDirection(), q->rect(), rect);
474 }
475}
476
477void QMenuBarPrivate::activateAction(QAction *action, QAction::ActionEvent action_e)
478{
479 Q_Q(QMenuBar);
480 if (!action || !action->isEnabled())
481 return;
482 action->activate(action_e);
483 if (action_e == QAction::Hover)
484 action->showStatusText(q);
485
486// if (action_e == QAction::Trigger)
487// emit q->activated(action);
488// else if (action_e == QAction::Hover)
489// emit q->highlighted(action);
490}
491
492
494{
495 Q_Q(QMenuBar);
496 if (QAction *action = qobject_cast<QAction *>(q->sender())) {
497 emit q->triggered(action);
498 }
499}
500
502{
503 Q_Q(QMenuBar);
504 if (QAction *action = qobject_cast<QAction *>(q->sender())) {
505 emit q->hovered(action);
506#if QT_CONFIG(accessibility)
507 if (QAccessible::isActive()) {
508 int actionIndex = actions.indexOf(action);
509 QAccessibleEvent focusEvent(q, QAccessible::Focus);
510 focusEvent.setChild(actionIndex);
511 QAccessible::updateAccessibility(&focusEvent);
512 }
513#endif // QT_CONFIG(accessibility)
514 }
515}
516
517/*!
518 Initialize \a option with the values from the menu bar and information from \a action. This method
519 is useful for subclasses when they need a QStyleOptionMenuItem, but don't want
520 to fill in all the information themselves.
521
522 \sa QStyleOption::initFrom(), QMenu::initStyleOption()
523*/
524void QMenuBar::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
525{
526 if (!option || !action)
527 return;
528 Q_D(const QMenuBar);
529 option->palette = palette();
530 option->state = QStyle::State_None;
531 if (isEnabled() && action->isEnabled())
532 option->state |= QStyle::State_Enabled;
533 else
534 option->palette.setCurrentColorGroup(QPalette::Disabled);
535 option->fontMetrics = fontMetrics();
536 if (d->currentAction && d->currentAction == action) {
537 option->state |= QStyle::State_Selected;
538 if (d->popupState && !d->closePopupMode)
539 option->state |= QStyle::State_Sunken;
540 }
541 if (hasFocus() || d->currentAction)
542 option->state |= QStyle::State_HasFocus;
543 if (d->hoverAction == action) {
544 if (auto optionV2 = qstyleoption_cast<QStyleOptionMenuItemV2 *>(option))
545 optionV2->mouseDown = d->mouseDown;
546 option->state |= QStyle::State_MouseOver;
547 }
548 option->menuRect = rect();
549 option->menuItemType = QStyleOptionMenuItem::Normal;
550 option->checkType = QStyleOptionMenuItem::NotCheckable;
551 option->text = action->text();
552 option->icon = action->icon();
553}
554
555/*!
556 \class QMenuBar
557 \brief The QMenuBar class provides a horizontal menu bar.
558
559 \ingroup mainwindow-classes
560 \inmodule QtWidgets
561
562 A menu bar consists of a list of pull-down menu items. You add
563 menu items with addMenu(). For example, assuming that \c menubar
564 is a pointer to a QMenuBar and \c fileMenu is a pointer to a
565 QMenu, the following statement inserts the menu into the menu bar:
566 \snippet code/src_gui_widgets_qmenubar.cpp 0
567
568 The ampersand in the menu item's text sets Alt+F as a shortcut for
569 this menu. (You can use "\&\&" to get a real ampersand in the menu
570 bar.)
571
572 There is no need to lay out a menu bar. It automatically sets its
573 own geometry to the top of the parent widget and changes it
574 appropriately whenever the parent is resized.
575
576 \section1 Usage
577
578 In most main window style applications you would use the
579 \l{QMainWindow::}{menuBar()} function provided in QMainWindow,
580 adding \l{QMenu}s to the menu bar and adding \l{QAction}s to the
581 pop-up menus.
582
583 Example (from the \l{mainwindows/menus}{Menus} example):
584
585 \snippet mainwindows/menus/mainwindow.cpp 9
586
587 Menu items may be removed with removeAction().
588
589 Widgets can be added to menus by using instances of the QWidgetAction
590 class to hold them. These actions can then be inserted into menus
591 in the usual way; see the QMenu documentation for more details.
592
593 \section1 Platform Dependent Look and Feel
594
595 Different platforms have different requirements for the appearance
596 of menu bars and their behavior when the user interacts with them.
597 For example, Windows systems are often configured so that the
598 underlined character mnemonics that indicate keyboard shortcuts
599 for items in the menu bar are only shown when the \uicontrol{Alt} key is
600 pressed.
601
602 \section1 QMenuBar as a Global Menu Bar
603
604 On \macos and on certain Linux desktop environments such as
605 Ubuntu Unity, QMenuBar is a wrapper for using the system-wide menu bar.
606 If you have multiple menu bars in one dialog the outermost menu bar
607 (normally inside a widget with widget flag Qt::Window) will
608 be used for the system-wide menu bar.
609
610 Qt for \macos also provides a menu bar merging feature to make
611 QMenuBar conform more closely to accepted \macos menu bar layout.
612 If an entry is moved its slots will still fire as if it was in the
613 original place.
614
615 The merging functionality is based on the QAction::menuRole() of
616 the menu entries. If an item has QAction::TextHeuristicRole,
617 the role is determined by string matching the title using the
618 following heuristics:
619
620 \table
621 \header \li String matches \li Placement \li Notes
622 \row \li about.*
623 \li Application Menu | About <application name>
624 \li The application name is fetched from the \c {Info.plist} file
625 (see note below). If this entry is not found no About item
626 will appear in the Application Menu.
627 \row \li config, options, setup, settings or preferences
628 \li Application Menu | Preferences
629 \li If this entry is not found the Settings item will be disabled
630 \row \li quit or exit
631 \li Application Menu | Quit <application name>
632 \li If this entry is not found a default Quit item will be
633 created to call QCoreApplication::quit()
634 \endtable
635
636 You can override this behavior by setting the QAction::menuRole()
637 property to QAction::NoRole.
638
639 If you want all windows in a Mac application to share one menu
640 bar, you must create a menu bar that does not have a parent.
641 Create a parent-less menu bar this way:
642
643 \snippet code/src_gui_widgets_qmenubar.cpp 1
644
645 \b{Note:} Do \e{not} call QMainWindow::menuBar() to create the
646 shared menu bar, because that menu bar will have the QMainWindow
647 as its parent. That menu bar would only be displayed for the
648 parent QMainWindow.
649
650 \b{Note:} The text used for the application name in the \macos menu
651 bar is obtained from the value set in the \c{Info.plist} file in
652 the application's bundle. See \l{Qt for macOS - Deployment}
653 for more information.
654
655 \b{Note:} On Linux, if the com.canonical.AppMenu.Registrar
656 service is available on the D-Bus session bus, then Qt will
657 communicate with it to install the application's menus into the
658 global menu bar, as described.
659
660 \section1 Examples
661
662 The \l{mainwindows/menus}{Menus} example shows how to use QMenuBar
663 and QMenu. The other \l{Main Window Examples}{main window
664 application examples} also provide menus using these classes.
665
666 \sa QMenu, QShortcut, QAction,
667 {http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGIntro/XHIGIntro.html}{Introduction to Apple Human Interface Guidelines},
668 {Menus Example}
669*/
670
671
672void QMenuBarPrivate::init()
673{
674 Q_Q(QMenuBar);
675 q->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
676 q->setAttribute(Qt::WA_CustomWhatsThis);
677
678 if (!QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuBar))
679 platformMenuBar = QGuiApplicationPrivate::platformTheme()->createPlatformMenuBar();
680
681 if (platformMenuBar)
682 q->hide();
683 q->setBackgroundRole(QPalette::Button);
685 q->setMouseTracking(q->style()->styleHint(QStyle::SH_MenuBar_MouseTracking, nullptr, q));
686
687 extension = new QMenuBarExtension(q);
688 extension->setFocusPolicy(Qt::NoFocus);
689 extension->hide();
690}
691
692//Gets the next action for keyboard navigation
693QAction *QMenuBarPrivate::getNextAction(const qsizetype _start, const qsizetype increment) const
694{
695 Q_Q(const QMenuBar);
696 const_cast<QMenuBarPrivate*>(this)->updateGeometries();
697 bool allowActiveAndDisabled = q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, nullptr, q);
698 const qsizetype start = (_start == -1 && increment == -1) ? actions.size() : _start;
699 const qsizetype end = increment == -1 ? 0 : actions.size() - 1;
700
701 for (qsizetype i = start; i != end;) {
702 i += increment;
703 QAction *current = actions.at(i);
704 if (!actionRects.at(i).isNull() && (allowActiveAndDisabled || current->isEnabled()))
705 return current;
706 }
707
708 if (_start != -1) //let's try from the beginning or the end
709 return getNextAction(-1, increment);
710
711 return nullptr;
712}
713
714/*!
715 Constructs a menu bar with parent \a parent.
716*/
717QMenuBar::QMenuBar(QWidget *parent) : QWidget(*new QMenuBarPrivate, parent, { })
718{
719 Q_D(QMenuBar);
720 d->init();
721}
722
723
724/*!
725 Destroys the menu bar.
726*/
727QMenuBar::~QMenuBar()
728{
729 Q_D(QMenuBar);
730 delete d->platformMenuBar;
731 d->platformMenuBar = nullptr;
732}
733
734/*!
735 Appends a new QMenu with \a title to the menu bar. The menu bar
736 takes ownership of the menu. Returns the new menu.
737
738 \sa QWidget::addAction(), QMenu::menuAction()
739*/
740QMenu *QMenuBar::addMenu(const QString &title)
741{
742 QMenu *menu = new QMenu(title, this);
743 addAction(menu->menuAction());
744 return menu;
745}
746
747/*!
748 Appends a new QMenu with \a icon and \a title to the menu bar. The menu bar
749 takes ownership of the menu. Returns the new menu.
750
751 \sa QWidget::addAction(), QMenu::menuAction()
752*/
753QMenu *QMenuBar::addMenu(const QIcon &icon, const QString &title)
754{
755 QMenu *menu = new QMenu(title, this);
756 menu->setIcon(icon);
757 addAction(menu->menuAction());
758 return menu;
759}
760
761/*!
762 Appends \a menu to the menu bar. Returns the menu's menuAction(). The menu bar
763 does not take ownership of the menu.
764
765 \note The returned QAction object can be used to hide the corresponding
766 menu.
767
768 \sa QWidget::addAction(), QMenu::menuAction()
769*/
770QAction *QMenuBar::addMenu(QMenu *menu)
771{
772 QAction *action = menu->menuAction();
773 addAction(action);
774 return action;
775}
776
777/*!
778 Appends a separator to the menu.
779*/
780QAction *QMenuBar::addSeparator()
781{
782 QAction *ret = new QAction(this);
783 ret->setSeparator(true);
784 addAction(ret);
785 return ret;
786}
787
788/*!
789 This convenience function creates a new separator action, i.e. an
790 action with QAction::isSeparator() returning true. The function inserts
791 the newly created action into this menu bar's list of actions before
792 action \a before and returns it.
793
794 \sa QWidget::insertAction(), addSeparator()
795*/
796QAction *QMenuBar::insertSeparator(QAction *before)
797{
798 QAction *action = new QAction(this);
799 action->setSeparator(true);
800 insertAction(before, action);
801 return action;
802}
803
804/*!
805 This convenience function inserts \a menu before action \a before
806 and returns the menus menuAction().
807
808 \sa QWidget::insertAction(), addMenu()
809*/
810QAction *QMenuBar::insertMenu(QAction *before, QMenu *menu)
811{
812 QAction *action = menu->menuAction();
813 insertAction(before, action);
814 return action;
815}
816
817/*!
818 Returns the QAction that is currently highlighted, if any,
819 else \nullptr.
820*/
821QAction *QMenuBar::activeAction() const
822{
823 Q_D(const QMenuBar);
824 return d->currentAction;
825}
826
827/*!
828 \since 4.1
829
830 Sets the currently highlighted action to \a act.
831*/
832void QMenuBar::setActiveAction(QAction *act)
833{
834 Q_D(QMenuBar);
835 d->setCurrentAction(act, true, false);
836}
837
838
839/*!
840 Removes all the actions from the menu bar.
841
842 \note On \macos, menu items that have been merged to the system
843 menu bar are not removed by this function. One way to handle this
844 would be to remove the extra actions yourself. You can set the
845 \l{QAction::MenuRole}{menu role} on the different menus, so that
846 you know ahead of time which menu items get merged and which do
847 not. Then decide what to recreate or remove yourself.
848
849 \sa removeAction()
850*/
851void QMenuBar::clear()
852{
853 const auto acts = actions();
854 for (const auto &act : acts)
855 removeAction(act);
856}
857
858/*!
859 \property QMenuBar::defaultUp
860 \brief the popup orientation
861
862 The default popup orientation. By default, menus pop "down" the
863 screen. By setting the property to true, the menu will pop "up".
864 You might call this for menus that are \e below the document to
865 which they refer.
866
867 If the menu would not fit on the screen, the other direction is
868 used automatically.
869*/
870void QMenuBar::setDefaultUp(bool b)
871{
872 Q_D(QMenuBar);
873 d->defaultPopDown = !b;
874}
875
876bool QMenuBar::isDefaultUp() const
877{
878 Q_D(const QMenuBar);
879 return !d->defaultPopDown;
880}
881
882/*!
883 \reimp
884*/
885void QMenuBar::resizeEvent(QResizeEvent *)
886{
887 Q_D(QMenuBar);
888 d->itemsDirty = true;
889 d->updateGeometries();
890}
891
892/*!
893 \reimp
894*/
895void QMenuBar::paintEvent(QPaintEvent *e)
896{
897 Q_D(QMenuBar);
898 QPainter p(this);
899 QRegion emptyArea(rect());
900
901 //draw the items
902 for (int i = 0; i < d->actions.size(); ++i) {
903 QAction *action = d->actions.at(i);
904 QRect adjustedActionRect = d->actionRect(action);
905 if (adjustedActionRect.isEmpty() || !d->isVisible(action))
906 continue;
907 if (!e->rect().intersects(adjustedActionRect))
908 continue;
909
910 emptyArea -= adjustedActionRect;
911 QStyleOptionMenuItemV2 opt;
912 initStyleOption(&opt, action);
913 opt.rect = adjustedActionRect;
914 p.setClipRect(adjustedActionRect);
915 style()->drawControl(QStyle::CE_MenuBarItem, &opt, &p, this);
916 }
917 //draw border
918 if (int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, this)) {
919 QRegion borderReg;
920 borderReg += QRect(0, 0, fw, height()); //left
921 borderReg += QRect(width()-fw, 0, fw, height()); //right
922 borderReg += QRect(0, 0, width(), fw); //top
923 borderReg += QRect(0, height()-fw, width(), fw); //bottom
924 p.setClipRegion(borderReg);
925 emptyArea -= borderReg;
926 QStyleOptionFrame frame;
927 frame.rect = rect();
928 frame.palette = palette();
929 frame.state = QStyle::State_None;
930 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, &frame, this);
931 frame.midLineWidth = 0;
932 style()->drawPrimitive(QStyle::PE_PanelMenuBar, &frame, &p, this);
933 }
934 p.setClipRegion(emptyArea);
935 QStyleOptionMenuItem menuOpt;
936 menuOpt.palette = palette();
937 menuOpt.state = QStyle::State_None;
938 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
939 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
940 menuOpt.rect = rect();
941 menuOpt.menuRect = rect();
942 style()->drawControl(QStyle::CE_MenuBarEmptyArea, &menuOpt, &p, this);
943}
944
945/*!
946 \reimp
947*/
948void QMenuBar::setVisible(bool visible)
949{
950 if (isNativeMenuBar()) {
951 if (!visible)
952 QWidget::setVisible(false);
953 return;
954 }
955 QWidget::setVisible(visible);
956}
957
958/*!
959 \reimp
960*/
961void QMenuBar::mousePressEvent(QMouseEvent *e)
962{
963 Q_D(QMenuBar);
964 if (e->button() != Qt::LeftButton)
965 return;
966
967 d->mouseDown = true;
968
969 QAction *action = d->actionAt(e->position().toPoint());
970 if (!action || !d->isVisible(action) || !action->isEnabled()) {
971 d->setCurrentAction(nullptr);
972#if QT_CONFIG(whatsthis)
973 if (QWhatsThis::inWhatsThisMode())
974 QWhatsThis::showText(e->globalPosition().toPoint(), d->whatsThis, this);
975#endif
976 return;
977 }
978
979 if (d->currentAction == action && d->popupState) {
980 if (QMenu *menu = d->activeMenu) {
981 d->activeMenu = nullptr;
982 menu->setAttribute(Qt::WA_NoMouseReplay);
983 menu->hide();
984 grabMouse(); // otherwise we don't get the corresponding mouseReleaseEvent...
985 }
986 } else {
987 d->setCurrentAction(action, true);
988 }
989 d->hoverAction = action;
990 update(d->actionRect(d->hoverAction));
991}
992
993/*!
994 \reimp
995*/
996void QMenuBar::mouseReleaseEvent(QMouseEvent *e)
997{
998 Q_D(QMenuBar);
999 if (e->button() != Qt::LeftButton)
1000 return;
1001
1002 releaseMouse();
1003
1004 d->mouseDown = false;
1005 QAction *action = d->actionAt(e->position().toPoint());
1006
1007 // do noting if the action is hidden
1008 if (!d->isVisible(action))
1009 return;
1010 if ((d->closePopupMode && action == d->currentAction) || !action || !action->menu()) {
1011 //we set the current action before activating
1012 //so that we let the leave event set the current back to 0
1013 d->setCurrentAction(action, false);
1014 if (action)
1015 d->activateAction(action, QAction::Trigger);
1016 }
1017 d->closePopupMode = 0;
1018 d->hoverAction = action;
1019 update(d->actionRect(d->hoverAction));
1020}
1021
1022/*!
1023 \reimp
1024*/
1025void QMenuBar::keyPressEvent(QKeyEvent *e)
1026{
1027 Q_D(QMenuBar);
1028 d->updateGeometries();
1029 int key = e->key();
1030 if (isRightToLeft()) { // in reverse mode open/close key for submenues are reversed
1031 if (key == Qt::Key_Left)
1032 key = Qt::Key_Right;
1033 else if (key == Qt::Key_Right)
1034 key = Qt::Key_Left;
1035 }
1036 if (key == Qt::Key_Tab) //means right
1037 key = Qt::Key_Right;
1038 else if (key == Qt::Key_Backtab) //means left
1039 key = Qt::Key_Left;
1040
1041 bool key_consumed = false;
1042 switch(key) {
1043 case Qt::Key_Up:
1044 case Qt::Key_Down:
1045 case Qt::Key_Enter:
1046 case Qt::Key_Space:
1047 case Qt::Key_Return: {
1048 if (!style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, nullptr, this) || !d->currentAction)
1049 break;
1050 if (d->currentAction->menu()) {
1051 d->popupAction(d->currentAction, true);
1052 } else if (key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Space) {
1053 d->activateAction(d->currentAction, QAction::Trigger);
1054 d->setCurrentAction(d->currentAction, false);
1055 d->setKeyboardMode(false);
1056 }
1057 key_consumed = true;
1058 break; }
1059
1060 case Qt::Key_Right:
1061 case Qt::Key_Left: {
1062 if (d->currentAction) {
1063 qsizetype index = d->actions.indexOf(d->currentAction);
1064 if (QAction *nextAction = d->getNextAction(index, key == Qt::Key_Left ? -1 : +1)) {
1065 d->setCurrentAction(nextAction, d->popupState, true);
1066 key_consumed = true;
1067 }
1068 }
1069 break; }
1070
1071 default:
1072 key_consumed = false;
1073 }
1074
1075#ifndef QT_NO_SHORTCUT
1076 if (!key_consumed && e->matches(QKeySequence::Cancel)) {
1077 d->setCurrentAction(nullptr);
1078 d->setKeyboardMode(false);
1079 key_consumed = true;
1080 }
1081#endif
1082
1083 if (!key_consumed &&
1084 (!e->modifiers() ||
1085 (e->modifiers()&(Qt::MetaModifier|Qt::AltModifier))) && e->text().size()==1 && !d->popupState) {
1086 int clashCount = 0;
1087 QAction *first = nullptr, *currentSelected = nullptr, *firstAfterCurrent = nullptr;
1088 {
1089 const QChar c = e->text().at(0).toUpper();
1090 for (qsizetype i = 0; i < d->actions.size(); ++i) {
1091 if (d->actionRects.at(i).isNull())
1092 continue;
1093 QAction *act = d->actions.at(i);
1094 QString s = act->text();
1095 if (!s.isEmpty()) {
1096 qsizetype ampersand = s.indexOf(u'&');
1097 if (ampersand >= 0) {
1098 if (s[ampersand+1].toUpper() == c) {
1099 clashCount++;
1100 if (!first)
1101 first = act;
1102 if (act == d->currentAction)
1103 currentSelected = act;
1104 else if (!firstAfterCurrent && currentSelected)
1105 firstAfterCurrent = act;
1106 }
1107 }
1108 }
1109 }
1110 }
1111 QAction *next_action = nullptr;
1112 if (clashCount >= 1) {
1113 if (clashCount == 1 || !d->currentAction || (currentSelected && !firstAfterCurrent))
1114 next_action = first;
1115 else
1116 next_action = firstAfterCurrent;
1117 }
1118 if (next_action) {
1119 key_consumed = true;
1120 d->setCurrentAction(next_action, true, true);
1121 }
1122 }
1123 if (key_consumed)
1124 e->accept();
1125 else
1126 e->ignore();
1127}
1128
1129/*!
1130 \reimp
1131*/
1132void QMenuBar::mouseMoveEvent(QMouseEvent *e)
1133{
1134 Q_D(QMenuBar);
1135 if (!(e->buttons() & Qt::LeftButton)) {
1136 d->mouseDown = false;
1137 // We receive mouse move and mouse press on touch.
1138 // Mouse move will open the menu and mouse press
1139 // will close it, so ignore mouse move.
1140 if (e->source() != Qt::MouseEventNotSynthesized)
1141 return;
1142 }
1143
1144 bool popupState = d->popupState || d->mouseDown;
1145 QAction *action = d->actionAt(e->position().toPoint());
1146 QAction *oldHoverAction = d->hoverAction;
1147 if ((action && d->isVisible(action)) || !popupState)
1148 d->setCurrentAction(action, popupState);
1149 if (oldHoverAction != action)
1150 update(d->actionRect(oldHoverAction));
1151 d->hoverAction = action;
1152}
1153
1154/*!
1155 \reimp
1156*/
1157void QMenuBar::leaveEvent(QEvent *)
1158{
1159 Q_D(QMenuBar);
1160 if ((!hasFocus() && !d->popupState) ||
1161 (d->currentAction && d->currentAction->menu() == nullptr)) {
1162 d->setCurrentAction(nullptr);
1163 d->hoverAction = nullptr;
1164 }
1165}
1166
1168{
1169 if (!action || !action->menu())
1170 return nullptr;
1171
1172 QPlatformMenu *platformMenu = action->menu()->platformMenu();
1173 if (!platformMenu && platformMenuBar) {
1174 platformMenu = platformMenuBar->createMenu();
1175 if (platformMenu)
1176 action->menu()->setPlatformMenu(platformMenu);
1177 }
1178
1179 return platformMenu;
1180}
1181
1183{
1184 Q_Q(QMenuBar);
1185 QPlatformMenu *beforeMenu = nullptr;
1186 for (qsizetype beforeIndex = indexOf(const_cast<QAction *>(action)) + 1;
1187 !beforeMenu && (beforeIndex < q->actions().size());
1188 ++beforeIndex) {
1189 beforeMenu = getPlatformMenu(q->actions().at(beforeIndex));
1190 }
1191
1192 return beforeMenu;
1193}
1194
1195void QMenuBarPrivate::copyActionToPlatformMenu(const QAction *action, QPlatformMenu *menu)
1196{
1197 const auto tag = reinterpret_cast<quintptr>(action);
1198 if (menu->tag() != tag)
1199 menu->setTag(tag);
1200 menu->setText(action->text());
1201 menu->setVisible(action->isVisible());
1202 menu->setEnabled(action->isEnabled());
1203}
1204
1205/*!
1206 \reimp
1207*/
1208void QMenuBar::actionEvent(QActionEvent *e)
1209{
1210 Q_D(QMenuBar);
1211 d->itemsDirty = true;
1212
1213 if (d->platformMenuBar) {
1214 QPlatformMenuBar *nativeMenuBar = d->platformMenuBar;
1215 if (!nativeMenuBar)
1216 return;
1217
1218 auto action = static_cast<QAction *>(e->action());
1219 if (e->type() == QEvent::ActionAdded) {
1220 QPlatformMenu *menu = d->getPlatformMenu(action);
1221 if (menu) {
1222 d->copyActionToPlatformMenu(action, menu);
1223
1224 QPlatformMenu *beforeMenu = d->findInsertionPlatformMenu(action);
1225 d->platformMenuBar->insertMenu(menu, beforeMenu);
1226 }
1227 } else if (e->type() == QEvent::ActionRemoved) {
1228 QPlatformMenu *menu = d->getPlatformMenu(action);
1229 if (menu)
1230 d->platformMenuBar->removeMenu(menu);
1231 } else if (e->type() == QEvent::ActionChanged) {
1232 QPlatformMenu *cur = d->platformMenuBar->menuForTag(reinterpret_cast<quintptr>(e->action()));
1233 QPlatformMenu *menu = d->getPlatformMenu(action);
1234
1235 // the menu associated with the action can change, need to
1236 // remove and/or insert the new platform menu
1237 if (menu != cur) {
1238 if (cur)
1239 d->platformMenuBar->removeMenu(cur);
1240 if (menu) {
1241 d->copyActionToPlatformMenu(action, menu);
1242
1243 QPlatformMenu *beforeMenu = d->findInsertionPlatformMenu(action);
1244 d->platformMenuBar->insertMenu(menu, beforeMenu);
1245 }
1246 } else if (menu) {
1247 d->copyActionToPlatformMenu(action, menu);
1248 d->platformMenuBar->syncMenu(menu);
1249 }
1250 }
1251 }
1252
1253 if (e->type() == QEvent::ActionAdded) {
1254 connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
1255 connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
1256 } else if (e->type() == QEvent::ActionRemoved) {
1257 e->action()->disconnect(this);
1258 }
1259 // updateGeometries() is also needed for native menu bars because
1260 // it updates shortcutIndexMap
1261 if (isVisible() || isNativeMenuBar())
1262 d->updateGeometries();
1263 if (isVisible())
1264 update();
1265}
1266
1267/*!
1268 \reimp
1269*/
1270void QMenuBar::focusInEvent(QFocusEvent *)
1271{
1272 Q_D(QMenuBar);
1273 if (d->keyboardState)
1274 d->focusFirstAction();
1275}
1276
1277/*!
1278 \reimp
1279*/
1280void QMenuBar::focusOutEvent(QFocusEvent *)
1281{
1282 Q_D(QMenuBar);
1283 if (!d->popupState) {
1284 d->setCurrentAction(nullptr);
1285 d->setKeyboardMode(false);
1286 }
1287}
1288
1289/*!
1290 \reimp
1291 */
1292void QMenuBar::timerEvent (QTimerEvent *e)
1293{
1294 Q_D(QMenuBar);
1295 if (e->timerId() == d->autoReleaseTimer.timerId()) {
1296 d->autoReleaseTimer.stop();
1297 d->setCurrentAction(nullptr);
1298 }
1299 QWidget::timerEvent(e);
1300}
1301
1302
1304{
1305 Q_Q(QMenuBar);
1306 QWidget *newParent = q->parentWidget();
1307
1308 //Note: if parent is reparented, then window may change even if parent doesn't.
1309 // We need to install an avent filter on each parent up to the parent that is
1310 // also a window (for shortcuts)
1311 QWidget *newWindow = newParent ? newParent->window() : nullptr;
1312
1313 QList<QPointer<QWidget>> newParents;
1314 // Remove event filters on ex-parents, keep them on still-parents
1315 // The parents are always ordered in the vector
1316 //
1317 // Take a copy because this method is called from changeEvent() and eventFilter(),
1318 // which might cause recursion into the class due to event processing, which might
1319 // modify oldParents.
1320 const auto copy = oldParents;
1321 for (const QPointer<QWidget> &w : copy) {
1322 if (w) {
1323 if (newParent == w) {
1324 newParents.append(w);
1325 if (newParent != newWindow) //stop at the window
1326 newParent = newParent->parentWidget();
1327 } else {
1328 w->removeEventFilter(q);
1329 }
1330 }
1331 }
1332
1333 // At this point, newParent is the next one to be added to newParents
1334 while (newParent && newParent != newWindow) {
1335 //install event filters all the way up to (excluding) the window
1336 newParents.append(newParent);
1337 newParent->installEventFilter(q);
1338 newParent = newParent->parentWidget();
1339 }
1340
1341 if (newParent && newWindow) {
1342 // Install the event filter on the window
1343 newParents.append(newParent);
1344 newParent->installEventFilter(q);
1345 }
1346 oldParents = newParents;
1347
1348 if (platformMenuBar) {
1349 if (newWindow) {
1350 // force the underlying platform window to be created, since
1351 // the platform menubar needs it (and we have no other way to
1352 // discover when the platform window is created)
1353 newWindow->createWinId();
1354 platformMenuBar->handleReparent(newWindow->windowHandle());
1355 } else {
1356 platformMenuBar->handleReparent(nullptr);
1357 }
1358 }
1359}
1360
1361/*!
1362 \reimp
1363*/
1364void QMenuBar::changeEvent(QEvent *e)
1365{
1366 Q_D(QMenuBar);
1367 if (e->type() == QEvent::StyleChange) {
1368 d->itemsDirty = true;
1369 setMouseTracking(style()->styleHint(QStyle::SH_MenuBar_MouseTracking, nullptr, this));
1370 if (parentWidget())
1371 resize(parentWidget()->width(), heightForWidth(parentWidget()->width()));
1372 d->updateGeometries();
1373 } else if (e->type() == QEvent::ParentChange) {
1374 d->handleReparent();
1375 } else if (e->type() == QEvent::FontChange
1376 || e->type() == QEvent::ApplicationFontChange) {
1377 d->itemsDirty = true;
1378 d->updateGeometries();
1379 }
1380
1381 QWidget::changeEvent(e);
1382}
1383
1384/*!
1385 \reimp
1386*/
1387bool QMenuBar::event(QEvent *e)
1388{
1389 Q_D(QMenuBar);
1390 switch (e->type()) {
1391 case QEvent::KeyPress: {
1392 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1393#if 0
1394 if (!d->keyboardState) { //all keypresses..
1395 d->setCurrentAction(0);
1396 return ;
1397 }
1398#endif
1399 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
1400 keyPressEvent(ke);
1401 return true;
1402 }
1403
1404 } break;
1405#ifndef QT_NO_SHORTCUT
1406 case QEvent::Shortcut: {
1407 QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
1408 int shortcutId = se->shortcutId();
1409 for (qsizetype j = 0; j < d->shortcutIndexMap.size(); ++j) {
1410 if (shortcutId == d->shortcutIndexMap.value(j))
1411 d->_q_internalShortcutActivated(j);
1412 }
1413 } break;
1414#endif
1415 case QEvent::Show:
1416 d->_q_updateLayout();
1417 break;
1418#ifndef QT_NO_SHORTCUT
1419 case QEvent::ShortcutOverride: {
1420 QKeyEvent *kev = static_cast<QKeyEvent *>(e);
1421 //we only filter out escape if there is a current action
1422 if (kev->matches(QKeySequence::Cancel) && d->currentAction) {
1423 e->accept();
1424 return true;
1425 }
1426 }
1427 break;
1428#endif
1429#if QT_CONFIG(whatsthis)
1430 case QEvent::QueryWhatsThis:
1431 e->setAccepted(d->whatsThis.size());
1432 if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) {
1433 if (action->whatsThis().size() || action->menu())
1434 e->accept();
1435 }
1436 return true;
1437#endif
1438 case QEvent::LayoutDirectionChange:
1439 d->_q_updateLayout();
1440 break;
1441 default:
1442 break;
1443 }
1444 return QWidget::event(e);
1445}
1446
1447/*!
1448 \reimp
1449*/
1450bool QMenuBar::eventFilter(QObject *object, QEvent *event)
1451{
1452 Q_D(QMenuBar);
1453 if (object && (event->type() == QEvent::ParentChange)) //GrandparentChange
1454 d->handleReparent();
1455
1456 if (object == d->leftWidget || object == d->rightWidget) {
1457 switch (event->type()) {
1458 case QEvent::ShowToParent:
1459 case QEvent::HideToParent:
1460 d->_q_updateLayout();
1461 break;
1462 default:
1463 break;
1464 }
1465 }
1466
1467 if (isNativeMenuBar() && event->type() == QEvent::ShowToParent) {
1468 // On some desktops like Unity, the D-Bus menu bar is unregistered
1469 // when the window is hidden. So when the window is shown, we need
1470 // to forcefully re-register it. The only way to force re-registering
1471 // with D-Bus menu is the handleReparent method.
1472 QWidget *widget = qobject_cast<QWidget *>(object);
1473 QWindow *handle = widget ? widget->windowHandle() : nullptr;
1474 if (handle != nullptr)
1475 d->platformMenuBar->handleReparent(handle);
1476 }
1477
1478 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, nullptr, this)) {
1479 if (d->altPressed) {
1480 switch (event->type()) {
1481 case QEvent::KeyPress:
1482 case QEvent::KeyRelease:
1483 {
1484 QKeyEvent *kev = static_cast<QKeyEvent*>(event);
1485 if (kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta) {
1486 if (event->type() == QEvent::KeyPress) // Alt-press does not interest us, we have the shortcut-override event
1487 break;
1488 d->setKeyboardMode(!d->keyboardState);
1489 }
1490 }
1491 Q_FALLTHROUGH();
1492 case QEvent::MouseButtonPress:
1493 case QEvent::MouseButtonRelease:
1494 case QEvent::MouseMove:
1495 case QEvent::FocusIn:
1496 case QEvent::FocusOut:
1497 case QEvent::ActivationChange:
1498 case QEvent::Shortcut:
1499 d->altPressed = false;
1500 qApp->removeEventFilter(this);
1501 break;
1502 default:
1503 break;
1504 }
1505 } else if (isVisible()) {
1506 if (event->type() == QEvent::ShortcutOverride) {
1507 QKeyEvent *kev = static_cast<QKeyEvent*>(event);
1508 if ((kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta)
1509 && kev->modifiers() == Qt::AltModifier) {
1510 d->altPressed = true;
1511 qApp->installEventFilter(this);
1512 }
1513 }
1514 }
1515 }
1516
1517 return false;
1518}
1519
1520/*!
1521 Returns the QAction at \a pt. Returns \nullptr if there is no action at \a pt or if
1522the location has a separator.
1523
1524 \sa QWidget::addAction(), addSeparator()
1525*/
1526QAction *QMenuBar::actionAt(const QPoint &pt) const
1527{
1528 Q_D(const QMenuBar);
1529 return d->actionAt(pt);
1530}
1531
1532/*!
1533 Returns the geometry of action \a act as a QRect.
1534
1535 \sa actionAt()
1536*/
1537QRect QMenuBar::actionGeometry(QAction *act) const
1538{
1539 Q_D(const QMenuBar);
1540 return d->actionRect(act);
1541}
1542
1543/*!
1544 \reimp
1545*/
1546QSize QMenuBar::minimumSizeHint() const
1547{
1548 Q_D(const QMenuBar);
1549 const bool as_gui_menubar = !isNativeMenuBar();
1550
1551 ensurePolished();
1552 QSize ret(0, 0);
1553 const_cast<QMenuBarPrivate*>(d)->updateGeometries();
1554 const int hmargin = style()->pixelMetric(QStyle::PM_MenuBarHMargin, nullptr, this);
1555 const int vmargin = style()->pixelMetric(QStyle::PM_MenuBarVMargin, nullptr, this);
1556 int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, this);
1557 int spaceBelowMenuBar = style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, nullptr, this);
1558 if (as_gui_menubar) {
1559 int w = parentWidget() ? parentWidget()->width() : QGuiApplication::primaryScreen()->virtualGeometry().width();
1560 d->calcActionRects(w - (2 * fw), 0);
1561 for (qsizetype i = 0; ret.isNull() && i < d->actions.size(); ++i)
1562 ret = d->actionRects.at(i).size();
1563 if (!d->extension->isHidden())
1564 ret += QSize(d->extension->sizeHint().width(), 0);
1565 ret += QSize(2*fw + hmargin, 2*fw + vmargin);
1566 }
1567 int margin = 2*vmargin + 2*fw + spaceBelowMenuBar;
1568 if (d->leftWidget) {
1569 QSize sz = d->leftWidget->minimumSizeHint();
1570 ret.setWidth(ret.width() + sz.width());
1571 if (sz.height() + margin > ret.height())
1572 ret.setHeight(sz.height() + margin);
1573 }
1574 if (d->rightWidget) {
1575 QSize sz = d->rightWidget->minimumSizeHint();
1576 ret.setWidth(ret.width() + sz.width());
1577 if (sz.height() + margin > ret.height())
1578 ret.setHeight(sz.height() + margin);
1579 }
1580 if (as_gui_menubar) {
1581 QStyleOptionMenuItem opt;
1582 opt.rect = rect();
1583 opt.menuRect = rect();
1584 opt.state = QStyle::State_None;
1585 opt.menuItemType = QStyleOptionMenuItem::Normal;
1586 opt.checkType = QStyleOptionMenuItem::NotCheckable;
1587 opt.palette = palette();
1588 return style()->sizeFromContents(QStyle::CT_MenuBar, &opt, ret, this);
1589 }
1590 return ret;
1591}
1592
1593/*!
1594 \reimp
1595*/
1596QSize QMenuBar::sizeHint() const
1597{
1598 Q_D(const QMenuBar);
1599 const bool as_gui_menubar = !isNativeMenuBar();
1600
1601 ensurePolished();
1602 QSize ret(0, 0);
1603 const_cast<QMenuBarPrivate*>(d)->updateGeometries();
1604 const int hmargin = style()->pixelMetric(QStyle::PM_MenuBarHMargin, nullptr, this);
1605 const int vmargin = style()->pixelMetric(QStyle::PM_MenuBarVMargin, nullptr, this);
1606 int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, this);
1607 int spaceBelowMenuBar = style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, nullptr, this);
1608 if (as_gui_menubar) {
1609 const int w = parentWidget() ? parentWidget()->width() : QGuiApplication::primaryScreen()->virtualGeometry().width();
1610 d->calcActionRects(w - (2 * fw), 0);
1611 for (const auto &actionRect : d->actionRects)
1612 ret = ret.expandedTo(QSize(actionRect.x() + actionRect.width(), actionRect.y() + actionRect.height()));
1613 //the action geometries already contain the top and left
1614 //margins. So we only need to add those from right and bottom.
1615 ret += QSize(fw + hmargin, fw + vmargin);
1616 }
1617 int margin = 2*vmargin + 2*fw + spaceBelowMenuBar;
1618 if (d->leftWidget) {
1619 QSize sz = d->leftWidget->sizeHint();
1620 sz.rheight() += margin;
1621 ret = ret.expandedTo(sz);
1622 }
1623 if (d->rightWidget) {
1624 QSize sz = d->rightWidget->sizeHint();
1625 ret.setWidth(ret.width() + sz.width());
1626 if (sz.height() + margin > ret.height())
1627 ret.setHeight(sz.height() + margin);
1628 }
1629 if (as_gui_menubar) {
1630 QStyleOptionMenuItem opt;
1631 opt.rect = rect();
1632 opt.menuRect = rect();
1633 opt.state = QStyle::State_None;
1634 opt.menuItemType = QStyleOptionMenuItem::Normal;
1635 opt.checkType = QStyleOptionMenuItem::NotCheckable;
1636 opt.palette = palette();
1637 return style()->sizeFromContents(QStyle::CT_MenuBar, &opt, ret, this);
1638 }
1639 return ret;
1640}
1641
1642/*!
1643 \reimp
1644*/
1645int QMenuBar::heightForWidth(int) const
1646{
1647 Q_D(const QMenuBar);
1648 const bool as_gui_menubar = !isNativeMenuBar();
1649
1650 const_cast<QMenuBarPrivate*>(d)->updateGeometries();
1651 int height = 0;
1652 const int vmargin = style()->pixelMetric(QStyle::PM_MenuBarVMargin, nullptr, this);
1653 int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, nullptr, this);
1654 int spaceBelowMenuBar = style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, nullptr, this);
1655 if (as_gui_menubar) {
1656 for (const auto &actionRect : d->actionRects)
1657 height = qMax(height, actionRect.height());
1658 if (height) //there is at least one non-null item
1659 height += spaceBelowMenuBar;
1660 height += 2*fw;
1661 height += 2*vmargin;
1662 }
1663 int margin = 2*vmargin + 2*fw + spaceBelowMenuBar;
1664 if (d->leftWidget)
1665 height = qMax(d->leftWidget->sizeHint().height() + margin, height);
1666 if (d->rightWidget)
1667 height = qMax(d->rightWidget->sizeHint().height() + margin, height);
1668 if (as_gui_menubar) {
1669 QStyleOptionMenuItem opt;
1670 opt.initFrom(this);
1671 opt.menuRect = rect();
1672 opt.state = QStyle::State_None;
1673 opt.menuItemType = QStyleOptionMenuItem::Normal;
1674 opt.checkType = QStyleOptionMenuItem::NotCheckable;
1675 return style()->sizeFromContents(QStyle::CT_MenuBar, &opt, QSize(0, height), this).height(); //not pretty..
1676 }
1677 return height;
1678}
1679
1680/*!
1681 \internal
1682*/
1684{
1685 Q_Q(QMenuBar);
1686 QAction *act = actions.at(id);
1687 if (act && act->menu()) {
1688 if (QPlatformMenu *platformMenu = act->menu()->platformMenu()) {
1689 platformMenu->showPopup(q->windowHandle(), actionRects.at(id), nullptr);
1690 return;
1691 }
1692 }
1693
1694 keyboardFocusWidget = QApplication::focusWidget();
1695 setCurrentAction(act, true, true);
1696 if (act && !act->menu()) {
1697 activateAction(act, QAction::Trigger);
1698 //100 is the same as the default value in QPushButton::animateClick
1699 autoReleaseTimer.start(100, q);
1700 } else if (act && q->style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, nullptr, q)) {
1701 // When we open a menu using a shortcut, we should end up in keyboard state
1703 }
1704}
1705
1707{
1708 Q_Q(QMenuBar);
1709 itemsDirty = true;
1710 if (q->isVisible()) {
1712 q->update();
1713 }
1714}
1715
1716/*!
1717 \fn void QMenuBar::setCornerWidget(QWidget *widget, Qt::Corner corner)
1718
1719 This sets the given \a widget to be shown directly on the left of the first
1720 menu item, or on the right of the last menu item, depending on \a corner.
1721
1722 The menu bar takes ownership of \a widget, reparenting it into the menu bar.
1723 However, if the \a corner already contains a widget, this previous widget
1724 will no longer be managed and will still be a visible child of the menu bar.
1725
1726 \note Using a corner other than Qt::TopRightCorner or Qt::TopLeftCorner
1727 will result in a warning.
1728*/
1729void QMenuBar::setCornerWidget(QWidget *w, Qt::Corner corner)
1730{
1731 Q_D(QMenuBar);
1732 switch (corner) {
1733 case Qt::TopLeftCorner:
1734 if (d->leftWidget)
1735 d->leftWidget->removeEventFilter(this);
1736 d->leftWidget = w;
1737 break;
1738 case Qt::TopRightCorner:
1739 if (d->rightWidget)
1740 d->rightWidget->removeEventFilter(this);
1741 d->rightWidget = w;
1742 break;
1743 default:
1744 qWarning("QMenuBar::setCornerWidget: Only TopLeftCorner and TopRightCorner are supported");
1745 return;
1746 }
1747
1748 if (w) {
1749 w->setParent(this);
1750 w->installEventFilter(this);
1751 }
1752
1753 d->_q_updateLayout();
1754}
1755
1756/*!
1757 Returns the widget on the left of the first or on the right of the last menu
1758 item, depending on \a corner.
1759
1760 \note Using a corner other than Qt::TopRightCorner or Qt::TopLeftCorner
1761 will result in a warning.
1762*/
1763QWidget *QMenuBar::cornerWidget(Qt::Corner corner) const
1764{
1765 Q_D(const QMenuBar);
1766 QWidget *w = nullptr;
1767 switch(corner) {
1768 case Qt::TopLeftCorner:
1769 w = d->leftWidget;
1770 break;
1771 case Qt::TopRightCorner:
1772 w = d->rightWidget;
1773 break;
1774 default:
1775 qWarning("QMenuBar::cornerWidget: Only TopLeftCorner and TopRightCorner are supported");
1776 break;
1777 }
1778
1779 return w;
1780}
1781
1782/*!
1783 \property QMenuBar::nativeMenuBar
1784 \brief Whether or not a menubar will be used as a native menubar on platforms that support it
1785 \since 4.6
1786
1787 This property specifies whether or not the menubar should be used as a native menubar on
1788 platforms that support it. The currently supported platforms are \macos, and
1789 Linux desktops which use the com.canonical.dbusmenu D-Bus interface (such as Ubuntu Unity).
1790 If this property is \c true, the menubar is used in the native menubar and is not in the window of
1791 its parent; if \c false the menubar remains in the window. On other platforms,
1792 setting this attribute has no effect, and reading this attribute will always return \c false.
1793
1794 The default is to follow whether the Qt::AA_DontUseNativeMenuBar attribute
1795 is set for the application. Explicitly setting this property overrides
1796 the presence (or absence) of the attribute.
1797*/
1798
1799void QMenuBar::setNativeMenuBar(bool nativeMenuBar)
1800{
1801 Q_D(QMenuBar);
1802 if (nativeMenuBar != bool(d->platformMenuBar)) {
1803 if (!nativeMenuBar) {
1804 delete d->platformMenuBar;
1805 d->platformMenuBar = nullptr;
1806 d->itemsDirty = true;
1807 } else {
1808 if (!d->platformMenuBar)
1809 d->platformMenuBar = QGuiApplicationPrivate::platformTheme()->createPlatformMenuBar();
1810 }
1811
1812 updateGeometry();
1813 if (!nativeMenuBar && parentWidget())
1814 setVisible(true);
1815 }
1816}
1817
1818bool QMenuBar::isNativeMenuBar() const
1819{
1820 Q_D(const QMenuBar);
1821 return bool(d->platformMenuBar);
1822}
1823
1824/*!
1825 \internal
1826*/
1827QPlatformMenuBar *QMenuBar::platformMenuBar()
1828{
1829 Q_D(const QMenuBar);
1830 return d->platformMenuBar;
1831}
1832
1833/*!
1834 \fn void QMenuBar::triggered(QAction *action)
1835
1836 This signal is emitted when an action in a menu belonging to this menubar
1837 is triggered as a result of a mouse click; \a action is the action that
1838 caused the signal to be emitted.
1839
1840 \note QMenuBar has to have ownership of the QMenu in order this signal to work.
1841
1842 Normally, you connect each menu action to a single slot using
1843 QAction::triggered(), but sometimes you will want to connect
1844 several items to a single slot (most often if the user selects
1845 from an array). This signal is useful in such cases.
1846
1847 \sa hovered(), QAction::triggered()
1848*/
1849
1850/*!
1851 \fn void QMenuBar::hovered(QAction *action)
1852
1853 This signal is emitted when a menu action is highlighted; \a action
1854 is the action that caused the event to be sent.
1855
1856 Often this is used to update status information.
1857
1858 \sa triggered(), QAction::hovered()
1859*/
1860
1861// for private slots
1862
1863QT_END_NAMESPACE
1864
1865#include <moc_qmenubar.cpp>
QSize sizeHint() const override
Definition qmenubar.cpp:69
void paintEvent(QPaintEvent *) override
\reimp
Definition qmenubar.cpp:58
QMenuBarExtension(QWidget *parent)
Definition qmenubar.cpp:47
QPlatformMenu * findInsertionPlatformMenu(const QAction *action)
QAction * getNextAction(const qsizetype start, const qsizetype increment) const
Definition qmenubar.cpp:693
QMenuBarExtension * extension
Definition qmenubar_p.h:93
QPlatformMenu * getPlatformMenu(const QAction *action)
QAction * actionAt(QPoint p) const
Definition qmenubar.cpp:79
void calcActionRects(int max_width, int start) const
Definition qmenubar.cpp:381
void _q_internalShortcutActivated(int)
void _q_actionTriggered()
Definition qmenubar.cpp:493
void _q_updateLayout()
void copyActionToPlatformMenu(const QAction *e, QPlatformMenu *menu)
void focusFirstAction()
Definition qmenubar.cpp:234
QPlatformMenuBar * platformMenuBar
Definition qmenubar_p.h:110
void popupAction(QAction *, bool)
Definition qmenubar.cpp:271
void setCurrentAction(QAction *, bool=false, bool=false)
Definition qmenubar.cpp:337
int indexOf(QAction *act) const
Definition qmenubar_p.h:115
void activateAction(QAction *, QAction::ActionEvent)
Definition qmenubar.cpp:477
void updateGeometries()
Definition qmenubar.cpp:127
void handleReparent()
QRect menuRect(bool) const
Definition qmenubar.cpp:88
bool isVisible(QAction *action)
Definition qmenubar.cpp:122
void _q_actionHovered()
Definition qmenubar.cpp:501
void setKeyboardMode(bool)
Definition qmenubar.cpp:245
QRect actionRect(QAction *) const
Definition qmenubar.cpp:221
friend class QWidget
Definition qpainter.h:432
Combined button and popup list for selecting options.
#define qApp
QMenu menu
[5]