diff options
Diffstat (limited to 'src/quicktemplates2/qquickmenu.cpp')
-rw-r--r-- | src/quicktemplates2/qquickmenu.cpp | 1506 |
1 files changed, 0 insertions, 1506 deletions
diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp deleted file mode 100644 index a19f8c75..00000000 --- a/src/quicktemplates2/qquickmenu.cpp +++ /dev/null @@ -1,1506 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickmenu_p.h" -#include "qquickmenu_p_p.h" -#include "qquickmenuitem_p_p.h" -#include "qquickmenubaritem_p.h" -#include "qquickmenubar_p.h" -#include "qquickpopupitem_p_p.h" -#include "qquickpopuppositioner_p_p.h" -#include "qquickaction_p.h" - -#include <QtGui/qevent.h> -#include <QtGui/qcursor.h> -#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/qquickitemview_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 qtquickcontrols2-menus - \ingroup qtquickcontrols2-popups - \brief Menu popup that can be used as a context menu or popup menu. - - \image qtquickcontrols2-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 - - 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. - - \sa {Customizing Menu}, MenuItem, {Menu Controls}, {Popup Controls} -*/ - -/*! - \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 false. - - \sa 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() -{ - Q_Q(QQuickMenu); - cascade = shouldCascade(); - 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); - if (qobject_cast<QQuickItemView *>(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 *creationContext = delegate->creationContext(); - if (!creationContext) - creationContext = qmlContext(q); - QQmlContext *context = new QQmlContext(creationContext, q); - context->setContextObject(q); - - QObject *object = delegate->beginCreate(context); - QQuickItem *item = qobject_cast<QQuickItem *>(object); - if (!item) - delete object; - - 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->widthValid = 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.count(); ++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() || 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); - } else if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(parentItem)) { - if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) { - event->accept(); - QCoreApplication::sendEvent(menuBar, event); - } - } -} - -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); - } -} - -int QQuickMenuPrivate::contentData_count(QQmlListProperty<QObject> *prop) -{ - QQuickMenu *q = static_cast<QQuickMenu *>(prop->object); - return QQuickMenuPrivate::get(q)->contentData.count(); -} - -QObject *QQuickMenuPrivate::contentData_at(QQmlListProperty<QObject> *prop, int 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); - connect(d->contentModel, &QQmlObjectModel::countChanged, this, &QQuickMenu::countChanged); -} - -/*! - \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<Object> QtQuick.Controls::Menu::contentData - \default - - 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(QString &title) -{ - Q_D(QQuickMenu); - if (title == d->title) - return; - d->title = title; - emit titleChanged(title); -} - -/*! - \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.isNull && d->parentItem) - pos = QPointF((d->parentItem->width() - width()) / 2, (d->parentItem->height() - height()) / 2); - - popup(pos.isNull ? QPointF() : pos.value, 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(QQmlV4Function *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.isNull && (len >= 2 || (!parentItem && len >= 1))) { - // point pos - QV4::ScopedValue posArg(scope, (*args)[parentItem ? 1 : 0]); - const QVariant var = v4->toVariant(posArg, -1); - if (var.userType() == QMetaType::QPointF) - pos = var.toPointF(); - } - - if (parentItem) - setParentItem(parentItem); - - if (pos.isNull) - popup(menuItem); - else - popup(pos, 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; - - 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" |