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