8#include <private/qtquicktemplates2-config_p.h>
9#if QT_CONFIG(quicktemplates2_container)
10#include "qquickmenubaritem_p.h"
11#include "qquickmenubar_p_p.h"
21#include <QtCore/qloggingcategory.h>
22#include <QtGui/qevent.h>
23#include <QtGui/qcursor.h>
24#if QT_CONFIG(shortcut)
25#include <QtGui/qkeysequence.h>
27#include <QtGui/qpa/qplatformintegration.h>
28#include <QtGui/qpa/qplatformtheme.h>
29#include <QtGui/private/qhighdpiscaling_p.h>
30#include <QtGui/private/qguiapplication_p.h>
31#include <QtQml/qqmlcontext.h>
32#include <QtQml/qqmlcomponent.h>
33#include <QtQml/private/qqmlengine_p.h>
34#include <QtQml/private/qqmldata_p.h>
35#include <QtQml/private/qv4scopedvalue_p.h>
36#include <QtQml/private/qv4variantobject_p.h>
37#include <QtQml/private/qv4qobjectwrapper_p.h>
38#include <private/qqmlobjectmodel_p.h>
39#include <QtQuick/private/qquickitem_p.h>
40#include <QtQuick/private/qquickitemchangelistener_p.h>
41#include <QtQuick/private/qquickitemview_p_p.h>
42#include <QtQuick/private/qquickevents_p_p.h>
43#include <QtQuick/private/qquicklistview_p.h>
44#include <QtQuick/private/qquickrendercontrol_p.h>
45#include <QtQuick/private/qquickwindow_p.h>
49Q_STATIC_LOGGING_CATEGORY(lcMenu,
"qt.quick.controls.menu")
50Q_STATIC_LOGGING_CATEGORY(lcNativeMenus,
"qt.quick.controls.nativemenus")
53static const int SUBMENU_DELAY = 225;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
254
255
256
257
258
259
260
261
262
263
264
265
266
267
274 return QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows);
288QQuickMenuPrivate::QQuickMenuPrivate()
290 cascade = shouldCascade();
291#if QT_CONFIG(wayland)
292 extendedWindowType = QNativeInterface::Private::QWaylandWindow::Menu;
296void QQuickMenuPrivate::init()
299 contentModel =
new QQmlObjectModel(q);
302QQuickMenu *QQuickMenuPrivate::rootMenu()
const
304 Q_Q(
const QQuickMenu);
305 const QQuickMenu *rootMenu = q;
306 const QObject *p = q->parent();
308 if (
auto menu = qobject_cast<
const QQuickMenu *>(p))
313 return const_cast<QQuickMenu *>(rootMenu);
316QQuickPopup::PopupType QQuickMenuPrivate::resolvedPopupType()
const
320 QQuickMenu *root = rootMenu();
321 QQuickMenuPrivate *root_d = QQuickMenuPrivate::get(rootMenu());
323#if QT_CONFIG(quicktemplates2_container)
324 if (
auto menuBar = QQuickMenuPrivate::get(root)->menuBar.get()) {
329 if (QQuickMenuBarPrivate::get(menuBar)->useNativeMenu(q_func()))
330 return QQuickPopup::Native;
335 if (root_d->maybeNativeHandle()) {
336 return QQuickPopup::Native;
337 }
else if (!root_d->triedToCreateNativeMenu) {
343 if (root->popupType() == QQuickPopup::Native
344 && !QGuiApplication::testAttribute(Qt::AA_DontUseNativeMenuWindows)) {
345 return QQuickPopup::Native;
353 const auto type = root_d->QQuickPopupPrivate::resolvedPopupType();
354 Q_ASSERT(type != QQuickPopup::Native);
358bool QQuickMenuPrivate::useNativeMenu()
const
360 return resolvedPopupType() == QQuickPopup::Native;
363QPlatformMenu *QQuickMenuPrivate::nativeHandle()
365 Q_ASSERT(handle || useNativeMenu());
366 if (!handle && !triedToCreateNativeMenu)
371QPlatformMenu *QQuickMenuPrivate::maybeNativeHandle()
const
376bool QQuickMenuPrivate::createNativeMenu()
380 qCDebug(lcNativeMenus) <<
"createNativeMenu called on" << q;
382 if (
auto menuBar = QQuickMenuPrivate::get(rootMenu())->menuBar) {
383#if QT_CONFIG(quicktemplates2_container)
384 auto menuBarPrivate = QQuickMenuBarPrivate::get(menuBar);
385 if (menuBarPrivate->useNativeMenuBar()) {
386 qCDebug(lcNativeMenus) <<
"- creating native menu from native menubar";
387 if (QPlatformMenuBar *menuBarHandle = menuBarPrivate->nativeHandle())
388 handle.reset(menuBarHandle->createMenu());
394 QPlatformMenu *parentMenuHandle(parentMenu ? get(parentMenu)->handle.get() :
nullptr);
395 if (parentMenu && parentMenuHandle) {
396 qCDebug(lcNativeMenus) <<
"- creating native sub-menu";
397 handle.reset(parentMenuHandle->createSubMenu());
399 qCDebug(lcNativeMenus) <<
"- creating native menu";
400 handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformMenu());
404 triedToCreateNativeMenu =
true;
409 q->connect(handle.get(), &QPlatformMenu::aboutToShow, q, [q,
this](){
410 emit q->aboutToShow();
412 emit q->visibleChanged();
413 emit q->openedChanged();
416 q->connect(handle.get(), &QPlatformMenu::aboutToHide, q, [q](){
417 qCDebug(lcNativeMenus) <<
"QPlatformMenu::aboutToHide called; about to call setVisible(false) on Menu";
418 emit q->aboutToHide();
424 q->connect(handle.get(), &QPlatformMenu::aboutToHide, q, [q,
this](){
426 emit q->visibleChanged();
427 emit q->openedChanged();
429 }, Qt::QueuedConnection);
431 recursivelyCreateNativeMenuItems(q);
432 syncWithNativeMenu();
439 if (nativeItems.isEmpty())
440 return QStringLiteral(
"(Empty)");
443 QTextStream debug(&str);
444 for (
const auto *nativeItem : nativeItems)
445 debug << nativeItem->debugText() <<
", ";
447 if (!nativeItems.isEmpty())
452void QQuickMenuPrivate::syncWithNativeMenu()
455 if (!complete || !handle)
458 qCDebug(lcNativeMenus).nospace() <<
"syncWithNativeMenu called on " << q
459 <<
" (complete: " << complete <<
" visible: " << visible <<
") - "
460 <<
"syncing " << nativeItems.size() <<
" item(s)...";
464 handle->setText(title);
465 handle->setEnabled(q->isEnabled());
466 handle->setMinimumWidth(q->implicitWidth());
468 handle->setFont(q->font());
486 for (QQuickNativeMenuItem *item : std::as_const(nativeItems)) {
487 qCDebug(lcNativeMenus) <<
"- syncing" << item <<
"action" << item->action()
488 <<
"sub-menu" << item->subMenu() << item->debugText();
492 qCDebug(lcNativeMenus) <<
"... finished syncing" << q;
496
497
498
499
500
501
502
503void QQuickMenuPrivate::removeNativeMenu()
506 const int qtyItemsToRemove = nativeItems.size();
507 if (qtyItemsToRemove != 0)
508 Q_ASSERT(q->count() == qtyItemsToRemove);
509 for (
int i = 0; i < qtyItemsToRemove; ++i)
511 Q_ASSERT(nativeItems.isEmpty());
518void QQuickMenuPrivate::syncWithUseNativeMenu()
523 if (q->isVisible() || parentMenu)
526 if (maybeNativeHandle() && !useNativeMenu()) {
530 }
else if (useNativeMenu()) {
531 Q_ASSERT(nativeItems.isEmpty());
539 QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(window);
541 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(quickWindow, offset);
548void QQuickMenuPrivate::setNativeMenuVisible(
bool visible)
551 qCDebug(lcNativeMenus) <<
"setNativeMenuVisible called with visible" << visible;
553 emit q->aboutToShow();
555 emit q->aboutToHide();
557 this->visible = visible;
558 syncWithNativeMenu();
562 QWindow *window =
nullptr;
564 window = effectiveWindow(parentItem->window(), &offset);
566 lastDevicePixelRatio = window ? window->devicePixelRatio() : qGuiApp->devicePixelRatio();
568 const QPointF globalPos = parentItem->mapToGlobal(x, y);
569 const QPoint windowPos = window ? window->mapFromGlobal(globalPos.toPoint()) : parentItem->mapToScene(QPoint(x, y)).toPoint();
570 QRect targetRect(windowPos, QSize(0, 0));
571 auto *daPriv = QQuickItemPrivate::get(parentItem)->deliveryAgentPrivate();
578 if (QPointerEvent *openingEvent = daPriv->eventInDelivery()) {
579 auto *devPriv = QPointingDevicePrivate::get(
const_cast<QPointingDevice *>(openingEvent->pointingDevice()));
580 for (
const auto &pt : std::as_const(openingEvent->points())) {
581 qCDebug(lcNativeMenus) <<
"popup over" << window <<
"its DA" << daPriv->q_func() <<
"opening due to" << openingEvent
582 <<
"with grabbers" << openingEvent->exclusiveGrabber(pt) << openingEvent->passiveGrabbers(pt);
584 if (
auto *opener = openingEvent->exclusiveGrabber(pt))
585 devPriv->removeGrabber(opener,
true);
586 for (
auto passiveGrabber : openingEvent->passiveGrabbers(pt)) {
587 if (
auto *opener = passiveGrabber.get())
588 devPriv->removeGrabber(opener,
true);
592 handle->showPopup(window, QHighDpi::toNativeLocalPosition(targetRect, window),
600void QQuickMenuPrivate::makeEditMenu()
602 handle->setMenuType(QPlatformMenu::EditMenu);
605QQuickItem *QQuickMenuPrivate::itemAt(
int index)
const
607 return qobject_cast<QQuickItem *>(contentModel->get(index));
610void QQuickMenuPrivate::insertItem(
int index, QQuickItem *item)
612 qCDebug(lcMenu) <<
"insert called with index" << index <<
"item" << item;
615 contentData.append(item);
616 item->setParentItem(contentItem);
617 QQuickItemPrivate::get(item)->setCulled(
true);
620 QQuickItemPrivate::get(item)->addItemChangeListener(
this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent);
621 QQuickItemPrivate::get(item)->updateOrAddGeometryChangeListener(
this, QQuickGeometryChange::Width);
622 contentModel->insert(index, item);
624 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
626 QQuickMenuItemPrivate::get(menuItem)->setMenu(q);
627 if (QQuickMenu *subMenu = menuItem->subMenu())
628 QQuickMenuPrivate::get(subMenu)->setParentMenu(q);
629 QObjectPrivate::connect(menuItem, &QQuickMenuItem::triggered,
this, &QQuickMenuPrivate::onItemTriggered);
630 QObjectPrivate::connect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged,
this, &QQuickMenuPrivate::updateTextPadding);
631 QObjectPrivate::connect(menuItem, &QQuickMenuItem::visibleChanged,
this, &QQuickMenuPrivate::onItemVisibleChanged);
632 QObjectPrivate::connect(menuItem, &QQuickItem::activeFocusChanged,
this, &QQuickMenuPrivate::onItemActiveFocusChanged);
633 QObjectPrivate::connect(menuItem, &QQuickControl::hoveredChanged,
this, &QQuickMenuPrivate::onItemHovered);
636 QQuickMenuSeparator *separator = qobject_cast<QQuickMenuSeparator *>(item);
638 QObjectPrivate::connect(separator, &QQuickMenuSeparator::visibleChanged,
this, &QQuickMenuPrivate::updateCollapsedSeparators);
640 if (maybeNativeHandle() && complete)
641 maybeCreateAndInsertNativeItem(index, item);
643 if (lcMenu().isDebugEnabled())
644 printContentModelItems();
648 updateCollapsedSeparators();
651void QQuickMenuPrivate::maybeCreateAndInsertNativeItem(
int index, QQuickItem *item)
655 Q_ASSERT_X(handle, Q_FUNC_INFO, qPrintable(QString::fromLatin1(
656 "Expected %1 to be using a native menu").arg(QDebug::toString(q))));
657 std::unique_ptr<QQuickNativeMenuItem> nativeMenuItem(QQuickNativeMenuItem::createFromNonNativeItem(q, item));
658 if (!nativeMenuItem) {
660 qmlWarning(q) <<
"Native menu failed to create a native menu item for item at index" << index;
664 nativeItems.insert(index, nativeMenuItem.get());
668 if (nativeMenuItem->handle()) {
669 QQuickNativeMenuItem *before = nativeItems.value(index + 1);
670 handle->insertMenuItem(nativeMenuItem->handle(), before ? before->handle() :
nullptr);
671 qCDebug(lcNativeMenus) <<
"inserted native menu item at index" << index
672 <<
"before" << (before ? before->debugText() : QStringLiteral(
"null"));
674 if (nativeMenuItem->subMenu() && QQuickMenuPrivate::get(nativeMenuItem->subMenu())->nativeItems.count()
675 < nativeMenuItem->subMenu()->count()) {
681 recursivelyCreateNativeMenuItems(nativeMenuItem->subMenu());
685 nativeMenuItem.release();
687 qCDebug(lcNativeMenus) <<
"nativeItems now contains the following items:"
688 << nativeMenuItemListToString(nativeItems);
691void QQuickMenuPrivate::moveItem(
int from,
int to)
693 contentModel->move(from, to);
695 if (maybeNativeHandle())
696 nativeItems.move(from, to);
700
701
702
703
704
705
706
707
708
709void QQuickMenuPrivate::removeItem(
int index, QQuickItem *item, DestructionPolicy destructionPolicy)
711 qCDebug(lcMenu) <<
"removeItem called with index" << index <<
"item" << item;
713 if (maybeNativeHandle())
714 removeNativeItem(index);
716 contentData.removeOne(item);
718 QQuickItemPrivate::get(item)->removeItemChangeListener(
this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent);
719 QQuickItemPrivate::get(item)->removeItemChangeListener(
this, QQuickItemPrivate::Geometry);
720 item->setParentItem(
nullptr);
721 contentModel->remove(index);
723 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
725 QQuickMenuItemPrivate::get(menuItem)->setMenu(
nullptr);
726 if (QQuickMenu *subMenu = menuItem->subMenu())
727 QQuickMenuPrivate::get(subMenu)->setParentMenu(
nullptr);
728 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::triggered,
this, &QQuickMenuPrivate::onItemTriggered);
729 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged,
this, &QQuickMenuPrivate::updateTextPadding);
730 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::visibleChanged,
this, &QQuickMenuPrivate::onItemVisibleChanged);
731 QObjectPrivate::disconnect(menuItem, &QQuickItem::activeFocusChanged,
this, &QQuickMenuPrivate::onItemActiveFocusChanged);
732 QObjectPrivate::disconnect(menuItem, &QQuickControl::hoveredChanged,
this, &QQuickMenuPrivate::onItemHovered);
735 QQuickMenuSeparator *separator = qobject_cast<QQuickMenuSeparator *>(item);
737 QObjectPrivate::disconnect(separator, &QQuickMenuSeparator::visibleChanged,
this, &QQuickMenuPrivate::updateCollapsedSeparators);
738 collapsedSeparators.remove(item);
741 if (destructionPolicy == DestructionPolicy::Destroy)
744 if (lcMenu().isDebugEnabled())
745 printContentModelItems();
748 updateCollapsedSeparators();
752
753
754
755
756
757
758
759
760
761void QQuickMenuPrivate::removeNativeItem(
int index, SyncPolicy syncPolicy)
766 Q_ASSERT_X(index >= 0 && index < nativeItems.size(), Q_FUNC_INFO, qPrintable(QString::fromLatin1(
767 "index %1 is less than 0 or greater than or equal to %2").arg(index).arg(nativeItems.size())));
771 std::unique_ptr<QQuickNativeMenuItem> nativeItem(nativeItems.takeAt(index));
772 qCDebug(lcNativeMenus) <<
"removing native item" << nativeItem->debugText() <<
"at index" << index
773 <<
"from" << q_func() <<
"...";
774 QQuickMenu *subMenu = nativeItem->subMenu();
776 Q_ASSERT(nativeItem->handle());
777 auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
778 while (!subMenuPrivate->nativeItems.isEmpty()) {
779 subMenuPrivate->removeNativeItem(0, SyncPolicy::DoNotSync);
783 Q_ASSERT(nativeItem->handle());
784 handle->removeMenuItem(nativeItem->handle());
785 if (syncPolicy == SyncPolicy::Sync)
786 syncWithNativeMenu();
789 auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
795 subMenuPrivate->resetNativeData();
798 qCDebug(lcNativeMenus).nospace() <<
"... after removing item at index " << index
799 <<
", nativeItems now contains the following items: " << nativeMenuItemListToString(nativeItems);
802void QQuickMenuPrivate::resetNativeData()
804 qCDebug(lcNativeMenus) <<
"resetNativeData called on" << q_func();
806 triedToCreateNativeMenu =
false;
809void QQuickMenuPrivate::recursivelyCreateNativeMenuItems(QQuickMenu *menu)
811 auto *menuPrivate = QQuickMenuPrivate::get(menu);
814 if (!menuPrivate->triedToCreateNativeMenu)
815 menuPrivate->createNativeMenu();
817 const int qtyItemsToCreate = menuPrivate->contentModel->count();
818 if (menuPrivate->nativeItems.count() == qtyItemsToCreate)
821 qCDebug(lcNativeMenus) <<
"recursively creating" << qtyItemsToCreate <<
"menu item(s) for" << menu;
822 Q_ASSERT(menuPrivate->nativeItems.count() == 0);
823 for (
int i = 0; i < qtyItemsToCreate; ++i) {
824 QQuickItem *item = menu->itemAt(i);
825 menuPrivate->maybeCreateAndInsertNativeItem(i, item);
826 auto *menuItem = qobject_cast<QQuickMenuItem *>(item);
827 if (menuItem && menuItem->subMenu())
828 recursivelyCreateNativeMenuItems(menuItem->subMenu());
832void QQuickMenuPrivate::printContentModelItems()
const
834 qCDebug(lcMenu) <<
"contentModel now contains:";
835 for (
int i = 0; i < contentModel->count(); ++i)
836 qCDebug(lcMenu) <<
"-" << itemAt(i);
839QQuickItem *QQuickMenuPrivate::beginCreateItem()
845 QQmlContext *context = delegate->creationContext();
847 context = qmlContext(q);
849 QObject *object = delegate->beginCreate(context);
850 QQuickItem *item = qobject_cast<QQuickItem *>(object);
854 QQml_setParent_noEvent(item, q);
859void QQuickMenuPrivate::completeCreateItem()
864 delegate->completeCreate();
867QQuickItem *QQuickMenuPrivate::createItem(QQuickMenu *menu)
869 QQuickItem *item = beginCreateItem();
870 if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item))
871 QQuickMenuItemPrivate::get(menuItem)->setSubMenu(menu);
872 completeCreateItem();
876QQuickItem *QQuickMenuPrivate::createItem(QQuickAction *action)
878 QQuickItem *item = beginCreateItem();
879 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(item))
880 button->setAction(action);
881 completeCreateItem();
885void QQuickMenuPrivate::resizeItem(QQuickItem *item)
887 if (!item || !contentItem)
890 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
891 if (!p->widthValid()) {
892 item->setWidth(contentItem->width());
893 p->widthValidFlag =
false;
897void QQuickMenuPrivate::resizeItems()
902 for (
int i = 0; i < contentModel->count(); ++i)
903 resizeItem(itemAt(i));
906void QQuickMenuPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
909 if (!QQuickItemPrivate::get(child)->isTransparentForPositioner() && !contentData.contains(child))
910 insertItem(contentModel->count(), child);
913void QQuickMenuPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
917 removeItem(contentModel->indexOf(item,
nullptr), item);
920void QQuickMenuPrivate::itemSiblingOrderChanged(QQuickItem *)
924 QList<QQuickItem *> siblings = contentItem->childItems();
927 for (
int i = 0; i < siblings.size(); ++i) {
928 QQuickItem* sibling = siblings.at(i);
929 if (QQuickItemPrivate::get(sibling)->isTransparentForPositioner())
931 int index = contentModel->indexOf(sibling,
nullptr);
932 q->moveItem(index, to++);
936void QQuickMenuPrivate::itemDestroyed(QQuickItem *item)
938 if (item == contentItem) {
941 QQuickPopupPrivate::itemDestroyed(item);
943 int index = contentModel->indexOf(item,
nullptr);
945 removeItem(index, item);
950void QQuickMenuPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange,
const QRectF &)
955 if (item == contentItem) {
966QQuickPopupPositioner *QQuickMenuPrivate::getPositioner()
970 positioner =
new QQuickMenuPositioner(q);
976 QQuickMenu *menu =
static_cast<QQuickMenu *>(popup());
977 QQuickMenuPrivate *menu_d = QQuickMenuPrivate::get(menu);
979 if (QQuickMenu *parentMenu = menu_d->parentMenu) {
980 if (menu_d->cascade) {
983 if (menu_d->popupItem->isMirrored()) {
984 const qreal distanceToFrame = parentMenu->leftPadding();
985 const qreal menuX = -menu->width() - distanceToFrame + menu->overlap();
986 menu->setPosition({menuX, -menu->topPadding()});
987 }
else if (menu_d->parentItem) {
988 const qreal distanceToFrame = parentMenu->rightPadding();
989 const qreal menuX = menu_d->parentItem->width() + distanceToFrame - menu->overlap();
990 menu->setPosition({menuX, -menu->topPadding()});
993 const qreal menuX = parentMenu->x() + (parentMenu->width() - menu->width()) / 2;
994 const qreal menuY = parentMenu->y() + (parentMenu->height() - menu->height()) / 2;
995 menu->setPosition({menuX, menuY});
999 QQuickPopupPositioner::reposition();
1002bool QQuickMenuPrivate::prepareEnterTransition()
1005 if (parentMenu && !cascade)
1006 parentMenu->close();
1010 allowHorizontalFlip = cascade && parentMenu;
1012 updateCollapsedSeparators();
1024 auto *contentItemAsListView = qobject_cast<QQuickListView *>(contentItem);
1025 if (contentItemAsListView) {
1026 if (QQuickItemViewPrivate::get(contentItemAsListView)->currentChanges.hasPendingChanges())
1027 contentItemAsListView->forceLayout();
1030 if (!QQuickPopupPrivate::prepareEnterTransition())
1033 if (!hasClosePolicy) {
1034 if (cascade && parentMenu)
1035 closePolicy = cascadingSubMenuClosePolicy;
1037 q->resetClosePolicy();
1042bool QQuickMenuPrivate::prepareExitTransition()
1044 if (!QQuickPopupPrivate::prepareExitTransition())
1049 QQuickMenu *subMenu = currentSubMenu();
1051 QPointer<QQuickMenuItem> currentSubMenuItem = QQuickMenuPrivate::get(subMenu)->currentItem;
1053 subMenu = currentSubMenuItem ? currentSubMenuItem->subMenu() :
nullptr;
1058bool QQuickMenuPrivate::blockInput(QQuickItem *item,
const QPointF &point)
const
1061 return (cascade && parentMenu && contains(point)) || QQuickPopupPrivate::blockInput(item, point);
1064bool QQuickMenuPrivate::handlePress(QQuickItem *item,
const QPointF &point, ulong timestamp)
1068 return QQuickPopupPrivate::handlePress(item, point, timestamp)
1069 || (popupItem == item);
1074
1075
1076
1077
1078
1079
1080bool QQuickMenuPrivate::handleReleaseWithoutGrab(
const QEventPoint &eventPoint)
1082 const QPointF scenePos = eventPoint.scenePosition();
1083 if (!contains(scenePos))
1086 auto *list = qobject_cast<QQuickListView *>(contentItem);
1090 const QPointF listPos = list->mapFromScene(scenePos);
1092 auto *menuItem = qobject_cast<QQuickMenuItem *>(list->itemAt(listPos.x(), listPos.y()));
1093 if (menuItem && menuItem->isHighlighted()) {
1094 menuItem->animateClick();
1101void QQuickMenuPrivate::onItemHovered()
1104 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(q->sender());
1105 if (!button || !button->isHovered() || !button->isEnabled() || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
1108 QQuickMenuItem *oldCurrentItem = currentItem;
1110 int index = contentModel->indexOf(button,
nullptr);
1112 setCurrentIndex(index, Qt::OtherFocusReason);
1113 if (oldCurrentItem != currentItem) {
1114 if (oldCurrentItem) {
1115 QQuickMenu *subMenu = oldCurrentItem->subMenu();
1120 QQuickMenu *subMenu = currentItem->menu();
1121 if (subMenu && subMenu->cascade())
1128void QQuickMenuPrivate::onItemTriggered()
1131 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(q->sender());
1135 if (QQuickMenu *subMenu = item->subMenu()) {
1136 auto subMenuPrivate = QQuickMenuPrivate::get(subMenu);
1137 subMenuPrivate->popup(subMenuPrivate->firstEnabledMenuItem());
1143void QQuickMenuPrivate::onItemActiveFocusChanged()
1146 QQuickItem *item = qobject_cast<QQuickItem*>(q->sender());
1147 if (!item->hasActiveFocus())
1150 int indexOfItem = contentModel->indexOf(item,
nullptr);
1151 QQuickControl *control = qobject_cast<QQuickControl *>(item);
1152 setCurrentIndex(indexOfItem, control ? control->focusReason() : Qt::OtherFocusReason);
1155void QQuickMenuPrivate::onItemVisibleChanged()
1157 updateTextPadding();
1158 updateCollapsedSeparators();
1161void QQuickMenuPrivate::updateTextPadding()
1168 for (
int i = 0; i < q->count(); ++i) {
1169 if (
const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i)))
1170 if (menuItem->isVisible())
1171 padding = qMax(padding, menuItem->implicitTextPadding());
1174 if (padding == textPadding)
1177 textPadding = padding;
1179 for (
int i = 0; i < q->count(); ++i) {
1180 if (
const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i)))
1181 emit menuItem->textPaddingChanged();
1185void QQuickMenuPrivate::updateCollapsedSeparators()
1187 if (!complete || updatingCollapsedSeparators)
1192 QScopedValueRollback guard(updatingCollapsedSeparators,
true);
1194 auto isExplicitlyVisible = [](
const QQuickItem *item) {
1195 return QQuickItemPrivate::get(item)->explicitVisible;
1198 auto hasVisibleBinding = [](QQuickItem *item) {
1199 static const int visibleIndex = QQuickItem::staticMetaObject.indexOfProperty(
"visible");
1200 auto ddata = QQmlData::get(item,
false);
1203 return ddata->hasBindingBit(visibleIndex);
1206 auto hideSeparator = [
this](QQuickItem *separatorItem) {
1207 separatorItem->setVisible(
false);
1208 separatorItem->setHeight(0);
1209 collapsedSeparators.insert(separatorItem);
1212 auto showSeparator = [
this](QQuickItem *separatorItem) {
1213 if (!collapsedSeparators.contains(separatorItem))
1215 separatorItem->setVisible(
true);
1216 separatorItem->resetHeight();
1217 collapsedSeparators.remove(separatorItem);
1220 const int count = contentModel->count();
1222 if (!collapsibleSeparators) {
1223 for (
int i = 0; i < count; ++i)
1224 showSeparator(itemAt(i));
1228 QQuickItem *visibleSeparatorBefore =
nullptr;
1229 bool hasVisibleItemBefore =
false;
1231 for (
int i = 0; i < count; ++i) {
1232 QQuickItem *item = itemAt(i);
1236 if (qobject_cast<QQuickMenuSeparator *>(item)) {
1237 if (hasVisibleBinding(item)) {
1238 if (isExplicitlyVisible(item) && !visibleSeparatorBefore)
1239 visibleSeparatorBefore = item;
1243 if (visibleSeparatorBefore) {
1244 hideSeparator(item);
1245 }
else if (hasVisibleItemBefore) {
1246 visibleSeparatorBefore = item;
1247 showSeparator(item);
1249 hideSeparator(item);
1251 }
else if (isExplicitlyVisible(item)) {
1252 hasVisibleItemBefore =
true;
1253 visibleSeparatorBefore =
nullptr;
1258 if (visibleSeparatorBefore && !hasVisibleBinding(visibleSeparatorBefore))
1259 hideSeparator(visibleSeparatorBefore);
1262QQuickMenu *QQuickMenuPrivate::currentSubMenu()
const
1267 return currentItem->subMenu();
1270void QQuickMenuPrivate::setParentMenu(QQuickMenu *parent)
1273 if (parentMenu == parent)
1277 QObject::disconnect(parentMenu.data(), &QQuickMenu::cascadeChanged, q, &QQuickMenu::setCascade);
1278 disconnect(parentMenu.data(), &QQuickMenu::parentChanged,
this, &QQuickMenuPrivate::resolveParentItem);
1281 QObject::connect(parent, &QQuickMenu::cascadeChanged, q, &QQuickMenu::setCascade);
1282 connect(parent, &QQuickMenu::parentChanged,
this, &QQuickMenuPrivate::resolveParentItem);
1285 parentMenu = parent;
1287 resolveParentItem();
1292 QQuickMenu *menu = QQuickMenuPrivate::get(subMenu)->parentMenu;
1293 for (
int i = 0; i < QQuickMenuPrivate::get(menu)->contentModel->count(); ++i) {
1294 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(menu->itemAt(i));
1295 if (item && item->subMenu() == subMenu)
1301void QQuickMenuPrivate::resolveParentItem()
1305 q->resetParentItem();
1307 q->setParentItem(parentMenu->parentItem());
1309 q->setParentItem(findParentMenuItem(q));
1312void QQuickMenuPrivate::propagateKeyEvent(QKeyEvent *event)
1314 if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(parentItem)) {
1315 if (QQuickMenu *menu = menuItem->menu())
1316 QQuickMenuPrivate::get(menu)->propagateKeyEvent(event);
1317#if QT_CONFIG(quicktemplates2_container)
1318 }
else if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(parentItem)) {
1319 if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) {
1321 QCoreApplication::sendEvent(menuBar, event);
1327void QQuickMenuPrivate::startHoverTimer()
1331 hoverTimer = q->startTimer(SUBMENU_DELAY);
1334void QQuickMenuPrivate::stopHoverTimer()
1340 q->killTimer(hoverTimer);
1344void QQuickMenuPrivate::setCurrentIndex(
int index, Qt::FocusReason reason)
1347 if (currentIndex == index)
1350 QQuickMenuItem *newCurrentItem = qobject_cast<QQuickMenuItem *>(itemAt(index));
1351 if (currentItem != newCurrentItem) {
1354 currentItem->setHighlighted(
false);
1355 if (!newCurrentItem && window) {
1356 QQuickItem *focusItem = QQuickItemPrivate::get(contentItem)->subFocusItem;
1358 auto *daPriv = QQuickWindowPrivate::get(window)->deliveryAgentPrivate();
1359 daPriv->clearFocusInScope(contentItem, focusItem, Qt::OtherFocusReason);
1363 if (newCurrentItem) {
1364 newCurrentItem->setHighlighted(
true);
1365 newCurrentItem->forceActiveFocus(reason);
1367 currentItem = newCurrentItem;
1370 currentIndex = index;
1371 emit q->currentIndexChanged();
1374bool QQuickMenuPrivate::activateNextItem()
1376 int index = currentIndex;
1377 int count = contentModel->count();
1378 while (++index < count) {
1379 QQuickItem *item = itemAt(index);
1380 if (!item || !item->activeFocusOnTab() || !item->isEnabled())
1382 setCurrentIndex(index, Qt::TabFocusReason);
1388bool QQuickMenuPrivate::activatePreviousItem()
1390 int index = currentIndex;
1391 while (--index >= 0) {
1392 QQuickItem *item = itemAt(index);
1393 if (!item || !item->activeFocusOnTab() || !item->isEnabled())
1395 setCurrentIndex(index, Qt::BacktabFocusReason);
1401QQuickMenuItem *QQuickMenuPrivate::firstEnabledMenuItem()
const
1403 for (
int i = 0; i < contentModel->count(); ++i) {
1404 QQuickItem *item = itemAt(i);
1405 if (!item || !item->isEnabled())
1408 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
1417void QQuickMenuPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
1419 QQuickMenu *q = qobject_cast<QQuickMenu *>(prop->object);
1420 QQuickMenuPrivate *p = QQuickMenuPrivate::get(q);
1422 QQuickItem *item = qobject_cast<QQuickItem *>(obj);
1424 if (QQuickAction *action = qobject_cast<QQuickAction *>(obj))
1425 item = p->createItem(action);
1426 else if (QQuickMenu *menu = qobject_cast<QQuickMenu *>(obj))
1427 item = p->createItem(menu);
1431 if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) {
1432 QQuickItemPrivate::get(item)->addItemChangeListener(p, QQuickItemPrivate::SiblingOrder);
1433 item->setParentItem(p->contentItem);
1434 }
else if (p->contentModel->indexOf(item,
nullptr) == -1) {
1438 p->contentData.append(obj);
1442qsizetype QQuickMenuPrivate::contentData_count(QQmlListProperty<QObject> *prop)
1444 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1445 return QQuickMenuPrivate::get(q)->contentData.size();
1448QObject *QQuickMenuPrivate::contentData_at(QQmlListProperty<QObject> *prop, qsizetype index)
1450 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1451 return QQuickMenuPrivate::get(q)->contentData.value(index);
1454QPalette QQuickMenuPrivate::defaultPalette()
const
1456 return QQuickTheme::palette(QQuickTheme::Menu);
1459void QQuickMenuPrivate::contentData_clear(QQmlListProperty<QObject> *prop)
1461 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1462 QQuickMenuPrivate::get(q)->contentData.clear();
1465void QQuickMenuPrivate::resetContentItem()
1468 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Children);
1469 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Destroyed);
1470 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Geometry);
1472 const auto children = contentItem->childItems();
1473 for (QQuickItem *child : std::as_const(children))
1474 QQuickItemPrivate::get(child)->removeItemChangeListener(
this, QQuickItemPrivate::SiblingOrder);
1475 contentItem =
nullptr;
1479QQuickMenu::QQuickMenu(QObject *parent)
1480 : QQuickPopup(*(
new QQuickMenuPrivate), parent)
1485 connect(d->contentModel, &QQmlObjectModel::countChanged,
this, &QQuickMenu::countChanged);
1488QQuickMenu::~QQuickMenu()
1491 qCDebug(lcNativeMenus) <<
"destroying" <<
this
1493 << d->contentModel->count()
1494 <<
"native item count:" << d->nativeItems.count();
1498 if (
auto *menuItem = qobject_cast<QQuickMenuItem *>(d->parentItem)) {
1499 if (menuItem->subMenu() ==
this) {
1500 auto *menuItemPriv = QQuickMenuItemPrivate::get(menuItem);
1501 menuItemPriv->setSubMenu(
nullptr);
1509 while (d->contentModel->count() > 0)
1510 d->removeItem(0, d->itemAt(0), QQuickMenuPrivate::DestructionPolicy::Destroy);
1512 d->resetContentItem();
1516
1517
1518
1519
1520QQuickItem *QQuickMenu::itemAt(
int index)
const
1522 Q_D(
const QQuickMenu);
1523 return d->itemAt(index);
1527
1528
1529
1530
1531
1532
1533
1534void QQuickMenu::addItem(QQuickItem *item)
1537 insertItem(d->contentModel->count(), item);
1541
1542
1543
1544
1545
1546
1547
1548void QQuickMenu::insertItem(
int index, QQuickItem *item)
1553 const int count = d->contentModel->count();
1554 if (index < 0 || index > count)
1557 int oldIndex = d->contentModel->indexOf(item,
nullptr);
1558 if (oldIndex != -1) {
1559 if (oldIndex < index)
1561 if (oldIndex != index) {
1562 d->moveItem(oldIndex, index);
1565 d->insertItem(index, item);
1570
1571
1572
1573
1574void QQuickMenu::moveItem(
int from,
int to)
1577 const int count = d->contentModel->count();
1578 if (from < 0 || from > count - 1)
1580 if (to < 0 || to > count - 1)
1584 d->moveItem(from, to);
1588
1589
1590
1591
1592
1593void QQuickMenu::removeItem(QQuickItem *item)
1599 const int index = d->contentModel->indexOf(item,
nullptr);
1603 d->removeItem(index, item, QQuickMenuPrivate::DestructionPolicy::Destroy);
1607
1608
1609
1610
1611
1612
1613
1614QQuickItem *QQuickMenu::takeItem(
int index)
1617 const int count = d->contentModel->count();
1618 if (index < 0 || index >= count)
1621 QQuickItem *item = itemAt(index);
1623 d->removeItem(index, item);
1628
1629
1630
1631
1632
1633
1634QQuickMenu *QQuickMenu::menuAt(
int index)
const
1636 Q_D(
const QQuickMenu);
1637 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1641 return item->subMenu();
1645
1646
1647
1648
1649
1650
1651void QQuickMenu::addMenu(QQuickMenu *menu)
1654 insertMenu(d->contentModel->count(), menu);
1658
1659
1660
1661
1662
1663
1664void QQuickMenu::insertMenu(
int index, QQuickMenu *menu)
1670 insertItem(index, d->createItem(menu));
1674
1675
1676
1677
1678
1679void QQuickMenu::removeMenu(QQuickMenu *menu)
1685 const int count = d->contentModel->count();
1686 for (
int i = 0; i < count; ++i) {
1687 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(i));
1688 if (!item || item->subMenu() != menu)
1695 menu->deleteLater();
1699
1700
1701
1702
1703
1704
1705
1706
1707QQuickMenu *QQuickMenu::takeMenu(
int index)
1710 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1714 QQuickMenu *subMenu = item->subMenu();
1718 d->removeItem(index, item);
1719 item->deleteLater();
1725
1726
1727
1728
1729
1730
1731QQuickAction *QQuickMenu::actionAt(
int index)
const
1733 Q_D(
const QQuickMenu);
1734 if (!
const_cast<QQuickMenuPrivate *>(d)->maybeNativeHandle()) {
1735 QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton *>(d->itemAt(index));
1739 return item->action();
1741 if (index < 0 || index >= d->nativeItems.size())
1744 return d->nativeItems.at(index)->action();
1749
1750
1751
1752
1753
1754
1755void QQuickMenu::addAction(QQuickAction *action)
1758 insertAction(d->contentModel->count(), action);
1762
1763
1764
1765
1766
1767
1768void QQuickMenu::insertAction(
int index, QQuickAction *action)
1774 insertItem(index, d->createItem(action));
1778
1779
1780
1781
1782
1783void QQuickMenu::removeAction(QQuickAction *action)
1789 const int count = d->contentModel->count();
1790 for (
int i = 0; i < count; ++i) {
1791 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(i));
1792 if (!item || item->action() != action)
1799 action->deleteLater();
1803
1804
1805
1806
1807
1808
1809
1810
1811QQuickAction *QQuickMenu::takeAction(
int index)
1814 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1818 QQuickAction *action = item->action();
1822 d->removeItem(index, item);
1823 item->deleteLater();
1827bool QQuickMenu::isVisible()
const
1829 Q_D(
const QQuickMenu);
1830 if (d->maybeNativeHandle())
1832 return QQuickPopup::isVisible();
1835void QQuickMenu::setVisible(
bool visible)
1838 if (visible == d->visible)
1841 auto *window =
this->window();
1842 if (visible && window) {
1845 QQuickWindowPrivate::get(window)->rmbContextMenuEventEnabled =
false;
1850 if (!d->parentMenu) {
1851 QQuickOverlay *overlay = QQuickOverlay::overlay(window, parentItem());
1853 const QList<QQuickPopup *> allPopups = QQuickOverlayPrivate::get(overlay)->allPopups;
1854 for (
auto *popup : allPopups) {
1855 if (popup !=
this && qobject_cast<QQuickMenu *>(popup))
1862 if (visible && ((d->useNativeMenu() && !d->maybeNativeHandle())
1863 || (!d->useNativeMenu() && d->maybeNativeHandle()))) {
1867 qCDebug(lcNativeMenus) <<
"setVisible called - useNativeMenu:" << d->useNativeMenu()
1868 <<
"maybeNativeHandle:" << d->maybeNativeHandle();
1869 d->syncWithUseNativeMenu();
1871 if (d->maybeNativeHandle()) {
1872 d->setNativeMenuVisible(visible);
1878 QQuickPopup::setVisible(visible);
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902QVariant QQuickMenu::contentModel()
const
1904 Q_D(
const QQuickMenu);
1905 return QVariant::fromValue(d->contentModel);
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923QQmlListProperty<QObject> QQuickMenu::contentData()
1926 if (!d->contentItem)
1927 QQuickControlPrivate::get(d->popupItem)->executeContentItem();
1928 return QQmlListProperty<QObject>(
this,
nullptr,
1929 QQuickMenuPrivate::contentData_append,
1930 QQuickMenuPrivate::contentData_count,
1931 QQuickMenuPrivate::contentData_at,
1932 QQuickMenuPrivate::contentData_clear);
1936
1937
1938
1939
1940
1941
1942
1943
1944QString QQuickMenu::title()
const
1946 Q_D(
const QQuickMenu);
1950void QQuickMenu::setTitle(
const QString &title)
1953 if (title == d->title)
1957 d->handle->setText(title);
1958 emit titleChanged(title);
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1978QQuickIcon QQuickMenu::icon()
const
1980 Q_D(
const QQuickMenu);
1984void QQuickMenu::setIcon(
const QQuickIcon &icon)
1987 if (icon == d->icon)
1990 d->icon.ensureRelativeSourceResolved(
this);
1991 emit iconChanged(icon);
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019bool QQuickMenu::separatorsCollapsible()
const
2021 Q_D(
const QQuickMenu);
2022 return d->collapsibleSeparators;
2025void QQuickMenu::setSeparatorsCollapsible(
bool collapsible)
2028 if (d->collapsibleSeparators == collapsible)
2030 d->collapsibleSeparators = collapsible;
2031 emit separatorsCollapsibleChanged();
2032 d->updateCollapsedSeparators();
2034 d->handle->syncSeparatorsCollapsible(collapsible);
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053bool QQuickMenu::cascade()
const
2055 Q_D(
const QQuickMenu);
2059void QQuickMenu::setCascade(
bool cascade)
2062 if (d->cascade == cascade)
2064 d->cascade = cascade;
2066 d->resolveParentItem();
2067 emit cascadeChanged(cascade);
2070void QQuickMenu::resetCascade()
2074 setCascade(d->parentMenu->cascade());
2076 setCascade(shouldCascade());
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095qreal QQuickMenu::overlap()
const
2097 Q_D(
const QQuickMenu);
2101void QQuickMenu::setOverlap(qreal overlap)
2104 if (d->overlap == overlap)
2106 d->overlap = overlap;
2107 emit overlapChanged();
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132QQmlComponent *QQuickMenu::delegate()
const
2134 Q_D(
const QQuickMenu);
2138void QQuickMenu::setDelegate(QQmlComponent *delegate)
2141 if (d->delegate == delegate)
2144 d->delegate = delegate;
2145 emit delegateChanged();
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160int QQuickMenu::currentIndex()
const
2162 Q_D(
const QQuickMenu);
2163 return d->currentIndex;
2166void QQuickMenu::setCurrentIndex(
int index)
2169 d->setCurrentIndex(index, Qt::OtherFocusReason);
2173
2174
2175
2176
2177
2178
2179int QQuickMenu::count()
const
2181 Q_D(
const QQuickMenu);
2182 return d->contentModel->count();
2185void QQuickMenuPrivate::popup(QQuickItem *menuItem)
2190 QQmlNullableValue<QPointF> pos;
2191#if QT_CONFIG(cursor)
2192 if (parentItem && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows))
2193 pos = parentItem->mapFromGlobal(QCursor::pos());
2197 if (!pos.isValid() && parentItem)
2198 pos = QPointF((parentItem->width() - q->width()) / 2, (parentItem->height() - q->height()) / 2);
2200 q->popup(pos.isValid() ? pos.value() : QPointF(), menuItem);
2203void QQuickMenu::popup(
const QPointF &position, QQuickItem *menuItem)
2207#if QT_CONFIG(cursor)
2209 offset = d->popupItem->mapFromItem(menuItem, QPointF(0, 0)).y();
2211 setPosition(position - QPointF(0, offset));
2214 d->setCurrentIndex(d->contentModel->indexOf(menuItem,
nullptr), Qt::PopupFocusReason);
2216 d->setCurrentIndex(-1, Qt::PopupFocusReason);
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2266void QQuickMenu::popup(QQuickItem *parent, qreal x, qreal y, QQuickItem *menuItem)
2268 popup(parent, QPointF {x, y}, menuItem);
2271void QQuickMenu::popup(QQuickItem *parent,
const QPointF &position, QQuickItem *menuItem)
2274 if (parent && !d->popupItem->isAncestorOf(parent))
2275 setParentItem(parent);
2276 popup(position, menuItem);
2279void QQuickMenu::popup(QQuickItem *parent, QQuickItem *menuItem)
2282 QQuickItem *parentItem =
nullptr;
2283 if (parent && !d->popupItem->isAncestorOf(parent))
2284 parentItem = parent;
2286 setParentItem(parentItem);
2292void QQuickMenu::popup(QQuickItem *parent)
2295 QQuickItem *menuItem =
nullptr;
2297 if (!d->popupItem->isAncestorOf(parent))
2298 setParentItem(parent);
2299 if (d->popupItem->isAncestorOf(parent))
2305void QQuickMenu::popup(qreal x, qreal y, QQuickItem *menuItem)
2307 popup(QPointF {x, y}, menuItem);
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325void QQuickMenu::dismiss()
2327 QQuickMenu *menu =
this;
2330 menu = QQuickMenuPrivate::get(menu)->parentMenu;
2334void QQuickMenu::componentComplete()
2337 QQuickPopup::componentComplete();
2339 d->updateTextPadding();
2340 d->syncWithUseNativeMenu();
2343void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
2346 QQuickPopup::contentItemChange(newItem, oldItem);
2349 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
2350 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Destroyed);
2351 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
2354 QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Children);
2355 QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Destroyed);
2356 QQuickItemPrivate::get(newItem)->updateOrAddGeometryChangeListener(d, QQuickGeometryChange::Width);
2359 d->contentItem = newItem;
2362void QQuickMenu::itemChange(QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData &data)
2365 QQuickPopup::itemChange(change, data);
2368 case QQuickItem::ItemVisibleHasChanged:
2369 if (!data.boolValue && d->cascade) {
2372 d->setCurrentIndex(-1, Qt::OtherFocusReason);
2380void QQuickMenu::keyPressEvent(QKeyEvent *event)
2383 QQuickPopup::keyPressEvent(event);
2391 switch (event->key()) {
2393 if (!d->activatePreviousItem())
2394 d->propagateKeyEvent(event);
2398 d->activateNextItem();
2404 if (d->popupItem->isMirrored() == (event->key() == Qt::Key_Right)) {
2405 if (d->parentMenu && d->currentItem) {
2407 d->parentMenu->open();
2412 if (QQuickMenu *subMenu = d->currentSubMenu()) {
2413 auto subMenuPrivate = QQuickMenuPrivate::get(subMenu);
2414 subMenuPrivate->popup(subMenuPrivate->firstEnabledMenuItem());
2418 if (!event->isAccepted())
2419 d->propagateKeyEvent(event);
2422#if QT_CONFIG(shortcut)
2426 if (!QKeySequence::mnemonic(QStringLiteral(
"&A")).isEmpty())
2435#if QT_CONFIG(shortcut)
2436 if (event->modifiers() == Qt::NoModifier) {
2437 for (
int i = 0; i < count(); ++i) {
2438 QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton*>(d->itemAt(i));
2441 const QKeySequence keySequence = QKeySequence::mnemonic(item->text());
2442 if (keySequence.isEmpty())
2444 if (keySequence[0].key() == event->key()) {
2453void QQuickMenu::timerEvent(QTimerEvent *event)
2456 if (event->timerId() == d->hoverTimer) {
2457 if (QQuickMenu *subMenu = d->currentSubMenu())
2459 d->stopHoverTimer();
2462 QQuickPopup::timerEvent(event);
2465QFont QQuickMenu::defaultFont()
const
2467 return QQuickTheme::font(QQuickTheme::Menu);
2470#if QT_CONFIG(accessibility)
2471QAccessible::Role QQuickMenu::accessibleRole()
const
2473 return QAccessible::PopupMenu;
2479#include "moc_qquickmenu_p.cpp"
Combined button and popup list for selecting options.