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;
295 wmWindowType = QNativeInterface::Private::QXcbWindow::PopupMenu;
299void QQuickMenuPrivate::init()
302 contentModel =
new QQmlObjectModel(q);
305QQuickMenu *QQuickMenuPrivate::rootMenu()
const
307 Q_Q(
const QQuickMenu);
308 const QQuickMenu *rootMenu = q;
309 const QObject *p = q->parent();
311 if (
auto menu = qobject_cast<
const QQuickMenu *>(p))
316 return const_cast<QQuickMenu *>(rootMenu);
319QQuickPopup::PopupType QQuickMenuPrivate::resolvedPopupType()
const
323 QQuickMenu *root = rootMenu();
324 QQuickMenuPrivate *root_d = QQuickMenuPrivate::get(rootMenu());
326#if QT_CONFIG(quicktemplates2_container)
327 if (
auto menuBar = QQuickMenuPrivate::get(root)->menuBar.get()) {
332 if (QQuickMenuBarPrivate::get(menuBar)->useNativeMenu(q_func()))
333 return QQuickPopup::Native;
338 if (root_d->maybeNativeHandle()) {
339 return QQuickPopup::Native;
340 }
else if (!root_d->triedToCreateNativeMenu) {
346 if (root->popupType() == QQuickPopup::Native
347 && !QGuiApplication::testAttribute(Qt::AA_DontUseNativeMenuWindows)) {
348 return QQuickPopup::Native;
356 const auto type = root_d->QQuickPopupPrivate::resolvedPopupType();
357 Q_ASSERT(type != QQuickPopup::Native);
361bool QQuickMenuPrivate::useNativeMenu()
const
363 return resolvedPopupType() == QQuickPopup::Native;
366QPlatformMenu *QQuickMenuPrivate::nativeHandle()
368 Q_ASSERT(handle || useNativeMenu());
369 if (!handle && !triedToCreateNativeMenu)
374QPlatformMenu *QQuickMenuPrivate::maybeNativeHandle()
const
379bool QQuickMenuPrivate::createNativeMenu()
383 qCDebug(lcNativeMenus) <<
"createNativeMenu called on" << q;
385 if (
auto menuBar = QQuickMenuPrivate::get(rootMenu())->menuBar) {
386#if QT_CONFIG(quicktemplates2_container)
387 auto menuBarPrivate = QQuickMenuBarPrivate::get(menuBar);
388 if (menuBarPrivate->useNativeMenuBar()) {
389 qCDebug(lcNativeMenus) <<
"- creating native menu from native menubar";
390 if (QPlatformMenuBar *menuBarHandle = menuBarPrivate->nativeHandle())
391 handle.reset(menuBarHandle->createMenu());
397 QPlatformMenu *parentMenuHandle(parentMenu ? get(parentMenu)->handle.get() :
nullptr);
398 if (parentMenu && parentMenuHandle) {
399 qCDebug(lcNativeMenus) <<
"- creating native sub-menu";
400 handle.reset(parentMenuHandle->createSubMenu());
402 qCDebug(lcNativeMenus) <<
"- creating native menu";
403 handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformMenu());
407 triedToCreateNativeMenu =
true;
412 q->connect(handle.get(), &QPlatformMenu::aboutToShow, q, [q,
this](){
413 emit q->aboutToShow();
415 emit q->visibleChanged();
416 emit q->openedChanged();
419 q->connect(handle.get(), &QPlatformMenu::aboutToHide, q, [q](){
420 qCDebug(lcNativeMenus) <<
"QPlatformMenu::aboutToHide called; about to call setVisible(false) on Menu";
421 emit q->aboutToHide();
427 q->connect(handle.get(), &QPlatformMenu::aboutToHide, q, [q,
this](){
429 emit q->visibleChanged();
430 emit q->openedChanged();
432 }, Qt::QueuedConnection);
434 recursivelyCreateNativeMenuItems(q);
435 syncWithNativeMenu();
442 if (nativeItems.isEmpty())
443 return QStringLiteral(
"(Empty)");
446 QTextStream debug(&str);
447 for (
const auto *nativeItem : nativeItems)
448 debug << nativeItem->debugText() <<
", ";
450 if (!nativeItems.isEmpty())
455void QQuickMenuPrivate::syncWithNativeMenu()
458 if (!complete || !handle)
461 qCDebug(lcNativeMenus).nospace() <<
"syncWithNativeMenu called on " << q
462 <<
" (complete: " << complete <<
" visible: " << visible <<
") - "
463 <<
"syncing " << nativeItems.size() <<
" item(s)...";
467 handle->setText(title);
468 handle->setEnabled(q->isEnabled());
469 handle->setMinimumWidth(q->implicitWidth());
471 handle->setFont(q->font());
489 for (QQuickNativeMenuItem *item : std::as_const(nativeItems)) {
490 qCDebug(lcNativeMenus) <<
"- syncing" << item <<
"action" << item->action()
491 <<
"sub-menu" << item->subMenu() << item->debugText();
495 qCDebug(lcNativeMenus) <<
"... finished syncing" << q;
499
500
501
502
503
504
505
506void QQuickMenuPrivate::removeNativeMenu()
509 const int qtyItemsToRemove = nativeItems.size();
510 if (qtyItemsToRemove != 0)
511 Q_ASSERT(q->count() == qtyItemsToRemove);
512 for (
int i = 0; i < qtyItemsToRemove; ++i)
514 Q_ASSERT(nativeItems.isEmpty());
521void QQuickMenuPrivate::syncWithUseNativeMenu()
526 if (q->isVisible() || parentMenu)
529 if (maybeNativeHandle() && !useNativeMenu()) {
533 }
else if (useNativeMenu()) {
534 Q_ASSERT(nativeItems.isEmpty());
540void QQuickMenuPrivate::setNativeMenuVisible(
bool visible)
543 qCDebug(lcNativeMenus) <<
"setNativeMenuVisible called with visible" << visible;
545 emit q->aboutToShow();
547 emit q->aboutToHide();
549 this->visible = visible;
550 syncWithNativeMenu();
554 QWindow *window =
nullptr;
556 window = QQuickItemPrivate::get(parentItem)->renderWindow(&offset);
558 lastDevicePixelRatio = window ? window->devicePixelRatio() : qGuiApp->devicePixelRatio();
560 const QPointF globalPos = parentItem->mapToGlobal(x, y);
561 const QPoint windowPos = window ? window->mapFromGlobal(globalPos.toPoint()) : parentItem->mapToScene(QPoint(x, y)).toPoint();
562 QRect targetRect(windowPos, QSize(0, 0));
563 auto *daPriv = QQuickItemPrivate::get(parentItem)->deliveryAgentPrivate();
570 if (QPointerEvent *openingEvent = daPriv->eventInDelivery()) {
571 auto *devPriv = QPointingDevicePrivate::get(
const_cast<QPointingDevice *>(openingEvent->pointingDevice()));
572 for (
const auto &pt : std::as_const(openingEvent->points())) {
573 qCDebug(lcNativeMenus) <<
"popup over" << window <<
"its DA" << daPriv->q_func() <<
"opening due to" << openingEvent
574 <<
"with grabbers" << openingEvent->exclusiveGrabber(pt) << openingEvent->passiveGrabbers(pt);
576 if (
auto *opener = openingEvent->exclusiveGrabber(pt))
577 devPriv->removeGrabber(opener,
true);
578 for (
auto passiveGrabber : openingEvent->passiveGrabbers(pt)) {
579 if (
auto *opener = passiveGrabber.get())
580 devPriv->removeGrabber(opener,
true);
584 handle->showPopup(window, QHighDpi::toNativeLocalPosition(targetRect, window),
592void QQuickMenuPrivate::makeEditMenu()
594 handle->setMenuType(QPlatformMenu::EditMenu);
597QQuickItem *QQuickMenuPrivate::itemAt(
int index)
const
599 return qobject_cast<QQuickItem *>(contentModel->get(index));
602void QQuickMenuPrivate::insertItem(
int index, QQuickItem *item)
604 qCDebug(lcMenu) <<
"insert called with index" << index <<
"item" << item;
607 contentData.append(item);
608 item->setParentItem(contentItem);
609 QQuickItemPrivate::get(item)->setCulled(
true);
612 QQuickItemPrivate::get(item)->addItemChangeListener(
this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent | QQuickItemPrivate::ImplicitWidth);
613 QQuickItemPrivate::get(item)->updateOrAddGeometryChangeListener(
this, QQuickGeometryChange::Width);
614 contentModel->insert(index, item);
616 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
618 QQuickMenuItemPrivate::get(menuItem)->setMenu(q);
619 if (QQuickMenu *subMenu = menuItem->subMenu())
620 QQuickMenuPrivate::get(subMenu)->setParentMenu(q);
621 QObjectPrivate::connect(menuItem, &QQuickMenuItem::triggered,
this, &QQuickMenuPrivate::onItemTriggered);
622 QObjectPrivate::connect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged,
this, &QQuickMenuPrivate::updateTextPadding);
623 QObjectPrivate::connect(menuItem, &QQuickMenuItem::visibleChanged,
this, &QQuickMenuPrivate::onItemVisibleChanged);
624 QObjectPrivate::connect(menuItem, &QQuickItem::activeFocusChanged,
this, &QQuickMenuPrivate::onItemActiveFocusChanged);
625 QObjectPrivate::connect(menuItem, &QQuickControl::hoveredChanged,
this, &QQuickMenuPrivate::onItemHovered);
628 QQuickMenuSeparator *separator = qobject_cast<QQuickMenuSeparator *>(item);
630 QObjectPrivate::connect(separator, &QQuickMenuSeparator::visibleChanged,
this, &QQuickMenuPrivate::updateCollapsedSeparators);
632 if (maybeNativeHandle() && complete)
633 maybeCreateAndInsertNativeItem(index, item);
635 if (lcMenu().isDebugEnabled())
636 printContentModelItems();
639 updateContentWidth();
641 updateCollapsedSeparators();
644void QQuickMenuPrivate::maybeCreateAndInsertNativeItem(
int index, QQuickItem *item)
648 Q_ASSERT_X(handle, Q_FUNC_INFO, qPrintable(QString::fromLatin1(
649 "Expected %1 to be using a native menu").arg(QDebug::toString(q))));
650 std::unique_ptr<QQuickNativeMenuItem> nativeMenuItem(QQuickNativeMenuItem::createFromNonNativeItem(q, item));
651 if (!nativeMenuItem) {
653 qmlWarning(q) <<
"Native menu failed to create a native menu item for item at index" << index;
657 nativeItems.insert(index, nativeMenuItem.get());
661 if (nativeMenuItem->handle()) {
662 QQuickNativeMenuItem *before = nativeItems.value(index + 1);
663 handle->insertMenuItem(nativeMenuItem->handle(), before ? before->handle() :
nullptr);
664 qCDebug(lcNativeMenus) <<
"inserted native menu item at index" << index
665 <<
"before" << (before ? before->debugText() : QStringLiteral(
"null"));
667 if (nativeMenuItem->subMenu() && QQuickMenuPrivate::get(nativeMenuItem->subMenu())->nativeItems.count()
668 < nativeMenuItem->subMenu()->count()) {
674 recursivelyCreateNativeMenuItems(nativeMenuItem->subMenu());
678 nativeMenuItem.release();
680 qCDebug(lcNativeMenus) <<
"nativeItems now contains the following items:"
681 << nativeMenuItemListToString(nativeItems);
684void QQuickMenuPrivate::moveItem(
int from,
int to)
686 contentModel->move(from, to);
688 if (maybeNativeHandle())
689 nativeItems.move(from, to);
693
694
695
696
697
698
699
700
701
702void QQuickMenuPrivate::removeItem(
int index, QQuickItem *item, DestructionPolicy destructionPolicy)
704 qCDebug(lcMenu) <<
"removeItem called with index" << index <<
"item" << item;
706 if (maybeNativeHandle())
707 removeNativeItem(index);
709 contentData.removeOne(item);
711 QQuickItemPrivate::get(item)->removeItemChangeListener(
this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent | QQuickItemPrivate::ImplicitWidth);
712 QQuickItemPrivate::get(item)->removeItemChangeListener(
this, QQuickItemPrivate::Geometry);
713 item->setParentItem(
nullptr);
714 contentModel->remove(index);
716 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
718 QQuickMenuItemPrivate::get(menuItem)->setMenu(
nullptr);
719 if (QQuickMenu *subMenu = menuItem->subMenu())
720 QQuickMenuPrivate::get(subMenu)->setParentMenu(
nullptr);
721 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::triggered,
this, &QQuickMenuPrivate::onItemTriggered);
722 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged,
this, &QQuickMenuPrivate::updateTextPadding);
723 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::visibleChanged,
this, &QQuickMenuPrivate::onItemVisibleChanged);
724 QObjectPrivate::disconnect(menuItem, &QQuickItem::activeFocusChanged,
this, &QQuickMenuPrivate::onItemActiveFocusChanged);
725 QObjectPrivate::disconnect(menuItem, &QQuickControl::hoveredChanged,
this, &QQuickMenuPrivate::onItemHovered);
728 QQuickMenuSeparator *separator = qobject_cast<QQuickMenuSeparator *>(item);
730 QObjectPrivate::disconnect(separator, &QQuickMenuSeparator::visibleChanged,
this, &QQuickMenuPrivate::updateCollapsedSeparators);
731 collapsedSeparators.remove(item);
734 if (destructionPolicy == DestructionPolicy::Destroy)
737 if (lcMenu().isDebugEnabled())
738 printContentModelItems();
740 updateContentWidth();
742 updateCollapsedSeparators();
746
747
748
749
750
751
752
753
754
755void QQuickMenuPrivate::removeNativeItem(
int index, SyncPolicy syncPolicy)
760 Q_ASSERT_X(index >= 0 && index < nativeItems.size(), Q_FUNC_INFO, qPrintable(QString::fromLatin1(
761 "index %1 is less than 0 or greater than or equal to %2").arg(index).arg(nativeItems.size())));
765 std::unique_ptr<QQuickNativeMenuItem> nativeItem(nativeItems.takeAt(index));
766 qCDebug(lcNativeMenus) <<
"removing native item" << nativeItem->debugText() <<
"at index" << index
767 <<
"from" << q_func() <<
"...";
768 QQuickMenu *subMenu = nativeItem->subMenu();
770 Q_ASSERT(nativeItem->handle());
771 auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
772 while (!subMenuPrivate->nativeItems.isEmpty()) {
773 subMenuPrivate->removeNativeItem(0, SyncPolicy::DoNotSync);
777 Q_ASSERT(nativeItem->handle());
778 handle->removeMenuItem(nativeItem->handle());
779 if (syncPolicy == SyncPolicy::Sync)
780 syncWithNativeMenu();
783 auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
789 subMenuPrivate->resetNativeData();
792 qCDebug(lcNativeMenus).nospace() <<
"... after removing item at index " << index
793 <<
", nativeItems now contains the following items: " << nativeMenuItemListToString(nativeItems);
796void QQuickMenuPrivate::resetNativeData()
798 qCDebug(lcNativeMenus) <<
"resetNativeData called on" << q_func();
800 triedToCreateNativeMenu =
false;
803void QQuickMenuPrivate::recursivelyCreateNativeMenuItems(QQuickMenu *menu)
805 auto *menuPrivate = QQuickMenuPrivate::get(menu);
808 if (!menuPrivate->triedToCreateNativeMenu)
809 menuPrivate->createNativeMenu();
811 const int qtyItemsToCreate = menuPrivate->contentModel->count();
812 if (menuPrivate->nativeItems.count() == qtyItemsToCreate)
815 qCDebug(lcNativeMenus) <<
"recursively creating" << qtyItemsToCreate <<
"menu item(s) for" << menu;
816 Q_ASSERT(menuPrivate->nativeItems.count() == 0);
817 for (
int i = 0; i < qtyItemsToCreate; ++i) {
818 QQuickItem *item = menu->itemAt(i);
819 menuPrivate->maybeCreateAndInsertNativeItem(i, item);
820 auto *menuItem = qobject_cast<QQuickMenuItem *>(item);
821 if (menuItem && menuItem->subMenu())
822 recursivelyCreateNativeMenuItems(menuItem->subMenu());
826void QQuickMenuPrivate::printContentModelItems()
const
828 qCDebug(lcMenu) <<
"contentModel now contains:";
829 for (
int i = 0; i < contentModel->count(); ++i)
830 qCDebug(lcMenu) <<
"-" << itemAt(i);
833QQuickItem *QQuickMenuPrivate::beginCreateItem()
839 QQmlContext *context = delegate->creationContext();
841 context = qmlContext(q);
843 QObject *object = delegate->beginCreate(context);
844 QQuickItem *item = qobject_cast<QQuickItem *>(object);
848 QQml_setParent_noEvent(item, q);
853void QQuickMenuPrivate::completeCreateItem()
858 delegate->completeCreate();
861QQuickItem *QQuickMenuPrivate::createItem(QQuickMenu *menu)
863 QQuickItem *item = beginCreateItem();
864 if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item))
865 QQuickMenuItemPrivate::get(menuItem)->setSubMenu(menu);
866 completeCreateItem();
870QQuickItem *QQuickMenuPrivate::createItem(QQuickAction *action)
872 QQuickItem *item = beginCreateItem();
873 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(item))
874 button->setAction(action);
875 completeCreateItem();
879void QQuickMenuPrivate::resizeItem(QQuickItem *item)
881 if (!item || !contentItem)
884 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
885 if (!p->widthValid()) {
886 item->setWidth(contentItem->width());
887 p->widthValidFlag =
false;
891void QQuickMenuPrivate::resizeItems()
896 for (
int i = 0; i < contentModel->count(); ++i)
897 resizeItem(itemAt(i));
900void QQuickMenuPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
903 if (!QQuickItemPrivate::get(child)->isTransparentForPositioner() && !contentData.contains(child))
904 insertItem(contentModel->count(), child);
907void QQuickMenuPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
911 removeItem(contentModel->indexOf(item,
nullptr), item);
914void QQuickMenuPrivate::itemSiblingOrderChanged(QQuickItem *)
918 QList<QQuickItem *> siblings = contentItem->childItems();
921 for (
int i = 0; i < siblings.size(); ++i) {
922 QQuickItem* sibling = siblings.at(i);
923 if (QQuickItemPrivate::get(sibling)->isTransparentForPositioner())
925 int index = contentModel->indexOf(sibling,
nullptr);
926 q->moveItem(index, to++);
930void QQuickMenuPrivate::itemDestroyed(QQuickItem *item)
932 if (item == contentItem) {
935 QQuickPopupPrivate::itemDestroyed(item);
937 int index = contentModel->indexOf(item,
nullptr);
939 removeItem(index, item);
944void QQuickMenuPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange,
const QRectF &)
949 if (item == contentItem) {
960void QQuickMenuPrivate::itemImplicitWidthChanged(QQuickItem *item)
962 if (item != contentItem)
963 updateContentWidth();
966void QQuickMenuPrivate::updateContentWidth()
968 if (!contentModel || !contentItem)
973 for (
int i = 0; i < contentModel->count(); ++i) {
974 QQuickItem *item = q->itemAt(i);
975 if (item && QQuickItemPrivate::get(item)->explicitVisible)
976 maxWidth = qMax(maxWidth, item->implicitWidth());
982 if (!qFuzzyIsNull(maxWidth))
983 contentItem->setImplicitWidth(maxWidth);
986QQuickPopupPositioner *QQuickMenuPrivate::getPositioner()
990 positioner =
new QQuickMenuPositioner(q);
996 QQuickMenu *menu =
static_cast<QQuickMenu *>(popup());
997 QQuickMenuPrivate *menu_d = QQuickMenuPrivate::get(menu);
999 if (QQuickMenu *parentMenu = menu_d->parentMenu) {
1000 if (menu_d->cascade) {
1003 if (menu_d->popupItem->isMirrored()) {
1004 const qreal distanceToFrame = parentMenu->leftPadding();
1005 const qreal menuX = -menu->width() - distanceToFrame + menu->overlap();
1006 menu->setPosition({menuX, -menu->topPadding()});
1007 }
else if (menu_d->parentItem) {
1008 const qreal distanceToFrame = parentMenu->rightPadding();
1009 const qreal menuX = menu_d->parentItem->width() + distanceToFrame - menu->overlap();
1010 menu->setPosition({menuX, -menu->topPadding()});
1013 const qreal menuX = parentMenu->x() + (parentMenu->width() - menu->width()) / 2;
1014 const qreal menuY = parentMenu->y() + (parentMenu->height() - menu->height()) / 2;
1015 menu->setPosition({menuX, menuY});
1019 QQuickPopupPositioner::reposition();
1022bool QQuickMenuPrivate::prepareEnterTransition()
1025 if (parentMenu && !cascade)
1026 parentMenu->close();
1030 allowHorizontalFlip = cascade && parentMenu;
1032 updateCollapsedSeparators();
1044 auto *contentItemAsListView = qobject_cast<QQuickListView *>(contentItem);
1045 if (contentItemAsListView) {
1046 if (QQuickItemViewPrivate::get(contentItemAsListView)->currentChanges.hasPendingChanges())
1047 contentItemAsListView->forceLayout();
1050 if (!QQuickPopupPrivate::prepareEnterTransition())
1053 if (!hasClosePolicy) {
1054 if (cascade && parentMenu)
1055 closePolicy = cascadingSubMenuClosePolicy;
1057 q->resetClosePolicy();
1062bool QQuickMenuPrivate::prepareExitTransition()
1064 if (!QQuickPopupPrivate::prepareExitTransition())
1069 QQuickMenu *subMenu = currentSubMenu();
1071 QPointer<QQuickMenuItem> currentSubMenuItem = QQuickMenuPrivate::get(subMenu)->currentItem;
1073 subMenu = currentSubMenuItem ? currentSubMenuItem->subMenu() :
nullptr;
1078bool QQuickMenuPrivate::blockInput(QQuickItem *item,
const QPointF &point)
const
1081 return (cascade && parentMenu && contains(point)) || QQuickPopupPrivate::blockInput(item, point);
1084bool QQuickMenuPrivate::handlePress(QQuickItem *item,
const QPointF &point, ulong timestamp)
1088 return QQuickPopupPrivate::handlePress(item, point, timestamp)
1089 || (popupItem == item);
1094
1095
1096
1097
1098
1099
1100bool QQuickMenuPrivate::handleReleaseWithoutGrab(
const QEventPoint &eventPoint)
1102 const QPointF scenePos = eventPoint.scenePosition();
1103 if (!contains(scenePos))
1106 auto *list = qobject_cast<QQuickListView *>(contentItem);
1110 const QPointF listPos = list->mapFromScene(scenePos);
1112 auto *menuItem = qobject_cast<QQuickMenuItem *>(list->itemAt(listPos.x(), listPos.y()));
1113 if (menuItem && menuItem->isHighlighted()) {
1114 menuItem->animateClick();
1121void QQuickMenuPrivate::onItemHovered()
1124 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(q->sender());
1125 if (!button || !button->isHovered() || !button->isEnabled() || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
1128 QQuickMenuItem *oldCurrentItem = currentItem;
1130 int index = contentModel->indexOf(button,
nullptr);
1132 setCurrentIndex(index, Qt::OtherFocusReason);
1133 if (oldCurrentItem != currentItem) {
1134 if (oldCurrentItem) {
1135 QQuickMenu *subMenu = oldCurrentItem->subMenu();
1140 QQuickMenu *subMenu = currentItem->menu();
1141 if (subMenu && subMenu->cascade())
1148void QQuickMenuPrivate::onItemTriggered()
1151 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(q->sender());
1155 if (QQuickMenu *subMenu = item->subMenu()) {
1156 auto subMenuPrivate = QQuickMenuPrivate::get(subMenu);
1157 subMenuPrivate->popup(subMenuPrivate->firstEnabledMenuItem());
1163void QQuickMenuPrivate::onItemActiveFocusChanged()
1166 QQuickItem *item = qobject_cast<QQuickItem*>(q->sender());
1167 if (!item->hasActiveFocus())
1170 int indexOfItem = contentModel->indexOf(item,
nullptr);
1171 QQuickControl *control = qobject_cast<QQuickControl *>(item);
1172 setCurrentIndex(indexOfItem, control ? control->focusReason() : Qt::OtherFocusReason);
1175void QQuickMenuPrivate::onItemVisibleChanged()
1177 updateTextPadding();
1178 updateCollapsedSeparators();
1181void QQuickMenuPrivate::updateTextPadding()
1188 for (
int i = 0; i < q->count(); ++i) {
1189 if (
const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i)))
1190 if (menuItem->isVisible())
1191 padding = qMax(padding, menuItem->implicitTextPadding());
1194 if (padding == textPadding)
1197 textPadding = padding;
1199 for (
int i = 0; i < q->count(); ++i) {
1200 if (
const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i)))
1201 emit menuItem->textPaddingChanged();
1205void QQuickMenuPrivate::updateCollapsedSeparators()
1207 if (!complete || updatingCollapsedSeparators)
1212 QScopedValueRollback guard(updatingCollapsedSeparators,
true);
1214 auto isExplicitlyVisible = [](
const QQuickItem *item) {
1215 return QQuickItemPrivate::get(item)->explicitVisible;
1218 auto hasVisibleBinding = [](QQuickItem *item) {
1219 static const int visibleIndex = QQuickItem::staticMetaObject.indexOfProperty(
"visible");
1220 auto ddata = QQmlData::get(item,
false);
1223 return ddata->hasBindingBit(visibleIndex);
1226 auto hideSeparator = [
this](QQuickItem *separatorItem) {
1227 separatorItem->setVisible(
false);
1228 separatorItem->setHeight(0);
1229 collapsedSeparators.insert(separatorItem);
1232 auto showSeparator = [
this](QQuickItem *separatorItem) {
1233 if (!collapsedSeparators.contains(separatorItem))
1235 separatorItem->setVisible(
true);
1236 separatorItem->resetHeight();
1237 collapsedSeparators.remove(separatorItem);
1240 const int count = contentModel->count();
1242 if (!collapsibleSeparators) {
1243 for (
int i = 0; i < count; ++i)
1244 showSeparator(itemAt(i));
1248 QQuickItem *visibleSeparatorBefore =
nullptr;
1249 bool hasVisibleItemBefore =
false;
1251 for (
int i = 0; i < count; ++i) {
1252 QQuickItem *item = itemAt(i);
1256 if (qobject_cast<QQuickMenuSeparator *>(item)) {
1257 if (hasVisibleBinding(item)) {
1258 if (isExplicitlyVisible(item) && !visibleSeparatorBefore)
1259 visibleSeparatorBefore = item;
1263 if (visibleSeparatorBefore) {
1264 hideSeparator(item);
1265 }
else if (hasVisibleItemBefore) {
1266 visibleSeparatorBefore = item;
1267 showSeparator(item);
1269 hideSeparator(item);
1271 }
else if (isExplicitlyVisible(item)) {
1272 hasVisibleItemBefore =
true;
1273 visibleSeparatorBefore =
nullptr;
1278 if (visibleSeparatorBefore && !hasVisibleBinding(visibleSeparatorBefore))
1279 hideSeparator(visibleSeparatorBefore);
1282QQuickMenu *QQuickMenuPrivate::currentSubMenu()
const
1287 return currentItem->subMenu();
1290void QQuickMenuPrivate::setParentMenu(QQuickMenu *parent)
1293 if (parentMenu == parent)
1297 QObject::disconnect(parentMenu.data(), &QQuickMenu::cascadeChanged, q, &QQuickMenu::setCascade);
1298 disconnect(parentMenu.data(), &QQuickMenu::parentChanged,
this, &QQuickMenuPrivate::resolveParentItem);
1301 QObject::connect(parent, &QQuickMenu::cascadeChanged, q, &QQuickMenu::setCascade);
1302 connect(parent, &QQuickMenu::parentChanged,
this, &QQuickMenuPrivate::resolveParentItem);
1305 parentMenu = parent;
1307 resolveParentItem();
1312 QQuickMenu *menu = QQuickMenuPrivate::get(subMenu)->parentMenu;
1313 for (
int i = 0; i < QQuickMenuPrivate::get(menu)->contentModel->count(); ++i) {
1314 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(menu->itemAt(i));
1315 if (item && item->subMenu() == subMenu)
1321void QQuickMenuPrivate::resolveParentItem()
1325 q->resetParentItem();
1327 q->setParentItem(parentMenu->parentItem());
1329 q->setParentItem(findParentMenuItem(q));
1332void QQuickMenuPrivate::propagateKeyEvent(QKeyEvent *event)
1334 if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(parentItem)) {
1335 if (QQuickMenu *menu = menuItem->menu())
1336 QQuickMenuPrivate::get(menu)->propagateKeyEvent(event);
1337#if QT_CONFIG(quicktemplates2_container)
1338 }
else if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(parentItem)) {
1339 if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) {
1341 QCoreApplication::sendEvent(menuBar, event);
1347void QQuickMenuPrivate::startHoverTimer()
1351 hoverTimer = q->startTimer(SUBMENU_DELAY);
1354void QQuickMenuPrivate::stopHoverTimer()
1360 q->killTimer(hoverTimer);
1364void QQuickMenuPrivate::setCurrentIndex(
int index, Qt::FocusReason reason)
1367 if (currentIndex == index)
1370 QQuickMenuItem *newCurrentItem = qobject_cast<QQuickMenuItem *>(itemAt(index));
1371 if (currentItem != newCurrentItem) {
1374 currentItem->setHighlighted(
false);
1375 if (!newCurrentItem && window) {
1376 QQuickItem *focusItem = QQuickItemPrivate::get(contentItem)->subFocusItem;
1378 auto *daPriv = QQuickWindowPrivate::get(window)->deliveryAgentPrivate();
1379 daPriv->clearFocusInScope(contentItem, focusItem, Qt::OtherFocusReason);
1383 if (newCurrentItem) {
1384 newCurrentItem->setHighlighted(
true);
1385 newCurrentItem->forceActiveFocus(reason);
1387 currentItem = newCurrentItem;
1390 currentIndex = index;
1391 emit q->currentIndexChanged();
1394bool QQuickMenuPrivate::activateNextItem()
1396 int index = currentIndex;
1397 int count = contentModel->count();
1398 while (++index < count) {
1399 QQuickItem *item = itemAt(index);
1400 if (!item || !item->activeFocusOnTab() || !item->isEnabled())
1402 setCurrentIndex(index, Qt::TabFocusReason);
1408bool QQuickMenuPrivate::activatePreviousItem()
1410 int index = currentIndex;
1411 while (--index >= 0) {
1412 QQuickItem *item = itemAt(index);
1413 if (!item || !item->activeFocusOnTab() || !item->isEnabled())
1415 setCurrentIndex(index, Qt::BacktabFocusReason);
1421QQuickMenuItem *QQuickMenuPrivate::firstEnabledMenuItem()
const
1423 for (
int i = 0; i < contentModel->count(); ++i) {
1424 QQuickItem *item = itemAt(i);
1425 if (!item || !item->isEnabled())
1428 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
1437void QQuickMenuPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
1439 QQuickMenu *q = qobject_cast<QQuickMenu *>(prop->object);
1440 QQuickMenuPrivate *p = QQuickMenuPrivate::get(q);
1442 QQuickItem *item = qobject_cast<QQuickItem *>(obj);
1444 if (QQuickAction *action = qobject_cast<QQuickAction *>(obj))
1445 item = p->createItem(action);
1446 else if (QQuickMenu *menu = qobject_cast<QQuickMenu *>(obj))
1447 item = p->createItem(menu);
1451 if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) {
1452 QQuickItemPrivate::get(item)->addItemChangeListener(p, QQuickItemPrivate::SiblingOrder);
1453 item->setParentItem(p->contentItem);
1454 }
else if (p->contentModel->indexOf(item,
nullptr) == -1) {
1458 p->contentData.append(obj);
1462qsizetype QQuickMenuPrivate::contentData_count(QQmlListProperty<QObject> *prop)
1464 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1465 return QQuickMenuPrivate::get(q)->contentData.size();
1468QObject *QQuickMenuPrivate::contentData_at(QQmlListProperty<QObject> *prop, qsizetype index)
1470 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1471 return QQuickMenuPrivate::get(q)->contentData.value(index);
1474QPalette QQuickMenuPrivate::defaultPalette()
const
1476 return QQuickTheme::palette(QQuickTheme::Menu);
1479void QQuickMenuPrivate::contentData_clear(QQmlListProperty<QObject> *prop)
1481 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1482 QQuickMenuPrivate::get(q)->contentData.clear();
1485void QQuickMenuPrivate::resetContentItem()
1488 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Children);
1489 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Destroyed);
1490 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Geometry);
1492 const auto children = contentItem->childItems();
1493 for (QQuickItem *child : std::as_const(children))
1494 QQuickItemPrivate::get(child)->removeItemChangeListener(
this, QQuickItemPrivate::SiblingOrder);
1495 contentItem =
nullptr;
1499QQuickMenu::QQuickMenu(QObject *parent)
1500 : QQuickPopup(*(
new QQuickMenuPrivate), parent)
1505 connect(d->contentModel, &QQmlObjectModel::countChanged,
this, &QQuickMenu::countChanged);
1508QQuickMenu::~QQuickMenu()
1511 qCDebug(lcNativeMenus) <<
"destroying" <<
this
1513 << d->contentModel->count()
1514 <<
"native item count:" << d->nativeItems.count();
1518 if (
auto *menuItem = qobject_cast<QQuickMenuItem *>(d->parentItem)) {
1519 if (menuItem->subMenu() ==
this) {
1520 auto *menuItemPriv = QQuickMenuItemPrivate::get(menuItem);
1521 menuItemPriv->setSubMenu(
nullptr);
1529 while (d->contentModel->count() > 0)
1530 d->removeItem(0, d->itemAt(0), QQuickMenuPrivate::DestructionPolicy::Destroy);
1532 d->resetContentItem();
1536
1537
1538
1539
1540QQuickItem *QQuickMenu::itemAt(
int index)
const
1542 Q_D(
const QQuickMenu);
1543 return d->itemAt(index);
1547
1548
1549
1550
1551
1552
1553
1554void QQuickMenu::addItem(QQuickItem *item)
1557 insertItem(d->contentModel->count(), item);
1561
1562
1563
1564
1565
1566
1567
1568void QQuickMenu::insertItem(
int index, QQuickItem *item)
1573 const int count = d->contentModel->count();
1574 if (index < 0 || index > count)
1577 int oldIndex = d->contentModel->indexOf(item,
nullptr);
1578 if (oldIndex != -1) {
1579 if (oldIndex < index)
1581 if (oldIndex != index) {
1582 d->moveItem(oldIndex, index);
1585 d->insertItem(index, item);
1590
1591
1592
1593
1594void QQuickMenu::moveItem(
int from,
int to)
1597 const int count = d->contentModel->count();
1598 if (from < 0 || from > count - 1)
1600 if (to < 0 || to > count - 1)
1604 d->moveItem(from, to);
1608
1609
1610
1611
1612
1613void QQuickMenu::removeItem(QQuickItem *item)
1619 const int index = d->contentModel->indexOf(item,
nullptr);
1623 d->removeItem(index, item, QQuickMenuPrivate::DestructionPolicy::Destroy);
1627
1628
1629
1630
1631
1632
1633
1634QQuickItem *QQuickMenu::takeItem(
int index)
1637 const int count = d->contentModel->count();
1638 if (index < 0 || index >= count)
1641 QQuickItem *item = itemAt(index);
1643 d->removeItem(index, item);
1648
1649
1650
1651
1652
1653
1654QQuickMenu *QQuickMenu::menuAt(
int index)
const
1656 Q_D(
const QQuickMenu);
1657 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1661 return item->subMenu();
1665
1666
1667
1668
1669
1670
1671void QQuickMenu::addMenu(QQuickMenu *menu)
1674 insertMenu(d->contentModel->count(), menu);
1678
1679
1680
1681
1682
1683
1684void QQuickMenu::insertMenu(
int index, QQuickMenu *menu)
1690 insertItem(index, d->createItem(menu));
1694
1695
1696
1697
1698
1699void QQuickMenu::removeMenu(QQuickMenu *menu)
1705 const int count = d->contentModel->count();
1706 for (
int i = 0; i < count; ++i) {
1707 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(i));
1708 if (!item || item->subMenu() != menu)
1715 menu->deleteLater();
1719
1720
1721
1722
1723
1724
1725
1726
1727QQuickMenu *QQuickMenu::takeMenu(
int index)
1730 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1734 QQuickMenu *subMenu = item->subMenu();
1738 d->removeItem(index, item);
1739 item->deleteLater();
1745
1746
1747
1748
1749
1750
1751QQuickAction *QQuickMenu::actionAt(
int index)
const
1753 Q_D(
const QQuickMenu);
1754 if (!
const_cast<QQuickMenuPrivate *>(d)->maybeNativeHandle()) {
1755 QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton *>(d->itemAt(index));
1759 return item->action();
1761 if (index < 0 || index >= d->nativeItems.size())
1764 return d->nativeItems.at(index)->action();
1769
1770
1771
1772
1773
1774
1775void QQuickMenu::addAction(QQuickAction *action)
1778 insertAction(d->contentModel->count(), action);
1782
1783
1784
1785
1786
1787
1788void QQuickMenu::insertAction(
int index, QQuickAction *action)
1794 insertItem(index, d->createItem(action));
1798
1799
1800
1801
1802
1803void QQuickMenu::removeAction(QQuickAction *action)
1809 const int count = d->contentModel->count();
1810 for (
int i = 0; i < count; ++i) {
1811 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(i));
1812 if (!item || item->action() != action)
1819 action->deleteLater();
1823
1824
1825
1826
1827
1828
1829
1830
1831QQuickAction *QQuickMenu::takeAction(
int index)
1834 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1838 QQuickAction *action = item->action();
1842 d->removeItem(index, item);
1843 item->deleteLater();
1847bool QQuickMenu::isVisible()
const
1849 Q_D(
const QQuickMenu);
1850 if (d->maybeNativeHandle())
1852 return QQuickPopup::isVisible();
1855void QQuickMenu::setVisible(
bool visible)
1858 if (visible == d->visible)
1861 auto *window =
this->window();
1862 if (visible && window) {
1865 QQuickWindowPrivate::get(window)->rmbContextMenuEventEnabled =
false;
1870 if (!d->parentMenu) {
1871 QQuickOverlay *overlay = QQuickOverlay::overlay(window, parentItem());
1873 const QList<QQuickPopup *> allPopups = QQuickOverlayPrivate::get(overlay)->allPopups;
1874 for (
auto *popup : allPopups) {
1875 if (popup !=
this && qobject_cast<QQuickMenu *>(popup))
1882 if (visible && ((d->useNativeMenu() && !d->maybeNativeHandle())
1883 || (!d->useNativeMenu() && d->maybeNativeHandle()))) {
1887 qCDebug(lcNativeMenus) <<
"setVisible called - useNativeMenu:" << d->useNativeMenu()
1888 <<
"maybeNativeHandle:" << d->maybeNativeHandle();
1889 d->syncWithUseNativeMenu();
1891 if (d->maybeNativeHandle()) {
1892 d->setNativeMenuVisible(visible);
1897 if (QQuickMenuPrivate::get(d->rootMenu())->menuBar)
1898 d->wmWindowType = QNativeInterface::Private::QXcbWindow::DropDownMenu;
1900 d->wmWindowType = QNativeInterface::Private::QXcbWindow::PopupMenu;
1905 QQuickPopup::setVisible(visible);
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929QVariant QQuickMenu::contentModel()
const
1931 Q_D(
const QQuickMenu);
1932 return QVariant::fromValue(d->contentModel);
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950QQmlListProperty<QObject> QQuickMenu::contentData()
1953 if (!d->contentItem)
1954 QQuickControlPrivate::get(d->popupItem)->executeContentItem();
1955 return QQmlListProperty<QObject>(
this,
nullptr,
1956 QQuickMenuPrivate::contentData_append,
1957 QQuickMenuPrivate::contentData_count,
1958 QQuickMenuPrivate::contentData_at,
1959 QQuickMenuPrivate::contentData_clear);
1963
1964
1965
1966
1967
1968
1969
1970
1971QString QQuickMenu::title()
const
1973 Q_D(
const QQuickMenu);
1977void QQuickMenu::setTitle(
const QString &title)
1980 if (title == d->title)
1984 d->handle->setText(title);
1985 emit titleChanged(title);
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2005QQuickIcon QQuickMenu::icon()
const
2007 Q_D(
const QQuickMenu);
2011void QQuickMenu::setIcon(
const QQuickIcon &icon)
2014 if (icon == d->icon)
2017 d->icon.ensureRelativeSourceResolved(
this);
2018 emit iconChanged(icon);
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046bool QQuickMenu::separatorsCollapsible()
const
2048 Q_D(
const QQuickMenu);
2049 return d->collapsibleSeparators;
2052void QQuickMenu::setSeparatorsCollapsible(
bool collapsible)
2055 if (d->collapsibleSeparators == collapsible)
2057 d->collapsibleSeparators = collapsible;
2058 emit separatorsCollapsibleChanged();
2059 d->updateCollapsedSeparators();
2061 d->handle->syncSeparatorsCollapsible(collapsible);
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080bool QQuickMenu::cascade()
const
2082 Q_D(
const QQuickMenu);
2086void QQuickMenu::setCascade(
bool cascade)
2089 if (d->cascade == cascade)
2091 d->cascade = cascade;
2093 d->resolveParentItem();
2094 emit cascadeChanged(cascade);
2097void QQuickMenu::resetCascade()
2101 setCascade(d->parentMenu->cascade());
2103 setCascade(shouldCascade());
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122qreal QQuickMenu::overlap()
const
2124 Q_D(
const QQuickMenu);
2128void QQuickMenu::setOverlap(qreal overlap)
2131 if (d->overlap == overlap)
2133 d->overlap = overlap;
2134 emit overlapChanged();
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159QQmlComponent *QQuickMenu::delegate()
const
2161 Q_D(
const QQuickMenu);
2165void QQuickMenu::setDelegate(QQmlComponent *delegate)
2168 if (d->delegate == delegate)
2171 d->delegate = delegate;
2172 emit delegateChanged();
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187int QQuickMenu::currentIndex()
const
2189 Q_D(
const QQuickMenu);
2190 return d->currentIndex;
2193void QQuickMenu::setCurrentIndex(
int index)
2196 d->setCurrentIndex(index, Qt::OtherFocusReason);
2200
2201
2202
2203
2204
2205
2206int QQuickMenu::count()
const
2208 Q_D(
const QQuickMenu);
2209 return d->contentModel->count();
2212void QQuickMenuPrivate::popup(QQuickItem *menuItem)
2217 QQmlNullableValue<QPointF> pos;
2218#if QT_CONFIG(cursor)
2219 if (parentItem && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows))
2220 pos = parentItem->mapFromGlobal(QCursor::pos());
2224 if (!pos.isValid() && parentItem)
2225 pos = QPointF((parentItem->width() - q->width()) / 2, (parentItem->height() - q->height()) / 2);
2227 q->popup(pos.isValid() ? pos.value() : QPointF(), menuItem);
2230void QQuickMenu::popup(
const QPointF &position, QQuickItem *menuItem)
2234#if QT_CONFIG(cursor)
2236 offset = d->popupItem->mapFromItem(menuItem, QPointF(0, 0)).y();
2238 setPosition(position - QPointF(0, offset));
2241 d->setCurrentIndex(d->contentModel->indexOf(menuItem,
nullptr), Qt::PopupFocusReason);
2243 d->setCurrentIndex(-1, Qt::PopupFocusReason);
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2293void QQuickMenu::popup(QQuickItem *parent, qreal x, qreal y, QQuickItem *menuItem)
2295 popup(parent, QPointF {x, y}, menuItem);
2298void QQuickMenu::popup(QQuickItem *parent,
const QPointF &position, QQuickItem *menuItem)
2301 if (parent && !d->popupItem->isAncestorOf(parent))
2302 setParentItem(parent);
2303 popup(position, menuItem);
2306void QQuickMenu::popup(QQuickItem *parent, QQuickItem *menuItem)
2309 QQuickItem *parentItem =
nullptr;
2310 if (parent && !d->popupItem->isAncestorOf(parent))
2311 parentItem = parent;
2313 setParentItem(parentItem);
2319void QQuickMenu::popup(QQuickItem *parent)
2322 QQuickItem *menuItem =
nullptr;
2324 if (!d->popupItem->isAncestorOf(parent))
2325 setParentItem(parent);
2326 if (d->popupItem->isAncestorOf(parent))
2332void QQuickMenu::popup(qreal x, qreal y, QQuickItem *menuItem)
2334 popup(QPointF {x, y}, menuItem);
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352void QQuickMenu::dismiss()
2354 QQuickMenu *menu =
this;
2357 menu = QQuickMenuPrivate::get(menu)->parentMenu;
2361void QQuickMenu::componentComplete()
2364 QQuickPopup::componentComplete();
2366 d->updateTextPadding();
2367 d->syncWithUseNativeMenu();
2370void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
2373 QQuickPopup::contentItemChange(newItem, oldItem);
2376 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
2377 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Destroyed);
2378 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
2381 QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Children);
2382 QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Destroyed);
2383 QQuickItemPrivate::get(newItem)->updateOrAddGeometryChangeListener(d, QQuickGeometryChange::Width);
2386 d->contentItem = newItem;
2389void QQuickMenu::itemChange(QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData &data)
2392 QQuickPopup::itemChange(change, data);
2395 case QQuickItem::ItemVisibleHasChanged:
2396 if (!data.boolValue && d->cascade) {
2399 d->setCurrentIndex(-1, Qt::OtherFocusReason);
2407void QQuickMenu::keyPressEvent(QKeyEvent *event)
2410 QQuickPopup::keyPressEvent(event);
2418 switch (event->key()) {
2420 if (!d->activatePreviousItem())
2421 d->propagateKeyEvent(event);
2425 d->activateNextItem();
2431 if (d->popupItem->isMirrored() == (event->key() == Qt::Key_Right)) {
2432 if (d->parentMenu && d->currentItem) {
2434 d->parentMenu->open();
2439 if (QQuickMenu *subMenu = d->currentSubMenu()) {
2440 auto subMenuPrivate = QQuickMenuPrivate::get(subMenu);
2441 subMenuPrivate->popup(subMenuPrivate->firstEnabledMenuItem());
2445 if (!event->isAccepted())
2446 d->propagateKeyEvent(event);
2449#if QT_CONFIG(shortcut)
2453 if (!QKeySequence::mnemonic(QStringLiteral(
"&A")).isEmpty())
2462#if QT_CONFIG(shortcut)
2463 if (event->modifiers() == Qt::NoModifier) {
2464 for (
int i = 0; i < count(); ++i) {
2465 QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton*>(d->itemAt(i));
2468 const QKeySequence keySequence = QKeySequence::mnemonic(item->text());
2469 if (keySequence.isEmpty())
2471 if (keySequence[0].key() == event->key()) {
2480void QQuickMenu::timerEvent(QTimerEvent *event)
2483 if (event->timerId() == d->hoverTimer) {
2484 if (QQuickMenu *subMenu = d->currentSubMenu())
2486 d->stopHoverTimer();
2489 QQuickPopup::timerEvent(event);
2492QFont QQuickMenu::defaultFont()
const
2494 return QQuickTheme::font(QQuickTheme::Menu);
2497#if QT_CONFIG(accessibility)
2498QAccessible::Role QQuickMenu::accessibleRole()
const
2500 return QAccessible::PopupMenu;
2506#include "moc_qquickmenu_p.cpp"
Combined button and popup list for selecting options.