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()
595 syncWithUseNativeMenu();
597 handle->setMenuType(QPlatformMenu::EditMenu);
600QQuickItem *QQuickMenuPrivate::itemAt(
int index)
const
602 return qobject_cast<QQuickItem *>(contentModel->get(index));
605void QQuickMenuPrivate::insertItem(
int index, QQuickItem *item)
607 qCDebug(lcMenu) <<
"insert called with index" << index <<
"item" << item;
610 contentData.append(item);
611 item->setParentItem(contentItem);
612 QQuickItemPrivate::get(item)->setCulled(
true);
615 QQuickItemPrivate::get(item)->addItemChangeListener(
this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent | QQuickItemPrivate::ImplicitWidth);
616 QQuickItemPrivate::get(item)->updateOrAddGeometryChangeListener(
this, QQuickGeometryChange::Width);
617 contentModel->insert(index, item);
619 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
621 QQuickMenuItemPrivate::get(menuItem)->setMenu(q);
622 if (QQuickMenu *subMenu = menuItem->subMenu())
623 QQuickMenuPrivate::get(subMenu)->setParentMenu(q);
624 QObjectPrivate::connect(menuItem, &QQuickMenuItem::triggered,
this, &QQuickMenuPrivate::onItemTriggered);
625 QObjectPrivate::connect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged,
this, &QQuickMenuPrivate::updateTextPadding);
626 QObjectPrivate::connect(menuItem, &QQuickMenuItem::visibleChanged,
this, &QQuickMenuPrivate::onItemVisibleChanged);
627 QObjectPrivate::connect(menuItem, &QQuickItem::activeFocusChanged,
this, &QQuickMenuPrivate::onItemActiveFocusChanged);
628 QObjectPrivate::connect(menuItem, &QQuickControl::hoveredChanged,
this, &QQuickMenuPrivate::onItemHovered);
631 QQuickMenuSeparator *separator = qobject_cast<QQuickMenuSeparator *>(item);
633 QObjectPrivate::connect(separator, &QQuickMenuSeparator::visibleChanged,
this, &QQuickMenuPrivate::updateCollapsedSeparators);
635 if (maybeNativeHandle() && complete)
636 maybeCreateAndInsertNativeItem(index, item);
638 if (lcMenu().isDebugEnabled())
639 printContentModelItems();
642 updateContentWidth();
644 updateCollapsedSeparators();
647void QQuickMenuPrivate::maybeCreateAndInsertNativeItem(
int index, QQuickItem *item)
651 Q_ASSERT_X(handle, Q_FUNC_INFO, qPrintable(QString::fromLatin1(
652 "Expected %1 to be using a native menu").arg(QDebug::toString(q))));
653 std::unique_ptr<QQuickNativeMenuItem> nativeMenuItem(QQuickNativeMenuItem::createFromNonNativeItem(q, item));
654 if (!nativeMenuItem) {
656 qmlWarning(q) <<
"Native menu failed to create a native menu item for item at index" << index;
660 nativeItems.insert(index, nativeMenuItem.get());
664 if (nativeMenuItem->handle()) {
665 QQuickNativeMenuItem *before = nativeItems.value(index + 1);
666 handle->insertMenuItem(nativeMenuItem->handle(), before ? before->handle() :
nullptr);
667 qCDebug(lcNativeMenus) <<
"inserted native menu item at index" << index
668 <<
"before" << (before ? before->debugText() : QStringLiteral(
"null"));
670 if (nativeMenuItem->subMenu() && QQuickMenuPrivate::get(nativeMenuItem->subMenu())->nativeItems.count()
671 < nativeMenuItem->subMenu()->count()) {
677 recursivelyCreateNativeMenuItems(nativeMenuItem->subMenu());
681 nativeMenuItem.release();
683 qCDebug(lcNativeMenus) <<
"nativeItems now contains the following items:"
684 << nativeMenuItemListToString(nativeItems);
687void QQuickMenuPrivate::moveItem(
int from,
int to)
689 contentModel->move(from, to);
691 if (maybeNativeHandle())
692 nativeItems.move(from, to);
696
697
698
699
700
701
702
703
704
705void QQuickMenuPrivate::removeItem(
int index, QQuickItem *item, DestructionPolicy destructionPolicy)
707 qCDebug(lcMenu) <<
"removeItem called with index" << index <<
"item" << item;
709 if (maybeNativeHandle())
710 removeNativeItem(index);
712 contentData.removeOne(item);
714 QQuickItemPrivate::get(item)->removeItemChangeListener(
this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent | QQuickItemPrivate::ImplicitWidth);
715 QQuickItemPrivate::get(item)->removeItemChangeListener(
this, QQuickItemPrivate::Geometry);
716 item->setParentItem(
nullptr);
717 contentModel->remove(index);
719 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
721 QQuickMenuItemPrivate::get(menuItem)->setMenu(
nullptr);
722 if (QQuickMenu *subMenu = menuItem->subMenu())
723 QQuickMenuPrivate::get(subMenu)->setParentMenu(
nullptr);
724 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::triggered,
this, &QQuickMenuPrivate::onItemTriggered);
725 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged,
this, &QQuickMenuPrivate::updateTextPadding);
726 QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::visibleChanged,
this, &QQuickMenuPrivate::onItemVisibleChanged);
727 QObjectPrivate::disconnect(menuItem, &QQuickItem::activeFocusChanged,
this, &QQuickMenuPrivate::onItemActiveFocusChanged);
728 QObjectPrivate::disconnect(menuItem, &QQuickControl::hoveredChanged,
this, &QQuickMenuPrivate::onItemHovered);
731 QQuickMenuSeparator *separator = qobject_cast<QQuickMenuSeparator *>(item);
733 QObjectPrivate::disconnect(separator, &QQuickMenuSeparator::visibleChanged,
this, &QQuickMenuPrivate::updateCollapsedSeparators);
734 collapsedSeparators.remove(item);
737 if (destructionPolicy == DestructionPolicy::Destroy)
740 if (lcMenu().isDebugEnabled())
741 printContentModelItems();
743 updateContentWidth();
745 updateCollapsedSeparators();
749
750
751
752
753
754
755
756
757
758void QQuickMenuPrivate::removeNativeItem(
int index, SyncPolicy syncPolicy)
763 Q_ASSERT_X(index >= 0 && index < nativeItems.size(), Q_FUNC_INFO, qPrintable(QString::fromLatin1(
764 "index %1 is less than 0 or greater than or equal to %2").arg(index).arg(nativeItems.size())));
768 std::unique_ptr<QQuickNativeMenuItem> nativeItem(nativeItems.takeAt(index));
769 qCDebug(lcNativeMenus) <<
"removing native item" << nativeItem->debugText() <<
"at index" << index
770 <<
"from" << q_func() <<
"...";
771 QQuickMenu *subMenu = nativeItem->subMenu();
773 Q_ASSERT(nativeItem->handle());
774 auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
775 while (!subMenuPrivate->nativeItems.isEmpty()) {
776 subMenuPrivate->removeNativeItem(0, SyncPolicy::DoNotSync);
780 Q_ASSERT(nativeItem->handle());
781 handle->removeMenuItem(nativeItem->handle());
782 if (syncPolicy == SyncPolicy::Sync)
783 syncWithNativeMenu();
786 auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
792 subMenuPrivate->resetNativeData();
795 qCDebug(lcNativeMenus).nospace() <<
"... after removing item at index " << index
796 <<
", nativeItems now contains the following items: " << nativeMenuItemListToString(nativeItems);
799void QQuickMenuPrivate::resetNativeData()
801 qCDebug(lcNativeMenus) <<
"resetNativeData called on" << q_func();
803 triedToCreateNativeMenu =
false;
806void QQuickMenuPrivate::recursivelyCreateNativeMenuItems(QQuickMenu *menu)
808 auto *menuPrivate = QQuickMenuPrivate::get(menu);
811 if (!menuPrivate->triedToCreateNativeMenu)
812 menuPrivate->createNativeMenu();
814 const int qtyItemsToCreate = menuPrivate->contentModel->count();
815 if (menuPrivate->nativeItems.count() == qtyItemsToCreate)
818 qCDebug(lcNativeMenus) <<
"recursively creating" << qtyItemsToCreate <<
"menu item(s) for" << menu;
819 Q_ASSERT(menuPrivate->nativeItems.count() == 0);
820 for (
int i = 0; i < qtyItemsToCreate; ++i) {
821 QQuickItem *item = menu->itemAt(i);
822 menuPrivate->maybeCreateAndInsertNativeItem(i, item);
823 auto *menuItem = qobject_cast<QQuickMenuItem *>(item);
824 if (menuItem && menuItem->subMenu())
825 recursivelyCreateNativeMenuItems(menuItem->subMenu());
829void QQuickMenuPrivate::printContentModelItems()
const
831 qCDebug(lcMenu) <<
"contentModel now contains:";
832 for (
int i = 0; i < contentModel->count(); ++i)
833 qCDebug(lcMenu) <<
"-" << itemAt(i);
836QQuickItem *QQuickMenuPrivate::beginCreateItem()
842 QQmlContext *context = delegate->creationContext();
844 context = qmlContext(q);
846 QObject *object = delegate->beginCreate(context);
847 QQuickItem *item = qobject_cast<QQuickItem *>(object);
851 QQml_setParent_noEvent(item, q);
856void QQuickMenuPrivate::completeCreateItem()
861 delegate->completeCreate();
864QQuickItem *QQuickMenuPrivate::createItem(QQuickMenu *menu)
866 QQuickItem *item = beginCreateItem();
867 if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item))
868 QQuickMenuItemPrivate::get(menuItem)->setSubMenu(menu);
869 completeCreateItem();
873QQuickItem *QQuickMenuPrivate::createItem(QQuickAction *action)
875 QQuickItem *item = beginCreateItem();
876 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(item))
877 button->setAction(action);
878 completeCreateItem();
882void QQuickMenuPrivate::resizeItem(QQuickItem *item)
884 if (!item || !contentItem)
887 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
888 if (!p->widthValid()) {
889 item->setWidth(contentItem->width());
890 p->widthValidFlag =
false;
894void QQuickMenuPrivate::resizeItems()
899 for (
int i = 0; i < contentModel->count(); ++i)
900 resizeItem(itemAt(i));
903void QQuickMenuPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
906 if (!QQuickItemPrivate::get(child)->isTransparentForPositioner() && !contentData.contains(child))
907 insertItem(contentModel->count(), child);
910void QQuickMenuPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
914 removeItem(contentModel->indexOf(item,
nullptr), item);
917void QQuickMenuPrivate::itemSiblingOrderChanged(QQuickItem *)
921 QList<QQuickItem *> siblings = contentItem->childItems();
924 for (
int i = 0; i < siblings.size(); ++i) {
925 QQuickItem* sibling = siblings.at(i);
926 if (QQuickItemPrivate::get(sibling)->isTransparentForPositioner())
928 int index = contentModel->indexOf(sibling,
nullptr);
929 q->moveItem(index, to++);
933void QQuickMenuPrivate::itemDestroyed(QQuickItem *item)
935 if (item == contentItem) {
938 QQuickPopupPrivate::itemDestroyed(item);
940 int index = contentModel->indexOf(item,
nullptr);
942 removeItem(index, item);
947void QQuickMenuPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange,
const QRectF &)
952 if (item == contentItem) {
963void QQuickMenuPrivate::itemImplicitWidthChanged(QQuickItem *item)
965 if (item != contentItem)
966 updateContentWidth();
969void QQuickMenuPrivate::updateContentWidth()
971 if (!contentModel || !contentItem)
976 for (
int i = 0; i < contentModel->count(); ++i) {
977 QQuickItem *item = q->itemAt(i);
978 if (item && QQuickItemPrivate::get(item)->explicitVisible)
979 maxWidth = qMax(maxWidth, item->implicitWidth());
985 if (!qFuzzyIsNull(maxWidth))
986 contentItem->setImplicitWidth(maxWidth);
989QQuickPopupPositioner *QQuickMenuPrivate::getPositioner()
993 positioner =
new QQuickMenuPositioner(q);
999 QQuickMenu *menu =
static_cast<QQuickMenu *>(popup());
1000 QQuickMenuPrivate *menu_d = QQuickMenuPrivate::get(menu);
1002 if (QQuickMenu *parentMenu = menu_d->parentMenu) {
1003 if (menu_d->cascade) {
1006 if (menu_d->popupItem->isMirrored()) {
1007 const qreal distanceToFrame = parentMenu->leftPadding();
1008 const qreal menuX = -menu->width() - distanceToFrame + menu->overlap();
1009 menu->setPosition({menuX, -menu->topPadding()});
1010 }
else if (menu_d->parentItem) {
1011 const qreal distanceToFrame = parentMenu->rightPadding();
1012 const qreal menuX = menu_d->parentItem->width() + distanceToFrame - menu->overlap();
1013 menu->setPosition({menuX, -menu->topPadding()});
1016 const qreal menuX = parentMenu->x() + (parentMenu->width() - menu->width()) / 2;
1017 const qreal menuY = parentMenu->y() + (parentMenu->height() - menu->height()) / 2;
1018 menu->setPosition({menuX, menuY});
1022 QQuickPopupPositioner::reposition();
1025bool QQuickMenuPrivate::prepareEnterTransition()
1028 if (parentMenu && !cascade)
1029 parentMenu->close();
1033 allowHorizontalFlip = cascade && parentMenu;
1035 updateCollapsedSeparators();
1047 auto *contentItemAsListView = qobject_cast<QQuickListView *>(contentItem);
1048 if (contentItemAsListView) {
1049 if (QQuickItemViewPrivate::get(contentItemAsListView)->currentChanges.hasPendingChanges())
1050 contentItemAsListView->forceLayout();
1053 if (!QQuickPopupPrivate::prepareEnterTransition())
1056 if (!hasClosePolicy) {
1057 if (cascade && parentMenu)
1058 closePolicy = cascadingSubMenuClosePolicy;
1060 q->resetClosePolicy();
1065bool QQuickMenuPrivate::prepareExitTransition()
1067 if (!QQuickPopupPrivate::prepareExitTransition())
1072 QQuickMenu *subMenu = currentSubMenu();
1074 QPointer<QQuickMenuItem> currentSubMenuItem = QQuickMenuPrivate::get(subMenu)->currentItem;
1076 subMenu = currentSubMenuItem ? currentSubMenuItem->subMenu() :
nullptr;
1081bool QQuickMenuPrivate::blockInput(QQuickItem *item,
const QPointF &point)
const
1084 return (cascade && parentMenu && contains(point)) || QQuickPopupPrivate::blockInput(item, point);
1087bool QQuickMenuPrivate::handlePress(QQuickItem *item,
const QPointF &point, ulong timestamp)
1091 return QQuickPopupPrivate::handlePress(item, point, timestamp)
1092 || (popupItem == item);
1097
1098
1099
1100
1101
1102
1103bool QQuickMenuPrivate::handleReleaseWithoutGrab(
const QEventPoint &eventPoint)
1105 const QPointF scenePos = eventPoint.scenePosition();
1106 if (!contains(scenePos))
1109 auto *list = qobject_cast<QQuickListView *>(contentItem);
1113 const QPointF listPos = list->mapFromScene(scenePos);
1115 auto *menuItem = qobject_cast<QQuickMenuItem *>(list->itemAt(listPos.x(), listPos.y()));
1116 if (menuItem && menuItem->isHighlighted()) {
1117 menuItem->animateClick();
1124void QQuickMenuPrivate::onItemHovered()
1127 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(q->sender());
1128 if (!button || !button->isHovered() || !button->isEnabled() || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
1131 QQuickMenuItem *oldCurrentItem = currentItem;
1133 int index = contentModel->indexOf(button,
nullptr);
1135 setCurrentIndex(index, Qt::OtherFocusReason);
1136 if (oldCurrentItem != currentItem) {
1137 if (oldCurrentItem) {
1138 QQuickMenu *subMenu = oldCurrentItem->subMenu();
1143 QQuickMenu *subMenu = currentItem->menu();
1144 if (subMenu && subMenu->cascade())
1151void QQuickMenuPrivate::onItemTriggered()
1154 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(q->sender());
1158 if (QQuickMenu *subMenu = item->subMenu()) {
1159 auto subMenuPrivate = QQuickMenuPrivate::get(subMenu);
1160 subMenuPrivate->popup(subMenuPrivate->firstEnabledMenuItem());
1166void QQuickMenuPrivate::onItemActiveFocusChanged()
1169 QQuickItem *item = qobject_cast<QQuickItem*>(q->sender());
1170 if (!item->hasActiveFocus())
1173 int indexOfItem = contentModel->indexOf(item,
nullptr);
1174 QQuickControl *control = qobject_cast<QQuickControl *>(item);
1175 setCurrentIndex(indexOfItem, control ? control->focusReason() : Qt::OtherFocusReason);
1178void QQuickMenuPrivate::onItemVisibleChanged()
1180 updateTextPadding();
1181 updateCollapsedSeparators();
1184void QQuickMenuPrivate::updateTextPadding()
1191 for (
int i = 0; i < q->count(); ++i) {
1192 if (
const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i)))
1193 if (menuItem->isVisible())
1194 padding = qMax(padding, menuItem->implicitTextPadding());
1197 if (padding == textPadding)
1200 textPadding = padding;
1202 for (
int i = 0; i < q->count(); ++i) {
1203 if (
const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i)))
1204 emit menuItem->textPaddingChanged();
1208void QQuickMenuPrivate::updateCollapsedSeparators()
1210 if (!complete || updatingCollapsedSeparators)
1215 QScopedValueRollback guard(updatingCollapsedSeparators,
true);
1217 auto isExplicitlyVisible = [](
const QQuickItem *item) {
1218 return QQuickItemPrivate::get(item)->explicitVisible;
1221 auto hasVisibleBinding = [](QQuickItem *item) {
1222 static const int visibleIndex = QQuickItem::staticMetaObject.indexOfProperty(
"visible");
1223 auto ddata = QQmlData::get(item,
false);
1226 return ddata->hasBindingBit(visibleIndex);
1229 auto hideSeparator = [
this](QQuickItem *separatorItem) {
1230 separatorItem->setVisible(
false);
1231 separatorItem->setHeight(0);
1232 collapsedSeparators.insert(separatorItem);
1235 auto showSeparator = [
this](QQuickItem *separatorItem) {
1236 if (!collapsedSeparators.contains(separatorItem))
1238 separatorItem->setVisible(
true);
1239 separatorItem->resetHeight();
1240 collapsedSeparators.remove(separatorItem);
1243 const int count = contentModel->count();
1245 if (!collapsibleSeparators) {
1246 for (
int i = 0; i < count; ++i)
1247 showSeparator(itemAt(i));
1251 QQuickItem *visibleSeparatorBefore =
nullptr;
1252 bool hasVisibleItemBefore =
false;
1254 for (
int i = 0; i < count; ++i) {
1255 QQuickItem *item = itemAt(i);
1259 if (qobject_cast<QQuickMenuSeparator *>(item)) {
1260 if (hasVisibleBinding(item)) {
1261 if (isExplicitlyVisible(item) && !visibleSeparatorBefore)
1262 visibleSeparatorBefore = item;
1266 if (visibleSeparatorBefore) {
1267 hideSeparator(item);
1268 }
else if (hasVisibleItemBefore) {
1269 visibleSeparatorBefore = item;
1270 showSeparator(item);
1272 hideSeparator(item);
1274 }
else if (isExplicitlyVisible(item)) {
1275 hasVisibleItemBefore =
true;
1276 visibleSeparatorBefore =
nullptr;
1281 if (visibleSeparatorBefore && !hasVisibleBinding(visibleSeparatorBefore))
1282 hideSeparator(visibleSeparatorBefore);
1285QQuickMenu *QQuickMenuPrivate::currentSubMenu()
const
1290 return currentItem->subMenu();
1293void QQuickMenuPrivate::setParentMenu(QQuickMenu *parent)
1296 if (parentMenu == parent)
1300 QObject::disconnect(parentMenu.data(), &QQuickMenu::cascadeChanged, q, &QQuickMenu::setCascade);
1301 disconnect(parentMenu.data(), &QQuickMenu::parentChanged,
this, &QQuickMenuPrivate::resolveParentItem);
1304 QObject::connect(parent, &QQuickMenu::cascadeChanged, q, &QQuickMenu::setCascade);
1305 connect(parent, &QQuickMenu::parentChanged,
this, &QQuickMenuPrivate::resolveParentItem);
1308 parentMenu = parent;
1310 resolveParentItem();
1315 QQuickMenu *menu = QQuickMenuPrivate::get(subMenu)->parentMenu;
1316 for (
int i = 0; i < QQuickMenuPrivate::get(menu)->contentModel->count(); ++i) {
1317 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(menu->itemAt(i));
1318 if (item && item->subMenu() == subMenu)
1324void QQuickMenuPrivate::resolveParentItem()
1328 q->resetParentItem();
1330 q->setParentItem(parentMenu->parentItem());
1332 q->setParentItem(findParentMenuItem(q));
1335void QQuickMenuPrivate::propagateKeyEvent(QKeyEvent *event)
1337 if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(parentItem)) {
1338 if (QQuickMenu *menu = menuItem->menu())
1339 QQuickMenuPrivate::get(menu)->propagateKeyEvent(event);
1340#if QT_CONFIG(quicktemplates2_container)
1341 }
else if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(parentItem)) {
1342 if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) {
1344 QCoreApplication::sendEvent(menuBar, event);
1350void QQuickMenuPrivate::startHoverTimer()
1354 hoverTimer = q->startTimer(SUBMENU_DELAY);
1357void QQuickMenuPrivate::stopHoverTimer()
1363 q->killTimer(hoverTimer);
1367void QQuickMenuPrivate::setCurrentIndex(
int index, Qt::FocusReason reason)
1370 if (currentIndex == index)
1373 QQuickMenuItem *newCurrentItem = qobject_cast<QQuickMenuItem *>(itemAt(index));
1374 if (currentItem != newCurrentItem) {
1377 currentItem->setHighlighted(
false);
1378 if (!newCurrentItem && window) {
1379 QQuickItem *focusItem = QQuickItemPrivate::get(contentItem)->subFocusItem;
1381 auto *daPriv = QQuickWindowPrivate::get(window)->deliveryAgentPrivate();
1382 daPriv->clearFocusInScope(contentItem, focusItem, Qt::OtherFocusReason);
1386 if (newCurrentItem) {
1387 newCurrentItem->setHighlighted(
true);
1388 newCurrentItem->forceActiveFocus(reason);
1390 currentItem = newCurrentItem;
1393 currentIndex = index;
1394 emit q->currentIndexChanged();
1397bool QQuickMenuPrivate::activateNextItem()
1399 int index = currentIndex;
1400 int count = contentModel->count();
1401 while (++index < count) {
1402 QQuickItem *item = itemAt(index);
1403 if (!item || !item->activeFocusOnTab() || !item->isEnabled())
1405 setCurrentIndex(index, Qt::TabFocusReason);
1411bool QQuickMenuPrivate::activatePreviousItem()
1413 int index = currentIndex;
1414 while (--index >= 0) {
1415 QQuickItem *item = itemAt(index);
1416 if (!item || !item->activeFocusOnTab() || !item->isEnabled())
1418 setCurrentIndex(index, Qt::BacktabFocusReason);
1424QQuickMenuItem *QQuickMenuPrivate::firstEnabledMenuItem()
const
1426 for (
int i = 0; i < contentModel->count(); ++i) {
1427 QQuickItem *item = itemAt(i);
1428 if (!item || !item->isEnabled())
1431 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
1440void QQuickMenuPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
1442 QQuickMenu *q = qobject_cast<QQuickMenu *>(prop->object);
1443 QQuickMenuPrivate *p = QQuickMenuPrivate::get(q);
1445 QQuickItem *item = qobject_cast<QQuickItem *>(obj);
1447 if (QQuickAction *action = qobject_cast<QQuickAction *>(obj))
1448 item = p->createItem(action);
1449 else if (QQuickMenu *menu = qobject_cast<QQuickMenu *>(obj))
1450 item = p->createItem(menu);
1454 if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) {
1455 QQuickItemPrivate::get(item)->addItemChangeListener(p, QQuickItemPrivate::SiblingOrder);
1456 item->setParentItem(p->contentItem);
1457 }
else if (p->contentModel->indexOf(item,
nullptr) == -1) {
1461 p->contentData.append(obj);
1465qsizetype QQuickMenuPrivate::contentData_count(QQmlListProperty<QObject> *prop)
1467 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1468 return QQuickMenuPrivate::get(q)->contentData.size();
1471QObject *QQuickMenuPrivate::contentData_at(QQmlListProperty<QObject> *prop, qsizetype index)
1473 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1474 return QQuickMenuPrivate::get(q)->contentData.value(index);
1477QPalette QQuickMenuPrivate::defaultPalette()
const
1479 return QQuickTheme::palette(QQuickTheme::Menu);
1482void QQuickMenuPrivate::contentData_clear(QQmlListProperty<QObject> *prop)
1484 QQuickMenu *q =
static_cast<QQuickMenu *>(prop->object);
1485 QQuickMenuPrivate::get(q)->contentData.clear();
1488void QQuickMenuPrivate::resetContentItem()
1491 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Children);
1492 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Destroyed);
1493 QQuickItemPrivate::get(contentItem)->removeItemChangeListener(
this, QQuickItemPrivate::Geometry);
1495 const auto children = contentItem->childItems();
1496 for (QQuickItem *child : std::as_const(children))
1497 QQuickItemPrivate::get(child)->removeItemChangeListener(
this, QQuickItemPrivate::SiblingOrder);
1498 contentItem =
nullptr;
1502QQuickMenu::QQuickMenu(QObject *parent)
1503 : QQuickPopup(*(
new QQuickMenuPrivate), parent)
1508 connect(d->contentModel, &QQmlObjectModel::countChanged,
this, &QQuickMenu::countChanged);
1511QQuickMenu::~QQuickMenu()
1514 qCDebug(lcNativeMenus) <<
"destroying" <<
this
1516 << d->contentModel->count()
1517 <<
"native item count:" << d->nativeItems.count();
1521 if (
auto *menuItem = qobject_cast<QQuickMenuItem *>(d->parentItem)) {
1522 if (menuItem->subMenu() ==
this) {
1523 auto *menuItemPriv = QQuickMenuItemPrivate::get(menuItem);
1524 menuItemPriv->setSubMenu(
nullptr);
1532 while (d->contentModel->count() > 0)
1533 d->removeItem(0, d->itemAt(0), QQuickMenuPrivate::DestructionPolicy::Destroy);
1535 d->resetContentItem();
1539
1540
1541
1542
1543QQuickItem *QQuickMenu::itemAt(
int index)
const
1545 Q_D(
const QQuickMenu);
1546 return d->itemAt(index);
1550
1551
1552
1553
1554
1555
1556
1557void QQuickMenu::addItem(QQuickItem *item)
1560 insertItem(d->contentModel->count(), item);
1564
1565
1566
1567
1568
1569
1570
1571void QQuickMenu::insertItem(
int index, QQuickItem *item)
1576 const int count = d->contentModel->count();
1577 if (index < 0 || index > count)
1580 int oldIndex = d->contentModel->indexOf(item,
nullptr);
1581 if (oldIndex != -1) {
1582 if (oldIndex < index)
1584 if (oldIndex != index) {
1585 d->moveItem(oldIndex, index);
1588 d->insertItem(index, item);
1593
1594
1595
1596
1597void QQuickMenu::moveItem(
int from,
int to)
1600 const int count = d->contentModel->count();
1601 if (from < 0 || from > count - 1)
1603 if (to < 0 || to > count - 1)
1607 d->moveItem(from, to);
1611
1612
1613
1614
1615
1616void QQuickMenu::removeItem(QQuickItem *item)
1622 const int index = d->contentModel->indexOf(item,
nullptr);
1626 d->removeItem(index, item, QQuickMenuPrivate::DestructionPolicy::Destroy);
1630
1631
1632
1633
1634
1635
1636
1637QQuickItem *QQuickMenu::takeItem(
int index)
1640 const int count = d->contentModel->count();
1641 if (index < 0 || index >= count)
1644 QQuickItem *item = itemAt(index);
1646 d->removeItem(index, item);
1651
1652
1653
1654
1655
1656
1657QQuickMenu *QQuickMenu::menuAt(
int index)
const
1659 Q_D(
const QQuickMenu);
1660 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1664 return item->subMenu();
1668
1669
1670
1671
1672
1673
1674void QQuickMenu::addMenu(QQuickMenu *menu)
1677 insertMenu(d->contentModel->count(), menu);
1681
1682
1683
1684
1685
1686
1687void QQuickMenu::insertMenu(
int index, QQuickMenu *menu)
1693 insertItem(index, d->createItem(menu));
1697
1698
1699
1700
1701
1702void QQuickMenu::removeMenu(QQuickMenu *menu)
1708 const int count = d->contentModel->count();
1709 for (
int i = 0; i < count; ++i) {
1710 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(i));
1711 if (!item || item->subMenu() != menu)
1718 menu->deleteLater();
1722
1723
1724
1725
1726
1727
1728
1729
1730QQuickMenu *QQuickMenu::takeMenu(
int index)
1733 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1737 QQuickMenu *subMenu = item->subMenu();
1741 d->removeItem(index, item);
1742 item->deleteLater();
1748
1749
1750
1751
1752
1753
1754QQuickAction *QQuickMenu::actionAt(
int index)
const
1756 Q_D(
const QQuickMenu);
1757 if (!
const_cast<QQuickMenuPrivate *>(d)->maybeNativeHandle()) {
1758 QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton *>(d->itemAt(index));
1762 return item->action();
1764 if (index < 0 || index >= d->nativeItems.size())
1767 return d->nativeItems.at(index)->action();
1772
1773
1774
1775
1776
1777
1778void QQuickMenu::addAction(QQuickAction *action)
1781 insertAction(d->contentModel->count(), action);
1785
1786
1787
1788
1789
1790
1791void QQuickMenu::insertAction(
int index, QQuickAction *action)
1797 insertItem(index, d->createItem(action));
1801
1802
1803
1804
1805
1806void QQuickMenu::removeAction(QQuickAction *action)
1812 const int count = d->contentModel->count();
1813 for (
int i = 0; i < count; ++i) {
1814 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(i));
1815 if (!item || item->action() != action)
1822 action->deleteLater();
1826
1827
1828
1829
1830
1831
1832
1833
1834QQuickAction *QQuickMenu::takeAction(
int index)
1837 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
1841 QQuickAction *action = item->action();
1845 d->removeItem(index, item);
1846 item->deleteLater();
1850bool QQuickMenu::isVisible()
const
1852 Q_D(
const QQuickMenu);
1853 if (d->maybeNativeHandle())
1855 return QQuickPopup::isVisible();
1858void QQuickMenu::setVisible(
bool visible)
1861 if (visible == d->visible)
1864 auto *window =
this->window();
1865 if (visible && window) {
1868 QQuickWindowPrivate::get(window)->rmbContextMenuEventEnabled =
false;
1873 if (!d->parentMenu) {
1874 QQuickOverlay *overlay = QQuickOverlay::overlay(window, parentItem());
1876 const QList<QQuickPopup *> allPopups = QQuickOverlayPrivate::get(overlay)->allPopups;
1877 for (
auto *popup : allPopups) {
1878 if (popup !=
this && qobject_cast<QQuickMenu *>(popup))
1885 if (visible && ((d->useNativeMenu() && !d->maybeNativeHandle())
1886 || (!d->useNativeMenu() && d->maybeNativeHandle()))) {
1890 qCDebug(lcNativeMenus) <<
"setVisible called - useNativeMenu:" << d->useNativeMenu()
1891 <<
"maybeNativeHandle:" << d->maybeNativeHandle();
1892 d->syncWithUseNativeMenu();
1894 if (d->maybeNativeHandle()) {
1895 d->setNativeMenuVisible(visible);
1900 if (QQuickMenuPrivate::get(d->rootMenu())->menuBar)
1901 d->wmWindowType = QNativeInterface::Private::QXcbWindow::DropDownMenu;
1903 d->wmWindowType = QNativeInterface::Private::QXcbWindow::PopupMenu;
1908 QQuickPopup::setVisible(visible);
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932QVariant QQuickMenu::contentModel()
const
1934 Q_D(
const QQuickMenu);
1935 return QVariant::fromValue(d->contentModel);
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953QQmlListProperty<QObject> QQuickMenu::contentData()
1956 if (!d->contentItem)
1957 QQuickControlPrivate::get(d->popupItem)->executeContentItem();
1958 return QQmlListProperty<QObject>(
this,
nullptr,
1959 QQuickMenuPrivate::contentData_append,
1960 QQuickMenuPrivate::contentData_count,
1961 QQuickMenuPrivate::contentData_at,
1962 QQuickMenuPrivate::contentData_clear);
1966
1967
1968
1969
1970
1971
1972
1973
1974QString QQuickMenu::title()
const
1976 Q_D(
const QQuickMenu);
1980void QQuickMenu::setTitle(
const QString &title)
1983 if (title == d->title)
1987 d->handle->setText(title);
1988 emit titleChanged(title);
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2008QQuickIcon QQuickMenu::icon()
const
2010 Q_D(
const QQuickMenu);
2014void QQuickMenu::setIcon(
const QQuickIcon &icon)
2017 if (icon == d->icon)
2020 d->icon.ensureRelativeSourceResolved(
this);
2021 emit iconChanged(icon);
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049bool QQuickMenu::separatorsCollapsible()
const
2051 Q_D(
const QQuickMenu);
2052 return d->collapsibleSeparators;
2055void QQuickMenu::setSeparatorsCollapsible(
bool collapsible)
2058 if (d->collapsibleSeparators == collapsible)
2060 d->collapsibleSeparators = collapsible;
2061 emit separatorsCollapsibleChanged();
2062 d->updateCollapsedSeparators();
2064 d->handle->syncSeparatorsCollapsible(collapsible);
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083bool QQuickMenu::cascade()
const
2085 Q_D(
const QQuickMenu);
2089void QQuickMenu::setCascade(
bool cascade)
2092 if (d->cascade == cascade)
2094 d->cascade = cascade;
2096 d->resolveParentItem();
2097 emit cascadeChanged(cascade);
2100void QQuickMenu::resetCascade()
2104 setCascade(d->parentMenu->cascade());
2106 setCascade(shouldCascade());
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125qreal QQuickMenu::overlap()
const
2127 Q_D(
const QQuickMenu);
2131void QQuickMenu::setOverlap(qreal overlap)
2134 if (d->overlap == overlap)
2136 d->overlap = overlap;
2137 emit overlapChanged();
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162QQmlComponent *QQuickMenu::delegate()
const
2164 Q_D(
const QQuickMenu);
2168void QQuickMenu::setDelegate(QQmlComponent *delegate)
2171 if (d->delegate == delegate)
2174 d->delegate = delegate;
2175 emit delegateChanged();
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190int QQuickMenu::currentIndex()
const
2192 Q_D(
const QQuickMenu);
2193 return d->currentIndex;
2196void QQuickMenu::setCurrentIndex(
int index)
2199 d->setCurrentIndex(index, Qt::OtherFocusReason);
2203
2204
2205
2206
2207
2208
2209int QQuickMenu::count()
const
2211 Q_D(
const QQuickMenu);
2212 return d->contentModel->count();
2215void QQuickMenuPrivate::popup(QQuickItem *menuItem)
2220 QQmlNullableValue<QPointF> pos;
2221#if QT_CONFIG(cursor)
2222 if (parentItem && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows))
2223 pos = parentItem->mapFromGlobal(QCursor::pos());
2227 if (!pos.isValid() && parentItem)
2228 pos = QPointF((parentItem->width() - q->width()) / 2, (parentItem->height() - q->height()) / 2);
2230 q->popup(pos.isValid() ? pos.value() : QPointF(), menuItem);
2233void QQuickMenu::popup(
const QPointF &position, QQuickItem *menuItem)
2237#if QT_CONFIG(cursor)
2239 offset = d->popupItem->mapFromItem(menuItem, QPointF(0, 0)).y();
2241 setPosition(position - QPointF(0, offset));
2244 d->setCurrentIndex(d->contentModel->indexOf(menuItem,
nullptr), Qt::PopupFocusReason);
2246 d->setCurrentIndex(-1, Qt::PopupFocusReason);
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2296void QQuickMenu::popup(QQuickItem *parent, qreal x, qreal y, QQuickItem *menuItem)
2298 popup(parent, QPointF {x, y}, menuItem);
2301void QQuickMenu::popup(QQuickItem *parent,
const QPointF &position, QQuickItem *menuItem)
2304 if (parent && !d->popupItem->isAncestorOf(parent))
2305 setParentItem(parent);
2306 popup(position, menuItem);
2309void QQuickMenu::popup(QQuickItem *parent, QQuickItem *menuItem)
2312 QQuickItem *parentItem =
nullptr;
2313 if (parent && !d->popupItem->isAncestorOf(parent))
2314 parentItem = parent;
2316 setParentItem(parentItem);
2322void QQuickMenu::popup(QQuickItem *parent)
2325 QQuickItem *menuItem =
nullptr;
2327 if (!d->popupItem->isAncestorOf(parent))
2328 setParentItem(parent);
2329 if (d->popupItem->isAncestorOf(parent))
2335void QQuickMenu::popup(qreal x, qreal y, QQuickItem *menuItem)
2337 popup(QPointF {x, y}, menuItem);
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355void QQuickMenu::dismiss()
2357 QQuickMenu *menu =
this;
2360 menu = QQuickMenuPrivate::get(menu)->parentMenu;
2364void QQuickMenu::componentComplete()
2367 QQuickPopup::componentComplete();
2369 d->updateTextPadding();
2370 d->syncWithUseNativeMenu();
2373void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
2376 QQuickPopup::contentItemChange(newItem, oldItem);
2379 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
2380 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Destroyed);
2381 QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
2384 QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Children);
2385 QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Destroyed);
2386 QQuickItemPrivate::get(newItem)->updateOrAddGeometryChangeListener(d, QQuickGeometryChange::Width);
2389 d->contentItem = newItem;
2392void QQuickMenu::itemChange(QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData &data)
2395 QQuickPopup::itemChange(change, data);
2398 case QQuickItem::ItemVisibleHasChanged:
2399 if (!data.boolValue && d->cascade) {
2402 d->setCurrentIndex(-1, Qt::OtherFocusReason);
2410void QQuickMenu::keyPressEvent(QKeyEvent *event)
2413 QQuickPopup::keyPressEvent(event);
2421 switch (event->key()) {
2423 if (!d->activatePreviousItem())
2424 d->propagateKeyEvent(event);
2428 d->activateNextItem();
2434 if (d->popupItem->isMirrored() == (event->key() == Qt::Key_Right)) {
2435 if (d->parentMenu && d->currentItem) {
2437 d->parentMenu->open();
2442 if (QQuickMenu *subMenu = d->currentSubMenu()) {
2443 auto subMenuPrivate = QQuickMenuPrivate::get(subMenu);
2444 subMenuPrivate->popup(subMenuPrivate->firstEnabledMenuItem());
2448 if (!event->isAccepted())
2449 d->propagateKeyEvent(event);
2452#if QT_CONFIG(shortcut)
2456 if (!QKeySequence::mnemonic(QStringLiteral(
"&A")).isEmpty())
2465#if QT_CONFIG(shortcut)
2466 if (event->modifiers() == Qt::NoModifier) {
2467 for (
int i = 0; i < count(); ++i) {
2468 QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton*>(d->itemAt(i));
2471 const QKeySequence keySequence = QKeySequence::mnemonic(item->text());
2472 if (keySequence.isEmpty())
2474 if (keySequence[0].key() == event->key()) {
2483void QQuickMenu::timerEvent(QTimerEvent *event)
2486 if (event->timerId() == d->hoverTimer) {
2487 if (QQuickMenu *subMenu = d->currentSubMenu())
2489 d->stopHoverTimer();
2492 QQuickPopup::timerEvent(event);
2495QFont QQuickMenu::defaultFont()
const
2497 return QQuickTheme::font(QQuickTheme::Menu);
2500#if QT_CONFIG(accessibility)
2501QAccessible::Role QQuickMenu::accessibleRole()
const
2503 return QAccessible::PopupMenu;
2509#include "moc_qquickmenu_p.cpp"
Combined button and popup list for selecting options.