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 Labs Platform 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 "qquickplatformmenu_p.h"
38#include "qquickplatformmenubar_p.h"
39#include "qquickplatformmenuitem_p.h"
40#include "qquickplatformiconloader_p.h"
41
42#include <QtCore/qloggingcategory.h>
43#include <QtGui/qicon.h>
44#include <QtGui/qcursor.h>
45#include <QtGui/qpa/qplatformtheme.h>
46#include <QtGui/private/qguiapplication_p.h>
47#include <QtGui/private/qhighdpiscaling_p.h>
48#include <QtQml/private/qqmlengine_p.h>
49#include <QtQml/private/qv4scopedvalue_p.h>
50#include <QtQml/private/qv4qobjectwrapper_p.h>
51#include <QtQuick/qquickrendercontrol.h>
52#include <QtQuick/qquickwindow.h>
53#include <QtQuick/qquickitem.h>
54
55#include "widgets/qwidgetplatform_p.h"
56
57#if QT_CONFIG(systemtrayicon)
58#include "qquickplatformsystemtrayicon_p.h"
59#endif
60
61QT_BEGIN_NAMESPACE
62
63/*!
64 \qmltype Menu
65 \inherits QtObject
66//! \instantiates QQuickPlatformMenu
67 \inqmlmodule Qt.labs.platform
68 \since 5.8
69 \brief A native menu.
70
71 The Menu type provides a QML API for native platform menu popups.
72
73 \image qtlabsplatform-menu.png
74
75 Menu can be used in a \l MenuBar, or as a stand-alone context menu.
76 The following example shows how to open a context menu on right mouse
77 click:
78
79 \code
80 MouseArea {
81 anchors.fill: parent
82 acceptedButtons: Qt.RightButton
83 onClicked: zoomMenu.open()
84 }
85
86 Menu {
87 id: zoomMenu
88
89 MenuItem {
90 text: qsTr("Zoom In")
91 shortcut: StandardKey.ZoomIn
92 onTriggered: zoomIn()
93 }
94
95 MenuItem {
96 text: qsTr("Zoom Out")
97 shortcut: StandardKey.ZoomOut
98 onTriggered: zoomOut()
99 }
100 }
101 \endcode
102
103 \section2 Submenus
104
105 To create submenus, declare a Menu as a child of another Menu:
106
107 \qml
108 Menu {
109 title: qsTr("Edit")
110
111 Menu {
112 title: qsTr("Advanced")
113
114 MenuItem {
115 text: qsTr("Auto-indent Selection")
116 onTriggered: autoIndentSelection()
117 }
118
119 MenuItem {
120 text: qsTr("Rewrap Paragraph")
121 onTriggered: rewrapParagraph()
122 }
123 }
124 }
125 \endqml
126
127 \section2 Dynamically Generating Menu Items
128
129 It is possible to dynamically generate menu items. One of the easiest ways
130 to do so is with \l[QtQml]{Instantiator}. For example, to implement a
131 "Recent Files" submenu, where the items are based on a list of files stored
132 in settings, the following code could be used:
133
134 \qml
135 Menu {
136 title: qsTr("File")
137
138 Menu {
139 id: recentFilesSubMenu
140 title: qsTr("Recent Files")
141 enabled: recentFilesInstantiator.count > 0
142
143 Instantiator {
144 id: recentFilesInstantiator
145 model: settings.recentFiles
146 delegate: MenuItem {
147 text: settings.displayableFilePath(modelData)
148 onTriggered: loadFile(modelData)
149 }
150
151 onObjectAdded: recentFilesSubMenu.insertItem(index, object)
152 onObjectRemoved: recentFilesSubMenu.removeItem(object)
153 }
154
155 MenuSeparator {}
156
157 MenuItem {
158 text: qsTr("Clear Recent Files")
159 onTriggered: settings.clearRecentFiles()
160 }
161 }
162 }
163 \endqml
164
165 \section2 Availability
166
167 A native platform menu is currently available on the following platforms:
168
169 \list
170 \li macOS
171 \li iOS
172 \li Android
173 \li Linux (only available as a stand-alone context menu when running with the GTK+ platform theme)
174 \endlist
175
176 \input includes/widgets.qdocinc 1
177
178 \labs
179
180 \sa MenuItem, MenuSeparator, MenuBar
181*/
182
183/*!
184 \qmlsignal Qt.labs.platform::Menu::aboutToShow()
185
186 This signal is emitted when the menu is about to be shown to the user.
187*/
188
189/*!
190 \qmlsignal Qt.labs.platform::Menu::aboutToHide()
191
192 This signal is emitted when the menu is about to be hidden from the user.
193*/
194
195Q_DECLARE_LOGGING_CATEGORY(qtLabsPlatformMenus)
196
197QQuickPlatformMenu::QQuickPlatformMenu(QObject *parent)
198 : QObject(parent),
199 m_complete(false),
200 m_enabled(true),
201 m_visible(true),
202 m_minimumWidth(-1),
203 m_type(QPlatformMenu::DefaultMenu),
204 m_menuBar(nullptr),
205 m_parentMenu(nullptr),
206 m_systemTrayIcon(nullptr),
207 m_menuItem(nullptr),
208 m_iconLoader(nullptr),
209 m_handle(nullptr)
210{
211}
212
213QQuickPlatformMenu::~QQuickPlatformMenu()
214{
215 if (m_menuBar)
216 m_menuBar->removeMenu(menu: this);
217 if (m_parentMenu)
218 m_parentMenu->removeMenu(menu: this);
219
220 unparentSubmenus();
221
222 delete m_iconLoader;
223 m_iconLoader = nullptr;
224 delete m_handle;
225 m_handle = nullptr;
226}
227
228void QQuickPlatformMenu::unparentSubmenus()
229{
230 for (QQuickPlatformMenuItem *item : qAsConst(t&: m_items)) {
231 if (QQuickPlatformMenu *subMenu = item->subMenu())
232 subMenu->setParentMenu(nullptr);
233 item->setMenu(nullptr);
234 }
235}
236
237QPlatformMenu *QQuickPlatformMenu::handle() const
238{
239 return m_handle;
240}
241
242QPlatformMenu * QQuickPlatformMenu::create()
243{
244 if (!m_handle) {
245 if (m_menuBar && m_menuBar->handle())
246 m_handle = m_menuBar->handle()->createMenu();
247 else if (m_parentMenu && m_parentMenu->handle())
248 m_handle = m_parentMenu->handle()->createSubMenu();
249#if QT_CONFIG(systemtrayicon)
250 else if (m_systemTrayIcon && m_systemTrayIcon->handle())
251 m_handle = m_systemTrayIcon->handle()->createMenu();
252#endif
253
254 // TODO: implement ^
255 // - QCocoaMenuBar::createMenu()
256 // - QCocoaMenu::createSubMenu()
257 // - QCocoaSystemTrayIcon::createMenu()
258 if (!m_handle)
259 m_handle = QGuiApplicationPrivate::platformTheme()->createPlatformMenu();
260
261 if (!m_handle)
262 m_handle = QWidgetPlatform::createMenu();
263
264 qCDebug(qtLabsPlatformMenus) << "Menu ->" << m_handle;
265
266 if (m_handle) {
267 connect(sender: m_handle, signal: &QPlatformMenu::aboutToShow, receiver: this, slot: &QQuickPlatformMenu::aboutToShow);
268 connect(sender: m_handle, signal: &QPlatformMenu::aboutToHide, receiver: this, slot: &QQuickPlatformMenu::aboutToHide);
269
270 for (QQuickPlatformMenuItem *item : qAsConst(t&: m_items))
271 m_handle->insertMenuItem(menuItem: item->create(), before: nullptr);
272
273 if (m_menuItem) {
274 if (QPlatformMenuItem *handle = m_menuItem->create())
275 handle->setMenu(m_handle);
276 }
277 }
278 }
279 return m_handle;
280}
281
282void QQuickPlatformMenu::destroy()
283{
284 if (!m_handle)
285 return;
286
287 // Ensure that all submenus are unparented before we are destroyed,
288 // so that they don't try to access a destroyed menu.
289 unparentSubmenus();
290
291 delete m_handle;
292 m_handle = nullptr;
293}
294
295void QQuickPlatformMenu::sync()
296{
297 if (!m_complete || !create())
298 return;
299
300 m_handle->setText(m_title);
301 m_handle->setEnabled(m_enabled);
302 m_handle->setVisible(m_visible);
303 m_handle->setMinimumWidth(m_minimumWidth);
304 m_handle->setMenuType(m_type);
305 m_handle->setFont(m_font);
306
307 if (m_menuBar && m_menuBar->handle())
308 m_menuBar->handle()->syncMenu(menuItem: m_handle);
309#if QT_CONFIG(systemtrayicon)
310 else if (m_systemTrayIcon && m_systemTrayIcon->handle())
311 m_systemTrayIcon->handle()->updateMenu(menu: m_handle);
312#endif
313
314 for (QQuickPlatformMenuItem *item : qAsConst(t&: m_items))
315 item->sync();
316}
317
318/*!
319 \default
320 \qmlproperty list<Object> Qt.labs.platform::Menu::data
321
322 This default property holds the list of all objects declared as children of
323 the menu. The data property includes objects that are not \l MenuItem instances,
324 such as \l Timer and \l QtObject.
325
326 \sa items
327*/
328QQmlListProperty<QObject> QQuickPlatformMenu::data()
329{
330 return QQmlListProperty<QObject>(this, nullptr, data_append, data_count, data_at, data_clear);
331}
332
333/*!
334 \qmlproperty list<MenuItem> Qt.labs.platform::Menu::items
335
336 This property holds the list of items in the menu.
337*/
338QQmlListProperty<QQuickPlatformMenuItem> QQuickPlatformMenu::items()
339{
340 return QQmlListProperty<QQuickPlatformMenuItem>(this, nullptr, items_append, items_count, items_at, items_clear);
341}
342
343/*!
344 \readonly
345 \qmlproperty MenuBar Qt.labs.platform::Menu::menuBar
346
347 This property holds the menubar that the menu belongs to, or \c null if the
348 menu is not in a menubar.
349*/
350QQuickPlatformMenuBar *QQuickPlatformMenu::menuBar() const
351{
352 return m_menuBar;
353}
354
355void QQuickPlatformMenu::setMenuBar(QQuickPlatformMenuBar *menuBar)
356{
357 if (m_menuBar == menuBar)
358 return;
359
360 m_menuBar = menuBar;
361 destroy();
362 emit menuBarChanged();
363}
364
365/*!
366 \readonly
367 \qmlproperty Menu Qt.labs.platform::Menu::parentMenu
368
369 This property holds the parent menu that the menu belongs to, or \c null if the
370 menu is not a sub-menu.
371*/
372QQuickPlatformMenu *QQuickPlatformMenu::parentMenu() const
373{
374 return m_parentMenu;
375}
376
377void QQuickPlatformMenu::setParentMenu(QQuickPlatformMenu *menu)
378{
379 if (m_parentMenu == menu)
380 return;
381
382 m_parentMenu = menu;
383 destroy();
384 emit parentMenuChanged();
385}
386
387/*!
388 \readonly
389 \qmlproperty SystemTrayIcon Qt.labs.platform::Menu::systemTrayIcon
390
391 This property holds the system tray icon that the menu belongs to, or \c null
392 if the menu is not in a system tray icon.
393*/
394QQuickPlatformSystemTrayIcon *QQuickPlatformMenu::systemTrayIcon() const
395{
396 return m_systemTrayIcon;
397}
398
399void QQuickPlatformMenu::setSystemTrayIcon(QQuickPlatformSystemTrayIcon *icon)
400{
401 if (m_systemTrayIcon == icon)
402 return;
403
404 m_systemTrayIcon = icon;
405 destroy();
406 emit systemTrayIconChanged();
407}
408
409/*!
410 \readonly
411 \qmlproperty MenuItem Qt.labs.platform::Menu::menuItem
412
413 This property holds the item that presents the menu (in a parent menu).
414*/
415QQuickPlatformMenuItem *QQuickPlatformMenu::menuItem() const
416{
417 if (!m_menuItem) {
418 QQuickPlatformMenu *that = const_cast<QQuickPlatformMenu *>(this);
419 m_menuItem = new QQuickPlatformMenuItem(that);
420 m_menuItem->setSubMenu(that);
421 m_menuItem->setText(m_title);
422 m_menuItem->setIconName(iconName());
423 m_menuItem->setIconSource(iconSource());
424 m_menuItem->setVisible(m_visible);
425 m_menuItem->setEnabled(m_enabled);
426 m_menuItem->componentComplete();
427 }
428 return m_menuItem;
429}
430
431/*!
432 \qmlproperty bool Qt.labs.platform::Menu::enabled
433
434 This property holds whether the menu is enabled. The default value is \c true.
435*/
436bool QQuickPlatformMenu::isEnabled() const
437{
438 return m_enabled;
439}
440
441void QQuickPlatformMenu::setEnabled(bool enabled)
442{
443 if (m_enabled == enabled)
444 return;
445
446 if (m_menuItem)
447 m_menuItem->setEnabled(enabled);
448
449 m_enabled = enabled;
450 sync();
451 emit enabledChanged();
452}
453
454/*!
455 \qmlproperty bool Qt.labs.platform::Menu::visible
456
457 This property holds whether the menu is visible. The default value is \c true.
458*/
459bool QQuickPlatformMenu::isVisible() const
460{
461 return m_visible;
462}
463
464void QQuickPlatformMenu::setVisible(bool visible)
465{
466 if (m_visible == visible)
467 return;
468
469 if (m_menuItem)
470 m_menuItem->setVisible(visible);
471
472 m_visible = visible;
473 sync();
474 emit visibleChanged();
475}
476
477/*!
478 \qmlproperty int Qt.labs.platform::Menu::minimumWidth
479
480 This property holds the minimum width of the menu. The default value is \c -1 (no minimum width).
481*/
482int QQuickPlatformMenu::minimumWidth() const
483{
484 return m_minimumWidth;
485}
486
487void QQuickPlatformMenu::setMinimumWidth(int width)
488{
489 if (m_minimumWidth == width)
490 return;
491
492 m_minimumWidth = width;
493 sync();
494 emit minimumWidthChanged();
495}
496
497/*!
498 \qmlproperty enumeration Qt.labs.platform::Menu::type
499
500 This property holds the type of the menu.
501
502 Available values:
503 \value Menu.DefaultMenu A normal menu (default).
504 \value Menu.EditMenu An edit menu with pre-populated cut, copy and paste items.
505*/
506QPlatformMenu::MenuType QQuickPlatformMenu::type() const
507{
508 return m_type;
509}
510
511void QQuickPlatformMenu::setType(QPlatformMenu::MenuType type)
512{
513 if (m_type == type)
514 return;
515
516 m_type = type;
517 sync();
518 emit typeChanged();
519}
520
521/*!
522 \qmlproperty string Qt.labs.platform::Menu::title
523
524 This property holds the menu's title.
525*/
526QString QQuickPlatformMenu::title() const
527{
528 return m_title;
529}
530
531void QQuickPlatformMenu::setTitle(const QString &title)
532{
533 if (m_title == title)
534 return;
535
536 if (m_menuItem)
537 m_menuItem->setText(title);
538
539 m_title = title;
540 sync();
541 emit titleChanged();
542}
543
544/*!
545 \qmlproperty url Qt.labs.platform::Menu::iconSource
546 \deprecated Use icon.source instead
547*/
548QUrl QQuickPlatformMenu::iconSource() const
549{
550 return icon().source();
551}
552
553void QQuickPlatformMenu::setIconSource(const QUrl& source)
554{
555 QQuickPlatformIcon newIcon = icon();
556 if (source == newIcon.source())
557 return;
558
559 if (m_menuItem)
560 m_menuItem->setIconSource(source);
561
562 newIcon.setSource(source);
563 iconLoader()->setIcon(newIcon);
564 emit iconSourceChanged();
565}
566
567/*!
568 \qmlproperty string Qt.labs.platform::Menu::iconName
569 \deprecated Use icon.name instead
570*/
571QString QQuickPlatformMenu::iconName() const
572{
573 return icon().name();
574}
575
576void QQuickPlatformMenu::setIconName(const QString& name)
577{
578 QQuickPlatformIcon newIcon = icon();
579 if (name == newIcon.name())
580 return;
581
582 if (m_menuItem)
583 m_menuItem->setIconName(name);
584
585 newIcon.setName(name);
586 iconLoader()->setIcon(newIcon);
587 emit iconNameChanged();}
588
589/*!
590 \qmlproperty font Qt.labs.platform::Menu::font
591
592 This property holds the menu's font.
593
594 \sa text
595*/
596QFont QQuickPlatformMenu::font() const
597{
598 return m_font;
599}
600
601void QQuickPlatformMenu::setFont(const QFont& font)
602{
603 if (m_font == font)
604 return;
605
606 m_font = font;
607 sync();
608 emit fontChanged();
609}
610
611/*!
612 \since Qt.labs.platform 1.1 (Qt 5.12)
613 \qmlproperty url Qt.labs.platform::Menu::icon.source
614 \qmlproperty string Qt.labs.platform::Menu::icon.name
615 \qmlproperty bool Qt.labs.platform::Menu::icon.mask
616
617 This property holds the menu item's icon.
618*/
619QQuickPlatformIcon QQuickPlatformMenu::icon() const
620{
621 if (!m_iconLoader)
622 return QQuickPlatformIcon();
623
624 return iconLoader()->icon();
625}
626
627void QQuickPlatformMenu::setIcon(const QQuickPlatformIcon &icon)
628{
629 if (iconLoader()->icon() == icon)
630 return;
631
632 if (m_menuItem)
633 m_menuItem->setIcon(icon);
634
635 iconLoader()->setIcon(icon);
636 emit iconChanged();
637}
638
639/*!
640 \qmlmethod void Qt.labs.platform::Menu::addItem(MenuItem item)
641
642 Adds an \a item to the end of the menu.
643*/
644void QQuickPlatformMenu::addItem(QQuickPlatformMenuItem *item)
645{
646 insertItem(index: m_items.count(), item);
647}
648
649/*!
650 \qmlmethod void Qt.labs.platform::Menu::insertItem(int index, MenuItem item)
651
652 Inserts an \a item at the specified \a index in the menu.
653*/
654void QQuickPlatformMenu::insertItem(int index, QQuickPlatformMenuItem *item)
655{
656 if (!item || m_items.contains(t: item))
657 return;
658
659 m_items.insert(i: index, t: item);
660 m_data.append(t: item);
661 item->setMenu(this);
662 if (m_handle && item->create()) {
663 QQuickPlatformMenuItem *before = m_items.value(i: index + 1);
664 m_handle->insertMenuItem(menuItem: item->handle(), before: before ? before->create() : nullptr);
665 }
666 sync();
667 emit itemsChanged();
668}
669
670/*!
671 \qmlmethod void Qt.labs.platform::Menu::removeItem(MenuItem item)
672
673 Removes an \a item from the menu.
674*/
675void QQuickPlatformMenu::removeItem(QQuickPlatformMenuItem *item)
676{
677 if (!item || !m_items.removeOne(t: item))
678 return;
679
680 m_data.removeOne(t: item);
681 if (m_handle)
682 m_handle->removeMenuItem(menuItem: item->handle());
683 item->setMenu(nullptr);
684 sync();
685 emit itemsChanged();
686}
687
688/*!
689 \qmlmethod void Qt.labs.platform::Menu::addMenu(Menu submenu)
690
691 Adds a \a submenu to the end of the menu.
692*/
693void QQuickPlatformMenu::addMenu(QQuickPlatformMenu *menu)
694{
695 insertMenu(index: m_items.count(), menu);
696}
697
698/*!
699 \qmlmethod void Qt.labs.platform::Menu::insertMenu(int index, Menu submenu)
700
701 Inserts a \a submenu at the specified \a index in the menu.
702*/
703void QQuickPlatformMenu::insertMenu(int index, QQuickPlatformMenu *menu)
704{
705 if (!menu)
706 return;
707
708 menu->setParentMenu(this);
709 insertItem(index, item: menu->menuItem());
710}
711
712/*!
713 \qmlmethod void Qt.labs.platform::Menu::removeMenu(Menu submenu)
714
715 Removes a \a submenu from the menu.
716*/
717void QQuickPlatformMenu::removeMenu(QQuickPlatformMenu *menu)
718{
719 if (!menu)
720 return;
721
722 menu->setParentMenu(nullptr);
723 removeItem(item: menu->menuItem());
724}
725
726/*!
727 \qmlmethod void Qt.labs.platform::Menu::clear()
728
729 Removes all items from the menu.
730*/
731void QQuickPlatformMenu::clear()
732{
733 if (m_items.isEmpty())
734 return;
735
736 for (QQuickPlatformMenuItem *item : qAsConst(t&: m_items)) {
737 m_data.removeOne(t: item);
738 if (m_handle)
739 m_handle->removeMenuItem(menuItem: item->handle());
740 item->setMenu(nullptr);
741 delete item;
742 }
743
744 m_items.clear();
745 sync();
746 emit itemsChanged();
747}
748
749/*!
750 \qmlmethod void Qt.labs.platform::Menu::open(MenuItem item)
751
752 Opens the menu at the current mouse position, optionally aligned to a menu \a item.
753*/
754
755/*!
756 \qmlmethod void Qt.labs.platform::Menu::open(Item target, MenuItem item)
757
758 Opens the menu at the specified \a target item, optionally aligned to a menu \a item.
759*/
760void QQuickPlatformMenu::open(QQmlV4Function *args)
761{
762 if (!m_handle)
763 return;
764
765 if (args->length() > 2) {
766 args->v4engine()->throwTypeError();
767 return;
768 }
769
770 QV4::ExecutionEngine *v4 = args->v4engine();
771 QV4::Scope scope(v4);
772
773 QQuickItem *targetItem = nullptr;
774 if (args->length() > 0) {
775 QV4::ScopedValue value(scope, (*args)[0]);
776 QV4::Scoped<QV4::QObjectWrapper> object(scope, value->as<QV4::QObjectWrapper>());
777 if (object)
778 targetItem = qobject_cast<QQuickItem *>(object: object->object());
779 }
780
781 QQuickPlatformMenuItem *menuItem = nullptr;
782 if (args->length() > 1) {
783 QV4::ScopedValue value(scope, (*args)[1]);
784 QV4::Scoped<QV4::QObjectWrapper> object(scope, value->as<QV4::QObjectWrapper>());
785 if (object)
786 menuItem = qobject_cast<QQuickPlatformMenuItem *>(object: object->object());
787 }
788
789 QPoint offset;
790 QWindow *window = findWindow(target: targetItem, offset: &offset);
791
792 QRect targetRect;
793 if (targetItem) {
794 QRectF sceneBounds = targetItem->mapRectToScene(rect: targetItem->boundingRect());
795 targetRect = sceneBounds.toAlignedRect().translated(p: offset);
796 } else {
797#if QT_CONFIG(cursor)
798 QPoint pos = QCursor::pos();
799 if (window)
800 pos = window->mapFromGlobal(pos);
801 targetRect.moveTo(p: pos);
802#endif
803 }
804 m_handle->showPopup(parentWindow: window,
805 targetRect: QHighDpi::toNativePixels(value: targetRect, context: window),
806 item: menuItem ? menuItem->handle() : nullptr);
807}
808
809/*!
810 \qmlmethod void Qt.labs.platform::Menu::close()
811
812 Closes the menu.
813*/
814void QQuickPlatformMenu::close()
815{
816 if (m_handle)
817 m_handle->dismiss();
818}
819
820void QQuickPlatformMenu::classBegin()
821{
822}
823
824void QQuickPlatformMenu::componentComplete()
825{
826 m_complete = true;
827 if (m_handle && m_iconLoader)
828 m_iconLoader->setEnabled(true);
829 sync();
830}
831
832QQuickPlatformIconLoader *QQuickPlatformMenu::iconLoader() const
833{
834 if (!m_iconLoader) {
835 QQuickPlatformMenu *that = const_cast<QQuickPlatformMenu *>(this);
836 static int slot = staticMetaObject.indexOfSlot(slot: "updateIcon()");
837 m_iconLoader = new QQuickPlatformIconLoader(slot, that);
838 m_iconLoader->setEnabled(m_complete);
839 }
840 return m_iconLoader;
841}
842
843static QWindow *effectiveWindow(QWindow *window, QPoint *offset)
844{
845 QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(object: window);
846 if (quickWindow) {
847 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(win: quickWindow, offset);
848 if (renderWindow)
849 return renderWindow;
850 }
851 return window;
852}
853
854QWindow *QQuickPlatformMenu::findWindow(QQuickItem *target, QPoint *offset) const
855{
856 if (target)
857 return effectiveWindow(window: target->window(), offset);
858
859 if (m_menuBar && m_menuBar->window())
860 return effectiveWindow(window: m_menuBar->window(), offset);
861
862 QObject *obj = parent();
863 while (obj) {
864 QWindow *window = qobject_cast<QWindow *>(o: obj);
865 if (window)
866 return effectiveWindow(window, offset);
867
868 QQuickItem *item = qobject_cast<QQuickItem *>(object: obj);
869 if (item && item->window())
870 return effectiveWindow(window: item->window(), offset);
871
872 obj = obj->parent();
873 }
874 return nullptr;
875}
876
877void QQuickPlatformMenu::data_append(QQmlListProperty<QObject> *property, QObject *object)
878{
879 QQuickPlatformMenu *menu = static_cast<QQuickPlatformMenu *>(property->object);
880 if (QQuickPlatformMenuItem *item = qobject_cast<QQuickPlatformMenuItem *>(object))
881 menu->addItem(item);
882 else if (QQuickPlatformMenu *subMenu = qobject_cast<QQuickPlatformMenu *>(object))
883 menu->addMenu(menu: subMenu);
884 else
885 menu->m_data.append(t: object);
886}
887
888int QQuickPlatformMenu::data_count(QQmlListProperty<QObject> *property)
889{
890 QQuickPlatformMenu *menu = static_cast<QQuickPlatformMenu *>(property->object);
891 return menu->m_data.count();
892}
893
894QObject *QQuickPlatformMenu::data_at(QQmlListProperty<QObject> *property, int index)
895{
896 QQuickPlatformMenu *menu = static_cast<QQuickPlatformMenu *>(property->object);
897 return menu->m_data.value(i: index);
898}
899
900void QQuickPlatformMenu::data_clear(QQmlListProperty<QObject> *property)
901{
902 QQuickPlatformMenu *menu = static_cast<QQuickPlatformMenu *>(property->object);
903 menu->m_data.clear();
904}
905
906void QQuickPlatformMenu::items_append(QQmlListProperty<QQuickPlatformMenuItem> *property, QQuickPlatformMenuItem *item)
907{
908 QQuickPlatformMenu *menu = static_cast<QQuickPlatformMenu *>(property->object);
909 menu->addItem(item);
910}
911
912int QQuickPlatformMenu::items_count(QQmlListProperty<QQuickPlatformMenuItem> *property)
913{
914 QQuickPlatformMenu *menu = static_cast<QQuickPlatformMenu *>(property->object);
915 return menu->m_items.count();
916}
917
918QQuickPlatformMenuItem *QQuickPlatformMenu::items_at(QQmlListProperty<QQuickPlatformMenuItem> *property, int index)
919{
920 QQuickPlatformMenu *menu = static_cast<QQuickPlatformMenu *>(property->object);
921 return menu->m_items.value(i: index);
922}
923
924void QQuickPlatformMenu::items_clear(QQmlListProperty<QQuickPlatformMenuItem> *property)
925{
926 QQuickPlatformMenu *menu = static_cast<QQuickPlatformMenu *>(property->object);
927 menu->clear();
928}
929
930void QQuickPlatformMenu::updateIcon()
931{
932 if (!m_handle || !m_iconLoader)
933 return;
934
935 m_handle->setIcon(m_iconLoader->toQIcon());
936 sync();
937}
938
939QT_END_NAMESPACE
940

source code of qtquickcontrols2/src/imports/platform/qquickplatformmenu.cpp