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 | |
47 | QT_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 | |
79 | QQuickItem *QQuickMenuBarPrivate::(QQuickMenu *) |
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 * = 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 | |
107 | void QQuickMenuBarPrivate::() |
108 | { |
109 | if (!delegate) |
110 | return; |
111 | |
112 | delegate->completeCreate(); |
113 | } |
114 | |
115 | QQuickItem *QQuickMenuBarPrivate::(QQuickMenu *) |
116 | { |
117 | QQuickItem *item = beginCreateItem(menu); |
118 | completeCreateItem(); |
119 | return item; |
120 | } |
121 | |
122 | void QQuickMenuBarPrivate::(bool visible, bool activate) |
123 | { |
124 | if (!currentItem || visible == popupMode) |
125 | return; |
126 | |
127 | QQuickMenu * = 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 | |
140 | void QQuickMenuBarPrivate::(QQuickMenuBarItem *item) |
141 | { |
142 | if (currentItem == item) |
143 | return; |
144 | |
145 | if (currentItem) { |
146 | currentItem->setHighlighted(false); |
147 | if (popupMode) { |
148 | if (QQuickMenu * = currentItem->menu()) |
149 | menu->dismiss(); |
150 | } |
151 | } |
152 | |
153 | if (item) { |
154 | item->setHighlighted(true); |
155 | if (popupMode) { |
156 | if (QQuickMenu * = item->menu()) |
157 | menu->open(); |
158 | } |
159 | } |
160 | |
161 | currentItem = item; |
162 | } |
163 | |
164 | void QQuickMenuBarPrivate::() |
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 | |
172 | void QQuickMenuBarPrivate::() |
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 | |
180 | void QQuickMenuBarPrivate::() |
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 | |
190 | void QQuickMenuBarPrivate::() |
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 | |
205 | void QQuickMenuBarPrivate::() |
206 | { |
207 | if (triggering || !currentItem || currentItem->isHovered() || !currentItem->isHighlighted()) |
208 | return; |
209 | |
210 | popupMode = false; |
211 | activateItem(item: nullptr); |
212 | } |
213 | |
214 | qreal QQuickMenuBarPrivate::() 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 | |
227 | qreal QQuickMenuBarPrivate::() 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 | |
240 | void QQuickMenuBarPrivate::(QQuickItem *item) |
241 | { |
242 | QQuickContainerPrivate::itemImplicitWidthChanged(item); |
243 | if (item != contentItem) |
244 | updateImplicitContentWidth(); |
245 | } |
246 | |
247 | void QQuickMenuBarPrivate::(QQuickItem *item) |
248 | { |
249 | QQuickContainerPrivate::itemImplicitHeightChanged(item); |
250 | if (item != contentItem) |
251 | updateImplicitContentHeight(); |
252 | } |
253 | |
254 | void QQuickMenuBarPrivate::(QQmlListProperty<QObject> *prop, QObject *obj) |
255 | { |
256 | QQuickMenuBar * = static_cast<QQuickMenuBar *>(prop->object); |
257 | if (QQuickMenu * = qobject_cast<QQuickMenu *>(object: obj)) |
258 | obj = QQuickMenuBarPrivate::get(menuBar)->createItem(menu); |
259 | QQuickContainerPrivate::contentData_append(prop, obj); |
260 | } |
261 | |
262 | void QQuickMenuBarPrivate::(QQmlListProperty<QQuickMenu> *prop, QQuickMenu *obj) |
263 | { |
264 | QQuickMenuBar * = static_cast<QQuickMenuBar *>(prop->object); |
265 | menuBar->addMenu(menu: obj); |
266 | } |
267 | |
268 | int QQuickMenuBarPrivate::(QQmlListProperty<QQuickMenu> *prop) |
269 | { |
270 | QQuickMenuBar * = static_cast<QQuickMenuBar *>(prop->object); |
271 | return menuBar->count(); |
272 | } |
273 | |
274 | QQuickMenu *QQuickMenuBarPrivate::(QQmlListProperty<QQuickMenu> *prop, int index) |
275 | { |
276 | QQuickMenuBar * = static_cast<QQuickMenuBar *>(prop->object); |
277 | return menuBar->menuAt(index); |
278 | } |
279 | |
280 | void QQuickMenuBarPrivate::(QQmlListProperty<QQuickMenu> *prop) |
281 | { |
282 | QQuickMenuBar * = static_cast<QQuickMenuBar *>(prop->object); |
283 | QQuickMenuBarPrivate::get(menuBar)->contentModel->clear(); |
284 | } |
285 | |
286 | 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 | */ |
303 | QQmlComponent *QQuickMenuBar::() const |
304 | { |
305 | Q_D(const QQuickMenuBar); |
306 | return d->delegate; |
307 | } |
308 | |
309 | void QQuickMenuBar::(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 | */ |
324 | QQuickMenu *QQuickMenuBar::(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 | */ |
338 | void QQuickMenuBar::(QQuickMenu *) |
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 | */ |
349 | void QQuickMenuBar::(int index, QQuickMenu *) |
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 | */ |
360 | void QQuickMenuBar::(QQuickMenu *) |
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 | */ |
386 | QQuickMenu *QQuickMenuBar::(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 * = 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 | */ |
437 | QQmlListProperty<QQuickMenu> QQuickMenuBarPrivate::() |
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 | |
447 | QQmlListProperty<QObject> QQuickMenuBarPrivate::() |
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 | |
457 | bool QQuickMenuBar::(QObject *object, QEvent *event) |
458 | { |
459 | return QObject::eventFilter(watched: object, event); |
460 | } |
461 | |
462 | void QQuickMenuBar::(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 | |
494 | void QQuickMenuBar::(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 | |
513 | void QQuickMenuBar::(QHoverEvent *event) |
514 | { |
515 | Q_D(QQuickMenuBar); |
516 | QQuickContainer::hoverLeaveEvent(event); |
517 | if (!d->popupMode && d->currentItem) |
518 | d->activateItem(item: nullptr); |
519 | } |
520 | |
521 | bool QQuickMenuBar::(QQuickItem *item) const |
522 | { |
523 | return qobject_cast<QQuickMenuBarItem *>(object: item); |
524 | } |
525 | |
526 | void QQuickMenuBar::(int index, QQuickItem *item) |
527 | { |
528 | Q_D(QQuickMenuBar); |
529 | QQuickContainer::itemAdded(index, item); |
530 | if (QQuickMenuBarItem * = 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 * = menuBarItem->menu()) |
535 | QObjectPrivate::connect(sender: menu, signal: &QQuickPopup::aboutToHide, receiverPrivate: d, slot: &QQuickMenuBarPrivate::onMenuAboutToHide); |
536 | } |
537 | d->updateImplicitContentSize(); |
538 | emit menusChanged(); |
539 | } |
540 | |
541 | void QQuickMenuBar::(int index, QQuickItem *item) |
542 | { |
543 | QQuickContainer::itemMoved(index, item); |
544 | emit menusChanged(); |
545 | } |
546 | |
547 | void QQuickMenuBar::(int index, QQuickItem *item) |
548 | { |
549 | Q_D(QQuickMenuBar); |
550 | QQuickContainer::itemRemoved(index, item); |
551 | if (QQuickMenuBarItem * = 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 * = menuBarItem->menu()) |
556 | QObjectPrivate::disconnect(sender: menu, signal: &QQuickPopup::aboutToHide, receiverPrivate: d, slot: &QQuickMenuBarPrivate::onMenuAboutToHide); |
557 | } |
558 | d->updateImplicitContentSize(); |
559 | emit menusChanged(); |
560 | } |
561 | |
562 | QFont QQuickMenuBar::() const |
563 | { |
564 | return QQuickTheme::font(scope: QQuickTheme::MenuBar); |
565 | } |
566 | |
567 | QPalette QQuickMenuBar::() const |
568 | { |
569 | return QQuickTheme::palette(scope: QQuickTheme::MenuBar); |
570 | } |
571 | |
572 | #if QT_CONFIG(accessibility) |
573 | QAccessible::Role QQuickMenuBar::() const |
574 | { |
575 | return QAccessible::MenuBar; |
576 | } |
577 | #endif |
578 | |
579 | QT_END_NAMESPACE |
580 | |
581 | #include "moc_qquickmenubar_p.cpp" |
582 | |