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