aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates/qquickmenu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates/qquickmenu.cpp')
-rw-r--r--src/quicktemplates/qquickmenu.cpp1574
1 files changed, 1574 insertions, 0 deletions
diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp
new file mode 100644
index 0000000000..707b115983
--- /dev/null
+++ b/src/quicktemplates/qquickmenu.cpp
@@ -0,0 +1,1574 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmenu_p.h"
+#include "qquickmenu_p_p.h"
+#include "qquickmenuitem_p_p.h"
+#include <private/qtquicktemplates2-config_p.h>
+#if QT_CONFIG(quicktemplates2_container)
+#include "qquickmenubaritem_p.h"
+#include "qquickmenubar_p.h"
+#endif
+#include "qquickpopupitem_p_p.h"
+#include "qquickpopuppositioner_p_p.h"
+#include "qquickaction_p.h"
+
+#include <QtGui/qevent.h>
+#include <QtGui/qcursor.h>
+#if QT_CONFIG(shortcut)
+#include <QtGui/qkeysequence.h>
+#endif
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/private/qqmlengine_p.h>
+#include <QtQml/private/qv4scopedvalue_p.h>
+#include <QtQml/private/qv4variantobject_p.h>
+#include <QtQml/private/qv4qobjectwrapper_p.h>
+#include <private/qqmlobjectmodel_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickitemchangelistener_p.h>
+#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// copied from qfusionstyle.cpp
+static const int SUBMENU_DELAY = 225;
+
+/*!
+ \qmltype Menu
+ \inherits Popup
+//! \instantiates QQuickMenu
+ \inqmlmodule QtQuick.Controls
+ \since 5.7
+ \ingroup qtquickcontrols-menus
+ \ingroup qtquickcontrols-popups
+ \brief Menu popup that can be used as a context menu or popup menu.
+
+ \image qtquickcontrols-menu.png
+
+ Menu has two main use cases:
+ \list
+ \li Context menus; for example, a menu that is shown after right clicking
+ \li Popup menus; for example, a menu that is shown after clicking a button
+ \endlist
+
+ When used as a context menu, the recommended way of opening the menu is to call
+ \l popup(). Unless a position is explicitly specified, the menu is positioned at
+ the mouse cursor on desktop platforms that have a mouse cursor available, and
+ otherwise centered over its parent item.
+
+ \code
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onClicked: {
+ if (mouse.button === Qt.RightButton)
+ contextMenu.popup()
+ }
+ onPressAndHold: {
+ if (mouse.source === Qt.MouseEventNotSynthesized)
+ contextMenu.popup()
+ }
+
+ Menu {
+ id: contextMenu
+ MenuItem { text: "Cut" }
+ MenuItem { text: "Copy" }
+ MenuItem { text: "Paste" }
+ }
+ }
+ \endcode
+
+ When used as a popup menu, it is easiest to specify the position by specifying
+ the desired \l {Popup::}{x} and \l {Popup::}{y} coordinates using the respective
+ properties, and call \l {Popup::}{open()} to open the menu.
+
+ \code
+ Button {
+ id: fileButton
+ text: "File"
+ onClicked: menu.open()
+
+ Menu {
+ id: menu
+ y: fileButton.height
+
+ MenuItem {
+ text: "New..."
+ }
+ MenuItem {
+ text: "Open..."
+ }
+ MenuItem {
+ text: "Save"
+ }
+ }
+ }
+ \endcode
+
+ If the button should also close the menu when clicked, use the
+ \c Popup.CloseOnPressOutsideParent flag:
+ \code
+ onClicked: menu.visible = !menu.visible
+
+ Menu {
+ // ...
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ \endcode
+
+ Since QtQuick.Controls 2.3 (Qt 5.10), it is also possible to create sub-menus
+ and declare Action objects inside Menu:
+
+ \code
+ Menu {
+ Action { text: "Cut" }
+ Action { text: "Copy" }
+ Action { text: "Paste" }
+
+ MenuSeparator { }
+
+ Menu {
+ title: "Find/Replace"
+ Action { text: "Find Next" }
+ Action { text: "Find Previous" }
+ Action { text: "Replace" }
+ }
+ }
+ \endcode
+
+ Sub-menus are \l {cascade}{cascading} by default on desktop platforms
+ that have a mouse cursor available. Non-cascading menus are shown one
+ menu at a time, and centered over the parent menu.
+
+ Typically, menu items are statically declared as children of the menu, but
+ Menu also provides API to \l {addItem}{add}, \l {insertItem}{insert},
+ \l {moveItem}{move} and \l {removeItem}{remove} items dynamically. The
+ items in a menu can be accessed using \l itemAt() or
+ \l {Popup::}{contentChildren}.
+
+ Although \l {MenuItem}{MenuItems} are most commonly used with Menu, it can
+ contain any type of item.
+
+ \section1 Margins
+
+ As it is inherited from Popup, Menu supports \l {Popup::}{margins}. By
+ default, all of the built-in styles specify \c 0 for Menu's margins to
+ ensure that the menu is kept within the bounds of the window. To allow a
+ menu to go outside of the window (to animate it moving into view, for
+ example), set the margins property to \c -1.
+
+ \section1 Dynamically Generating Menu Items
+
+ You can dynamically create menu items with \l Instantiator or
+ \l {Dynamic QML Object Creation from JavaScript} {dynamic object creation}.
+
+ \section2 Using Instantiator
+
+ You can dynamically generate menu items with \l Instantiator. The
+ following code shows how you can implement a "Recent Files" submenu,
+ where the items come from a list of files stored in settings:
+
+ \snippet qtquickcontrols-menu-instantiator.qml menu
+
+ \section2 Using Dynamic Object Creation
+
+ You can also dynamically load a component from a QML file using
+ \l {QtQml::Qt::createComponent()} {Qt.createComponent()}. Once the component
+ is ready, you can call its \l {Component::createObject()} {createObject()}
+ method to create an instance of that component.
+
+ \snippet qtquickcontrols-menu-createObject.qml createObject
+
+ \sa {Customizing Menu}, MenuItem, {Menu Controls}, {Popup Controls},
+ {Dynamic QML Object Creation from JavaScript}
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Controls::Menu::focus
+
+ This property holds whether the popup wants focus.
+
+ When the popup actually receives focus, \l{Popup::}{activeFocus}
+ will be \c true. For more information, see
+ \l {Keyboard Focus in Qt Quick}.
+
+ The default value is \c true.
+
+ \sa {Popup::}{activeFocus}
+*/
+
+static const QQuickPopup::ClosePolicy cascadingSubMenuClosePolicy = QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent;
+
+static bool shouldCascade()
+{
+#if QT_CONFIG(cursor)
+ return QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows);
+#else
+ return false;
+#endif
+}
+
+class QQuickMenuPositioner : public QQuickPopupPositioner
+{
+public:
+ QQuickMenuPositioner(QQuickMenu *menu) : QQuickPopupPositioner(menu) { }
+
+ void reposition() override;
+};
+
+QQuickMenuPrivate::QQuickMenuPrivate()
+{
+ cascade = shouldCascade();
+}
+
+void QQuickMenuPrivate::init()
+{
+ Q_Q(QQuickMenu);
+ contentModel = new QQmlObjectModel(q);
+}
+
+QQuickItem *QQuickMenuPrivate::itemAt(int index) const
+{
+ return qobject_cast<QQuickItem *>(contentModel->get(index));
+}
+
+void QQuickMenuPrivate::insertItem(int index, QQuickItem *item)
+{
+ contentData.append(item);
+ item->setParentItem(contentItem);
+ QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-53262
+ if (complete)
+ resizeItem(item);
+ QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent);
+ QQuickItemPrivate::get(item)->updateOrAddGeometryChangeListener(this, QQuickGeometryChange::Width);
+ contentModel->insert(index, item);
+
+ QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
+ if (menuItem) {
+ Q_Q(QQuickMenu);
+ QQuickMenuItemPrivate::get(menuItem)->setMenu(q);
+ if (QQuickMenu *subMenu = menuItem->subMenu())
+ QQuickMenuPrivate::get(subMenu)->setParentMenu(q);
+ QObjectPrivate::connect(menuItem, &QQuickMenuItem::triggered, this, &QQuickMenuPrivate::onItemTriggered);
+ QObjectPrivate::connect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged);
+ QObjectPrivate::connect(menuItem, &QQuickControl::hoveredChanged, this, &QQuickMenuPrivate::onItemHovered);
+ }
+}
+
+void QQuickMenuPrivate::moveItem(int from, int to)
+{
+ contentModel->move(from, to);
+}
+
+void QQuickMenuPrivate::removeItem(int index, QQuickItem *item)
+{
+ contentData.removeOne(item);
+
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent);
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+ item->setParentItem(nullptr);
+ contentModel->remove(index);
+
+ QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
+ if (menuItem) {
+ QQuickMenuItemPrivate::get(menuItem)->setMenu(nullptr);
+ if (QQuickMenu *subMenu = menuItem->subMenu())
+ QQuickMenuPrivate::get(subMenu)->setParentMenu(nullptr);
+ QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::triggered, this, &QQuickMenuPrivate::onItemTriggered);
+ QObjectPrivate::disconnect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged);
+ QObjectPrivate::disconnect(menuItem, &QQuickControl::hoveredChanged, this, &QQuickMenuPrivate::onItemHovered);
+ }
+}
+
+QQuickItem *QQuickMenuPrivate::beginCreateItem()
+{
+ Q_Q(QQuickMenu);
+ if (!delegate)
+ return nullptr;
+
+ QQmlContext *context = delegate->creationContext();
+ if (!context)
+ context = qmlContext(q);
+
+ QObject *object = delegate->beginCreate(context);
+ QQuickItem *item = qobject_cast<QQuickItem *>(object);
+ if (!item)
+ delete object;
+ else
+ QQml_setParent_noEvent(item, q);
+
+ return item;
+}
+
+void QQuickMenuPrivate::completeCreateItem()
+{
+ if (!delegate)
+ return;
+
+ delegate->completeCreate();
+}
+
+QQuickItem *QQuickMenuPrivate::createItem(QQuickMenu *menu)
+{
+ QQuickItem *item = beginCreateItem();
+ if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item))
+ QQuickMenuItemPrivate::get(menuItem)->setSubMenu(menu);
+ completeCreateItem();
+ return item;
+}
+
+QQuickItem *QQuickMenuPrivate::createItem(QQuickAction *action)
+{
+ QQuickItem *item = beginCreateItem();
+ if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(item))
+ button->setAction(action);
+ completeCreateItem();
+ return item;
+}
+
+void QQuickMenuPrivate::resizeItem(QQuickItem *item)
+{
+ if (!item || !contentItem)
+ return;
+
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ if (!p->widthValid()) {
+ item->setWidth(contentItem->width());
+ p->widthValidFlag = false;
+ }
+}
+
+void QQuickMenuPrivate::resizeItems()
+{
+ if (!contentModel)
+ return;
+
+ for (int i = 0; i < contentModel->count(); ++i)
+ resizeItem(itemAt(i));
+}
+
+void QQuickMenuPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
+{
+ // add dynamically reparented items (eg. by a Repeater)
+ if (!QQuickItemPrivate::get(child)->isTransparentForPositioner() && !contentData.contains(child))
+ insertItem(contentModel->count(), child);
+}
+
+void QQuickMenuPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
+{
+ // remove dynamically unparented items (eg. by a Repeater)
+ if (!parent)
+ removeItem(contentModel->indexOf(item, nullptr), item);
+}
+
+void QQuickMenuPrivate::itemSiblingOrderChanged(QQuickItem *)
+{
+ // reorder the restacked items (eg. by a Repeater)
+ Q_Q(QQuickMenu);
+ QList<QQuickItem *> siblings = contentItem->childItems();
+
+ int to = 0;
+ for (int i = 0; i < siblings.size(); ++i) {
+ QQuickItem* sibling = siblings.at(i);
+ if (QQuickItemPrivate::get(sibling)->isTransparentForPositioner())
+ continue;
+ int index = contentModel->indexOf(sibling, nullptr);
+ q->moveItem(index, to++);
+ }
+}
+
+void QQuickMenuPrivate::itemDestroyed(QQuickItem *item)
+{
+ QQuickPopupPrivate::itemDestroyed(item);
+ int index = contentModel->indexOf(item, nullptr);
+ if (index != -1)
+ removeItem(index, item);
+}
+
+void QQuickMenuPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange, const QRectF &)
+{
+ if (!complete)
+ return;
+
+ if (item == contentItem) {
+ // The contentItem's geometry changed, so resize any items
+ // that don't have explicit widths set so that they fill the width of the menu.
+ resizeItems();
+ } else {
+ // The geometry of an item in the menu changed. If the item
+ // doesn't have an explicit width set, make it fill the width of the menu.
+ resizeItem(item);
+ }
+}
+
+QQuickPopupPositioner *QQuickMenuPrivate::getPositioner()
+{
+ Q_Q(QQuickMenu);
+ if (!positioner)
+ positioner = new QQuickMenuPositioner(q);
+ return positioner;
+}
+
+void QQuickMenuPositioner::reposition()
+{
+ QQuickMenu *menu = static_cast<QQuickMenu *>(popup());
+ QQuickMenuPrivate *p = QQuickMenuPrivate::get(menu);
+ if (p->parentMenu) {
+ if (p->cascade) {
+ if (p->popupItem->isMirrored())
+ menu->setPosition(QPointF(-menu->width() - p->parentMenu->leftPadding() + menu->overlap(), -menu->topPadding()));
+ else if (p->parentItem)
+ menu->setPosition(QPointF(p->parentItem->width() + p->parentMenu->rightPadding() - menu->overlap(), -menu->topPadding()));
+ } else {
+ menu->setPosition(QPointF(p->parentMenu->x() + (p->parentMenu->width() - menu->width()) / 2,
+ p->parentMenu->y() + (p->parentMenu->height() - menu->height()) / 2));
+ }
+ }
+ QQuickPopupPositioner::reposition();
+}
+
+bool QQuickMenuPrivate::prepareEnterTransition()
+{
+ Q_Q(QQuickMenu);
+ if (parentMenu && !cascade)
+ parentMenu->close();
+
+ // If a cascading sub-menu doesn't have enough space to open on
+ // the right, it flips on the other side of the parent menu.
+ allowHorizontalFlip = cascade && parentMenu;
+
+ if (!QQuickPopupPrivate::prepareEnterTransition())
+ return false;
+
+ if (!hasClosePolicy) {
+ if (cascade && parentMenu)
+ closePolicy = cascadingSubMenuClosePolicy;
+ else
+ q->resetClosePolicy();
+ }
+ return true;
+}
+
+bool QQuickMenuPrivate::prepareExitTransition()
+{
+ if (!QQuickPopupPrivate::prepareExitTransition())
+ return false;
+
+ stopHoverTimer();
+
+ QQuickMenu *subMenu = currentSubMenu();
+ while (subMenu) {
+ QPointer<QQuickMenuItem> currentSubMenuItem = QQuickMenuPrivate::get(subMenu)->currentItem;
+ subMenu->close();
+ subMenu = currentSubMenuItem ? currentSubMenuItem->subMenu() : nullptr;
+ }
+ return true;
+}
+
+bool QQuickMenuPrivate::blockInput(QQuickItem *item, const QPointF &point) const
+{
+ // keep the parent menu open when a cascading sub-menu (this menu) is interacted with
+ return (cascade && parentMenu && contains(point)) || QQuickPopupPrivate::blockInput(item, point);
+}
+
+void QQuickMenuPrivate::onItemHovered()
+{
+ Q_Q(QQuickMenu);
+ QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(q->sender());
+ if (!button || !button->isHovered() || !button->isEnabled() || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
+ return;
+
+ QQuickMenuItem *oldCurrentItem = currentItem;
+
+ int index = contentModel->indexOf(button, nullptr);
+ if (index != -1) {
+ setCurrentIndex(index, Qt::OtherFocusReason);
+ if (oldCurrentItem != currentItem) {
+ if (oldCurrentItem) {
+ QQuickMenu *subMenu = oldCurrentItem->subMenu();
+ if (subMenu)
+ subMenu->close();
+ }
+ if (currentItem) {
+ QQuickMenu *subMenu = currentItem->menu();
+ if (subMenu && subMenu->cascade())
+ startHoverTimer();
+ }
+ }
+ }
+}
+
+void QQuickMenuPrivate::onItemTriggered()
+{
+ Q_Q(QQuickMenu);
+ QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(q->sender());
+ if (!item)
+ return;
+
+ if (QQuickMenu *subMenu = item->subMenu()) {
+ auto subMenuPrivate = QQuickMenuPrivate::get(subMenu);
+ subMenu->popup(subMenuPrivate->firstEnabledMenuItem());
+ } else {
+ q->dismiss();
+ }
+}
+
+void QQuickMenuPrivate::onItemActiveFocusChanged()
+{
+ Q_Q(QQuickMenu);
+ QQuickItem *item = qobject_cast<QQuickItem*>(q->sender());
+ if (!item->hasActiveFocus())
+ return;
+
+ int indexOfItem = contentModel->indexOf(item, nullptr);
+ QQuickControl *control = qobject_cast<QQuickControl *>(item);
+ setCurrentIndex(indexOfItem, control ? control->focusReason() : Qt::OtherFocusReason);
+}
+
+QQuickMenu *QQuickMenuPrivate::currentSubMenu() const
+{
+ if (!currentItem)
+ return nullptr;
+
+ return currentItem->subMenu();
+}
+
+void QQuickMenuPrivate::setParentMenu(QQuickMenu *parent)
+{
+ Q_Q(QQuickMenu);
+ if (parentMenu == parent)
+ return;
+
+ if (parentMenu) {
+ QObject::disconnect(parentMenu.data(), &QQuickMenu::cascadeChanged, q, &QQuickMenu::setCascade);
+ disconnect(parentMenu.data(), &QQuickMenu::parentChanged, this, &QQuickMenuPrivate::resolveParentItem);
+ }
+ if (parent) {
+ QObject::connect(parent, &QQuickMenu::cascadeChanged, q, &QQuickMenu::setCascade);
+ connect(parent, &QQuickMenu::parentChanged, this, &QQuickMenuPrivate::resolveParentItem);
+ }
+
+ parentMenu = parent;
+ q->resetCascade();
+ resolveParentItem();
+}
+
+static QQuickItem *findParentMenuItem(QQuickMenu *subMenu)
+{
+ QQuickMenu *menu = QQuickMenuPrivate::get(subMenu)->parentMenu;
+ for (int i = 0; i < QQuickMenuPrivate::get(menu)->contentModel->count(); ++i) {
+ QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(menu->itemAt(i));
+ if (item && item->subMenu() == subMenu)
+ return item;
+ }
+ return nullptr;
+}
+
+void QQuickMenuPrivate::resolveParentItem()
+{
+ Q_Q(QQuickMenu);
+ if (!parentMenu)
+ q->resetParentItem();
+ else if (!cascade)
+ q->setParentItem(parentMenu->parentItem());
+ else
+ q->setParentItem(findParentMenuItem(q));
+}
+
+void QQuickMenuPrivate::propagateKeyEvent(QKeyEvent *event)
+{
+ if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(parentItem)) {
+ if (QQuickMenu *menu = menuItem->menu())
+ QQuickMenuPrivate::get(menu)->propagateKeyEvent(event);
+#if QT_CONFIG(quicktemplates2_container)
+ } else if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(parentItem)) {
+ if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) {
+ event->accept();
+ QCoreApplication::sendEvent(menuBar, event);
+ }
+#endif
+ }
+}
+
+void QQuickMenuPrivate::startHoverTimer()
+{
+ Q_Q(QQuickMenu);
+ stopHoverTimer();
+ hoverTimer = q->startTimer(SUBMENU_DELAY);
+}
+
+void QQuickMenuPrivate::stopHoverTimer()
+{
+ Q_Q(QQuickMenu);
+ if (!hoverTimer)
+ return;
+
+ q->killTimer(hoverTimer);
+ hoverTimer = 0;
+}
+
+void QQuickMenuPrivate::setCurrentIndex(int index, Qt::FocusReason reason)
+{
+ Q_Q(QQuickMenu);
+ if (currentIndex == index)
+ return;
+
+ QQuickMenuItem *newCurrentItem = qobject_cast<QQuickMenuItem *>(itemAt(index));
+ if (currentItem != newCurrentItem) {
+ stopHoverTimer();
+ if (currentItem) {
+ currentItem->setHighlighted(false);
+ if (!newCurrentItem && window) {
+ QQuickItem *focusItem = QQuickItemPrivate::get(contentItem)->subFocusItem;
+ if (focusItem)
+ QQuickWindowPrivate::get(window)->clearFocusInScope(contentItem, focusItem, Qt::OtherFocusReason);
+ }
+ }
+ if (newCurrentItem) {
+ newCurrentItem->setHighlighted(true);
+ newCurrentItem->forceActiveFocus(reason);
+ }
+ currentItem = newCurrentItem;
+ }
+
+ currentIndex = index;
+ emit q->currentIndexChanged();
+}
+
+bool QQuickMenuPrivate::activateNextItem()
+{
+ int index = currentIndex;
+ int count = contentModel->count();
+ while (++index < count) {
+ QQuickItem *item = itemAt(index);
+ if (!item || !item->activeFocusOnTab() || !item->isEnabled())
+ continue;
+ setCurrentIndex(index, Qt::TabFocusReason);
+ return true;
+ }
+ return false;
+}
+
+bool QQuickMenuPrivate::activatePreviousItem()
+{
+ int index = currentIndex;
+ while (--index >= 0) {
+ QQuickItem *item = itemAt(index);
+ if (!item || !item->activeFocusOnTab() || !item->isEnabled())
+ continue;
+ setCurrentIndex(index, Qt::BacktabFocusReason);
+ return true;
+ }
+ return false;
+}
+
+QQuickMenuItem *QQuickMenuPrivate::firstEnabledMenuItem() const
+{
+ for (int i = 0; i < contentModel->count(); ++i) {
+ QQuickItem *item = itemAt(i);
+ if (!item || !item->isEnabled())
+ continue;
+
+ QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
+ if (!menuItem)
+ continue;
+
+ return menuItem;
+ }
+ return nullptr;
+}
+
+void QQuickMenuPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
+{
+ QQuickMenu *q = qobject_cast<QQuickMenu *>(prop->object);
+ QQuickMenuPrivate *p = QQuickMenuPrivate::get(q);
+
+ QQuickItem *item = qobject_cast<QQuickItem *>(obj);
+ if (!item) {
+ if (QQuickAction *action = qobject_cast<QQuickAction *>(obj))
+ item = p->createItem(action);
+ else if (QQuickMenu *menu = qobject_cast<QQuickMenu *>(obj))
+ item = p->createItem(menu);
+ }
+
+ if (item) {
+ if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) {
+ QQuickItemPrivate::get(item)->addItemChangeListener(p, QQuickItemPrivate::SiblingOrder);
+ item->setParentItem(p->contentItem);
+ } else if (p->contentModel->indexOf(item, nullptr) == -1) {
+ q->addItem(item);
+ }
+ } else {
+ p->contentData.append(obj);
+ }
+}
+
+qsizetype QQuickMenuPrivate::contentData_count(QQmlListProperty<QObject> *prop)
+{
+ QQuickMenu *q = static_cast<QQuickMenu *>(prop->object);
+ return QQuickMenuPrivate::get(q)->contentData.size();
+}
+
+QObject *QQuickMenuPrivate::contentData_at(QQmlListProperty<QObject> *prop, qsizetype index)
+{
+ QQuickMenu *q = static_cast<QQuickMenu *>(prop->object);
+ return QQuickMenuPrivate::get(q)->contentData.value(index);
+}
+
+QPalette QQuickMenuPrivate::defaultPalette() const
+{
+ return QQuickTheme::palette(QQuickTheme::Menu);
+}
+
+void QQuickMenuPrivate::contentData_clear(QQmlListProperty<QObject> *prop)
+{
+ QQuickMenu *q = static_cast<QQuickMenu *>(prop->object);
+ QQuickMenuPrivate::get(q)->contentData.clear();
+}
+
+QQuickMenu::QQuickMenu(QObject *parent)
+ : QQuickPopup(*(new QQuickMenuPrivate), parent)
+{
+ Q_D(QQuickMenu);
+ setFocus(true);
+ d->init();
+ connect(d->contentModel, &QQmlObjectModel::countChanged, this, &QQuickMenu::countChanged);
+}
+
+QQuickMenu::~QQuickMenu()
+{
+ Q_D(QQuickMenu);
+ // We have to do this to ensure that the change listeners are removed.
+ // It's too late to do this in ~QQuickMenuPrivate, as contentModel has already
+ // been destroyed before that is called.
+ while (d->contentModel->count() > 0)
+ d->removeItem(0, d->itemAt(0));
+
+ if (d->contentItem) {
+ QQuickItemPrivate::get(d->contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
+ QQuickItemPrivate::get(d->contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
+
+ const auto children = d->contentItem->childItems();
+ for (QQuickItem *child : std::as_const(children))
+ QQuickItemPrivate::get(child)->removeItemChangeListener(d, QQuickItemPrivate::SiblingOrder);
+ }
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::Menu::itemAt(int index)
+
+ Returns the item at \a index, or \c null if it does not exist.
+*/
+QQuickItem *QQuickMenu::itemAt(int index) const
+{
+ Q_D(const QQuickMenu);
+ return d->itemAt(index);
+}
+
+/*!
+ \qmlmethod void QtQuick.Controls::Menu::addItem(Item item)
+
+ Adds \a item to the end of the list of items.
+*/
+void QQuickMenu::addItem(QQuickItem *item)
+{
+ Q_D(QQuickMenu);
+ insertItem(d->contentModel->count(), item);
+}
+
+/*!
+ \qmlmethod void QtQuick.Controls::Menu::insertItem(int index, Item item)
+
+ Inserts \a item at \a index.
+*/
+void QQuickMenu::insertItem(int index, QQuickItem *item)
+{
+ Q_D(QQuickMenu);
+ if (!item)
+ return;
+ const int count = d->contentModel->count();
+ if (index < 0 || index > count)
+ index = count;
+
+ int oldIndex = d->contentModel->indexOf(item, nullptr);
+ if (oldIndex != -1) {
+ if (oldIndex < index)
+ --index;
+ if (oldIndex != index)
+ d->moveItem(oldIndex, index);
+ } else {
+ d->insertItem(index, item);
+ }
+}
+
+/*!
+ \qmlmethod void QtQuick.Controls::Menu::moveItem(int from, int to)
+
+ Moves an item \a from one index \a to another.
+*/
+void QQuickMenu::moveItem(int from, int to)
+{
+ Q_D(QQuickMenu);
+ const int count = d->contentModel->count();
+ if (from < 0 || from > count - 1)
+ return;
+ if (to < 0 || to > count - 1)
+ to = count - 1;
+
+ if (from != to)
+ d->moveItem(from, to);
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::removeItem(Item item)
+
+ Removes and destroys the specified \a item.
+*/
+void QQuickMenu::removeItem(QQuickItem *item)
+{
+ Q_D(QQuickMenu);
+ if (!item)
+ return;
+
+ const int index = d->contentModel->indexOf(item, nullptr);
+ if (index == -1)
+ return;
+
+ d->removeItem(index, item);
+ item->deleteLater();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod MenuItem QtQuick.Controls::Menu::takeItem(int index)
+
+ Removes and returns the item at \a index.
+
+ \note The ownership of the item is transferred to the caller.
+*/
+QQuickItem *QQuickMenu::takeItem(int index)
+{
+ Q_D(QQuickMenu);
+ const int count = d->contentModel->count();
+ if (index < 0 || index >= count)
+ return nullptr;
+
+ QQuickItem *item = itemAt(index);
+ if (item)
+ d->removeItem(index, item);
+ return item;
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod Menu QtQuick.Controls::Menu::menuAt(int index)
+
+ Returns the sub-menu at \a index, or \c null if the index is not valid or
+ there is no sub-menu at the specified index.
+*/
+QQuickMenu *QQuickMenu::menuAt(int index) const
+{
+ Q_D(const QQuickMenu);
+ QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
+ if (!item)
+ return nullptr;
+
+ return item->subMenu();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::addMenu(Menu menu)
+
+ Adds \a menu as a sub-menu to the end of this menu.
+*/
+void QQuickMenu::addMenu(QQuickMenu *menu)
+{
+ Q_D(QQuickMenu);
+ insertMenu(d->contentModel->count(), menu);
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::insertMenu(int index, Menu menu)
+
+ Inserts \a menu as a sub-menu at \a index. The index is within all items in the menu.
+*/
+void QQuickMenu::insertMenu(int index, QQuickMenu *menu)
+{
+ Q_D(QQuickMenu);
+ if (!menu)
+ return;
+
+ insertItem(index, d->createItem(menu));
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::removeMenu(Menu menu)
+
+ Removes and destroys the specified \a menu.
+*/
+void QQuickMenu::removeMenu(QQuickMenu *menu)
+{
+ Q_D(QQuickMenu);
+ if (!menu)
+ return;
+
+ const int count = d->contentModel->count();
+ for (int i = 0; i < count; ++i) {
+ QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(i));
+ if (!item || item->subMenu() != menu)
+ continue;
+
+ removeItem(item);
+ break;
+ }
+
+ menu->deleteLater();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod Menu QtQuick.Controls::Menu::takeMenu(int index)
+
+ Removes and returns the menu at \a index. The index is within all items in the menu.
+
+ \note The ownership of the menu is transferred to the caller.
+*/
+QQuickMenu *QQuickMenu::takeMenu(int index)
+{
+ Q_D(QQuickMenu);
+ QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
+ if (!item)
+ return nullptr;
+
+ QQuickMenu *subMenu = item->subMenu();
+ if (!subMenu)
+ return nullptr;
+
+ d->removeItem(index, item);
+ item->deleteLater();
+ return subMenu;
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod Action QtQuick.Controls::Menu::actionAt(int index)
+
+ Returns the action at \a index, or \c null if the index is not valid or
+ there is no action at the specified index.
+*/
+QQuickAction *QQuickMenu::actionAt(int index) const
+{
+ Q_D(const QQuickMenu);
+ QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton *>(d->itemAt(index));
+ if (!item)
+ return nullptr;
+
+ return item->action();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::addAction(Action action)
+
+ Adds \a action to the end of this menu.
+*/
+void QQuickMenu::addAction(QQuickAction *action)
+{
+ Q_D(QQuickMenu);
+ insertAction(d->contentModel->count(), action);
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::insertAction(int index, Action action)
+
+ Inserts \a action at \a index. The index is within all items in the menu.
+*/
+void QQuickMenu::insertAction(int index, QQuickAction *action)
+{
+ Q_D(QQuickMenu);
+ if (!action)
+ return;
+
+ insertItem(index, d->createItem(action));
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::removeAction(Action action)
+
+ Removes and destroys the specified \a action.
+*/
+void QQuickMenu::removeAction(QQuickAction *action)
+{
+ Q_D(QQuickMenu);
+ if (!action)
+ return;
+
+ const int count = d->contentModel->count();
+ for (int i = 0; i < count; ++i) {
+ QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(i));
+ if (!item || item->action() != action)
+ continue;
+
+ removeItem(item);
+ break;
+ }
+
+ action->deleteLater();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod Action QtQuick.Controls::Menu::takeAction(int index)
+
+ Removes and returns the action at \a index. The index is within all items in the menu.
+
+ \note The ownership of the action is transferred to the caller.
+*/
+QQuickAction *QQuickMenu::takeAction(int index)
+{
+ Q_D(QQuickMenu);
+ QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(d->itemAt(index));
+ if (!item)
+ return nullptr;
+
+ QQuickAction *action = item->action();
+ if (!action)
+ return nullptr;
+
+ d->removeItem(index, item);
+ item->deleteLater();
+ return action;
+}
+
+/*!
+ \qmlproperty model QtQuick.Controls::Menu::contentModel
+ \readonly
+
+ This property holds the model used to display menu items.
+
+ The content model is provided for visualization purposes. It can be assigned
+ as a model to a content item that presents the contents of the menu.
+
+ \code
+ Menu {
+ id: menu
+ contentItem: ListView {
+ model: menu.contentModel
+ }
+ }
+ \endcode
+
+ The model allows menu items to be statically declared as children of the
+ menu.
+*/
+QVariant QQuickMenu::contentModel() const
+{
+ Q_D(const QQuickMenu);
+ return QVariant::fromValue(d->contentModel);
+}
+
+/*!
+ \qmlproperty list<QtObject> QtQuick.Controls::Menu::contentData
+ \qmldefault
+
+ This property holds the list of content data.
+
+ The list contains all objects that have been declared in QML as children
+ of the menu, and also items that have been dynamically added or
+ inserted using the \l addItem() and \l insertItem() methods, respectively.
+
+ \note Unlike \c contentChildren, \c contentData does include non-visual QML
+ objects. It is not re-ordered when items are inserted or moved.
+
+ \sa Item::data, {Popup::}{contentChildren}
+*/
+QQmlListProperty<QObject> QQuickMenu::contentData()
+{
+ Q_D(QQuickMenu);
+ if (!d->contentItem)
+ QQuickControlPrivate::get(d->popupItem)->executeContentItem();
+ return QQmlListProperty<QObject>(this, nullptr,
+ QQuickMenuPrivate::contentData_append,
+ QQuickMenuPrivate::contentData_count,
+ QQuickMenuPrivate::contentData_at,
+ QQuickMenuPrivate::contentData_clear);
+}
+
+/*!
+ \qmlproperty string QtQuick.Controls::Menu::title
+
+ This property holds the title for the menu.
+
+ The title of a menu is often displayed in the text of a menu item when the
+ menu is a submenu, and in the text of a tool button when it is in a
+ menubar.
+*/
+QString QQuickMenu::title() const
+{
+ Q_D(const QQuickMenu);
+ return d->title;
+}
+
+void QQuickMenu::setTitle(const QString &title)
+{
+ Q_D(QQuickMenu);
+ if (title == d->title)
+ return;
+ d->title = title;
+ emit titleChanged(title);
+}
+
+/*!
+ \qmlproperty string QtQuick.Controls::Menu::icon.name
+ \qmlproperty url QtQuick.Controls::Menu::icon.source
+ \qmlproperty int QtQuick.Controls::Menu::icon.width
+ \qmlproperty int QtQuick.Controls::Menu::icon.height
+ \qmlproperty color QtQuick.Controls::Menu::icon.color
+ \qmlproperty bool QtQuick.Controls::Menu::icon.cache
+
+ This property group was added in QtQuick.Controls 6.5.
+
+ \include qquickicon.qdocinc grouped-properties
+
+ \sa AbstractButton::text, AbstractButton::display, {Icons in Qt Quick Controls}
+*/
+
+QQuickIcon QQuickMenu::icon() const
+{
+ Q_D(const QQuickMenu);
+ return d->icon;
+}
+
+void QQuickMenu::setIcon(const QQuickIcon &icon)
+{
+ Q_D(QQuickMenu);
+ if (icon == d->icon)
+ return;
+ d->icon = icon;
+ d->icon.ensureRelativeSourceResolved(this);
+ emit iconChanged(icon);
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlproperty bool QtQuick.Controls::Menu::cascade
+
+ This property holds whether the menu cascades its sub-menus.
+
+ The default value is platform-specific. Menus are cascading by default on
+ desktop platforms that have a mouse cursor available. Non-cascading menus
+ are shown one menu at a time, and centered over the parent menu.
+
+ \note Changing the value of the property has no effect while the menu is open.
+
+ \sa overlap
+*/
+bool QQuickMenu::cascade() const
+{
+ Q_D(const QQuickMenu);
+ return d->cascade;
+}
+
+void QQuickMenu::setCascade(bool cascade)
+{
+ Q_D(QQuickMenu);
+ if (d->cascade == cascade)
+ return;
+ d->cascade = cascade;
+ if (d->parentMenu)
+ d->resolveParentItem();
+ emit cascadeChanged(cascade);
+}
+
+void QQuickMenu::resetCascade()
+{
+ Q_D(QQuickMenu);
+ if (d->parentMenu)
+ setCascade(d->parentMenu->cascade());
+ else
+ setCascade(shouldCascade());
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlproperty real QtQuick.Controls::Menu::overlap
+
+ This property holds the amount of pixels by which the menu horizontally overlaps its parent menu.
+
+ The property only has effect when the menu is used as a cascading sub-menu.
+
+ The default value is style-specific.
+
+ \note Changing the value of the property has no effect while the menu is open.
+
+ \sa cascade
+*/
+qreal QQuickMenu::overlap() const
+{
+ Q_D(const QQuickMenu);
+ return d->overlap;
+}
+
+void QQuickMenu::setOverlap(qreal overlap)
+{
+ Q_D(QQuickMenu);
+ if (d->overlap == overlap)
+ return;
+ d->overlap = overlap;
+ emit overlapChanged();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlproperty Component QtQuick.Controls::Menu::delegate
+
+ This property holds the component that is used to create items
+ to present actions.
+
+ \code
+ Menu {
+ Action { text: "Cut" }
+ Action { text: "Copy" }
+ Action { text: "Paste" }
+ }
+ \endcode
+
+ \sa Action
+*/
+QQmlComponent *QQuickMenu::delegate() const
+{
+ Q_D(const QQuickMenu);
+ return d->delegate;
+}
+
+void QQuickMenu::setDelegate(QQmlComponent *delegate)
+{
+ Q_D(QQuickMenu);
+ if (d->delegate == delegate)
+ return;
+
+ d->delegate = delegate;
+ emit delegateChanged();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlproperty int QtQuick.Controls::Menu::currentIndex
+
+ This property holds the index of the currently highlighted item.
+
+ Menu items can be highlighted by mouse hover or keyboard navigation.
+
+ \sa MenuItem::highlighted
+*/
+int QQuickMenu::currentIndex() const
+{
+ Q_D(const QQuickMenu);
+ return d->currentIndex;
+}
+
+void QQuickMenu::setCurrentIndex(int index)
+{
+ Q_D(QQuickMenu);
+ d->setCurrentIndex(index, Qt::OtherFocusReason);
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlproperty int QtQuick.Controls::Menu::count
+ \readonly
+
+ This property holds the number of items.
+*/
+int QQuickMenu::count() const
+{
+ Q_D(const QQuickMenu);
+ return d->contentModel->count();
+}
+
+void QQuickMenu::popup(QQuickItem *menuItem)
+{
+ Q_D(QQuickMenu);
+ // No position has been explicitly specified, so position the menu at the mouse cursor
+ // on desktop platforms that have a mouse cursor available and support multiple windows.
+ QQmlNullableValue<QPointF> pos;
+#if QT_CONFIG(cursor)
+ if (d->parentItem && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows))
+ pos = d->parentItem->mapFromGlobal(QCursor::pos());
+#endif
+
+ // As a fallback, center the menu over its parent item.
+ if (!pos.isValid() && d->parentItem)
+ pos = QPointF((d->parentItem->width() - width()) / 2, (d->parentItem->height() - height()) / 2);
+
+ popup(pos.isValid() ? pos.value() : QPointF(), menuItem);
+}
+
+void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
+{
+ Q_D(QQuickMenu);
+ qreal offset = 0;
+#if QT_CONFIG(cursor)
+ if (menuItem)
+ offset = d->popupItem->mapFromItem(menuItem, QPointF(0, 0)).y();
+#endif
+ setPosition(pos - QPointF(0, offset));
+
+ if (menuItem)
+ d->setCurrentIndex(d->contentModel->indexOf(menuItem, nullptr), Qt::PopupFocusReason);
+ open();
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::popup(MenuItem item = null)
+ \qmlmethod void QtQuick.Controls::Menu::popup(Item parent, MenuItem item = null)
+
+ Opens the menu at the mouse cursor on desktop platforms that have a mouse cursor
+ available, and otherwise centers the menu over its \a parent item.
+
+ The menu can be optionally aligned to a specific menu \a item.
+
+ \sa Popup::open()
+*/
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::popup(point pos, MenuItem item = null)
+ \qmlmethod void QtQuick.Controls::Menu::popup(Item parent, point pos, MenuItem item = null)
+
+ Opens the menu at the specified position \a pos in the popups coordinate system,
+ that is, a coordinate relative to its \a parent item.
+
+ The menu can be optionally aligned to a specific menu \a item.
+
+ \sa Popup::open()
+*/
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::popup(real x, real y, MenuItem item = null)
+ \qmlmethod void QtQuick.Controls::Menu::popup(Item parent, real x, real y, MenuItem item = null)
+
+ Opens the menu at the specified position \a x, \a y in the popups coordinate system,
+ that is, a coordinate relative to its \a parent item.
+
+ The menu can be optionally aligned to a specific menu \a item.
+
+ \sa dismiss(), Popup::open()
+*/
+void QQuickMenu::popup(QQmlV4FunctionPtr args)
+{
+ Q_D(QQuickMenu);
+ const int len = args->length();
+ if (len > 4) {
+ args->v4engine()->throwTypeError();
+ return;
+ }
+
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ QV4::Scope scope(v4);
+
+ QQmlNullableValue<QPointF> pos;
+ QQuickItem *menuItem = nullptr;
+ QQuickItem *parentItem = nullptr;
+
+ if (len > 0) {
+ // Item parent
+ QV4::ScopedValue firstArg(scope, (*args)[0]);
+ if (const QV4::QObjectWrapper *obj = firstArg->as<QV4::QObjectWrapper>()) {
+ QQuickItem *item = qobject_cast<QQuickItem *>(obj->object());
+ if (item && !d->popupItem->isAncestorOf(item))
+ parentItem = item;
+ } else if (firstArg->isUndefined()) {
+ resetParentItem();
+ parentItem = d->parentItem;
+ }
+
+ // MenuItem item
+ QV4::ScopedValue lastArg(scope, (*args)[len - 1]);
+ if (const QV4::QObjectWrapper *obj = lastArg->as<QV4::QObjectWrapper>()) {
+ QQuickItem *item = qobject_cast<QQuickItem *>(obj->object());
+ if (item && d->popupItem->isAncestorOf(item))
+ menuItem = item;
+ }
+ }
+
+ if (len >= 3 || (!parentItem && len >= 2)) {
+ // real x, real y
+ QV4::ScopedValue xArg(scope, (*args)[parentItem ? 1 : 0]);
+ QV4::ScopedValue yArg(scope, (*args)[parentItem ? 2 : 1]);
+ if (xArg->isNumber() && yArg->isNumber())
+ pos = QPointF(xArg->asDouble(), yArg->asDouble());
+ }
+
+ if (!pos.isValid() && (len >= 2 || (!parentItem && len >= 1))) {
+ // point pos
+ QV4::ScopedValue posArg(scope, (*args)[parentItem ? 1 : 0]);
+ const QVariant var = QV4::ExecutionEngine::toVariant(posArg, QMetaType {});
+ if (var.userType() == QMetaType::QPointF)
+ pos = var.toPointF();
+ }
+
+ if (parentItem)
+ setParentItem(parentItem);
+
+ if (pos.isValid())
+ popup(pos, menuItem);
+ else
+ popup(menuItem);
+}
+
+/*!
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \qmlmethod void QtQuick.Controls::Menu::dismiss()
+
+ Closes all menus in the hierarchy that this menu belongs to.
+
+ \note Unlike \l {Popup::}{close()} that only closes a menu and its sub-menus,
+ \c dismiss() closes the whole hierarchy of menus, including the parent menus.
+ In practice, \c close() is suitable e.g. for implementing navigation in a
+ hierarchy of menus, and \c dismiss() is the appropriate method for closing
+ the whole hierarchy of menus.
+
+ \sa popup(), Popup::close()
+*/
+void QQuickMenu::dismiss()
+{
+ QQuickMenu *menu = this;
+ while (menu) {
+ menu->close();
+ menu = QQuickMenuPrivate::get(menu)->parentMenu;
+ }
+}
+
+void QQuickMenu::componentComplete()
+{
+ Q_D(QQuickMenu);
+ QQuickPopup::componentComplete();
+ d->resizeItems();
+}
+
+void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
+{
+ Q_D(QQuickMenu);
+ QQuickPopup::contentItemChange(newItem, oldItem);
+
+ if (oldItem) {
+ QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
+ QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
+ }
+ if (newItem) {
+ QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Children);
+ QQuickItemPrivate::get(newItem)->updateOrAddGeometryChangeListener(d, QQuickGeometryChange::Width);
+ }
+
+ d->contentItem = newItem;
+}
+
+void QQuickMenu::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
+{
+ Q_D(QQuickMenu);
+ QQuickPopup::itemChange(change, data);
+
+ if (change == QQuickItem::ItemVisibleHasChanged) {
+ if (!data.boolValue && d->cascade) {
+ // Ensure that when the menu isn't visible, there's no current item
+ // the next time it's opened.
+ d->setCurrentIndex(-1, Qt::OtherFocusReason);
+ }
+ }
+}
+
+void QQuickMenu::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QQuickMenu);
+ QQuickPopup::keyPressEvent(event);
+
+ // QTBUG-17051
+ // Work around the fact that ListView has no way of distinguishing between
+ // mouse and keyboard interaction, thanks to the "interactive" bool in Flickable.
+ // What we actually want is to have a way to always allow keyboard interaction but
+ // only allow flicking with the mouse when there are too many menu items to be
+ // shown at once.
+ switch (event->key()) {
+ case Qt::Key_Up:
+ if (!d->activatePreviousItem())
+ d->propagateKeyEvent(event);
+ break;
+
+ case Qt::Key_Down:
+ d->activateNextItem();
+ break;
+
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ event->ignore();
+ if (d->popupItem->isMirrored() == (event->key() == Qt::Key_Right)) {
+ if (d->parentMenu && d->currentItem) {
+ if (!d->cascade)
+ d->parentMenu->open();
+ close();
+ event->accept();
+ }
+ } else {
+ if (QQuickMenu *subMenu = d->currentSubMenu()) {
+ auto subMenuPrivate = QQuickMenuPrivate::get(subMenu);
+ subMenu->popup(subMenuPrivate->firstEnabledMenuItem());
+ event->accept();
+ }
+ }
+ if (!event->isAccepted())
+ d->propagateKeyEvent(event);
+ break;
+
+#if QT_CONFIG(shortcut)
+ case Qt::Key_Alt:
+ // If &mnemonic shortcut is enabled, go back to (possibly) the parent
+ // menu bar so the shortcut key will be processed by the menu bar.
+ if (!QKeySequence::mnemonic(QStringLiteral("&A")).isEmpty())
+ close();
+ break;
+#endif
+
+ default:
+ break;
+ }
+}
+
+void QQuickMenu::timerEvent(QTimerEvent *event)
+{
+ Q_D(QQuickMenu);
+ if (event->timerId() == d->hoverTimer) {
+ if (QQuickMenu *subMenu = d->currentSubMenu())
+ subMenu->open();
+ d->stopHoverTimer();
+ return;
+ }
+ QQuickPopup::timerEvent(event);
+}
+
+QFont QQuickMenu::defaultFont() const
+{
+ return QQuickTheme::font(QQuickTheme::Menu);
+}
+
+#if QT_CONFIG(accessibility)
+QAccessible::Role QQuickMenu::accessibleRole() const
+{
+ return QAccessible::PopupMenu;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qquickmenu_p.cpp"