1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qquickmenubar_p.h"
38#include "qquickmenubar_p_p.h"
39#include "qquickmenubaritem_p_p.h"
40#include "qquickmenu_p.h"
41#include "qquickmenu_p_p.h"
42
43#include <QtQml/qqmlcontext.h>
44#include <QtQml/qqmlcomponent.h>
45#include <QtQml/qqmlengine.h>
46
47QT_BEGIN_NAMESPACE
48
49/*!
50 \qmltype MenuBar
51 \inherits Container
52//! \instantiates QQuickMenuBar
53 \inqmlmodule QtQuick.Controls
54 \since 5.10
55 \ingroup qtquickcontrols2-menus
56 \ingroup qtquickcontrols2-focusscopes
57 \brief Provides a window menu bar.
58
59 \image qtquickcontrols2-menubar.png
60
61 MenuBar consists of drop-down menus, and is normally located at the top
62 edge of the window.
63
64 \quotefromfile qtquickcontrols2-menubar.qml
65 \skipuntil begin
66 \printto skipfrom
67 \skipuntil skipto
68 \printto end
69
70 Typically, menus are statically declared as children of the menu bar, but
71 MenuBar also provides API to \l {addMenu}{add}, \l {insertMenu}{insert},
72 \l {removeMenu}{remove}, and \l {takeMenu}{take} menus dynamically. The
73 menus in a menu bar can be accessed using \l menuAt().
74
75 \sa {Customizing MenuBar}, Menu, MenuBarItem, {Menu Controls},
76 {Focus Management in Qt Quick Controls}
77*/
78
79QQuickItem *QQuickMenuBarPrivate::beginCreateItem(QQuickMenu *menu)
80{
81 Q_Q(QQuickMenuBar);
82 if (!delegate)
83 return nullptr;
84
85 QQmlContext *creationContext = delegate->creationContext();
86 if (!creationContext)
87 creationContext = qmlContext(q);
88 QQmlContext *context = new QQmlContext(creationContext, q);
89 context->setContextObject(q);
90
91 QObject *object = delegate->beginCreate(context);
92 QQuickItem *item = qobject_cast<QQuickItem *>(object);
93 if (!item) {
94 delete object;
95 delete context;
96 return nullptr;
97 }
98
99 if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(object: item))
100 menuBarItem->setMenu(menu);
101 item->setParentItem(q);
102 QQml_setParent_noEvent(object: item, parent: q);
103
104 return item;
105}
106
107void QQuickMenuBarPrivate::completeCreateItem()
108{
109 if (!delegate)
110 return;
111
112 delegate->completeCreate();
113}
114
115QQuickItem *QQuickMenuBarPrivate::createItem(QQuickMenu *menu)
116{
117 QQuickItem *item = beginCreateItem(menu);
118 completeCreateItem();
119 return item;
120}
121
122void QQuickMenuBarPrivate::toggleCurrentMenu(bool visible, bool activate)
123{
124 if (!currentItem || visible == popupMode)
125 return;
126
127 QQuickMenu *menu = currentItem->menu();
128
129 triggering = true;
130 popupMode = visible;
131 if (menu)
132 menu->setVisible(visible);
133 if (!visible)
134 currentItem->forceActiveFocus();
135 else if (menu && activate)
136 menu->setCurrentIndex(0);
137 triggering = false;
138}
139
140void QQuickMenuBarPrivate::activateItem(QQuickMenuBarItem *item)
141{
142 if (currentItem == item)
143 return;
144
145 if (currentItem) {
146 currentItem->setHighlighted(false);
147 if (popupMode) {
148 if (QQuickMenu *menu = currentItem->menu())
149 menu->dismiss();
150 }
151 }
152
153 if (item) {
154 item->setHighlighted(true);
155 if (popupMode) {
156 if (QQuickMenu *menu = item->menu())
157 menu->open();
158 }
159 }
160
161 currentItem = item;
162}
163
164void QQuickMenuBarPrivate::activateNextItem()
165{
166 int index = currentItem ? contentModel->indexOf(object: currentItem, objectContext: nullptr) : -1;
167 if (index >= contentModel->count() - 1)
168 index = -1;
169 activateItem(item: qobject_cast<QQuickMenuBarItem *>(object: itemAt(index: ++index)));
170}
171
172void QQuickMenuBarPrivate::activatePreviousItem()
173{
174 int index = currentItem ? contentModel->indexOf(object: currentItem, objectContext: nullptr) : contentModel->count();
175 if (index <= 0)
176 index = contentModel->count();
177 activateItem(item: qobject_cast<QQuickMenuBarItem *>(object: itemAt(index: --index)));
178}
179
180void QQuickMenuBarPrivate::onItemHovered()
181{
182 Q_Q(QQuickMenuBar);
183 QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(object: q->sender());
184 if (!item || item == currentItem || !item->isHovered() || QQuickMenuBarItemPrivate::get(item)->touchId != -1)
185 return;
186
187 activateItem(item);
188}
189
190void QQuickMenuBarPrivate::onItemTriggered()
191{
192 Q_Q(QQuickMenuBar);
193 QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(object: q->sender());
194 if (!item)
195 return;
196
197 if (item == currentItem) {
198 toggleCurrentMenu(visible: !popupMode, activate: false);
199 } else {
200 popupMode = true;
201 activateItem(item);
202 }
203}
204
205void QQuickMenuBarPrivate::onMenuAboutToHide()
206{
207 if (triggering || !currentItem || currentItem->isHovered() || !currentItem->isHighlighted())
208 return;
209
210 popupMode = false;
211 activateItem(item: nullptr);
212}
213
214qreal QQuickMenuBarPrivate::getContentWidth() const
215{
216 Q_Q(const QQuickMenuBar);
217 const int count = contentModel->count();
218 qreal totalWidth = qMax(a: 0, b: count - 1) * spacing;
219 for (int i = 0; i < count; ++i) {
220 QQuickItem *item = q->itemAt(index: i);
221 if (item)
222 totalWidth += item->implicitWidth();
223 }
224 return totalWidth;
225}
226
227qreal QQuickMenuBarPrivate::getContentHeight() const
228{
229 Q_Q(const QQuickMenuBar);
230 const int count = contentModel->count();
231 qreal maxHeight = 0;
232 for (int i = 0; i < count; ++i) {
233 QQuickItem *item = q->itemAt(index: i);
234 if (item)
235 maxHeight = qMax(a: maxHeight, b: item->implicitHeight());
236 }
237 return maxHeight;
238}
239
240void QQuickMenuBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
241{
242 QQuickContainerPrivate::itemImplicitWidthChanged(item);
243 if (item != contentItem)
244 updateImplicitContentWidth();
245}
246
247void QQuickMenuBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
248{
249 QQuickContainerPrivate::itemImplicitHeightChanged(item);
250 if (item != contentItem)
251 updateImplicitContentHeight();
252}
253
254void QQuickMenuBarPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
255{
256 QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
257 if (QQuickMenu *menu = qobject_cast<QQuickMenu *>(object: obj))
258 obj = QQuickMenuBarPrivate::get(menuBar)->createItem(menu);
259 QQuickContainerPrivate::contentData_append(prop, obj);
260}
261
262void QQuickMenuBarPrivate::menus_append(QQmlListProperty<QQuickMenu> *prop, QQuickMenu *obj)
263{
264 QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
265 menuBar->addMenu(menu: obj);
266}
267
268int QQuickMenuBarPrivate::menus_count(QQmlListProperty<QQuickMenu> *prop)
269{
270 QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
271 return menuBar->count();
272}
273
274QQuickMenu *QQuickMenuBarPrivate::menus_at(QQmlListProperty<QQuickMenu> *prop, int index)
275{
276 QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
277 return menuBar->menuAt(index);
278}
279
280void QQuickMenuBarPrivate::menus_clear(QQmlListProperty<QQuickMenu> *prop)
281{
282 QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
283 QQuickMenuBarPrivate::get(menuBar)->contentModel->clear();
284}
285
286QQuickMenuBar::QQuickMenuBar(QQuickItem *parent)
287 : QQuickContainer(*(new QQuickMenuBarPrivate), parent)
288{
289 Q_D(QQuickMenuBar);
290 d->changeTypes |= QQuickItemPrivate::Geometry;
291 setFlag(flag: ItemIsFocusScope);
292 setFocusPolicy(Qt::ClickFocus);
293}
294
295/*!
296 \qmlproperty Component QtQuick.Controls::MenuBar::delegate
297
298 This property holds the component that is used to create menu bar
299 items to present menus in the menu bar.
300
301 \sa MenuBarItem
302*/
303QQmlComponent *QQuickMenuBar::delegate() const
304{
305 Q_D(const QQuickMenuBar);
306 return d->delegate;
307}
308
309void QQuickMenuBar::setDelegate(QQmlComponent *delegate)
310{
311 Q_D(QQuickMenuBar);
312 if (d->delegate == delegate)
313 return;
314
315 d->delegate = delegate;
316 emit delegateChanged();
317}
318
319/*!
320 \qmlmethod Menu QtQuick.Controls::MenuBar::menuAt(int index)
321
322 Returns the menu at \a index, or \c null if it does not exist.
323*/
324QQuickMenu *QQuickMenuBar::menuAt(int index) const
325{
326 Q_D(const QQuickMenuBar);
327 QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(object: d->itemAt(index));
328 if (!item)
329 return nullptr;
330 return item->menu();
331}
332
333/*!
334 \qmlmethod void QtQuick.Controls::MenuBar::addMenu(Menu menu)
335
336 Adds \a menu to the end of the list of menus.
337*/
338void QQuickMenuBar::addMenu(QQuickMenu *menu)
339{
340 Q_D(QQuickMenuBar);
341 addItem(item: d->createItem(menu));
342}
343
344/*!
345 \qmlmethod void QtQuick.Controls::MenuBar::insertMenu(int index, Menu menu)
346
347 Inserts \a menu at \a index.
348*/
349void QQuickMenuBar::insertMenu(int index, QQuickMenu *menu)
350{
351 Q_D(QQuickMenuBar);
352 insertItem(index, item: d->createItem(menu));
353}
354
355/*!
356 \qmlmethod void QtQuick.Controls::MenuBar::removeMenu(Menu menu)
357
358 Removes and destroys the specified \a menu.
359*/
360void QQuickMenuBar::removeMenu(QQuickMenu *menu)
361{
362 Q_D(QQuickMenuBar);
363 if (!menu)
364 return;
365
366 const int count = d->contentModel->count();
367 for (int i = 0; i < count; ++i) {
368 QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(object: itemAt(index: i));
369 if (!item || item->menu() != menu)
370 continue;
371
372 removeItem(item);
373 break;
374 }
375
376 menu->deleteLater();
377}
378
379/*!
380 \qmlmethod Menu QtQuick.Controls::MenuBar::takeMenu(int index)
381
382 Removes and returns the menu at \a index.
383
384 \note The ownership of the item is transferred to the caller.
385*/
386QQuickMenu *QQuickMenuBar::takeMenu(int index)
387{
388 Q_D(QQuickMenuBar);
389 QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(object: itemAt(index));
390 if (!item)
391 return nullptr;
392
393 QQuickMenu *menu = item->menu();
394 if (!menu)
395 return nullptr;
396
397 d->removeItem(index, item);
398 item->deleteLater();
399 return menu;
400}
401
402/*!
403 \since QtQuick.Controls 2.3 (Qt 5.10)
404 \qmlproperty real QtQuick.Controls::MenuBar::contentWidth
405
406 This property holds the content width. It is used for calculating the total
407 implicit width of the menu bar.
408
409 \note This property is available in MenuBar since QtQuick.Controls 2.3 (Qt 5.10),
410 but it was promoted to the Container base type in QtQuick.Controls 2.5 (Qt 5.12).
411
412 \sa Container::contentWidth
413*/
414
415/*!
416 \since QtQuick.Controls 2.3 (Qt 5.10)
417 \qmlproperty real QtQuick.Controls::MenuBar::contentHeight
418
419 This property holds the content height. It is used for calculating the total
420 implicit height of the menu bar.
421
422 \note This property is available in MenuBar since QtQuick.Controls 2.3 (Qt 5.10),
423 but it was promoted to the Container base type in QtQuick.Controls 2.5 (Qt 5.12).
424
425 \sa Container::contentHeight
426*/
427
428/*!
429 \qmlproperty list<Menu> QtQuick.Controls::MenuBar::menus
430
431 This property holds the list of menus.
432
433 The list contains all menus that have been declared in QML as children
434 of the menu bar, and also menus that have been dynamically added or
435 inserted using the \l addMenu() and \l insertMenu() methods, respectively.
436*/
437QQmlListProperty<QQuickMenu> QQuickMenuBarPrivate::menus()
438{
439 Q_Q(QQuickMenuBar);
440 return QQmlListProperty<QQuickMenu>(q, nullptr,
441 QQuickMenuBarPrivate::menus_append,
442 QQuickMenuBarPrivate::menus_count,
443 QQuickMenuBarPrivate::menus_at,
444 QQuickMenuBarPrivate::menus_clear);
445}
446
447QQmlListProperty<QObject> QQuickMenuBarPrivate::contentData()
448{
449 Q_Q(QQuickMenuBar);
450 return QQmlListProperty<QObject>(q, nullptr,
451 QQuickMenuBarPrivate::contentData_append,
452 QQuickContainerPrivate::contentData_count,
453 QQuickContainerPrivate::contentData_at,
454 QQuickContainerPrivate::contentData_clear);
455}
456
457bool QQuickMenuBar::eventFilter(QObject *object, QEvent *event)
458{
459 return QObject::eventFilter(watched: object, event);
460}
461
462void QQuickMenuBar::keyPressEvent(QKeyEvent *event)
463{
464 Q_D(QQuickMenuBar);
465 QQuickContainer::keyReleaseEvent(event);
466
467 switch (event->key()) {
468 case Qt::Key_Up:
469 d->toggleCurrentMenu(visible: false, activate: false);
470 break;
471
472 case Qt::Key_Down:
473 d->toggleCurrentMenu(visible: true, activate: true);
474 break;
475
476 case Qt::Key_Left:
477 case Qt::Key_Right:
478 if (isMirrored() == (event->key() == Qt::Key_Left))
479 d->activateNextItem();
480 else
481 d->activatePreviousItem();
482 break;
483 case Qt::Key_Escape:
484 if (d->currentItem) {
485 d->activateItem(item: nullptr);
486 setFocus(false);
487 }
488 break;
489 default:
490 break;
491 }
492}
493
494void QQuickMenuBar::keyReleaseEvent(QKeyEvent *event)
495{
496 QQuickContainer::keyReleaseEvent(event);
497
498 switch (event->key()) {
499 case Qt::Key_Up:
500 case Qt::Key_Down:
501 case Qt::Key_Left:
502 case Qt::Key_Right:
503 case Qt::Key_Escape:
504 event->accept();
505 break;
506
507 default:
508 event->ignore();
509 break;
510 }
511}
512
513void QQuickMenuBar::hoverLeaveEvent(QHoverEvent *event)
514{
515 Q_D(QQuickMenuBar);
516 QQuickContainer::hoverLeaveEvent(event);
517 if (!d->popupMode && d->currentItem)
518 d->activateItem(item: nullptr);
519}
520
521bool QQuickMenuBar::isContent(QQuickItem *item) const
522{
523 return qobject_cast<QQuickMenuBarItem *>(object: item);
524}
525
526void QQuickMenuBar::itemAdded(int index, QQuickItem *item)
527{
528 Q_D(QQuickMenuBar);
529 QQuickContainer::itemAdded(index, item);
530 if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(object: item)) {
531 QQuickMenuBarItemPrivate::get(item: menuBarItem)->setMenuBar(this);
532 QObjectPrivate::connect(sender: menuBarItem, signal: &QQuickControl::hoveredChanged, receiverPrivate: d, slot: &QQuickMenuBarPrivate::onItemHovered);
533 QObjectPrivate::connect(sender: menuBarItem, signal: &QQuickMenuBarItem::triggered, receiverPrivate: d, slot: &QQuickMenuBarPrivate::onItemTriggered);
534 if (QQuickMenu *menu = menuBarItem->menu())
535 QObjectPrivate::connect(sender: menu, signal: &QQuickPopup::aboutToHide, receiverPrivate: d, slot: &QQuickMenuBarPrivate::onMenuAboutToHide);
536 }
537 d->updateImplicitContentSize();
538 emit menusChanged();
539}
540
541void QQuickMenuBar::itemMoved(int index, QQuickItem *item)
542{
543 QQuickContainer::itemMoved(index, item);
544 emit menusChanged();
545}
546
547void QQuickMenuBar::itemRemoved(int index, QQuickItem *item)
548{
549 Q_D(QQuickMenuBar);
550 QQuickContainer::itemRemoved(index, item);
551 if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(object: item)) {
552 QQuickMenuBarItemPrivate::get(item: menuBarItem)->setMenuBar(nullptr);
553 QObjectPrivate::disconnect(sender: menuBarItem, signal: &QQuickControl::hoveredChanged, receiverPrivate: d, slot: &QQuickMenuBarPrivate::onItemHovered);
554 QObjectPrivate::disconnect(sender: menuBarItem, signal: &QQuickMenuBarItem::triggered, receiverPrivate: d, slot: &QQuickMenuBarPrivate::onItemTriggered);
555 if (QQuickMenu *menu = menuBarItem->menu())
556 QObjectPrivate::disconnect(sender: menu, signal: &QQuickPopup::aboutToHide, receiverPrivate: d, slot: &QQuickMenuBarPrivate::onMenuAboutToHide);
557 }
558 d->updateImplicitContentSize();
559 emit menusChanged();
560}
561
562QFont QQuickMenuBar::defaultFont() const
563{
564 return QQuickTheme::font(scope: QQuickTheme::MenuBar);
565}
566
567QPalette QQuickMenuBar::defaultPalette() const
568{
569 return QQuickTheme::palette(scope: QQuickTheme::MenuBar);
570}
571
572#if QT_CONFIG(accessibility)
573QAccessible::Role QQuickMenuBar::accessibleRole() const
574{
575 return QAccessible::MenuBar;
576}
577#endif
578
579QT_END_NAMESPACE
580
581#include "moc_qquickmenubar_p.cpp"
582

source code of qtquickcontrols2/src/quicktemplates2/qquickmenubar.cpp