Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qquickmenubar.cpp
Go to the documentation of this file.
1
// Copyright (C) 2020 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:significant reason:default
4
5
#
include
"qquickmenubar_p.h"
6
#
include
"qquickmenubar_p_p.h"
7
#
include
"qquickmenubaritem_p_p.h"
8
#
include
"qquickmenu_p.h"
9
#
include
"qquickmenu_p_p.h"
10
11
#
include
<
QtGui
/
private
/
qguiapplication_p
.
h
>
12
#
include
<
QtGui
/
qpa
/
qplatformtheme
.
h
>
13
14
#
include
<
QtQml
/
qqmlcontext
.
h
>
15
#
include
<
QtQml
/
qqmlcomponent
.
h
>
16
#
include
<
QtQml
/
qqmlengine
.
h
>
17
18
QT_BEGIN_NAMESPACE
19
20
/*!
21
\qmltype MenuBar
22
\inherits Container
23
//! \nativetype QQuickMenuBar
24
\inqmlmodule QtQuick.Controls
25
\since 5.10
26
\ingroup qtquickcontrols-menus
27
\ingroup qtquickcontrols-focusscopes
28
\brief Provides a window menu bar.
29
30
\image qtquickcontrols-menubar.png
31
{Menu bar with File, Edit, and View menus}
32
33
MenuBar consists of drop-down menus, and is normally located at the top
34
edge of the window.
35
36
\quotefromfile qtquickcontrols-menubar.qml
37
\skipuntil begin
38
\printto skipfrom
39
\skipuntil skipto
40
\printto end
41
42
Typically, menus are statically declared as children of the menu bar, but
43
MenuBar also provides API to \l {addMenu}{add}, \l {insertMenu}{insert},
44
\l {removeMenu}{remove}, and \l {takeMenu}{take} menus dynamically. The
45
menus in a menu bar can be accessed using \l menuAt().
46
47
\section1 Native menu bars
48
49
Since Qt 6.8, a MenuBar is implemented as a native menu bar on \macos. As a
50
result, all Menus, MenuItems and MenuBarItems within a MenuBar will also be native.
51
While this has the advantage that everything will look native, it also comes with the
52
disadvantage that the delegates set on the mentioned controls will not be used
53
for rendering.
54
If a native MenuBar is not wanted, you can set
55
\l {Qt::AA_DontUseNativeMenuBar}{QGuiApplication::setAttribute(Qt::AA_DontUseNativeMenuBar)}
56
to disable it.
57
58
\sa {Customizing MenuBar}, Menu, MenuBarItem, {Menu Controls},
59
{Focus Management in Qt Quick Controls}
60
*/
61
62
Q_STATIC_LOGGING_CATEGORY(lcMenuBar,
"qt.quick.controls.menubar"
)
63
64
static
const
char
* kCreatedFromDelegate =
"_qt_createdFromDelegate"
;
65
66
QQuickItem *QQuickMenuBarPrivate::createItemFromDelegate()
67
{
68
Q_Q(QQuickMenuBar);
69
Q_ASSERT(delegate);
70
QQmlContext *context = delegate->creationContext();
71
if
(!context)
72
context = qmlContext(q);
73
74
QObject *object = delegate->beginCreate(context);
75
QQuickItem *item = qobject_cast<QQuickItem *>(object);
76
if
(!item) {
77
delete
object;
78
return
nullptr
;
79
}
80
81
QQml_setParent_noEvent(item, q);
82
delegate->completeCreate();
83
84
return
item;
85
}
86
87
QQuickMenuBarItem *QQuickMenuBarPrivate::createMenuBarItem(QQuickMenu *menu)
88
{
89
Q_Q(QQuickMenuBar);
90
91
QQuickMenuBarItem *menuBarItem =
nullptr
;
92
if
(delegate) {
93
QQuickItem *item = createItemFromDelegate();
94
menuBarItem = qobject_cast<QQuickMenuBarItem *>(item);
95
if
(!menuBarItem) {
96
qmlWarning(q) <<
"cannot insert menu: the delegate is not a MenuBarItem."
;
97
delete
item;
98
}
99
}
100
101
if
(!menuBarItem) {
102
// When we fail to create a delegate item, create a hidden placeholder
103
// instead. This is needed, since we store the menus inside the container
104
// using MenuBarItem. And without a MenuBarItem, we would therefore lose
105
// the menu, even if the delegate is changed later.
106
qCDebug(lcMenuBar) <<
"creating hidden placeholder MenuBarItem for:"
<< menu->title();
107
menuBarItem =
new
QQuickMenuBarItem(q);
108
menuBarItem->setParentItem(q);
109
menuBarItem->setVisible(
false
);
110
}
111
112
menuBarItem->setMenu(menu);
113
114
// Tag the menuBarItem, so that we know which container items to change if the
115
// delegate is changed. This is needed since you can add MenuBarItems directly
116
// to the menu bar, which should not change when the delegate changes.
117
menuBarItem->setProperty(kCreatedFromDelegate,
true
);
118
119
return
menuBarItem;
120
}
121
122
void
QQuickMenuBarPrivate::openCurrentMenu()
123
{
124
if
(!currentItem || currentMenuOpen)
125
return
;
126
QQuickMenu *menu = currentItem->menu();
127
if
(!menu || menu->isOpened())
128
return
;
129
130
#
ifdef
Q_OS_MACOS
131
// On macOS, the menu should open underneath the MenuBar
132
Q_Q(QQuickMenuBar);
133
const
QPointF posInParentItem = q->mapToItem(currentItem, {currentItem->x(), q->height()});
134
#
else
135
// On other platforms, it should open underneath the MenuBarItem
136
const
QPointF posInParentItem{0, currentItem->y() + currentItem->height()};
137
#
endif
138
139
// Store explicit if the current menu is logically supposed to be open.
140
// menu->isVisible() is async when using top-level menus, and will not become
141
// "true" before the menu is actually shown by the OS. This will cause us to
142
// lose track of if a menu is (supposed to be) open, if relying on menu->isVisible().
143
currentMenuOpen =
true
;
144
145
// The position should be the coordinate system of the parent item. Note that
146
// the parentItem() of a menu will be the MenuBarItem (currentItem), and not the
147
// MenuBar (even if parent() usually points to the MenuBar).
148
menu->popup(posInParentItem);
149
}
150
151
void
QQuickMenuBarPrivate::closeCurrentMenu()
152
{
153
if
(!currentItem || !currentMenuOpen)
154
return
;
155
currentMenuOpen =
false
;
156
QQuickMenu *menu = currentItem->menu();
157
QScopedValueRollback triggerRollback(closingCurrentMenu,
true
);
158
menu->dismiss();
159
}
160
161
void
QQuickMenuBarPrivate::activateMenuItem(
int
index)
162
{
163
if
(!currentItem)
164
return
;
165
QQuickMenu *menu = currentItem->menu();
166
if
(!menu)
167
return
;
168
menu->setCurrentIndex(index);
169
}
170
171
void
QQuickMenuBarPrivate::activateItem(QQuickMenuBarItem *item)
172
{
173
if
(currentItem == item)
174
return
;
175
176
const
bool
stayOpen = currentMenuOpen;
177
178
if
(currentItem) {
179
currentItem->setHighlighted(
false
);
180
closeCurrentMenu();
181
}
182
183
currentItem = item;
184
185
if
(currentItem) {
186
currentItem->setHighlighted(
true
);
187
if
(stayOpen)
188
openCurrentMenu();
189
}
190
}
191
192
void
QQuickMenuBarPrivate::activateNextItem()
193
{
194
int
index = currentItem ? contentModel->indexOf(currentItem,
nullptr
) : -1;
195
if
(index >= contentModel->count() - 1)
196
index = -1;
197
activateItem(qobject_cast<QQuickMenuBarItem *>(itemAt(++index)));
198
}
199
200
void
QQuickMenuBarPrivate::activatePreviousItem()
201
{
202
int
index = currentItem ? contentModel->indexOf(currentItem,
nullptr
) : contentModel->count();
203
if
(index <= 0)
204
index = contentModel->count();
205
activateItem(qobject_cast<QQuickMenuBarItem *>(itemAt(--index)));
206
}
207
208
void
QQuickMenuBarPrivate::onItemHovered()
209
{
210
Q_Q(QQuickMenuBar);
211
QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(q->sender());
212
if
(!item || item == currentItem || !item->isHovered() || !item->isEnabled() || QQuickMenuBarItemPrivate::get(item)->touchId != -1)
213
return
;
214
215
activateItem(item);
216
}
217
218
void
QQuickMenuBarPrivate::onItemTriggered()
219
{
220
Q_Q(QQuickMenuBar);
221
QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(q->sender());
222
if
(!item)
223
return
;
224
225
if
(item == currentItem) {
226
if
(currentMenuOpen) {
227
closeCurrentMenu();
228
currentItem->forceActiveFocus();
229
}
else
{
230
openCurrentMenu();
231
}
232
}
else
{
233
activateItem(item);
234
openCurrentMenu();
235
}
236
}
237
238
void
QQuickMenuBarPrivate::onMenuAboutToHide(QQuickMenu *menu)
239
{
240
if
(closingCurrentMenu) {
241
// We only react on a menu closing if it's
242
// initiated from outside of QQuickMenuBar.
243
return
;
244
}
245
246
if
(!currentItem || currentItem->menu() != menu)
247
return
;
248
249
currentMenuOpen =
false
;
250
251
if
(!currentItem->isHighlighted() || currentItem->isHovered())
252
return
;
253
254
activateItem(
nullptr
);
255
}
256
257
qreal QQuickMenuBarPrivate::getContentWidth()
const
258
{
259
Q_Q(
const
QQuickMenuBar);
260
const
int
count = contentModel->count();
261
qreal totalWidth = qMax(0, count - 1) * spacing;
262
for
(
int
i = 0; i < count; ++i) {
263
QQuickItem *item = q->itemAt(i);
264
if
(item)
265
totalWidth += item->implicitWidth();
266
}
267
return
totalWidth;
268
}
269
270
qreal QQuickMenuBarPrivate::getContentHeight()
const
271
{
272
Q_Q(
const
QQuickMenuBar);
273
const
int
count = contentModel->count();
274
qreal maxHeight = 0;
275
for
(
int
i = 0; i < count; ++i) {
276
QQuickItem *item = q->itemAt(i);
277
if
(item)
278
maxHeight = qMax(maxHeight, item->implicitHeight());
279
}
280
return
maxHeight;
281
}
282
283
void
QQuickMenuBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
284
{
285
QQuickContainerPrivate::itemImplicitWidthChanged(item);
286
if
(item != contentItem)
287
updateImplicitContentWidth();
288
}
289
290
void
QQuickMenuBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
291
{
292
QQuickContainerPrivate::itemImplicitHeightChanged(item);
293
if
(item != contentItem)
294
updateImplicitContentHeight();
295
}
296
297
void
QQuickMenuBarPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
298
{
299
auto
menuBar =
static_cast
<QQuickMenuBar *>(prop->object);
300
auto
menuBarPriv = QQuickMenuBarPrivate::get(menuBar);
301
302
if
(
auto
*menu = qobject_cast<QQuickMenu *>(obj)) {
303
QQuickMenuBarItem *delegateItem = menuBarPriv->createMenuBarItem(menu);
304
menuBarPriv->insertMenu(menuBar->count(), menu, delegateItem);
305
QQuickContainerPrivate::contentData_append(prop, delegateItem);
306
return
;
307
}
308
309
if
(
auto
*menuBarItem = qobject_cast<QQuickMenuBarItem *>(obj)) {
310
menuBarPriv->insertMenu(menuBar->count(), menuBarItem->menu(), menuBarItem);
311
QQuickContainerPrivate::contentData_append(prop, menuBarItem);
312
return
;
313
}
314
315
QQuickContainerPrivate::contentData_append(prop, obj);
316
}
317
318
void
QQuickMenuBarPrivate::menus_append(QQmlListProperty<QQuickMenu> *prop, QQuickMenu *obj)
319
{
320
// This function is only called if the application assigns a list of menus
321
// directly to the 'menus' property. Otherwise, contentData_append is used.
322
// Since the functions belonging to the 'menus' list anyway returns data from
323
// the menuBar, calls such as "menuBar.menus.length" works as expected
324
// regardless of how the menus were added.
325
QQuickMenuBar *menuBar =
static_cast
<QQuickMenuBar *>(prop->object);
326
menuBar->addMenu(obj);
327
}
328
329
qsizetype QQuickMenuBarPrivate::menus_count(QQmlListProperty<QQuickMenu> *prop)
330
{
331
QQuickMenuBar *menuBar =
static_cast
<QQuickMenuBar *>(prop->object);
332
return
menuBar->count();
333
}
334
335
QQuickMenu *QQuickMenuBarPrivate::menus_at(QQmlListProperty<QQuickMenu> *prop, qsizetype index)
336
{
337
QQuickMenuBar *menuBar =
static_cast
<QQuickMenuBar *>(prop->object);
338
return
menuBar->menuAt(index);
339
}
340
341
void
QQuickMenuBarPrivate::menus_clear(QQmlListProperty<QQuickMenu> *prop)
342
{
343
QQuickMenuBar *menuBar =
static_cast
<QQuickMenuBar *>(prop->object);
344
for
(
int
count = menuBar->count(); count > 0; count = menuBar->count())
345
menuBar->takeMenu(count - 1);
346
}
347
348
QPalette QQuickMenuBarPrivate::defaultPalette()
const
349
{
350
return
QQuickTheme::palette(QQuickTheme::MenuBar);
351
}
352
353
QWindow* QQuickMenuBarPrivate::window()
const
354
{
355
Q_Q(
const
QQuickMenuBar);
356
QObject *obj = q->parent();
357
while
(obj) {
358
if
(QWindow *window = qobject_cast<QWindow *>(obj))
359
return
window;
360
QQuickItem *item = qobject_cast<QQuickItem *>(obj);
361
if
(item && item->window())
362
return
item->window();
363
obj = obj->parent();
364
}
365
return
nullptr
;
366
}
367
368
int
QQuickMenuBarPrivate::menuIndex(QQuickMenu *menu)
const
369
{
370
Q_Q(
const
QQuickMenuBar);
371
for
(
int
i = 0; i < q->count(); ++i) {
372
if
(q->menuAt(i) == menu)
373
return
i;
374
}
375
376
return
-1;
377
}
378
379
QPlatformMenuBar* QQuickMenuBarPrivate::nativeHandle()
const
380
{
381
return
handle.get();
382
}
383
384
void
QQuickMenuBarPrivate::insertNativeMenu(QQuickMenu *menu)
385
{
386
Q_Q(QQuickMenuBar);
387
Q_ASSERT(handle);
388
Q_ASSERT(menu);
389
390
QPlatformMenu *insertBeforeHandle =
nullptr
;
391
392
// This function assumes that the QQuickMenuBarItem that corresponds to \a menu
393
// has already been added to the container at the correct index. So we search for
394
// it, to determine where to insert it in the native menubar. Since the QPA API
395
// expects a pointer to the QPlatformMenu that comes after it, we need to search
396
// for that one as well, since some MenuBarItems in the container can be hidden.
397
bool
foundInContainer =
false
;
398
for
(
int
i = 0; i < q->count(); ++i) {
399
if
(q->menuAt(i) != menu)
400
continue
;
401
foundInContainer =
true
;
402
403
for
(
int
j = i + 1; j < q->count(); ++j) {
404
insertBeforeHandle = QQuickMenuPrivate::get(q->menuAt(j))->maybeNativeHandle();
405
if
(insertBeforeHandle)
406
break
;
407
}
408
409
break
;
410
}
411
412
Q_ASSERT(foundInContainer);
413
QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
414
if
(QPlatformMenu *menuHandle = menuPrivate->nativeHandle()) {
415
qCDebug(lcMenuBar) <<
"insert native menu:"
<< menu->title() << menuHandle <<
"before:"
<< insertBeforeHandle;
416
handle->insertMenu(menuPrivate->nativeHandle(), insertBeforeHandle);
417
}
else
{
418
qmlWarning(q) <<
"failed to create native menu for:"
<< menu->title();
419
}
420
}
421
422
void
QQuickMenuBarPrivate::removeNativeMenu(QQuickMenu *menu)
423
{
424
Q_ASSERT(handle);
425
Q_ASSERT(menu);
426
427
QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
428
if
(!menuPrivate->maybeNativeHandle())
429
return
;
430
431
qCDebug(lcMenuBar) <<
"remove native menu:"
<< menu << menu->title();
432
handle->removeMenu(menuPrivate->nativeHandle());
433
menuPrivate->removeNativeMenu();
434
}
435
436
void
QQuickMenuBarPrivate::syncMenuBarItemVisibilty(QQuickMenuBarItem *menuBarItem)
437
{
438
if
(!handle) {
439
// We only need to update visibility on native menu bar items
440
return
;
441
}
442
443
QQuickMenu *menu = menuBarItem->menu();
444
if
(!menu)
445
return
;
446
QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
447
448
if
(menuBarItem->isVisible()) {
449
Q_ASSERT(!menuPrivate->maybeNativeHandle());
450
insertNativeMenu(menu);
451
}
else
{
452
if
(menuPrivate->maybeNativeHandle())
453
removeNativeMenu(menu);
454
}
455
}
456
457
void
QQuickMenuBarPrivate::insertMenu(
int
index, QQuickMenu *menu, QQuickMenuBarItem *menuBarItem)
458
{
459
Q_Q(QQuickMenuBar);
460
if
(!menu) {
461
qmlWarning(q) <<
"cannot insert menu: menu is null."
;
462
return
;
463
}
464
465
auto
menuPrivate = QQuickMenuPrivate::get(menu);
466
menuPrivate->menuBar = q;
467
468
QObject::connect(menuBarItem, &QQuickMenuBarItem::visibleChanged, q, [
this
, menuBarItem]{
469
syncMenuBarItemVisibilty(menuBarItem);
470
});
471
472
// Always insert menu into the container, even when using a native
473
// menubar, so that container API such as 'count' and 'itemAt'
474
// continues to work as expected.
475
q->insertItem(index, menuBarItem);
476
477
// Create or remove a native (QPlatformMenu) menu. Note that we should only create
478
// a native menu if it's supposed to be visible in the menu bar.
479
if
(menuBarItem->isVisible()) {
480
if
(handle)
481
insertNativeMenu(menu);
482
}
else
{
483
if
(menuPrivate->maybeNativeHandle()) {
484
// If the menu was added from an explicit call to addMenu(m), it will have been
485
// created before we enter here. And in that case, QQuickMenuBar::useNativeMenu(m)
486
// was never called, and a QPlatformMenu might have been created for it. In that
487
// case, we remove it again now, since the menu is not supposed to be visible in
488
// the menu bar.
489
menuPrivate->removeNativeMenu();
490
}
491
}
492
}
493
494
QQuickMenu *QQuickMenuBarPrivate::takeMenu(
int
index)
495
{
496
Q_Q(QQuickMenuBar);
497
QQuickItem *item = q->itemAt(index);
498
Q_ASSERT(item);
499
QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(item);
500
if
(!menuBarItem) {
501
qmlWarning(q) <<
"cannot take/remove menu: item at index "
<< index <<
" is not a MenuBarItem."
;
502
return
nullptr
;
503
}
504
QQuickMenu *menu = menuBarItem->menu();
505
if
(!menu) {
506
qmlWarning(q) <<
"cannot take/remove menu: MenuBarItem.menu at index "
<< index <<
" is null."
;
507
return
nullptr
;
508
}
509
510
// Dismiss the menu if it's open. Otherwise, when we now remove it from
511
// the menubar, it will stay open without the user being able to dismiss
512
// it (at least if it's non-native).
513
menu->dismiss();
514
515
if
(item == currentItem)
516
activateItem(
nullptr
);
517
518
if
(QQuickMenuPrivate::get(menu)->maybeNativeHandle())
519
removeNativeMenu(menu);
520
521
removeItem(index, item);
522
523
// Delete the MenuBarItem. This will also cause the menu to be deleted by
524
// the garbage collector, unless other QML references are being held to it.
525
// Note: We might consider leaving it to the garbage collector to also
526
// delete the MenuBarItem in the future.
527
item->deleteLater();
528
529
QQuickMenuPrivate::get(menu)->menuBar =
nullptr
;
530
menuBarItem->disconnect(q);
531
532
return
menu;
533
}
534
535
bool
QQuickMenuBarPrivate::useNativeMenuBar()
const
536
{
537
// We current only use native menu bars on macOS. Especially, the
538
// QPA menu bar for Windows is old and unused, and looks broken and non-native.
539
#
ifdef
Q_OS_MACOS
540
return
!QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuBar);
541
#
else
542
return
false
;
543
#
endif
544
}
545
546
bool
QQuickMenuBarPrivate::useNativeMenu(
const
QQuickMenu *menu)
const
547
{
548
Q_Q(
const
QQuickMenuBar);
549
if
(!useNativeMenuBar())
550
return
false
;
551
552
// Since we cannot hide a QPlatformMenu, we have to avoid
553
// creating it if it shouldn't be visible in the menu bar.
554
for
(
int
i = 0; i < q->count(); ++i) {
555
if
(q->menuAt(i) == menu) {
556
QQuickItem *itemAtI = itemAt(i);
557
return
itemAtI && itemAtI->isVisible();
558
}
559
}
560
561
return
true
;
562
}
563
564
void
QQuickMenuBarPrivate::syncNativeMenuBarVisible()
565
{
566
Q_Q(QQuickMenuBar);
567
if
(!componentComplete)
568
return
;
569
570
const
bool
shouldBeVisible = q->isVisible() && useNativeMenuBar();
571
qCDebug(lcMenuBar) <<
"syncNativeMenuBarVisible called - q->isVisible()"
<< q->isVisible()
572
<<
"useNativeMenuBar()"
<< useNativeMenuBar() <<
"handle"
<< handle.get();
573
if
(shouldBeVisible && !handle)
574
createNativeMenuBar();
575
else
if
(!shouldBeVisible && handle)
576
removeNativeMenuBar();
577
}
578
579
void
QQuickMenuBarPrivate::createNativeMenuBar()
580
{
581
Q_Q(QQuickMenuBar);
582
Q_ASSERT(!handle);
583
qCDebug(lcMenuBar) <<
"creating native menubar"
;
584
585
handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformMenuBar());
586
if
(!handle) {
587
qCDebug(lcMenuBar) <<
"QPlatformTheme failed to create a QPlatformMenuBar!"
;
588
return
;
589
}
590
591
handle->handleReparent(window());
592
qCDebug(lcMenuBar) <<
"native menubar parented to window:"
<< handle->parentWindow();
593
594
// Add all the native menus. We need to do this right-to-left
595
// because of the QPA API (insertBefore).
596
for
(
int
i = q->count() - 1; i >= 0; --i) {
597
if
(QQuickMenu *menu = q->menuAt(i)) {
598
if
(useNativeMenu(menu))
599
insertNativeMenu(menu);
600
}
601
}
602
603
// Hide the non-native menubar and set it's height to 0. The
604
// latter will cause a relayout to happen in ApplicationWindow
605
// which effectively removes the menubar from the contentItem.
606
setCulled(
true
);
607
q->setHeight(0);
608
}
609
610
void
QQuickMenuBarPrivate::removeNativeMenuBar()
611
{
612
Q_Q(QQuickMenuBar);
613
Q_ASSERT(handle);
614
qCDebug(lcMenuBar) <<
"removing native menubar"
;
615
616
// Remove all native menus.
617
for
(
int
i = 0; i < q->count(); ++i) {
618
if
(QQuickMenu *menu = q->menuAt(i))
619
removeNativeMenu(menu);
620
}
621
622
// Delete the menubar
623
handle.reset();
624
625
// Show the non-native menubar and reset it's height. The
626
// latter will cause a relayout to happen in ApplicationWindow
627
// which will effectively add the menubar to the contentItem.
628
setCulled(
false
);
629
q->resetHeight();
630
}
631
632
QQuickMenuBar::QQuickMenuBar(QQuickItem *parent)
633
: QQuickContainer(*(
new
QQuickMenuBarPrivate), parent)
634
{
635
Q_D(QQuickMenuBar);
636
d->changeTypes |= QQuickItemPrivate::Geometry;
637
setFlag(ItemIsFocusScope);
638
setFocusPolicy(Qt::ClickFocus);
639
}
640
641
QQuickMenuBar::~QQuickMenuBar()
642
{
643
Q_D(QQuickMenuBar);
644
if
(d->handle)
645
d->removeNativeMenuBar();
646
}
647
648
/*!
649
\qmlproperty Component QtQuick.Controls::MenuBar::delegate
650
651
This property holds the component that is used to create menu bar
652
items to present menus in the menu bar.
653
654
\include delegate-ownership.qdocinc {no-ownership} {MenuBar}
655
656
\sa MenuBarItem
657
*/
658
QQmlComponent *QQuickMenuBar::delegate()
const
659
{
660
Q_D(
const
QQuickMenuBar);
661
return
d->delegate;
662
}
663
664
void
QQuickMenuBar::setDelegate(QQmlComponent *delegate)
665
{
666
Q_D(QQuickMenuBar);
667
if
(d->delegate == delegate)
668
return
;
669
670
d->delegate = delegate;
671
672
for
(
int
i = count() - 1; i >= 0; --i) {
673
auto
item = itemAt(i);
674
if
(!item || !item->property(kCreatedFromDelegate).toBool())
675
continue
;
676
677
QQuickMenuBarItem *menuBarItem =
static_cast
<QQuickMenuBarItem *>(item);
678
if
(QQuickMenu *menu = menuBarItem->menu()) {
679
removeMenu(menu);
680
d->insertMenu(i, menu, d->createMenuBarItem(menu));
681
}
else
{
682
removeItem(menuBarItem);
683
}
684
}
685
686
emit delegateChanged();
687
}
688
689
/*!
690
\qmlmethod Menu QtQuick.Controls::MenuBar::menuAt(int index)
691
692
Returns the menu at \a index, or \c null if it does not exist.
693
*/
694
QQuickMenu *QQuickMenuBar::menuAt(
int
index)
const
695
{
696
Q_D(
const
QQuickMenuBar);
697
QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(d->itemAt(index));
698
if
(!item)
699
return
nullptr
;
700
return
item->menu();
701
}
702
703
/*!
704
\qmlmethod void QtQuick.Controls::MenuBar::addMenu(Menu menu)
705
706
Adds \a menu to the end of the list of menus.
707
*/
708
void
QQuickMenuBar::addMenu(QQuickMenu *menu)
709
{
710
Q_D(QQuickMenuBar);
711
if
(d->menuIndex(menu) >= 0) {
712
qmlWarning(
this
) <<
"cannot add menu: '"
<< menu->title() <<
"' is already in the MenuBar."
;
713
return
;
714
}
715
716
d->insertMenu(count(), menu, d->createMenuBarItem(menu));
717
}
718
719
/*!
720
\qmlmethod void QtQuick.Controls::MenuBar::insertMenu(int index, Menu menu)
721
722
Inserts \a menu at \a index.
723
*/
724
void
QQuickMenuBar::insertMenu(
int
index, QQuickMenu *menu)
725
{
726
Q_D(QQuickMenuBar);
727
if
(d->menuIndex(menu) >= 0) {
728
qmlWarning(
this
) <<
"cannot insert menu: '"
<< menu->title() <<
"' is already in the MenuBar."
;
729
return
;
730
}
731
732
d->insertMenu(index, menu, d->createMenuBarItem(menu));
733
}
734
735
/*!
736
\qmlmethod void QtQuick.Controls::MenuBar::removeMenu(Menu menu)
737
738
Removes specified \a menu. If the menu is \l {Menu::popup()}{open},
739
it will first be \l {Menu::dismiss()}{dismissed}.
740
The \a menu will eventually be deleted by the garbage collector when the
741
application no longer holds any QML references to it.
742
*/
743
void
QQuickMenuBar::removeMenu(QQuickMenu *menu)
744
{
745
Q_D(QQuickMenuBar);
746
const
int
index = d->menuIndex(menu);
747
if
(index < 0) {
748
qmlWarning(
this
) <<
"cannot remove menu: '"
<< menu->title() <<
"' is not in the MenuBar."
;
749
return
;
750
}
751
752
d->takeMenu(index);
753
}
754
755
/*!
756
\qmlmethod Menu QtQuick.Controls::MenuBar::takeMenu(int index)
757
758
Removes and returns the menu at \a index. If the menu is
759
\l {Menu::popup()}{open}, it will first be
760
\l {Menu::dismiss()}{dismissed}.
761
The menu will eventually be deleted by the garbage collector when the
762
application no longer holds any QML references to it.
763
*/
764
QQuickMenu *QQuickMenuBar::takeMenu(
int
index)
765
{
766
Q_D(QQuickMenuBar);
767
if
(index < 0 || index > count() - 1) {
768
qmlWarning(
this
) <<
"index out of range: "
<< index;
769
return
nullptr
;
770
}
771
772
return
d->takeMenu(index);
773
}
774
775
/*!
776
\qmlproperty list<Menu> QtQuick.Controls::MenuBar::menus
777
778
This property holds the list of menus.
779
780
The list contains all menus that have been declared in QML as children
781
of the menu bar, and also menus that have been dynamically added or
782
inserted using the \l addMenu() and \l insertMenu() methods, respectively.
783
*/
784
QQmlListProperty<QQuickMenu> QQuickMenuBarPrivate::menus()
785
{
786
Q_Q(QQuickMenuBar);
787
return
QQmlListProperty<QQuickMenu>(q,
nullptr
,
788
QQuickMenuBarPrivate::menus_append,
789
QQuickMenuBarPrivate::menus_count,
790
QQuickMenuBarPrivate::menus_at,
791
QQuickMenuBarPrivate::menus_clear);
792
}
793
794
QQmlListProperty<QObject> QQuickMenuBarPrivate::contentData()
795
{
796
Q_Q(QQuickMenuBar);
797
return
QQmlListProperty<QObject>(q,
nullptr
,
798
QQuickMenuBarPrivate::contentData_append,
799
QQuickContainerPrivate::contentData_count,
800
QQuickContainerPrivate::contentData_at,
801
QQuickContainerPrivate::contentData_clear);
802
}
803
804
bool
QQuickMenuBar::eventFilter(QObject *object, QEvent *event)
805
{
806
Q_D(QQuickMenuBar);
807
808
if
(d->altPressed) {
809
switch
(event->type()) {
810
case
QEvent::KeyRelease: {
811
const
QKeyEvent *keyEvent =
static_cast
<
const
QKeyEvent *>(event);
812
if
((keyEvent->key() == Qt::Key_Alt || keyEvent->key() == Qt::Key_Meta)
813
&& keyEvent->modifiers() == Qt::NoModifier) {
814
for
(
int
i = 0; i < count(); ++i) {
815
if
(
auto
*item = qobject_cast<QQuickMenuBarItem *>(d->itemAt(i))) {
816
d->activateItem(item);
817
setFocusReason(Qt::MenuBarFocusReason);
818
setFocus(
true
);
819
break
;
820
}
821
}
822
}
823
Q_FALLTHROUGH();
824
}
825
case
QEvent::MouseButtonPress:
826
case
QEvent::MouseButtonRelease:
827
case
QEvent::MouseMove:
828
case
QEvent::TabletPress:
829
case
QEvent::TabletMove:
830
case
QEvent::TabletRelease:
831
case
QEvent::TouchBegin:
832
case
QEvent::TouchUpdate:
833
case
QEvent::TouchEnd:
834
case
QEvent::FocusIn:
835
case
QEvent::FocusOut:
836
case
QEvent::ActivationChange:
837
case
QEvent::Shortcut:
838
d->altPressed =
false
;
839
qApp->removeEventFilter(
this
);
840
break
;
841
default
:
842
break
;
843
}
844
}
else
if
(isVisible() && event->type() == QEvent::ShortcutOverride) {
845
const
bool
altKeyNavigation = QGuiApplicationPrivate::platformTheme()
846
->themeHint(QPlatformTheme::MenuBarFocusOnAltPressRelease).toBool();
847
if
(altKeyNavigation) {
848
const
QKeyEvent *keyEvent =
static_cast
<
const
QKeyEvent *>(event);
849
if
((keyEvent->key() == Qt::Key_Alt || keyEvent->key() == Qt::Key_Meta)
850
&& keyEvent->modifiers() == Qt::AltModifier) {
851
d->altPressed =
true
;
852
qApp->installEventFilter(
this
);
853
}
854
}
855
}
856
return
QObject::eventFilter(object, event);
857
}
858
859
void
QQuickMenuBar::keyPressEvent(QKeyEvent *event)
860
{
861
Q_D(QQuickMenuBar);
862
QQuickContainer::keyReleaseEvent(event);
863
864
switch
(event->key()) {
865
case
Qt::Key_Up:
866
d->closeCurrentMenu();
867
break
;
868
869
case
Qt::Key_Down:
870
d->openCurrentMenu();
871
d->activateMenuItem(0);
872
break
;
873
874
case
Qt::Key_Left:
875
case
Qt::Key_Right:
876
if
(isMirrored() == (event->key() == Qt::Key_Left))
877
d->activateNextItem();
878
else
879
d->activatePreviousItem();
880
break
;
881
// This is triggered when no popup is open but a menu bar item is highlighted and has focus.
882
case
Qt::Key_Escape:
883
if
(d->currentItem) {
884
d->activateItem(
nullptr
);
885
setFocus(
false
);
886
}
887
break
;
888
default
:
889
#
if
QT_CONFIG
(
shortcut
)
890
if
(!event->text().isEmpty() && event->modifiers() == Qt::NoModifier) {
891
const
QKeyCombination mnemonic(Qt::AltModifier, Qt::Key(event->key()));
892
for
(
int
i = 0; i < count(); ++i) {
893
if
(
auto
*item = qobject_cast<QQuickMenuBarItem *>(d->itemAt(i))) {
894
if
(item->shortcut() == mnemonic) {
895
d->activateItem(item);
896
d->openCurrentMenu();
897
d->activateMenuItem(0);
898
}
899
}
900
}
901
}
902
#
endif
903
break
;
904
}
905
}
906
907
void
QQuickMenuBar::keyReleaseEvent(QKeyEvent *event)
908
{
909
QQuickContainer::keyReleaseEvent(event);
910
911
switch
(event->key()) {
912
case
Qt::Key_Up:
913
case
Qt::Key_Down:
914
case
Qt::Key_Left:
915
case
Qt::Key_Right:
916
case
Qt::Key_Escape:
917
event->accept();
918
break
;
919
920
default
:
921
event->ignore();
922
break
;
923
}
924
}
925
926
void
QQuickMenuBar::hoverLeaveEvent(QHoverEvent *event)
927
{
928
Q_D(QQuickMenuBar);
929
QQuickContainer::hoverLeaveEvent(event);
930
if
(!d->currentMenuOpen && d->currentItem)
931
d->activateItem(
nullptr
);
932
}
933
934
bool
QQuickMenuBar::isContent(QQuickItem *item)
const
935
{
936
return
qobject_cast<QQuickMenuBarItem *>(item);
937
}
938
939
void
QQuickMenuBar::itemChange(QQuickItem::ItemChange change,
const
QQuickItem::ItemChangeData &value)
940
{
941
Q_D(QQuickMenuBar);
942
QQuickContainer::itemChange(change, value);
943
switch
(change) {
944
case
ItemSceneChange:
945
if
(d->windowContentItem)
946
d->windowContentItem->removeEventFilter(
this
);
947
if
(value.window) {
948
d->windowContentItem = value.window->contentItem();
949
if
(d->windowContentItem)
950
d->windowContentItem->installEventFilter(
this
);
951
}
952
break
;
953
case
ItemVisibleHasChanged:
954
qCDebug(lcMenuBar) <<
"visibility of"
<<
this
<<
"changed to"
<< isVisible();
955
d->syncNativeMenuBarVisible();
956
break
;
957
default
:
958
break
;
959
}
960
}
961
962
void
QQuickMenuBar::itemAdded(
int
index, QQuickItem *item)
963
{
964
Q_D(QQuickMenuBar);
965
QQuickContainer::itemAdded(index, item);
966
if
(QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(item)) {
967
QQuickMenuBarItemPrivate::get(menuBarItem)->setMenuBar(
this
);
968
QObjectPrivate::connect(menuBarItem, &QQuickControl::hoveredChanged, d, &QQuickMenuBarPrivate::onItemHovered);
969
QObjectPrivate::connect(menuBarItem, &QQuickMenuBarItem::triggered, d, &QQuickMenuBarPrivate::onItemTriggered);
970
if
(QQuickMenu *menu = menuBarItem->menu())
971
connect(menu, &QQuickPopup::aboutToHide,
this
,
972
[
this
, menu] { d_func()->onMenuAboutToHide(menu); });
973
}
974
d->updateImplicitContentSize();
975
emit menusChanged();
976
}
977
978
void
QQuickMenuBar::itemMoved(
int
index, QQuickItem *item)
979
{
980
QQuickContainer::itemMoved(index, item);
981
emit menusChanged();
982
}
983
984
void
QQuickMenuBar::itemRemoved(
int
index, QQuickItem *item)
985
{
986
Q_D(QQuickMenuBar);
987
QQuickContainer::itemRemoved(index, item);
988
if
(QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(item)) {
989
QQuickMenuBarItemPrivate::get(menuBarItem)->setMenuBar(
nullptr
);
990
QObjectPrivate::disconnect(menuBarItem, &QQuickControl::hoveredChanged, d, &QQuickMenuBarPrivate::onItemHovered);
991
QObjectPrivate::disconnect(menuBarItem, &QQuickMenuBarItem::triggered, d, &QQuickMenuBarPrivate::onItemTriggered);
992
if
(QQuickMenu *menu = menuBarItem->menu())
993
menu->disconnect(
this
);
994
}
995
d->updateImplicitContentSize();
996
emit menusChanged();
997
}
998
999
void
QQuickMenuBar::componentComplete()
1000
{
1001
Q_D(QQuickMenuBar);
1002
QQuickContainer::componentComplete();
1003
d->syncNativeMenuBarVisible();
1004
}
1005
1006
QFont QQuickMenuBar::defaultFont()
const
1007
{
1008
return
QQuickTheme::font(QQuickTheme::MenuBar);
1009
}
1010
1011
#
if
QT_CONFIG
(
accessibility
)
1012
QAccessible::Role QQuickMenuBar::accessibleRole()
const
1013
{
1014
return
QAccessible::MenuBar;
1015
}
1016
#
endif
1017
1018
QT_END_NAMESPACE
1019
1020
#
include
"moc_qquickmenubar_p.cpp"
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
qtdeclarative
src
quicktemplates
qquickmenubar.cpp
Generated on
for Qt by
1.18.0