1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Controls module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickmenu_p.h"
41#include "qquickmenubar_p.h"
42#include "qquickmenuitemcontainer_p.h"
43#include "qquickmenupopupwindow_p.h"
44
45#include <qdebug.h>
46#include <qabstractitemmodel.h>
47#include <qcursor.h>
48#include <private/qhighdpiscaling_p.h>
49#include <private/qguiapplication_p.h>
50#include <QtGui/qpa/qplatformtheme.h>
51#include <QtGui/qpa/qplatformmenu.h>
52#include <qquickitem.h>
53#include <QtQuick/QQuickRenderControl>
54
55QT_BEGIN_NAMESPACE
56
57/*!
58 \class QQuickMenu1
59 \internal
60 */
61
62/*!
63 \qmltype MenuPrivate
64 \instantiates QQuickMenu1
65 \internal
66 \inqmlmodule QtQuick.Controls
67 */
68
69/*!
70 \qmlproperty list<Object> Menu::items
71 \default
72
73 The list of items in the menu.
74
75 \l Menu only accepts objects of type \l Menu, \l MenuItem, and \l MenuSeparator
76 as children. It also supports \l Instantiator objects as long as the insertion is
77 being done manually using \l insertItem().
78
79 \qml
80 Menu {
81 id: recentFilesMenu
82
83 Instantiator {
84 model: recentFilesModel
85 MenuItem {
86 text: model.fileName
87 }
88 onObjectAdded: recentFilesMenu.insertItem(index, object)
89 onObjectRemoved: recentFilesMenu.removeItem(object)
90 }
91
92 MenuSeparator {
93 visible: recentFilesModel.count > 0
94 }
95
96 MenuItem {
97 text: "Clear menu"
98 enabled: recentFilesModel.count > 0
99 onTriggered: recentFilesModel.clear()
100 }
101 }
102 \endqml
103
104 Note that in this case, the \c index parameter passed to \l insertItem() is relative
105 to the position of the \l Instantiator in the menu, as opposed to absolute position
106 in the menu.
107
108 \sa MenuItem, MenuSeparator
109*/
110
111/*!
112 \qmlproperty bool Menu::visible
113
114 Whether the menu should be visible as a submenu of another Menu, or as a menu on a MenuBar.
115 Its value defaults to \c true.
116
117 \note This has nothing to do with the actual menu pop-up window being visible. Use
118 \l aboutToShow() and \l aboutToHide() if you need to know when the pop-up window will
119 be shown or hidden.
120*/
121
122/*!
123 \qmlproperty enumeration Menu::type
124
125 This property is read-only and constant, and its value is \l {QtQuick.Controls::MenuItem::}{type}.
126*/
127
128/*!
129 \qmlproperty string Menu::title
130
131 Title for the menu as a submenu or in a menubar.
132
133 Mnemonics are supported by prefixing the shortcut letter with \&.
134 For instance, \c "\&File" will bind the \c Alt-F shortcut to the
135 \c "File" menu. Note that not all platforms support mnemonics.
136
137 Its value defaults to an empty string.
138*/
139
140/*!
141 \qmlproperty bool Menu::enabled
142
143 Whether the menu is enabled, and responsive to user interaction as a submenu.
144 Its value defaults to \c true.
145*/
146
147/*!
148 \qmlproperty url Menu::iconSource
149
150 Sets the icon file or resource url for the menu icon as a submenu.
151 Defaults to an empty URL.
152
153 \sa iconName
154*/
155
156/*!
157 \qmlproperty string Menu::iconName
158
159 Sets the icon name for the menu icon. This will pick the icon
160 with the given name from the current theme. Only works as a submenu.
161
162 Its value defaults to an empty string.
163
164 \sa iconSource
165*/
166
167/*!
168 \qmlmethod void Menu::popup()
169
170 Opens this menu under the mouse cursor.
171 It can block on some platforms, so test it accordingly.
172*/
173
174/*!
175 \qmlmethod MenuItem Menu::addItem(string text)
176
177 Adds a \a text item to the menu. Returns the newly created \l MenuItem.
178
179 \sa insertItem()
180*/
181
182/*!
183 \qmlmethod MenuItem Menu::insertItem(int before, string title)
184
185 Creates and inserts an item with title \a title at the index \a before in the current menu.
186 Returns the newly created \l MenuItem.
187
188 \sa addItem()
189*/
190
191/*!
192 \qmlmethod void Menu::addSeparator()
193
194 Adds a separator to the menu.
195
196 \sa insertSeparator()
197*/
198
199/*!
200 \qmlmethod void Menu::insertSeparator(int before)
201
202 Creates and inserts a separator at the index \a before in the current menu.
203
204 \sa addSeparator()
205*/
206
207/*!
208 \qmlmethod Menu Menu::addMenu(string title)
209 Adds a submenu with a title \a title to the menu. Returns the newly created \l Menu.
210
211 \sa insertMenu()
212*/
213
214/*!
215 \qmlmethod MenuItem Menu::insertMenu(int before, string title)
216
217 Creates and inserts a submenu with a title \a title at the index \a before in the current menu.
218 Returns the newly created \l Menu.
219
220 \sa addMenu()
221*/
222
223/*!
224 \qmlmethod void Menu::insertItem(int before, object item)
225
226 Inserts the \a item at the index \a before in the current menu.
227 In this case, \c item can be either a \l MenuItem, a \l MenuSeparator,
228 or a \l Menu.
229
230 \sa removeItem()
231*/
232
233/*!
234 \qmlmethod void Menu::removeItem(item)
235
236 Removes the \a item from the menu.
237 In this case, \a item can be either a \l MenuItem, a \l MenuSeparator,
238 or a \l Menu.
239
240 \sa insertItem()
241*/
242
243
244/*!
245 \qmlsignal Menu::aboutToShow()
246 \since QtQuick.Controls 1.4
247
248 This signal is emitted just before the menu is shown to the user.
249
250 \sa aboutToHide()
251*/
252
253/*!
254 \qmlsignal Menu::aboutToHide()
255 \since QtQuick.Controls 1.4
256
257 This signal is emitted just before the menu is hidden from the user.
258
259 \sa aboutToShow()
260*/
261
262QQuickMenu1::QQuickMenu1(QObject *parent)
263 : QQuickMenuText1(parent, QQuickMenuItemType1::Menu),
264 m_platformMenu(0),
265 m_itemsCount(0),
266 m_selectedIndex(-1),
267 m_parentWindow(0),
268 m_minimumWidth(0),
269 m_popupWindow(0),
270 m_menuContentItem(0),
271 m_popupVisible(false),
272 m_containersCount(0),
273 m_xOffset(0),
274 m_yOffset(0),
275 m_triggerCount(0),
276 m_proxy(false)
277{
278 connect(sender: this, SIGNAL(__textChanged()), receiver: this, SIGNAL(titleChanged()));
279
280 if (QGuiApplication::platformName() != QStringLiteral("xcb")) { // QTBUG-51372
281 m_platformMenu = QGuiApplicationPrivate::platformTheme()->createPlatformMenu();
282 if (m_platformMenu) {
283 connect(sender: m_platformMenu, SIGNAL(aboutToShow()), receiver: this, SIGNAL(aboutToShow()));
284 connect(sender: m_platformMenu, SIGNAL(aboutToHide()), receiver: this, SLOT(hideMenu()));
285 if (platformItem())
286 platformItem()->setMenu(m_platformMenu);
287 }
288 }
289 if (const QFont *font = QGuiApplicationPrivate::platformTheme()->font(type: QPlatformTheme::MenuItemFont))
290 m_font = *const_cast<QFont*>(font);
291}
292
293QQuickMenu1::~QQuickMenu1()
294{
295 while (!m_menuItems.empty()) {
296 QQuickMenuBase1 *item = m_menuItems.takeFirst();
297 if (item)
298 item->setParentMenu(0);
299 }
300
301 if (platformItem())
302 platformItem()->setMenu(0);
303
304 delete m_platformMenu;
305 m_platformMenu = 0;
306}
307
308void QQuickMenu1::syncParentMenuBar()
309{
310 QQuickMenuBar1 *menubar = qobject_cast<QQuickMenuBar1 *>(object: parent());
311 if (menubar && menubar->platformMenuBar())
312 menubar->platformMenuBar()->syncMenu(menuItem: m_platformMenu);
313}
314
315void QQuickMenu1::setVisible(bool v)
316{
317 QQuickMenuBase1::setVisible(v);
318 if (m_platformMenu) {
319 m_platformMenu->setVisible(v);
320 syncParentMenuBar();
321 }
322}
323
324void QQuickMenu1::setEnabled(bool e)
325{
326 QQuickMenuText1::setEnabled(e);
327 if (m_platformMenu) {
328 m_platformMenu->setEnabled(e);
329 syncParentMenuBar();
330 }
331}
332
333void QQuickMenu1::updateText()
334{
335 if (m_platformMenu)
336 m_platformMenu->setText(this->text());
337 QQuickMenuText1::updateText();
338}
339
340void QQuickMenu1::setMinimumWidth(int w)
341{
342 if (w == m_minimumWidth)
343 return;
344
345 m_minimumWidth = w;
346 if (m_platformMenu)
347 m_platformMenu->setMinimumWidth(w);
348
349 emit minimumWidthChanged();
350}
351
352void QQuickMenu1::setFont(const QFont &arg)
353{
354 if (arg == m_font)
355 return;
356
357 m_font = arg;
358 if (m_platformMenu)
359 m_platformMenu->setFont(arg);
360}
361
362void QQuickMenu1::setXOffset(qreal x)
363{
364 m_xOffset = x;
365}
366
367void QQuickMenu1::setYOffset(qreal y)
368{
369 m_yOffset = y;
370}
371
372void QQuickMenu1::setSelectedIndex(int index)
373{
374 if (m_selectedIndex == index)
375 return;
376
377 m_selectedIndex = index;
378 emit __selectedIndexChanged();
379}
380
381void QQuickMenu1::updateSelectedIndex()
382{
383 if (QQuickMenuItem1 *menuItem = qobject_cast<QQuickMenuItem1*>(object: sender())) {
384 int index = indexOfMenuItem(menuItem);
385 setSelectedIndex(index);
386 }
387}
388
389QQuickMenuItems QQuickMenu1::menuItems()
390{
391 return QQuickMenuItems(this, 0, &QQuickMenu1::append_menuItems, &QQuickMenu1::count_menuItems,
392 &QQuickMenu1::at_menuItems, &QQuickMenu1::clear_menuItems);
393}
394
395QQuickWindow *QQuickMenu1::findParentWindow()
396{
397 if (!m_parentWindow) {
398 QQuickItem *parentAsItem = qobject_cast<QQuickItem *>(object: parent());
399 m_parentWindow = visualItem() ? visualItem()->window() : // Menu as menu item case
400 parentAsItem ? parentAsItem->window() : 0; //Menu as context menu/popup case
401 }
402 return m_parentWindow;
403}
404
405void QQuickMenu1::popup()
406{
407 QQuickWindow *quickWindow = findParentWindow();
408 QPoint renderOffset;
409 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(win: quickWindow, offset: &renderOffset);
410 QWindow *parentWindow = renderWindow ? renderWindow : quickWindow;
411 QScreen *screen = parentWindow ? parentWindow->screen() : qGuiApp->primaryScreen();
412 QPoint mousePos = QCursor::pos(screen);
413
414 if (mousePos.x() == int(qInf())) {
415 // ### fixme: no mouse pos registered. Get pos from touch...
416 mousePos = screen->availableGeometry().center();
417 }
418
419 if (parentWindow)
420 mousePos = parentWindow->mapFromGlobal(pos: mousePos);
421
422 __popup(targetRect: QRectF(mousePos.x() - renderOffset.x(), mousePos.y() - renderOffset.y(), 0, 0));
423}
424
425void QQuickMenu1::__popup(const QRectF &targetRect, int atItemIndex, MenuType menuType)
426{
427 if (popupVisible()) {
428 hideMenu();
429 // Mac and Windows would normally move the menu under the cursor, so we should not
430 // return here. However, very often we want to re-contextualize the menu, and this
431 // has to be done at the application level.
432 return;
433 }
434
435 setPopupVisible(true);
436
437 QQuickMenuBase1 *atItem = menuItemAtIndex(index: atItemIndex);
438
439 QQuickWindow *quickWindow = findParentWindow();
440 QPoint renderOffset;
441 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(win: quickWindow, offset: &renderOffset);
442 QWindow *parentWindow = renderWindow ? renderWindow : quickWindow;
443 // parentWindow may not be a QQuickWindow (happens when using QQuickWidget)
444
445 if (m_platformMenu) {
446 if (m_windowConnection)
447 QObject::disconnect(m_windowConnection);
448 m_windowConnection = connect(sender: parentWindow, signal: &QWindow::visibleChanged, receiver: this,
449 slot: &QQuickMenu1::platformMenuWindowVisibleChanged, type: Qt::UniqueConnection);
450 QRectF globalTargetRect = targetRect.translated(dx: m_xOffset, dy: m_yOffset);
451 if (visualItem()) {
452 if (qGuiApp->isRightToLeft()) {
453 qreal w = qMax(a: static_cast<qreal>(m_minimumWidth), b: m_menuContentItem->width());
454 globalTargetRect.moveLeft(pos: w - targetRect.x() - targetRect.width());
455 }
456 globalTargetRect = visualItem()->mapRectToScene(rect: globalTargetRect);
457 }
458 globalTargetRect.translate(p: renderOffset);
459 m_platformMenu->setMenuType(QPlatformMenu::MenuType(menuType));
460 m_platformMenu->showPopup(parentWindow,
461 targetRect: QHighDpi::toNativePixels(value: globalTargetRect.toRect(), context: parentWindow),
462 item: atItem ? atItem->platformItem() : 0);
463 } else {
464 m_popupWindow = new QQuickMenuPopupWindow1(this);
465 if (visualItem())
466 m_popupWindow->setParentItem(visualItem());
467 else
468 m_popupWindow->setParentWindow(effectiveParentWindow: parentWindow, parentWindow: quickWindow);
469 m_popupWindow->setPopupContentItem(m_menuContentItem);
470 m_popupWindow->setItemAt(atItem ? atItem->visualItem() : 0);
471
472 connect(sender: m_popupWindow, SIGNAL(visibleChanged(bool)), receiver: this, SLOT(windowVisibleChanged(bool)));
473 connect(sender: m_popupWindow, SIGNAL(geometryChanged()), receiver: this, SIGNAL(__popupGeometryChanged()));
474 connect(sender: m_popupWindow, SIGNAL(willBeDeletedLater()), receiver: this, SLOT(clearPopupWindow()));
475
476 m_popupWindow->setPosition(posx: targetRect.x() + m_xOffset + renderOffset.x(),
477 posy: targetRect.y() + targetRect.height() + m_yOffset + renderOffset.y());
478 emit aboutToShow();
479 m_popupWindow->show();
480 }
481}
482
483void QQuickMenu1::setMenuContentItem(QQuickItem *item)
484{
485 if (m_menuContentItem != item) {
486 m_menuContentItem = item;
487 emit menuContentItemChanged();
488 }
489}
490
491void QQuickMenu1::setPopupVisible(bool v)
492{
493 if (m_popupVisible != v) {
494 m_popupVisible = v;
495 emit popupVisibleChanged();
496 }
497}
498
499QRect QQuickMenu1::popupGeometry() const
500{
501 if (!m_popupWindow || !m_popupVisible)
502 return QRect();
503
504 return m_popupWindow->geometry();
505}
506
507void QQuickMenu1::prepareItemTrigger(QQuickMenuItem1 *)
508{
509 m_triggerCount++;
510 __dismissMenu();
511}
512
513void QQuickMenu1::concludeItemTrigger(QQuickMenuItem1 *)
514{
515 if (--m_triggerCount == 0)
516 destroyAllMenuPopups();
517}
518
519/*!
520 * \internal
521 * Close this menu's popup window. Emits aboutToHide and sets __popupVisible to false.
522 */
523void QQuickMenu1::hideMenu()
524{
525 if (m_popupVisible) {
526 emit aboutToHide();
527 setPopupVisible(false);
528 }
529 if (m_popupWindow && m_popupWindow->isVisible())
530 m_popupWindow->hide();
531 m_parentWindow = 0;
532}
533
534QQuickMenuPopupWindow1 *QQuickMenu1::topMenuPopup() const
535{
536 QQuickMenuPopupWindow1 *topMenuWindow = m_popupWindow;
537 while (topMenuWindow) {
538 QQuickMenuPopupWindow1 *pw = qobject_cast<QQuickMenuPopupWindow1 *>(object: topMenuWindow->transientParent());
539 if (!pw)
540 return topMenuWindow;
541 topMenuWindow = pw;
542 }
543
544 return 0;
545}
546
547/*!
548 * \internal
549 * Dismiss all the menus this menu is attached to, bottom-up.
550 * In QQuickPopupWindow, before closing, dismissPopup() emits popupDismissed()
551 * which is connected to dismissPopup() on any child popup.
552 */
553void QQuickMenu1::__dismissMenu()
554{
555 if (m_platformMenu) {
556 m_platformMenu->dismiss();
557 } else if (QQuickMenuPopupWindow1 *topPopup = topMenuPopup()) {
558 topPopup->dismissPopup();
559 }
560}
561
562/*!
563 * \internal
564 * Called when the popup window visible property changes.
565 */
566void QQuickMenu1::windowVisibleChanged(bool v)
567{
568 if (!v) {
569 if (m_popupWindow) {
570 QQuickMenuPopupWindow1 *parentMenuPopup = qobject_cast<QQuickMenuPopupWindow1 *>(object: m_popupWindow->transientParent());
571 if (parentMenuPopup) {
572 parentMenuPopup->setMouseGrabEnabled(true);
573 parentMenuPopup->setKeyboardGrabEnabled(true);
574 }
575 }
576 if (m_popupVisible)
577 __closeAndDestroy();
578 }
579}
580
581void QQuickMenu1::platformMenuWindowVisibleChanged(bool visible)
582{
583 if (!visible) {
584 if (m_windowConnection) {
585 QObject::disconnect(m_windowConnection);
586 m_windowConnection = QMetaObject::Connection();
587 }
588 if (m_platformMenu) {
589 m_platformMenu->dismiss();
590 }
591 }
592}
593
594void QQuickMenu1::clearPopupWindow()
595{
596 m_popupWindow = 0;
597 emit __menuPopupDestroyed();
598}
599
600void QQuickMenu1::destroyMenuPopup()
601{
602 if (m_triggerCount > 0)
603 return;
604 if (m_popupWindow)
605 m_popupWindow->setToBeDeletedLater();
606}
607
608void QQuickMenu1::destroyAllMenuPopups() {
609 if (m_triggerCount > 0)
610 return;
611 QQuickMenuPopupWindow1 *popup = topMenuPopup();
612 if (popup)
613 popup->setToBeDeletedLater();
614}
615
616QQuickMenuBar1 *QQuickMenu1::menuBar()
617{
618 QObject *pi = parentMenuOrMenuBar();
619 while (pi) {
620 if (QQuickMenuBar1 *menuBar = qobject_cast<QQuickMenuBar1*>(object: pi))
621 return menuBar;
622 else if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1*>(object: pi))
623 pi = menu->parentMenuOrMenuBar();
624 else
625 return 0;
626 }
627 return 0;
628}
629
630void QQuickMenu1::__closeAndDestroy()
631{
632 hideMenu();
633 destroyMenuPopup();
634}
635
636void QQuickMenu1::__dismissAndDestroy()
637{
638 if (m_platformMenu)
639 return;
640
641 __dismissMenu();
642 destroyAllMenuPopups();
643}
644
645void QQuickMenu1::itemIndexToListIndex(int itemIndex, int *listIndex, int *containerIndex) const
646{
647 *listIndex = -1;
648 QQuickMenuItemContainer1 *container = 0;
649 while (itemIndex >= 0 && ++*listIndex < m_menuItems.count())
650 if ((container = qobject_cast<QQuickMenuItemContainer1 *>(object: m_menuItems[*listIndex])))
651 itemIndex -= container->items().count();
652 else
653 --itemIndex;
654
655 if (container)
656 *containerIndex = container->items().count() + itemIndex;
657 else
658 *containerIndex = -1;
659}
660
661int QQuickMenu1::itemIndexForListIndex(int listIndex) const
662{
663 int index = 0;
664 int i = 0;
665 while (i < listIndex && i < m_menuItems.count())
666 if (QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(object: m_menuItems[i++]))
667 index += container->items().count();
668 else
669 ++index;
670
671 return index;
672}
673
674QQuickMenuBase1 *QQuickMenu1::nextMenuItem(QQuickMenu1::MenuItemIterator *it) const
675{
676 if (it->containerIndex != -1) {
677 QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(object: m_menuItems[it->index]);
678 if (++it->containerIndex < container->items().count())
679 return container->items()[it->containerIndex];
680 }
681
682 if (++it->index < m_menuItems.count()) {
683 if (QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(object: m_menuItems[it->index])) {
684 it->containerIndex = 0;
685 return container->items()[0];
686 } else {
687 it->containerIndex = -1;
688 return m_menuItems[it->index];
689 }
690 }
691
692 return 0;
693}
694
695QQuickMenuBase1 *QQuickMenu1::menuItemAtIndex(int index) const
696{
697 if (0 <= index && index < m_itemsCount) {
698 if (!m_containersCount) {
699 return m_menuItems[index];
700 } else if (m_containersCount == 1 && m_menuItems.count() == 1) {
701 QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(object: m_menuItems[0]);
702 return container->items()[index];
703 } else {
704 int containerIndex;
705 int i;
706 itemIndexToListIndex(itemIndex: index, listIndex: &i, containerIndex: &containerIndex);
707 if (containerIndex != -1) {
708 QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(object: m_menuItems[i]);
709 return container->items()[containerIndex];
710 } else {
711 return m_menuItems[i];
712 }
713 }
714 }
715
716 return 0;
717}
718
719bool QQuickMenu1::contains(QQuickMenuBase1 *item)
720{
721 if (item->container())
722 return item->container()->items().contains(t: item);
723
724 return m_menuItems.contains(t: item);
725}
726
727int QQuickMenu1::indexOfMenuItem(QQuickMenuBase1 *item) const
728{
729 if (!item)
730 return -1;
731 if (item->container()) {
732 int containerIndex = m_menuItems.indexOf(t: item->container());
733 if (containerIndex == -1)
734 return -1;
735 int index = item->container()->items().indexOf(t: item);
736 return index == -1 ? -1 : itemIndexForListIndex(listIndex: containerIndex) + index;
737 } else {
738 int index = m_menuItems.indexOf(t: item);
739 return index == -1 ? -1 : itemIndexForListIndex(listIndex: index);
740 }
741}
742
743QQuickMenuItem1 *QQuickMenu1::addItem(const QString &title)
744{
745 return insertItem(m_itemsCount, title);
746}
747
748QQuickMenuItem1 *QQuickMenu1::insertItem(int index, const QString &title)
749{
750 QQuickMenuItem1 *item = new QQuickMenuItem1(this);
751 item->setText(title);
752 insertItem(index, item);
753 return item;
754}
755
756void QQuickMenu1::addSeparator()
757{
758 insertSeparator(m_itemsCount);
759}
760
761void QQuickMenu1::insertSeparator(int index)
762{
763 QQuickMenuSeparator1 *item = new QQuickMenuSeparator1(this);
764 insertItem(index, item);
765}
766
767void QQuickMenu1::insertItem(int index, QQuickMenuBase1 *menuItem)
768{
769 if (!menuItem)
770 return;
771 int itemIndex;
772 if (m_containersCount) {
773 QQuickMenuItemContainer1 *container = menuItem->parent() != this ? m_containers[menuItem->parent()] : 0;
774 if (container) {
775 container->insertItem(index, item: menuItem);
776 itemIndex = itemIndexForListIndex(listIndex: m_menuItems.indexOf(t: container)) + index;
777 } else {
778 itemIndex = itemIndexForListIndex(listIndex: index);
779 m_menuItems.insert(i: itemIndex, t: menuItem);
780 }
781 } else {
782 itemIndex = index;
783 m_menuItems.insert(i: index, t: menuItem);
784 }
785
786 setupMenuItem(item: menuItem, platformIndex: itemIndex);
787 emit itemsChanged();
788}
789
790void QQuickMenu1::removeItem(QQuickMenuBase1 *menuItem)
791{
792 // Removes the item, but if it's a container, the container is kept
793 if (menuItem) {
794 unparentItem(menuItem);
795 emit itemsChanged();
796 }
797}
798
799void QQuickMenu1::clear()
800{
801 if (m_itemsCount > 0) {
802 while (m_itemsCount > 0)
803 unparentItem(menuItem: menuItemAtIndex(index: 0));
804
805 // We can delete the containers now, as there cannot be any further items in them.
806 qDeleteAll(c: m_containers);
807 m_containers.clear();
808 m_containersCount = 0;
809
810 // The containers are also kept in m_menuItems, so we have to clear explicitly.
811 m_menuItems.clear();
812
813 emit itemsChanged();
814 }
815}
816
817void QQuickMenu1::unparentItem(QQuickMenuBase1 *menuItem)
818{
819 menuItem->setParentMenu(nullptr);
820 QQuickMenuItemContainer1 *container = (menuItem->parent() != this)
821 ? m_containers[menuItem->parent()] : nullptr;
822 if (container)
823 container->removeItem(item: menuItem);
824 else
825 m_menuItems.removeOne(t: menuItem);
826 --m_itemsCount;
827}
828
829void QQuickMenu1::setupMenuItem(QQuickMenuBase1 *item, int platformIndex)
830{
831 item->setParentMenu(this);
832 if (m_platformMenu) {
833 QPlatformMenuItem *before = 0;
834 if (platformIndex != -1)
835 before = m_platformMenu->menuItemAt(position: platformIndex);
836 m_platformMenu->insertMenuItem(menuItem: item->platformItem(), before);
837 }
838 ++m_itemsCount;
839}
840
841void QQuickMenu1::append_menuItems(QQuickMenuItems *list, QObject *o)
842{
843 if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1 *>(object: list->object)) {
844 if (QQuickMenuBase1 *menuItem = qobject_cast<QQuickMenuBase1 *>(object: o)) {
845 menu->m_menuItems.append(t: menuItem);
846 menu->setupMenuItem(item: menuItem);
847 } else {
848 QQuickMenuItemContainer1 *menuItemContainer = new QQuickMenuItemContainer1(menu);
849 menu->m_menuItems.append(t: menuItemContainer);
850 menu->m_containers.insert(akey: o, avalue: menuItemContainer);
851 menuItemContainer->setParentMenu(menu);
852 ++menu->m_containersCount;
853 const auto children = o->children();
854 for (QObject *child : children) {
855 if (QQuickMenuBase1 *item = qobject_cast<QQuickMenuBase1 *>(object: child)) {
856 menuItemContainer->insertItem(index: -1, item);
857 menu->setupMenuItem(item);
858 }
859 }
860 }
861 }
862}
863
864int QQuickMenu1::count_menuItems(QQuickMenuItems *list)
865{
866 if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1 *>(object: list->object))
867 return menu->m_itemsCount;
868
869 return 0;
870}
871
872QObject *QQuickMenu1::at_menuItems(QQuickMenuItems *list, int index)
873{
874 if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1 *>(object: list->object))
875 return menu->menuItemAtIndex(index);
876
877 return 0;
878}
879
880void QQuickMenu1::clear_menuItems(QQuickMenuItems *list)
881{
882 if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1 *>(object: list->object)) {
883 // There may be stray containers that don't appear in m_menuItems. This is because we may
884 // remove a container with removeItem(), which will only remove it from m_menuItems.
885 // Therefore, make sure that all containers are removed from m_menuItems first.
886 for (QQuickMenuItemContainer1 *container : menu->m_containers)
887 menu->m_menuItems.removeOne(t: container);
888
889 // Delete or unparent the items first. They may have references to the containers.
890 // QTBUG-48927: a proxy menu (ApplicationWindowStyle.qml) must not
891 // delete its items, because they are owned by the menubar
892 // We still do own the containers, though. We create them on append_menuItems, after all.
893 while (!menu->m_menuItems.empty()) {
894 if (menu->m_proxy)
895 menu->unparentItem(menuItem: menu->m_menuItems[0]);
896 else
897 delete menu->m_menuItems.takeFirst();
898 }
899 menu->m_menuItems.clear();
900
901 qDeleteAll(c: menu->m_containers);
902 menu->m_containers.clear();
903 menu->m_containersCount = 0;
904
905 menu->m_itemsCount = 0;
906 }
907}
908
909QT_END_NAMESPACE
910

source code of qtquickcontrols/src/controls/qquickmenu.cpp