1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qquickmenu_p.h"
38#include "qquickmenu_p_p.h"
39#include "qquickmenuitem_p_p.h"
40#include "qquickmenubaritem_p.h"
41#include "qquickmenubar_p.h"
42#include "qquickpopupitem_p_p.h"
43#include "qquickpopuppositioner_p_p.h"
44#include "qquickaction_p.h"
45
46#include <QtGui/qevent.h>
47#include <QtGui/qcursor.h>
48#include <QtGui/qpa/qplatformintegration.h>
49#include <QtGui/private/qguiapplication_p.h>
50#include <QtQml/qqmlcontext.h>
51#include <QtQml/qqmlcomponent.h>
52#include <QtQml/private/qqmlengine_p.h>
53#include <QtQml/private/qv4scopedvalue_p.h>
54#include <QtQml/private/qv4variantobject_p.h>
55#include <QtQml/private/qv4qobjectwrapper_p.h>
56#include <private/qqmlobjectmodel_p.h>
57#include <QtQuick/private/qquickitem_p.h>
58#include <QtQuick/private/qquickitemchangelistener_p.h>
59#include <QtQuick/private/qquickitemview_p.h>
60#include <QtQuick/private/qquickevents_p_p.h>
61#include <QtQuick/private/qquickwindow_p.h>
62
63QT_BEGIN_NAMESPACE
64
65// copied from qfusionstyle.cpp
66static const int SUBMENU_DELAY = 225;
67
68/*!
69 \qmltype Menu
70 \inherits Popup
71//! \instantiates QQuickMenu
72 \inqmlmodule QtQuick.Controls
73 \since 5.7
74 \ingroup qtquickcontrols2-menus
75 \ingroup qtquickcontrols2-popups
76 \brief Menu popup that can be used as a context menu or popup menu.
77
78 \image qtquickcontrols2-menu.png
79
80 Menu has two main use cases:
81 \list
82 \li Context menus; for example, a menu that is shown after right clicking
83 \li Popup menus; for example, a menu that is shown after clicking a button
84 \endlist
85
86 When used as a context menu, the recommended way of opening the menu is to call
87 \l popup(). Unless a position is explicitly specified, the menu is positioned at
88 the mouse cursor on desktop platforms that have a mouse cursor available, and
89 otherwise centered over its parent item.
90
91 \code
92 MouseArea {
93 anchors.fill: parent
94 acceptedButtons: Qt.LeftButton | Qt.RightButton
95 onClicked: {
96 if (mouse.button === Qt.RightButton)
97 contextMenu.popup()
98 }
99 onPressAndHold: {
100 if (mouse.source === Qt.MouseEventNotSynthesized)
101 contextMenu.popup()
102 }
103
104 Menu {
105 id: contextMenu
106 MenuItem { text: "Cut" }
107 MenuItem { text: "Copy" }
108 MenuItem { text: "Paste" }
109 }
110 }
111 \endcode
112
113 When used as a popup menu, it is easiest to specify the position by specifying
114 the desired \l {Popup::}{x} and \l {Popup::}{y} coordinates using the respective
115 properties, and call \l {Popup::}{open()} to open the menu.
116
117 \code
118 Button {
119 id: fileButton
120 text: "File"
121 onClicked: menu.open()
122
123 Menu {
124 id: menu
125 y: fileButton.height
126
127 MenuItem {
128 text: "New..."
129 }
130 MenuItem {
131 text: "Open..."
132 }
133 MenuItem {
134 text: "Save"
135 }
136 }
137 }
138 \endcode
139
140 Since QtQuick.Controls 2.3 (Qt 5.10), it is also possible to create sub-menus
141 and declare Action objects inside Menu:
142
143 \code
144 Menu {
145 Action { text: "Cut" }
146 Action { text: "Copy" }
147 Action { text: "Paste" }
148
149 MenuSeparator { }
150
151 Menu {
152 title: "Find/Replace"
153 Action { text: "Find Next" }
154 Action { text: "Find Previous" }
155 Action { text: "Replace" }
156 }
157 }
158 \endcode
159
160 Sub-menus are \l {cascade}{cascading} by default on desktop platforms
161 that have a mouse cursor available. Non-cascading menus are shown one
162 menu at a time, and centered over the parent menu.
163
164 Typically, menu items are statically declared as children of the menu, but
165 Menu also provides API to \l {addItem}{add}, \l {insertItem}{insert},
166 \l {moveItem}{move} and \l {removeItem}{remove} items dynamically. The
167 items in a menu can be accessed using \l itemAt() or
168 \l {Popup::}{contentChildren}.
169
170 Although \l {MenuItem}{MenuItems} are most commonly used with Menu, it can
171 contain any type of item.
172
173 \section1 Margins
174
175 As it is inherited from Popup, Menu supports \l {Popup::}{margins}. By
176 default, all of the built-in styles specify \c 0 for Menu's margins to
177 ensure that the menu is kept within the bounds of the window. To allow a
178 menu to go outside of the window (to animate it moving into view, for
179 example), set the margins property to \c -1.
180
181 \sa {Customizing Menu}, MenuItem, {Menu Controls}, {Popup Controls}
182*/
183
184/*!
185 \qmlproperty bool QtQuick.Controls::Menu::focus
186
187 This property holds whether the popup wants focus.
188
189 When the popup actually receives focus, \l{Popup::}{activeFocus}
190 will be \c true. For more information, see
191 \l {Keyboard Focus in Qt Quick}.
192
193 The default value is \c false.
194
195 \sa activeFocus
196*/
197
198static const QQuickPopup::ClosePolicy cascadingSubMenuClosePolicy = QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent;
199
200static bool shouldCascade()
201{
202#if QT_CONFIG(cursor)
203 return QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::MultipleWindows);
204#else
205 return false;
206#endif
207}
208
209class QQuickMenuPositioner : public QQuickPopupPositioner
210{
211public:
212 QQuickMenuPositioner(QQuickMenu *menu) : QQuickPopupPositioner(menu) { }
213
214 void reposition() override;
215};
216
217QQuickMenuPrivate::QQuickMenuPrivate()
218{
219 cascade = shouldCascade();
220}
221
222void QQuickMenuPrivate::init()
223{
224 Q_Q(QQuickMenu);
225 contentModel = new QQmlObjectModel(q);
226}
227
228QQuickItem *QQuickMenuPrivate::itemAt(int index) const
229{
230 return qobject_cast<QQuickItem *>(object: contentModel->get(index));
231}
232
233void QQuickMenuPrivate::insertItem(int index, QQuickItem *item)
234{
235 contentData.append(t: item);
236 item->setParentItem(contentItem);
237 if (qobject_cast<QQuickItemView *>(object: contentItem))
238 QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-53262
239 if (complete)
240 resizeItem(item);
241 QQuickItemPrivate::get(item)->addItemChangeListener(listener: this, types: QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent);
242 QQuickItemPrivate::get(item)->updateOrAddGeometryChangeListener(listener: this, types: QQuickGeometryChange::Width);
243 contentModel->insert(index, object: item);
244
245 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(object: item);
246 if (menuItem) {
247 Q_Q(QQuickMenu);
248 QQuickMenuItemPrivate::get(item: menuItem)->setMenu(q);
249 if (QQuickMenu *subMenu = menuItem->subMenu())
250 QQuickMenuPrivate::get(menu: subMenu)->setParentMenu(q);
251 QObjectPrivate::connect(sender: menuItem, signal: &QQuickMenuItem::triggered, receiverPrivate: this, slot: &QQuickMenuPrivate::onItemTriggered);
252 QObjectPrivate::connect(sender: menuItem, signal: &QQuickItem::activeFocusChanged, receiverPrivate: this, slot: &QQuickMenuPrivate::onItemActiveFocusChanged);
253 QObjectPrivate::connect(sender: menuItem, signal: &QQuickControl::hoveredChanged, receiverPrivate: this, slot: &QQuickMenuPrivate::onItemHovered);
254 }
255}
256
257void QQuickMenuPrivate::moveItem(int from, int to)
258{
259 contentModel->move(from, to);
260}
261
262void QQuickMenuPrivate::removeItem(int index, QQuickItem *item)
263{
264 contentData.removeOne(t: item);
265
266 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent);
267 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: QQuickItemPrivate::Geometry);
268 item->setParentItem(nullptr);
269 contentModel->remove(index);
270
271 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(object: item);
272 if (menuItem) {
273 QQuickMenuItemPrivate::get(item: menuItem)->setMenu(nullptr);
274 if (QQuickMenu *subMenu = menuItem->subMenu())
275 QQuickMenuPrivate::get(menu: subMenu)->setParentMenu(nullptr);
276 QObjectPrivate::disconnect(sender: menuItem, signal: &QQuickMenuItem::triggered, receiverPrivate: this, slot: &QQuickMenuPrivate::onItemTriggered);
277 QObjectPrivate::disconnect(sender: menuItem, signal: &QQuickItem::activeFocusChanged, receiverPrivate: this, slot: &QQuickMenuPrivate::onItemActiveFocusChanged);
278 QObjectPrivate::disconnect(sender: menuItem, signal: &QQuickControl::hoveredChanged, receiverPrivate: this, slot: &QQuickMenuPrivate::onItemHovered);
279 }
280}
281
282QQuickItem *QQuickMenuPrivate::beginCreateItem()
283{
284 Q_Q(QQuickMenu);
285 if (!delegate)
286 return nullptr;
287
288 QQmlContext *creationContext = delegate->creationContext();
289 if (!creationContext)
290 creationContext = qmlContext(q);
291 QQmlContext *context = new QQmlContext(creationContext, q);
292 context->setContextObject(q);
293
294 QObject *object = delegate->beginCreate(context);
295 QQuickItem *item = qobject_cast<QQuickItem *>(object);
296 if (!item)
297 delete object;
298 else
299 QQml_setParent_noEvent(object: item, parent: q);
300
301 return item;
302}
303
304void QQuickMenuPrivate::completeCreateItem()
305{
306 if (!delegate)
307 return;
308
309 delegate->completeCreate();
310}
311
312QQuickItem *QQuickMenuPrivate::createItem(QQuickMenu *menu)
313{
314 QQuickItem *item = beginCreateItem();
315 if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(object: item))
316 QQuickMenuItemPrivate::get(item: menuItem)->setSubMenu(menu);
317 completeCreateItem();
318 return item;
319}
320
321QQuickItem *QQuickMenuPrivate::createItem(QQuickAction *action)
322{
323 QQuickItem *item = beginCreateItem();
324 if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: item))
325 button->setAction(action);
326 completeCreateItem();
327 return item;
328}
329
330void QQuickMenuPrivate::resizeItem(QQuickItem *item)
331{
332 if (!item || !contentItem)
333 return;
334
335 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
336 if (!p->widthValid) {
337 item->setWidth(contentItem->width());
338 p->widthValid = false;
339 }
340}
341
342void QQuickMenuPrivate::resizeItems()
343{
344 if (!contentModel)
345 return;
346
347 for (int i = 0; i < contentModel->count(); ++i)
348 resizeItem(item: itemAt(index: i));
349}
350
351void QQuickMenuPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
352{
353 // add dynamically reparented items (eg. by a Repeater)
354 if (!QQuickItemPrivate::get(item: child)->isTransparentForPositioner() && !contentData.contains(t: child))
355 insertItem(index: contentModel->count(), item: child);
356}
357
358void QQuickMenuPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent)
359{
360 // remove dynamically unparented items (eg. by a Repeater)
361 if (!parent)
362 removeItem(index: contentModel->indexOf(object: item, objectContext: nullptr), item);
363}
364
365void QQuickMenuPrivate::itemSiblingOrderChanged(QQuickItem *)
366{
367 // reorder the restacked items (eg. by a Repeater)
368 Q_Q(QQuickMenu);
369 QList<QQuickItem *> siblings = contentItem->childItems();
370
371 int to = 0;
372 for (int i = 0; i < siblings.count(); ++i) {
373 QQuickItem* sibling = siblings.at(i);
374 if (QQuickItemPrivate::get(item: sibling)->isTransparentForPositioner())
375 continue;
376 int index = contentModel->indexOf(object: sibling, objectContext: nullptr);
377 q->moveItem(from: index, to: to++);
378 }
379}
380
381void QQuickMenuPrivate::itemDestroyed(QQuickItem *item)
382{
383 QQuickPopupPrivate::itemDestroyed(item);
384 int index = contentModel->indexOf(object: item, objectContext: nullptr);
385 if (index != -1)
386 removeItem(index, item);
387}
388
389void QQuickMenuPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange, const QRectF &)
390{
391 if (!complete)
392 return;
393
394 if (item == contentItem) {
395 // The contentItem's geometry changed, so resize any items
396 // that don't have explicit widths set so that they fill the width of the menu.
397 resizeItems();
398 } else {
399 // The geometry of an item in the menu changed. If the item
400 // doesn't have an explicit width set, make it fill the width of the menu.
401 resizeItem(item);
402 }
403}
404
405QQuickPopupPositioner *QQuickMenuPrivate::getPositioner()
406{
407 Q_Q(QQuickMenu);
408 if (!positioner)
409 positioner = new QQuickMenuPositioner(q);
410 return positioner;
411}
412
413void QQuickMenuPositioner::reposition()
414{
415 QQuickMenu *menu = static_cast<QQuickMenu *>(popup());
416 QQuickMenuPrivate *p = QQuickMenuPrivate::get(menu);
417 if (p->parentMenu) {
418 if (p->cascade) {
419 if (p->popupItem->isMirrored())
420 menu->setPosition(QPointF(-menu->width() - p->parentMenu->leftPadding() + menu->overlap(), -menu->topPadding()));
421 else if (p->parentItem)
422 menu->setPosition(QPointF(p->parentItem->width() + p->parentMenu->rightPadding() - menu->overlap(), -menu->topPadding()));
423 } else {
424 menu->setPosition(QPointF(p->parentMenu->x() + (p->parentMenu->width() - menu->width()) / 2,
425 p->parentMenu->y() + (p->parentMenu->height() - menu->height()) / 2));
426 }
427 }
428 QQuickPopupPositioner::reposition();
429}
430
431bool QQuickMenuPrivate::prepareEnterTransition()
432{
433 Q_Q(QQuickMenu);
434 if (parentMenu && !cascade)
435 parentMenu->close();
436
437 // If a cascading sub-menu doesn't have enough space to open on
438 // the right, it flips on the other side of the parent menu.
439 allowHorizontalFlip = cascade && parentMenu;
440
441 if (!QQuickPopupPrivate::prepareEnterTransition())
442 return false;
443
444 if (!hasClosePolicy) {
445 if (cascade && parentMenu)
446 closePolicy = cascadingSubMenuClosePolicy;
447 else
448 q->resetClosePolicy();
449 }
450 return true;
451}
452
453bool QQuickMenuPrivate::prepareExitTransition()
454{
455 if (!QQuickPopupPrivate::prepareExitTransition())
456 return false;
457
458 stopHoverTimer();
459
460 QQuickMenu *subMenu = currentSubMenu();
461 while (subMenu) {
462 QPointer<QQuickMenuItem> currentSubMenuItem = QQuickMenuPrivate::get(menu: subMenu)->currentItem;
463 subMenu->close();
464 subMenu = currentSubMenuItem ? currentSubMenuItem->subMenu() : nullptr;
465 }
466 return true;
467}
468
469bool QQuickMenuPrivate::blockInput(QQuickItem *item, const QPointF &point) const
470{
471 // keep the parent menu open when a cascading sub-menu (this menu) is interacted with
472 return (cascade && parentMenu && contains(scenePos: point)) || QQuickPopupPrivate::blockInput(item, point);
473}
474
475void QQuickMenuPrivate::onItemHovered()
476{
477 Q_Q(QQuickMenu);
478 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object: q->sender());
479 if (!button || !button->isHovered() || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
480 return;
481
482 QQuickMenuItem *oldCurrentItem = currentItem;
483
484 int index = contentModel->indexOf(object: button, objectContext: nullptr);
485 if (index != -1) {
486 setCurrentIndex(index, reason: Qt::OtherFocusReason);
487 if (oldCurrentItem != currentItem) {
488 if (oldCurrentItem) {
489 QQuickMenu *subMenu = oldCurrentItem->subMenu();
490 if (subMenu)
491 subMenu->close();
492 }
493 if (currentItem) {
494 QQuickMenu *subMenu = currentItem->menu();
495 if (subMenu && subMenu->cascade())
496 startHoverTimer();
497 }
498 }
499 }
500}
501
502void QQuickMenuPrivate::onItemTriggered()
503{
504 Q_Q(QQuickMenu);
505 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(object: q->sender());
506 if (!item)
507 return;
508
509 if (QQuickMenu *subMenu = item->subMenu()) {
510 auto subMenuPrivate = QQuickMenuPrivate::get(menu: subMenu);
511 subMenu->popup(menuItem: subMenuPrivate->firstEnabledMenuItem());
512 } else {
513 q->dismiss();
514 }
515}
516
517void QQuickMenuPrivate::onItemActiveFocusChanged()
518{
519 Q_Q(QQuickMenu);
520 QQuickItem *item = qobject_cast<QQuickItem*>(object: q->sender());
521 if (!item->hasActiveFocus())
522 return;
523
524 int indexOfItem = contentModel->indexOf(object: item, objectContext: nullptr);
525 QQuickControl *control = qobject_cast<QQuickControl *>(object: item);
526 setCurrentIndex(index: indexOfItem, reason: control ? control->focusReason() : Qt::OtherFocusReason);
527}
528
529QQuickMenu *QQuickMenuPrivate::currentSubMenu() const
530{
531 if (!currentItem)
532 return nullptr;
533
534 return currentItem->subMenu();
535}
536
537void QQuickMenuPrivate::setParentMenu(QQuickMenu *parent)
538{
539 Q_Q(QQuickMenu);
540 if (parentMenu == parent)
541 return;
542
543 if (parentMenu) {
544 QObject::disconnect(sender: parentMenu.data(), signal: &QQuickMenu::cascadeChanged, receiver: q, slot: &QQuickMenu::setCascade);
545 disconnect(sender: parentMenu.data(), signal: &QQuickMenu::parentChanged, receiverPrivate: this, slot: &QQuickMenuPrivate::resolveParentItem);
546 }
547 if (parent) {
548 QObject::connect(sender: parent, signal: &QQuickMenu::cascadeChanged, receiver: q, slot: &QQuickMenu::setCascade);
549 connect(sender: parent, signal: &QQuickMenu::parentChanged, receiverPrivate: this, slot: &QQuickMenuPrivate::resolveParentItem);
550 }
551
552 parentMenu = parent;
553 q->resetCascade();
554 resolveParentItem();
555}
556
557static QQuickItem *findParentMenuItem(QQuickMenu *subMenu)
558{
559 QQuickMenu *menu = QQuickMenuPrivate::get(menu: subMenu)->parentMenu;
560 for (int i = 0; i < QQuickMenuPrivate::get(menu)->contentModel->count(); ++i) {
561 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(object: menu->itemAt(index: i));
562 if (item && item->subMenu() == subMenu)
563 return item;
564 }
565 return nullptr;
566}
567
568void QQuickMenuPrivate::resolveParentItem()
569{
570 Q_Q(QQuickMenu);
571 if (!parentMenu)
572 q->resetParentItem();
573 else if (!cascade)
574 q->setParentItem(parentMenu->parentItem());
575 else
576 q->setParentItem(findParentMenuItem(subMenu: q));
577}
578
579void QQuickMenuPrivate::propagateKeyEvent(QKeyEvent *event)
580{
581 if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(object: parentItem)) {
582 if (QQuickMenu *menu = menuItem->menu())
583 QQuickMenuPrivate::get(menu)->propagateKeyEvent(event);
584 } else if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(object: parentItem)) {
585 if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) {
586 event->accept();
587 QCoreApplication::sendEvent(receiver: menuBar, event);
588 }
589 }
590}
591
592void QQuickMenuPrivate::startHoverTimer()
593{
594 Q_Q(QQuickMenu);
595 stopHoverTimer();
596 hoverTimer = q->startTimer(interval: SUBMENU_DELAY);
597}
598
599void QQuickMenuPrivate::stopHoverTimer()
600{
601 Q_Q(QQuickMenu);
602 if (!hoverTimer)
603 return;
604
605 q->killTimer(id: hoverTimer);
606 hoverTimer = 0;
607}
608
609void QQuickMenuPrivate::setCurrentIndex(int index, Qt::FocusReason reason)
610{
611 Q_Q(QQuickMenu);
612 if (currentIndex == index)
613 return;
614
615 QQuickMenuItem *newCurrentItem = qobject_cast<QQuickMenuItem *>(object: itemAt(index));
616 if (currentItem != newCurrentItem) {
617 stopHoverTimer();
618 if (currentItem) {
619 currentItem->setHighlighted(false);
620 if (!newCurrentItem && window) {
621 QQuickItem *focusItem = QQuickItemPrivate::get(item: contentItem)->subFocusItem;
622 if (focusItem)
623 QQuickWindowPrivate::get(c: window)->clearFocusInScope(scope: contentItem, item: focusItem, reason: Qt::OtherFocusReason);
624 }
625 }
626 if (newCurrentItem) {
627 newCurrentItem->setHighlighted(true);
628 newCurrentItem->forceActiveFocus(reason);
629 }
630 currentItem = newCurrentItem;
631 }
632
633 currentIndex = index;
634 emit q->currentIndexChanged();
635}
636
637bool QQuickMenuPrivate::activateNextItem()
638{
639 int index = currentIndex;
640 int count = contentModel->count();
641 while (++index < count) {
642 QQuickItem *item = itemAt(index);
643 if (!item || !item->activeFocusOnTab() || !item->isEnabled())
644 continue;
645 setCurrentIndex(index, reason: Qt::TabFocusReason);
646 return true;
647 }
648 return false;
649}
650
651bool QQuickMenuPrivate::activatePreviousItem()
652{
653 int index = currentIndex;
654 while (--index >= 0) {
655 QQuickItem *item = itemAt(index);
656 if (!item || !item->activeFocusOnTab() || !item->isEnabled())
657 continue;
658 setCurrentIndex(index, reason: Qt::BacktabFocusReason);
659 return true;
660 }
661 return false;
662}
663
664QQuickMenuItem *QQuickMenuPrivate::firstEnabledMenuItem() const
665{
666 for (int i = 0; i < contentModel->count(); ++i) {
667 QQuickItem *item = itemAt(index: i);
668 if (!item || !item->isEnabled())
669 continue;
670
671 QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(object: item);
672 if (!menuItem)
673 continue;
674
675 return menuItem;
676 }
677 return nullptr;
678}
679
680void QQuickMenuPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
681{
682 QQuickMenu *q = qobject_cast<QQuickMenu *>(object: prop->object);
683 QQuickMenuPrivate *p = QQuickMenuPrivate::get(menu: q);
684
685 QQuickItem *item = qobject_cast<QQuickItem *>(object: obj);
686 if (!item) {
687 if (QQuickAction *action = qobject_cast<QQuickAction *>(object: obj))
688 item = p->createItem(action);
689 else if (QQuickMenu *menu = qobject_cast<QQuickMenu *>(object: obj))
690 item = p->createItem(menu);
691 }
692
693 if (item) {
694 if (QQuickItemPrivate::get(item)->isTransparentForPositioner()) {
695 QQuickItemPrivate::get(item)->addItemChangeListener(listener: p, types: QQuickItemPrivate::SiblingOrder);
696 item->setParentItem(p->contentItem);
697 } else if (p->contentModel->indexOf(object: item, objectContext: nullptr) == -1) {
698 q->addItem(item);
699 }
700 } else {
701 p->contentData.append(t: obj);
702 }
703}
704
705int QQuickMenuPrivate::contentData_count(QQmlListProperty<QObject> *prop)
706{
707 QQuickMenu *q = static_cast<QQuickMenu *>(prop->object);
708 return QQuickMenuPrivate::get(menu: q)->contentData.count();
709}
710
711QObject *QQuickMenuPrivate::contentData_at(QQmlListProperty<QObject> *prop, int index)
712{
713 QQuickMenu *q = static_cast<QQuickMenu *>(prop->object);
714 return QQuickMenuPrivate::get(menu: q)->contentData.value(i: index);
715}
716
717void QQuickMenuPrivate::contentData_clear(QQmlListProperty<QObject> *prop)
718{
719 QQuickMenu *q = static_cast<QQuickMenu *>(prop->object);
720 QQuickMenuPrivate::get(menu: q)->contentData.clear();
721}
722
723QQuickMenu::QQuickMenu(QObject *parent)
724 : QQuickPopup(*(new QQuickMenuPrivate), parent)
725{
726 Q_D(QQuickMenu);
727 setFocus(true);
728 d->init();
729 connect(sender: d->contentModel, signal: &QQmlObjectModel::countChanged, receiver: this, slot: &QQuickMenu::countChanged);
730}
731
732QQuickMenu::~QQuickMenu()
733{
734 Q_D(QQuickMenu);
735 // We have to do this to ensure that the change listeners are removed.
736 // It's too late to do this in ~QQuickMenuPrivate, as contentModel has already
737 // been destroyed before that is called.
738 while (d->contentModel->count() > 0)
739 d->removeItem(index: 0, item: d->itemAt(index: 0));
740}
741
742/*!
743 \qmlmethod Item QtQuick.Controls::Menu::itemAt(int index)
744
745 Returns the item at \a index, or \c null if it does not exist.
746*/
747QQuickItem *QQuickMenu::itemAt(int index) const
748{
749 Q_D(const QQuickMenu);
750 return d->itemAt(index);
751}
752
753/*!
754 \qmlmethod void QtQuick.Controls::Menu::addItem(Item item)
755
756 Adds \a item to the end of the list of items.
757*/
758void QQuickMenu::addItem(QQuickItem *item)
759{
760 Q_D(QQuickMenu);
761 insertItem(index: d->contentModel->count(), item);
762}
763
764/*!
765 \qmlmethod void QtQuick.Controls::Menu::insertItem(int index, Item item)
766
767 Inserts \a item at \a index.
768*/
769void QQuickMenu::insertItem(int index, QQuickItem *item)
770{
771 Q_D(QQuickMenu);
772 if (!item)
773 return;
774 const int count = d->contentModel->count();
775 if (index < 0 || index > count)
776 index = count;
777
778 int oldIndex = d->contentModel->indexOf(object: item, objectContext: nullptr);
779 if (oldIndex != -1) {
780 if (oldIndex < index)
781 --index;
782 if (oldIndex != index)
783 d->moveItem(from: oldIndex, to: index);
784 } else {
785 d->insertItem(index, item);
786 }
787}
788
789/*!
790 \qmlmethod void QtQuick.Controls::Menu::moveItem(int from, int to)
791
792 Moves an item \a from one index \a to another.
793*/
794void QQuickMenu::moveItem(int from, int to)
795{
796 Q_D(QQuickMenu);
797 const int count = d->contentModel->count();
798 if (from < 0 || from > count - 1)
799 return;
800 if (to < 0 || to > count - 1)
801 to = count - 1;
802
803 if (from != to)
804 d->moveItem(from, to);
805}
806
807/*!
808 \deprecated
809 \qmlmethod void QtQuick.Controls::Menu::removeItem(int index)
810
811 Use Menu::removeItem(Item) or Menu::takeItem(int) instead.
812*/
813void QQuickMenu::removeItem(const QVariant &var)
814{
815 if (var.userType() == QMetaType::Nullptr)
816 return;
817
818 if (QQuickItem *item = var.value<QQuickItem *>())
819 removeItem(item);
820 else
821 takeItem(index: var.toInt());
822}
823
824/*!
825 \since QtQuick.Controls 2.3 (Qt 5.10)
826 \qmlmethod void QtQuick.Controls::Menu::removeItem(Item item)
827
828 Removes and destroys the specified \a item.
829*/
830void QQuickMenu::removeItem(QQuickItem *item)
831{
832 Q_D(QQuickMenu);
833 if (!item)
834 return;
835
836 const int index = d->contentModel->indexOf(object: item, objectContext: nullptr);
837 if (index == -1)
838 return;
839
840 d->removeItem(index, item);
841 item->deleteLater();
842}
843
844/*!
845 \since QtQuick.Controls 2.3 (Qt 5.10)
846 \qmlmethod MenuItem QtQuick.Controls::Menu::takeItem(int index)
847
848 Removes and returns the item at \a index.
849
850 \note The ownership of the item is transferred to the caller.
851*/
852QQuickItem *QQuickMenu::takeItem(int index)
853{
854 Q_D(QQuickMenu);
855 const int count = d->contentModel->count();
856 if (index < 0 || index >= count)
857 return nullptr;
858
859 QQuickItem *item = itemAt(index);
860 if (item)
861 d->removeItem(index, item);
862 return item;
863}
864
865/*!
866 \since QtQuick.Controls 2.3 (Qt 5.10)
867 \qmlmethod Menu QtQuick.Controls::Menu::menuAt(int index)
868
869 Returns the sub-menu at \a index, or \c null if the index is not valid or
870 there is no sub-menu at the specified index.
871*/
872QQuickMenu *QQuickMenu::menuAt(int index) const
873{
874 Q_D(const QQuickMenu);
875 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(object: d->itemAt(index));
876 if (!item)
877 return nullptr;
878
879 return item->subMenu();
880}
881
882/*!
883 \since QtQuick.Controls 2.3 (Qt 5.10)
884 \qmlmethod void QtQuick.Controls::Menu::addMenu(Menu menu)
885
886 Adds \a menu as a sub-menu to the end of this menu.
887*/
888void QQuickMenu::addMenu(QQuickMenu *menu)
889{
890 Q_D(QQuickMenu);
891 insertMenu(index: d->contentModel->count(), menu);
892}
893
894/*!
895 \since QtQuick.Controls 2.3 (Qt 5.10)
896 \qmlmethod void QtQuick.Controls::Menu::insertMenu(int index, Menu menu)
897
898 Inserts \a menu as a sub-menu at \a index. The index is within all items in the menu.
899*/
900void QQuickMenu::insertMenu(int index, QQuickMenu *menu)
901{
902 Q_D(QQuickMenu);
903 if (!menu)
904 return;
905
906 insertItem(index, item: d->createItem(menu));
907}
908
909/*!
910 \since QtQuick.Controls 2.3 (Qt 5.10)
911 \qmlmethod void QtQuick.Controls::Menu::removeMenu(Menu menu)
912
913 Removes and destroys the specified \a menu.
914*/
915void QQuickMenu::removeMenu(QQuickMenu *menu)
916{
917 Q_D(QQuickMenu);
918 if (!menu)
919 return;
920
921 const int count = d->contentModel->count();
922 for (int i = 0; i < count; ++i) {
923 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(object: d->itemAt(index: i));
924 if (!item || item->subMenu() != menu)
925 continue;
926
927 removeItem(item);
928 break;
929 }
930
931 menu->deleteLater();
932}
933
934/*!
935 \since QtQuick.Controls 2.3 (Qt 5.10)
936 \qmlmethod Menu QtQuick.Controls::Menu::takeMenu(int index)
937
938 Removes and returns the menu at \a index. The index is within all items in the menu.
939
940 \note The ownership of the menu is transferred to the caller.
941*/
942QQuickMenu *QQuickMenu::takeMenu(int index)
943{
944 Q_D(QQuickMenu);
945 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(object: d->itemAt(index));
946 if (!item)
947 return nullptr;
948
949 QQuickMenu *subMenu = item->subMenu();
950 if (!subMenu)
951 return nullptr;
952
953 d->removeItem(index, item);
954 item->deleteLater();
955 return subMenu;
956}
957
958/*!
959 \since QtQuick.Controls 2.3 (Qt 5.10)
960 \qmlmethod Action QtQuick.Controls::Menu::actionAt(int index)
961
962 Returns the action at \a index, or \c null if the index is not valid or
963 there is no action at the specified index.
964*/
965QQuickAction *QQuickMenu::actionAt(int index) const
966{
967 Q_D(const QQuickMenu);
968 QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton *>(object: d->itemAt(index));
969 if (!item)
970 return nullptr;
971
972 return item->action();
973}
974
975/*!
976 \since QtQuick.Controls 2.3 (Qt 5.10)
977 \qmlmethod void QtQuick.Controls::Menu::addAction(Action action)
978
979 Adds \a action to the end of this menu.
980*/
981void QQuickMenu::addAction(QQuickAction *action)
982{
983 Q_D(QQuickMenu);
984 insertAction(index: d->contentModel->count(), action);
985}
986
987/*!
988 \since QtQuick.Controls 2.3 (Qt 5.10)
989 \qmlmethod void QtQuick.Controls::Menu::insertAction(int index, Action action)
990
991 Inserts \a action at \a index. The index is within all items in the menu.
992*/
993void QQuickMenu::insertAction(int index, QQuickAction *action)
994{
995 Q_D(QQuickMenu);
996 if (!action)
997 return;
998
999 insertItem(index, item: d->createItem(action));
1000}
1001
1002/*!
1003 \since QtQuick.Controls 2.3 (Qt 5.10)
1004 \qmlmethod void QtQuick.Controls::Menu::removeAction(Action action)
1005
1006 Removes and destroys the specified \a action.
1007*/
1008void QQuickMenu::removeAction(QQuickAction *action)
1009{
1010 Q_D(QQuickMenu);
1011 if (!action)
1012 return;
1013
1014 const int count = d->contentModel->count();
1015 for (int i = 0; i < count; ++i) {
1016 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(object: d->itemAt(index: i));
1017 if (!item || item->action() != action)
1018 continue;
1019
1020 removeItem(item);
1021 break;
1022 }
1023
1024 action->deleteLater();
1025}
1026
1027/*!
1028 \since QtQuick.Controls 2.3 (Qt 5.10)
1029 \qmlmethod Action QtQuick.Controls::Menu::takeAction(int index)
1030
1031 Removes and returns the action at \a index. The index is within all items in the menu.
1032
1033 \note The ownership of the action is transferred to the caller.
1034*/
1035QQuickAction *QQuickMenu::takeAction(int index)
1036{
1037 Q_D(QQuickMenu);
1038 QQuickMenuItem *item = qobject_cast<QQuickMenuItem *>(object: d->itemAt(index));
1039 if (!item)
1040 return nullptr;
1041
1042 QQuickAction *action = item->action();
1043 if (!action)
1044 return nullptr;
1045
1046 d->removeItem(index, item);
1047 item->deleteLater();
1048 return action;
1049}
1050
1051/*!
1052 \qmlproperty model QtQuick.Controls::Menu::contentModel
1053 \readonly
1054
1055 This property holds the model used to display menu items.
1056
1057 The content model is provided for visualization purposes. It can be assigned
1058 as a model to a content item that presents the contents of the menu.
1059
1060 \code
1061 Menu {
1062 id: menu
1063 contentItem: ListView {
1064 model: menu.contentModel
1065 }
1066 }
1067 \endcode
1068
1069 The model allows menu items to be statically declared as children of the
1070 menu.
1071*/
1072QVariant QQuickMenu::contentModel() const
1073{
1074 Q_D(const QQuickMenu);
1075 return QVariant::fromValue(value: d->contentModel);
1076}
1077
1078/*!
1079 \qmlproperty list<Object> QtQuick.Controls::Menu::contentData
1080 \default
1081
1082 This property holds the list of content data.
1083
1084 The list contains all objects that have been declared in QML as children
1085 of the menu, and also items that have been dynamically added or
1086 inserted using the \l addItem() and \l insertItem() methods, respectively.
1087
1088 \note Unlike \c contentChildren, \c contentData does include non-visual QML
1089 objects. It is not re-ordered when items are inserted or moved.
1090
1091 \sa Item::data, {Popup::}{contentChildren}
1092*/
1093QQmlListProperty<QObject> QQuickMenu::contentData()
1094{
1095 Q_D(QQuickMenu);
1096 if (!d->contentItem)
1097 QQuickControlPrivate::get(control: d->popupItem)->executeContentItem();
1098 return QQmlListProperty<QObject>(this, nullptr,
1099 QQuickMenuPrivate::contentData_append,
1100 QQuickMenuPrivate::contentData_count,
1101 QQuickMenuPrivate::contentData_at,
1102 QQuickMenuPrivate::contentData_clear);
1103}
1104
1105/*!
1106 \qmlproperty string QtQuick.Controls::Menu::title
1107
1108 This property holds the title for the menu.
1109
1110 The title of a menu is often displayed in the text of a menu item when the
1111 menu is a submenu, and in the text of a tool button when it is in a
1112 menubar.
1113*/
1114QString QQuickMenu::title() const
1115{
1116 Q_D(const QQuickMenu);
1117 return d->title;
1118}
1119
1120void QQuickMenu::setTitle(QString &title)
1121{
1122 Q_D(QQuickMenu);
1123 if (title == d->title)
1124 return;
1125 d->title = title;
1126 emit titleChanged(title);
1127}
1128
1129/*!
1130 \since QtQuick.Controls 2.3 (Qt 5.10)
1131 \qmlproperty bool QtQuick.Controls::Menu::cascade
1132
1133 This property holds whether the menu cascades its sub-menus.
1134
1135 The default value is platform-specific. Menus are cascading by default on
1136 desktop platforms that have a mouse cursor available. Non-cascading menus
1137 are shown one menu at a time, and centered over the parent menu.
1138
1139 \note Changing the value of the property has no effect while the menu is open.
1140
1141 \sa overlap
1142*/
1143bool QQuickMenu::cascade() const
1144{
1145 Q_D(const QQuickMenu);
1146 return d->cascade;
1147}
1148
1149void QQuickMenu::setCascade(bool cascade)
1150{
1151 Q_D(QQuickMenu);
1152 if (d->cascade == cascade)
1153 return;
1154 d->cascade = cascade;
1155 if (d->parentMenu)
1156 d->resolveParentItem();
1157 emit cascadeChanged(cascade);
1158}
1159
1160void QQuickMenu::resetCascade()
1161{
1162 Q_D(QQuickMenu);
1163 if (d->parentMenu)
1164 setCascade(d->parentMenu->cascade());
1165 else
1166 setCascade(shouldCascade());
1167}
1168
1169/*!
1170 \since QtQuick.Controls 2.3 (Qt 5.10)
1171 \qmlproperty real QtQuick.Controls::Menu::overlap
1172
1173 This property holds the amount of pixels by which the menu horizontally overlaps its parent menu.
1174
1175 The property only has effect when the menu is used as a cascading sub-menu.
1176
1177 The default value is style-specific.
1178
1179 \note Changing the value of the property has no effect while the menu is open.
1180
1181 \sa cascade
1182*/
1183qreal QQuickMenu::overlap() const
1184{
1185 Q_D(const QQuickMenu);
1186 return d->overlap;
1187}
1188
1189void QQuickMenu::setOverlap(qreal overlap)
1190{
1191 Q_D(QQuickMenu);
1192 if (d->overlap == overlap)
1193 return;
1194 d->overlap = overlap;
1195 emit overlapChanged();
1196}
1197
1198/*!
1199 \since QtQuick.Controls 2.3 (Qt 5.10)
1200 \qmlproperty Component QtQuick.Controls::Menu::delegate
1201
1202 This property holds the component that is used to create items
1203 to present actions.
1204
1205 \code
1206 Menu {
1207 Action { text: "Cut" }
1208 Action { text: "Copy" }
1209 Action { text: "Paste" }
1210 }
1211 \endcode
1212
1213 \sa Action
1214*/
1215QQmlComponent *QQuickMenu::delegate() const
1216{
1217 Q_D(const QQuickMenu);
1218 return d->delegate;
1219}
1220
1221void QQuickMenu::setDelegate(QQmlComponent *delegate)
1222{
1223 Q_D(QQuickMenu);
1224 if (d->delegate == delegate)
1225 return;
1226
1227 d->delegate = delegate;
1228 emit delegateChanged();
1229}
1230
1231/*!
1232 \since QtQuick.Controls 2.3 (Qt 5.10)
1233 \qmlproperty int QtQuick.Controls::Menu::currentIndex
1234
1235 This property holds the index of the currently highlighted item.
1236
1237 Menu items can be highlighted by mouse hover or keyboard navigation.
1238
1239 \sa MenuItem::highlighted
1240*/
1241int QQuickMenu::currentIndex() const
1242{
1243 Q_D(const QQuickMenu);
1244 return d->currentIndex;
1245}
1246
1247void QQuickMenu::setCurrentIndex(int index)
1248{
1249 Q_D(QQuickMenu);
1250 d->setCurrentIndex(index, reason: Qt::OtherFocusReason);
1251}
1252
1253/*!
1254 \since QtQuick.Controls 2.3 (Qt 5.10)
1255 \qmlproperty int QtQuick.Controls::Menu::count
1256 \readonly
1257
1258 This property holds the number of items.
1259*/
1260int QQuickMenu::count() const
1261{
1262 Q_D(const QQuickMenu);
1263 return d->contentModel->count();
1264}
1265
1266void QQuickMenu::popup(QQuickItem *menuItem)
1267{
1268 Q_D(QQuickMenu);
1269 // No position has been explicitly specified, so position the menu at the mouse cursor
1270 // on desktop platforms that have a mouse cursor available and support multiple windows.
1271 QQmlNullableValue<QPointF> pos;
1272#if QT_CONFIG(cursor)
1273 if (d->parentItem && QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::MultipleWindows))
1274 pos = d->parentItem->mapFromGlobal(point: QCursor::pos());
1275#endif
1276
1277 // As a fallback, center the menu over its parent item.
1278 if (pos.isNull && d->parentItem)
1279 pos = QPointF((d->parentItem->width() - width()) / 2, (d->parentItem->height() - height()) / 2);
1280
1281 popup(pos: pos.isNull ? QPointF() : pos.value, menuItem);
1282}
1283
1284void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
1285{
1286 Q_D(QQuickMenu);
1287 qreal offset = 0;
1288#if QT_CONFIG(cursor)
1289 if (menuItem)
1290 offset = d->popupItem->mapFromItem(item: menuItem, point: QPointF(0, 0)).y();
1291#endif
1292 setPosition(pos - QPointF(0, offset));
1293
1294 if (menuItem)
1295 d->setCurrentIndex(index: d->contentModel->indexOf(object: menuItem, objectContext: nullptr), reason: Qt::PopupFocusReason);
1296 open();
1297}
1298
1299/*!
1300 \since QtQuick.Controls 2.3 (Qt 5.10)
1301 \qmlmethod void QtQuick.Controls::Menu::popup(MenuItem item = null)
1302 \qmlmethod void QtQuick.Controls::Menu::popup(Item parent, MenuItem item = null)
1303
1304 Opens the menu at the mouse cursor on desktop platforms that have a mouse cursor
1305 available, and otherwise centers the menu over its \a parent item.
1306
1307 The menu can be optionally aligned to a specific menu \a item.
1308
1309 \sa Popup::open()
1310*/
1311
1312/*!
1313 \since QtQuick.Controls 2.3 (Qt 5.10)
1314 \qmlmethod void QtQuick.Controls::Menu::popup(point pos, MenuItem item = null)
1315 \qmlmethod void QtQuick.Controls::Menu::popup(Item parent, point pos, MenuItem item = null)
1316
1317 Opens the menu at the specified position \a pos in the popups coordinate system,
1318 that is, a coordinate relative to its \a parent item.
1319
1320 The menu can be optionally aligned to a specific menu \a item.
1321
1322 \sa Popup::open()
1323*/
1324
1325/*!
1326 \since QtQuick.Controls 2.3 (Qt 5.10)
1327 \qmlmethod void QtQuick.Controls::Menu::popup(real x, real y, MenuItem item = null)
1328 \qmlmethod void QtQuick.Controls::Menu::popup(Item parent, real x, real y, MenuItem item = null)
1329
1330 Opens the menu at the specified position \a x, \a y in the popups coordinate system,
1331 that is, a coordinate relative to its \a parent item.
1332
1333 The menu can be optionally aligned to a specific menu \a item.
1334
1335 \sa dismiss(), Popup::open()
1336*/
1337void QQuickMenu::popup(QQmlV4Function *args)
1338{
1339 Q_D(QQuickMenu);
1340 const int len = args->length();
1341 if (len > 4) {
1342 args->v4engine()->throwTypeError();
1343 return;
1344 }
1345
1346 QV4::ExecutionEngine *v4 = args->v4engine();
1347 QV4::Scope scope(v4);
1348
1349 QQmlNullableValue<QPointF> pos;
1350 QQuickItem *menuItem = nullptr;
1351 QQuickItem *parentItem = nullptr;
1352
1353 if (len > 0) {
1354 // Item parent
1355 QV4::ScopedValue firstArg(scope, (*args)[0]);
1356 if (const QV4::QObjectWrapper *obj = firstArg->as<QV4::QObjectWrapper>()) {
1357 QQuickItem *item = qobject_cast<QQuickItem *>(object: obj->object());
1358 if (item && !d->popupItem->isAncestorOf(child: item))
1359 parentItem = item;
1360 } else if (firstArg->isUndefined()) {
1361 resetParentItem();
1362 parentItem = d->parentItem;
1363 }
1364
1365 // MenuItem item
1366 QV4::ScopedValue lastArg(scope, (*args)[len - 1]);
1367 if (const QV4::QObjectWrapper *obj = lastArg->as<QV4::QObjectWrapper>()) {
1368 QQuickItem *item = qobject_cast<QQuickItem *>(object: obj->object());
1369 if (item && d->popupItem->isAncestorOf(child: item))
1370 menuItem = item;
1371 }
1372 }
1373
1374 if (len >= 3 || (!parentItem && len >= 2)) {
1375 // real x, real y
1376 QV4::ScopedValue xArg(scope, (*args)[parentItem ? 1 : 0]);
1377 QV4::ScopedValue yArg(scope, (*args)[parentItem ? 2 : 1]);
1378 if (xArg->isNumber() && yArg->isNumber())
1379 pos = QPointF(xArg->asDouble(), yArg->asDouble());
1380 }
1381
1382 if (pos.isNull && (len >= 2 || (!parentItem && len >= 1))) {
1383 // point pos
1384 QV4::ScopedValue posArg(scope, (*args)[parentItem ? 1 : 0]);
1385 const QVariant var = v4->toVariant(value: posArg, typeHint: -1);
1386 if (var.userType() == QMetaType::QPointF)
1387 pos = var.toPointF();
1388 }
1389
1390 if (parentItem)
1391 setParentItem(parentItem);
1392
1393 if (pos.isNull)
1394 popup(menuItem);
1395 else
1396 popup(pos, menuItem);
1397}
1398
1399/*!
1400 \since QtQuick.Controls 2.3 (Qt 5.10)
1401 \qmlmethod void QtQuick.Controls::Menu::dismiss()
1402
1403 Closes all menus in the hierarchy that this menu belongs to.
1404
1405 \note Unlike \l {Popup::}{close()} that only closes a menu and its sub-menus,
1406 \c dismiss() closes the whole hierarchy of menus, including the parent menus.
1407 In practice, \c close() is suitable e.g. for implementing navigation in a
1408 hierarchy of menus, and \c dismiss() is the appropriate method for closing
1409 the whole hierarchy of menus.
1410
1411 \sa popup(), Popup::close()
1412*/
1413void QQuickMenu::dismiss()
1414{
1415 QQuickMenu *menu = this;
1416 while (menu) {
1417 menu->close();
1418 menu = QQuickMenuPrivate::get(menu)->parentMenu;
1419 }
1420}
1421
1422void QQuickMenu::componentComplete()
1423{
1424 Q_D(QQuickMenu);
1425 QQuickPopup::componentComplete();
1426 d->resizeItems();
1427}
1428
1429void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
1430{
1431 Q_D(QQuickMenu);
1432 QQuickPopup::contentItemChange(newItem, oldItem);
1433
1434 if (oldItem) {
1435 QQuickItemPrivate::get(item: oldItem)->removeItemChangeListener(d, types: QQuickItemPrivate::Children);
1436 QQuickItemPrivate::get(item: oldItem)->removeItemChangeListener(d, types: QQuickItemPrivate::Geometry);
1437 }
1438 if (newItem) {
1439 QQuickItemPrivate::get(item: newItem)->addItemChangeListener(listener: d, types: QQuickItemPrivate::Children);
1440 QQuickItemPrivate::get(item: newItem)->updateOrAddGeometryChangeListener(listener: d, types: QQuickGeometryChange::Width);
1441 }
1442
1443 d->contentItem = newItem;
1444}
1445
1446void QQuickMenu::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
1447{
1448 Q_D(QQuickMenu);
1449 QQuickPopup::itemChange(change, data);
1450
1451 if (change == QQuickItem::ItemVisibleHasChanged) {
1452 if (!data.boolValue && d->cascade) {
1453 // Ensure that when the menu isn't visible, there's no current item
1454 // the next time it's opened.
1455 d->setCurrentIndex(index: -1, reason: Qt::OtherFocusReason);
1456 }
1457 }
1458}
1459
1460void QQuickMenu::keyPressEvent(QKeyEvent *event)
1461{
1462 Q_D(QQuickMenu);
1463 QQuickPopup::keyPressEvent(event);
1464
1465 // QTBUG-17051
1466 // Work around the fact that ListView has no way of distinguishing between
1467 // mouse and keyboard interaction, thanks to the "interactive" bool in Flickable.
1468 // What we actually want is to have a way to always allow keyboard interaction but
1469 // only allow flicking with the mouse when there are too many menu items to be
1470 // shown at once.
1471 switch (event->key()) {
1472 case Qt::Key_Up:
1473 if (!d->activatePreviousItem())
1474 d->propagateKeyEvent(event);
1475 break;
1476
1477 case Qt::Key_Down:
1478 d->activateNextItem();
1479 break;
1480
1481 case Qt::Key_Left:
1482 case Qt::Key_Right:
1483 event->ignore();
1484 if (d->popupItem->isMirrored() == (event->key() == Qt::Key_Right)) {
1485 if (d->parentMenu && d->currentItem) {
1486 if (!d->cascade)
1487 d->parentMenu->open();
1488 close();
1489 event->accept();
1490 }
1491 } else {
1492 if (QQuickMenu *subMenu = d->currentSubMenu()) {
1493 auto subMenuPrivate = QQuickMenuPrivate::get(menu: subMenu);
1494 subMenu->popup(menuItem: subMenuPrivate->firstEnabledMenuItem());
1495 event->accept();
1496 }
1497 }
1498 if (!event->isAccepted())
1499 d->propagateKeyEvent(event);
1500 break;
1501
1502 default:
1503 break;
1504 }
1505}
1506
1507void QQuickMenu::timerEvent(QTimerEvent *event)
1508{
1509 Q_D(QQuickMenu);
1510 if (event->timerId() == d->hoverTimer) {
1511 if (QQuickMenu *subMenu = d->currentSubMenu())
1512 subMenu->open();
1513 d->stopHoverTimer();
1514 return;
1515 }
1516 QQuickPopup::timerEvent(event);
1517}
1518
1519QFont QQuickMenu::defaultFont() const
1520{
1521 return QQuickTheme::font(scope: QQuickTheme::Menu);
1522}
1523
1524QPalette QQuickMenu::defaultPalette() const
1525{
1526 return QQuickTheme::palette(scope: QQuickTheme::Menu);
1527}
1528
1529#if QT_CONFIG(accessibility)
1530QAccessible::Role QQuickMenu::accessibleRole() const
1531{
1532 return QAccessible::PopupMenu;
1533}
1534#endif
1535
1536QT_END_NAMESPACE
1537
1538#include "moc_qquickmenu_p.cpp"
1539

source code of qtquickcontrols2/src/quicktemplates2/qquickmenu.cpp