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