aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates2/qquickmenu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates2/qquickmenu.cpp')
-rw-r--r--src/quicktemplates2/qquickmenu.cpp299
1 files changed, 292 insertions, 7 deletions
diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp
index e6f22e2b..fa6b88ef 100644
--- a/src/quicktemplates2/qquickmenu.cpp
+++ b/src/quicktemplates2/qquickmenu.cpp
@@ -36,10 +36,20 @@
#include "qquickmenu_p.h"
#include "qquickmenu_p_p.h"
-#include "qquickmenuitem_p.h"
-#include "qquickcontrol_p_p.h"
+#include "qquickmenuitem_p_p.h"
+#include "qquickpopupitem_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 <QtQml/private/qqmlobjectmodel_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
@@ -67,6 +77,37 @@ QT_BEGIN_NAMESPACE
\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
@@ -90,6 +131,17 @@ QT_BEGIN_NAMESPACE
}
\endcode
+ Since QtQuick.Controls 2.3 (Qt 5.10), it is also possible to declare
+ Action objects inside Menu:
+
+ \code
+ Menu {
+ Action { text: "Cut" }
+ Action { text: "Copy" }
+ Action { text: "Paste" }
+ }
+ \endcode
+
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
@@ -104,7 +156,8 @@ QT_BEGIN_NAMESPACE
QQuickMenuPrivate::QQuickMenuPrivate()
: contentItem(nullptr),
- contentModel(nullptr)
+ contentModel(nullptr),
+ delegate(nullptr)
{
Q_Q(QQuickMenu);
contentModel = new QQmlObjectModel(q);
@@ -129,9 +182,11 @@ void QQuickMenuPrivate::insertItem(int index, QQuickItem *item)
QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
if (menuItem) {
Q_Q(QQuickMenu);
+ QQuickMenuItemPrivate::get(menuItem)->setMenu(q);
QObjectPrivate::connect(menuItem, &QQuickMenuItem::pressed, this, &QQuickMenuPrivate::onItemPressed);
QObject::connect(menuItem, &QQuickMenuItem::triggered, q, &QQuickPopup::close);
QObjectPrivate::connect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged);
+ QObjectPrivate::connect(menuItem, &QQuickControl::hoveredChanged, this, &QQuickMenuPrivate::onItemHovered);
}
}
@@ -151,10 +206,36 @@ void QQuickMenuPrivate::removeItem(int index, QQuickItem *item)
QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
if (menuItem) {
Q_Q(QQuickMenu);
+ QQuickMenuItemPrivate::get(menuItem)->setMenu(nullptr);
QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::pressed, this, &QQuickMenuPrivate::onItemPressed);
QObject::disconnect(menuItem, &QQuickMenuItem::triggered, q, &QQuickPopup::close);
QObjectPrivate::disconnect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged);
+ QObjectPrivate::disconnect(menuItem, &QQuickControl::hoveredChanged, this, &QQuickMenuPrivate::onItemHovered);
+ }
+}
+
+QQuickItem *QQuickMenuPrivate::createItem(QQuickAction *action)
+{
+ 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);
+ if (QQuickItem *item = qobject_cast<QQuickItem *>(object)) {
+ if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(object))
+ button->setAction(action);
+ delegate->completeCreate();
+ return item;
}
+
+ delete object;
+ return nullptr;
}
void QQuickMenuPrivate::resizeItem(QQuickItem *item)
@@ -226,6 +307,18 @@ void QQuickMenuPrivate::onItemPressed()
item->forceActiveFocus();
}
+void QQuickMenuPrivate::onItemHovered()
+{
+ Q_Q(QQuickMenu);
+ QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(q->sender());
+ if (!button || !button->isHovered() || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
+ return;
+
+ int index = contentModel->indexOf(button, nullptr);
+ if (index != -1)
+ setCurrentIndex(index);
+}
+
void QQuickMenuPrivate::onItemActiveFocusChanged()
{
Q_Q(QQuickMenu);
@@ -248,13 +341,29 @@ int QQuickMenuPrivate::currentIndex() const
void QQuickMenuPrivate::setCurrentIndex(int index)
{
contentItem->setProperty("currentIndex", index);
+
+ QQuickMenuItem *newCurrentItem = contentItem->property("currentItem").value<QQuickMenuItem *>();
+
+ if (currentItem != newCurrentItem) {
+ if (currentItem)
+ currentItem->setHighlighted(false);
+ if (newCurrentItem)
+ newCurrentItem->setHighlighted(true);
+ currentItem = newCurrentItem;
+ }
}
void QQuickMenuPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
{
QQuickMenuPrivate *p = static_cast<QQuickMenuPrivate *>(prop->data);
QQuickMenu *q = static_cast<QQuickMenu *>(prop->object);
+
QQuickItem *item = qobject_cast<QQuickItem *>(obj);
+ if (!item) {
+ if (QQuickAction *action = qobject_cast<QQuickAction *>(obj))
+ item = p->createItem(action);
+ }
+
if (item) {
if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) {
QQuickItemPrivate::get(item)->addItemChangeListener(p, QQuickItemPrivate::SiblingOrder);
@@ -358,22 +467,61 @@ void QQuickMenu::moveItem(int from, int to)
}
/*!
+ \deprecated
\qmlmethod void QtQuick.Controls::Menu::removeItem(int index)
- Removes the item at \a index.
+ Use Menu::removeItem(Item) or Menu::takeItem(int) instead.
+*/
+void QQuickMenu::removeItem(const QVariant &var)
+{
+ if (var.userType() == QMetaType::Nullptr)
+ return;
+
+ if (QQuickItem *item = var.value<QQuickItem *>())
+ removeItem(item);
+ else
+ takeItem(var.toInt());
+}
+
+/*!
+ \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.
*/
-void QQuickMenu::removeItem(int index)
+QQuickItem *QQuickMenu::takeItem(int index)
{
Q_D(QQuickMenu);
const int count = d->contentModel->count();
if (index < 0 || index >= count)
- return;
+ return nullptr;
QQuickItem *item = itemAt(index);
if (item)
d->removeItem(index, item);
+ return item;
}
/*!
@@ -449,7 +597,139 @@ void QQuickMenu::setTitle(QString &title)
if (title == d->title)
return;
d->title = title;
- emit titleChanged();
+ emit titleChanged(title);
+}
+
+/*!
+ \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)
+ \qmlmethod void QtQuick.Controls::Menu::popup(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 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)
+
+ Opens the menu at the specified position \a pos in the popups coordinate system,
+ that is, a coordinate relative to its 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)
+
+ Opens the menu at the specified position \a x, \a y in the popups coordinate system,
+ that is, a coordinate relative to its parent item.
+
+ The menu can be optionally aligned to a specific menu \a item.
+
+ \sa Popup::open()
+*/
+void QQuickMenu::popup(QQmlV4Function *args)
+{
+ Q_D(QQuickMenu);
+ const int len = args->length();
+ if (len > 3) {
+ args->v4engine()->throwTypeError();
+ return;
+ }
+
+ QV4::ExecutionEngine *v4 = args->v4engine();
+ QV4::Scope scope(v4);
+
+ QQmlNullableValue<QPointF> pos;
+ QQuickMenuItem *menuItem = nullptr;
+
+ if (len > 0) {
+ // MenuItem item
+ QV4::ScopedValue lastArg(scope, (*args)[len - 1]);
+ const QV4::QObjectWrapper *obj = lastArg->as<QV4::QObjectWrapper>();
+ if (obj)
+ menuItem = qobject_cast<QQuickMenuItem *>(obj->object());
+ }
+
+ if (len >= 2) {
+ // real x, real y
+ QV4::ScopedValue firstArg(scope, (*args)[0]);
+ QV4::ScopedValue secondArg(scope, (*args)[1]);
+ if (firstArg->isNumber() && secondArg->isNumber())
+ pos = QPointF(firstArg->asDouble(), secondArg->asDouble());
+ }
+
+ if (pos.isNull && len >= 1) {
+ // point pos
+ QV4::ScopedValue firstArg(scope, (*args)[0]);
+ const QVariant var = v4->toVariant(firstArg, -1);
+ if (var.userType() == QMetaType::QPointF)
+ pos = var.toPointF();
+ }
+
+ // Unless the position has been explicitly specified, position the menu at
+ // the mouse cursor on desktop platforms that have a mouse cursor available
+ // and support multiple windows.
+#if QT_CONFIG(cursor)
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows)) {
+ if (pos.isNull && d->parentItem)
+ pos = d->parentItem->mapFromGlobal(QCursor::pos());
+ if (menuItem)
+ pos.value.ry() -= d->popupItem->mapFromItem(menuItem, QPointF(0, 0)).y();
+ }
+#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);
+
+ if (!pos.isNull)
+ setPosition(pos);
+
+ if (menuItem)
+ d->setCurrentIndex(d->contentModel->indexOf(menuItem, nullptr));
+
+ open();
}
void QQuickMenu::componentComplete()
@@ -531,6 +811,11 @@ QFont QQuickMenu::defaultFont() const
return QQuickControlPrivate::themeFont(QPlatformTheme::MenuFont);
}
+QPalette QQuickMenu::defaultPalette() const
+{
+ return QQuickControlPrivate::themePalette(QPlatformTheme::MenuPalette);
+}
+
#if QT_CONFIG(accessibility)
QAccessible::Role QQuickMenu::accessibleRole() const
{