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