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