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 | |
61 | QT_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 | |
195 | Q_DECLARE_LOGGING_CATEGORY() |
196 | |
197 | 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 | |
213 | 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 | |
228 | void QQuickPlatformMenu::() |
229 | { |
230 | for (QQuickPlatformMenuItem *item : qAsConst(t&: m_items)) { |
231 | if (QQuickPlatformMenu * = item->subMenu()) |
232 | subMenu->setParentMenu(nullptr); |
233 | item->setMenu(nullptr); |
234 | } |
235 | } |
236 | |
237 | QPlatformMenu *QQuickPlatformMenu::handle() const |
238 | { |
239 | return m_handle; |
240 | } |
241 | |
242 | QPlatformMenu * QQuickPlatformMenu::() |
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 | |
282 | void QQuickPlatformMenu::() |
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 | |
295 | void QQuickPlatformMenu::() |
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 | */ |
328 | QQmlListProperty<QObject> QQuickPlatformMenu::() |
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 | */ |
338 | QQmlListProperty<QQuickPlatformMenuItem> QQuickPlatformMenu::() |
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 | */ |
350 | QQuickPlatformMenuBar *QQuickPlatformMenu::() const |
351 | { |
352 | return m_menuBar; |
353 | } |
354 | |
355 | void QQuickPlatformMenu::(QQuickPlatformMenuBar *) |
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 | */ |
372 | QQuickPlatformMenu *QQuickPlatformMenu::() const |
373 | { |
374 | return m_parentMenu; |
375 | } |
376 | |
377 | void QQuickPlatformMenu::(QQuickPlatformMenu *) |
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 | */ |
394 | QQuickPlatformSystemTrayIcon *QQuickPlatformMenu::() const |
395 | { |
396 | return m_systemTrayIcon; |
397 | } |
398 | |
399 | void QQuickPlatformMenu::(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 | */ |
415 | QQuickPlatformMenuItem *QQuickPlatformMenu::() 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 | */ |
436 | bool QQuickPlatformMenu::() const |
437 | { |
438 | return m_enabled; |
439 | } |
440 | |
441 | void QQuickPlatformMenu::(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 | */ |
459 | bool QQuickPlatformMenu::() const |
460 | { |
461 | return m_visible; |
462 | } |
463 | |
464 | void QQuickPlatformMenu::(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 | */ |
482 | int QQuickPlatformMenu::() const |
483 | { |
484 | return m_minimumWidth; |
485 | } |
486 | |
487 | void QQuickPlatformMenu::(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 | */ |
506 | QPlatformMenu::MenuType QQuickPlatformMenu::() const |
507 | { |
508 | return m_type; |
509 | } |
510 | |
511 | void QQuickPlatformMenu::(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 | */ |
526 | QString QQuickPlatformMenu::() const |
527 | { |
528 | return m_title; |
529 | } |
530 | |
531 | void QQuickPlatformMenu::(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 | */ |
548 | QUrl QQuickPlatformMenu::() const |
549 | { |
550 | return icon().source(); |
551 | } |
552 | |
553 | void QQuickPlatformMenu::(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 | */ |
571 | QString QQuickPlatformMenu::() const |
572 | { |
573 | return icon().name(); |
574 | } |
575 | |
576 | void QQuickPlatformMenu::(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 | */ |
596 | QFont QQuickPlatformMenu::() const |
597 | { |
598 | return m_font; |
599 | } |
600 | |
601 | void QQuickPlatformMenu::(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 | */ |
619 | QQuickPlatformIcon QQuickPlatformMenu::() const |
620 | { |
621 | if (!m_iconLoader) |
622 | return QQuickPlatformIcon(); |
623 | |
624 | return iconLoader()->icon(); |
625 | } |
626 | |
627 | void QQuickPlatformMenu::(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 | */ |
644 | void QQuickPlatformMenu::(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 | */ |
654 | void QQuickPlatformMenu::(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 | */ |
675 | void QQuickPlatformMenu::(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 | */ |
693 | void QQuickPlatformMenu::(QQuickPlatformMenu *) |
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 | */ |
703 | void QQuickPlatformMenu::(int index, QQuickPlatformMenu *) |
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 | */ |
717 | void QQuickPlatformMenu::(QQuickPlatformMenu *) |
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 | */ |
731 | void QQuickPlatformMenu::() |
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 | */ |
760 | void QQuickPlatformMenu::(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 * = 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 | */ |
814 | void QQuickPlatformMenu::() |
815 | { |
816 | if (m_handle) |
817 | m_handle->dismiss(); |
818 | } |
819 | |
820 | void QQuickPlatformMenu::() |
821 | { |
822 | } |
823 | |
824 | void QQuickPlatformMenu::() |
825 | { |
826 | m_complete = true; |
827 | if (m_handle && m_iconLoader) |
828 | m_iconLoader->setEnabled(true); |
829 | sync(); |
830 | } |
831 | |
832 | QQuickPlatformIconLoader *QQuickPlatformMenu::() 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 | |
843 | static 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 | |
854 | QWindow *QQuickPlatformMenu::(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 | |
877 | void QQuickPlatformMenu::(QQmlListProperty<QObject> *property, QObject *object) |
878 | { |
879 | QQuickPlatformMenu * = static_cast<QQuickPlatformMenu *>(property->object); |
880 | if (QQuickPlatformMenuItem *item = qobject_cast<QQuickPlatformMenuItem *>(object)) |
881 | menu->addItem(item); |
882 | else if (QQuickPlatformMenu * = qobject_cast<QQuickPlatformMenu *>(object)) |
883 | menu->addMenu(menu: subMenu); |
884 | else |
885 | menu->m_data.append(t: object); |
886 | } |
887 | |
888 | int QQuickPlatformMenu::(QQmlListProperty<QObject> *property) |
889 | { |
890 | QQuickPlatformMenu * = static_cast<QQuickPlatformMenu *>(property->object); |
891 | return menu->m_data.count(); |
892 | } |
893 | |
894 | QObject *QQuickPlatformMenu::(QQmlListProperty<QObject> *property, int index) |
895 | { |
896 | QQuickPlatformMenu * = static_cast<QQuickPlatformMenu *>(property->object); |
897 | return menu->m_data.value(i: index); |
898 | } |
899 | |
900 | void QQuickPlatformMenu::(QQmlListProperty<QObject> *property) |
901 | { |
902 | QQuickPlatformMenu * = static_cast<QQuickPlatformMenu *>(property->object); |
903 | menu->m_data.clear(); |
904 | } |
905 | |
906 | void QQuickPlatformMenu::(QQmlListProperty<QQuickPlatformMenuItem> *property, QQuickPlatformMenuItem *item) |
907 | { |
908 | QQuickPlatformMenu * = static_cast<QQuickPlatformMenu *>(property->object); |
909 | menu->addItem(item); |
910 | } |
911 | |
912 | int QQuickPlatformMenu::(QQmlListProperty<QQuickPlatformMenuItem> *property) |
913 | { |
914 | QQuickPlatformMenu * = static_cast<QQuickPlatformMenu *>(property->object); |
915 | return menu->m_items.count(); |
916 | } |
917 | |
918 | QQuickPlatformMenuItem *QQuickPlatformMenu::(QQmlListProperty<QQuickPlatformMenuItem> *property, int index) |
919 | { |
920 | QQuickPlatformMenu * = static_cast<QQuickPlatformMenu *>(property->object); |
921 | return menu->m_items.value(i: index); |
922 | } |
923 | |
924 | void QQuickPlatformMenu::(QQmlListProperty<QQuickPlatformMenuItem> *property) |
925 | { |
926 | QQuickPlatformMenu * = static_cast<QQuickPlatformMenu *>(property->object); |
927 | menu->clear(); |
928 | } |
929 | |
930 | void QQuickPlatformMenu::() |
931 | { |
932 | if (!m_handle || !m_iconLoader) |
933 | return; |
934 | |
935 | m_handle->setIcon(m_iconLoader->toQIcon()); |
936 | sync(); |
937 | } |
938 | |
939 | QT_END_NAMESPACE |
940 | |