| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Quick Controls module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qquickmenu_p.h" |
| #include "qquickmenubar_p.h" |
| #include "qquickmenuitemcontainer_p.h" |
| #include "qquickmenupopupwindow_p.h" |
| |
| #include <qdebug.h> |
| #include <qabstractitemmodel.h> |
| #include <qcursor.h> |
| #include <private/qhighdpiscaling_p.h> |
| #include <private/qguiapplication_p.h> |
| #include <QtGui/qpa/qplatformtheme.h> |
| #include <QtGui/qpa/qplatformmenu.h> |
| #include <qquickitem.h> |
| #include <QtQuick/QQuickRenderControl> |
| |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \class QQuickMenu1 |
| \internal |
| */ |
| |
| /*! |
| \qmltype MenuPrivate |
| \instantiates QQuickMenu1 |
| \internal |
| \inqmlmodule QtQuick.Controls |
| */ |
| |
| /*! |
| \qmlproperty list<Object> Menu::items |
| \default |
| |
| The list of items in the menu. |
| |
| \l Menu only accepts objects of type \l Menu, \l MenuItem, and \l MenuSeparator |
| as children. It also supports \l Instantiator objects as long as the insertion is |
| being done manually using \l insertItem(). |
| |
| \qml |
| Menu { |
| id: recentFilesMenu |
| |
| Instantiator { |
| model: recentFilesModel |
| MenuItem { |
| text: model.fileName |
| } |
| onObjectAdded: recentFilesMenu.insertItem(index, object) |
| onObjectRemoved: recentFilesMenu.removeItem(object) |
| } |
| |
| MenuSeparator { |
| visible: recentFilesModel.count > 0 |
| } |
| |
| MenuItem { |
| text: "Clear menu" |
| enabled: recentFilesModel.count > 0 |
| onTriggered: recentFilesModel.clear() |
| } |
| } |
| \endqml |
| |
| Note that in this case, the \c index parameter passed to \l insertItem() is relative |
| to the position of the \l Instantiator in the menu, as opposed to absolute position |
| in the menu. |
| |
| \sa MenuItem, MenuSeparator |
| */ |
| |
| /*! |
| \qmlproperty bool Menu::visible |
| |
| Whether the menu should be visible as a submenu of another Menu, or as a menu on a MenuBar. |
| Its value defaults to \c true. |
| |
| \note This has nothing to do with the actual menu pop-up window being visible. Use |
| \l aboutToShow() and \l aboutToHide() if you need to know when the pop-up window will |
| be shown or hidden. |
| */ |
| |
| /*! |
| \qmlproperty enumeration Menu::type |
| |
| This property is read-only and constant, and its value is \l {QtQuick.Controls::MenuItem::}{type}. |
| */ |
| |
| /*! |
| \qmlproperty string Menu::title |
| |
| Title for the menu as a submenu or in a menubar. |
| |
| Mnemonics are supported by prefixing the shortcut letter with \&. |
| For instance, \c "\&File" will bind the \c Alt-F shortcut to the |
| \c "File" menu. Note that not all platforms support mnemonics. |
| |
| Its value defaults to an empty string. |
| */ |
| |
| /*! |
| \qmlproperty bool Menu::enabled |
| |
| Whether the menu is enabled, and responsive to user interaction as a submenu. |
| Its value defaults to \c true. |
| */ |
| |
| /*! |
| \qmlproperty url Menu::iconSource |
| |
| Sets the icon file or resource url for the menu icon as a submenu. |
| Defaults to an empty URL. |
| |
| \sa iconName |
| */ |
| |
| /*! |
| \qmlproperty string Menu::iconName |
| |
| Sets the icon name for the menu icon. This will pick the icon |
| with the given name from the current theme. Only works as a submenu. |
| |
| Its value defaults to an empty string. |
| |
| \sa iconSource |
| */ |
| |
| /*! |
| \qmlmethod void Menu::popup() |
| |
| Opens this menu under the mouse cursor. |
| It can block on some platforms, so test it accordingly. |
| */ |
| |
| /*! |
| \qmlmethod MenuItem Menu::addItem(string text) |
| |
| Adds a \a text item to the menu. Returns the newly created \l MenuItem. |
| |
| \sa insertItem() |
| */ |
| |
| /*! |
| \qmlmethod MenuItem Menu::insertItem(int before, string title) |
| |
| Creates and inserts an item with title \a title at the index \a before in the current menu. |
| Returns the newly created \l MenuItem. |
| |
| \sa addItem() |
| */ |
| |
| /*! |
| \qmlmethod void Menu::addSeparator() |
| |
| Adds a separator to the menu. |
| |
| \sa insertSeparator() |
| */ |
| |
| /*! |
| \qmlmethod void Menu::insertSeparator(int before) |
| |
| Creates and inserts a separator at the index \a before in the current menu. |
| |
| \sa addSeparator() |
| */ |
| |
| /*! |
| \qmlmethod Menu Menu::addMenu(string title) |
| Adds a submenu with a title \a title to the menu. Returns the newly created \l Menu. |
| |
| \sa insertMenu() |
| */ |
| |
| /*! |
| \qmlmethod MenuItem Menu::insertMenu(int before, string title) |
| |
| Creates and inserts a submenu with a title \a title at the index \a before in the current menu. |
| Returns the newly created \l Menu. |
| |
| \sa addMenu() |
| */ |
| |
| /*! |
| \qmlmethod void Menu::insertItem(int before, object item) |
| |
| Inserts the \a item at the index \a before in the current menu. |
| In this case, \c item can be either a \l MenuItem, a \l MenuSeparator, |
| or a \l Menu. |
| |
| \sa removeItem() |
| */ |
| |
| /*! |
| \qmlmethod void Menu::removeItem(item) |
| |
| Removes the \a item from the menu. |
| In this case, \a item can be either a \l MenuItem, a \l MenuSeparator, |
| or a \l Menu. |
| |
| \sa insertItem() |
| */ |
| |
| |
| /*! |
| \qmlsignal Menu::aboutToShow() |
| \since QtQuick.Controls 1.4 |
| |
| This signal is emitted just before the menu is shown to the user. |
| |
| \sa aboutToHide() |
| */ |
| |
| /*! |
| \qmlsignal Menu::aboutToHide() |
| \since QtQuick.Controls 1.4 |
| |
| This signal is emitted just before the menu is hidden from the user. |
| |
| \sa aboutToShow() |
| */ |
| |
| QQuickMenu1::QQuickMenu1(QObject *parent) |
| : QQuickMenuText1(parent, QQuickMenuItemType1::Menu), |
| m_platformMenu(0), |
| m_itemsCount(0), |
| m_selectedIndex(-1), |
| m_parentWindow(0), |
| m_minimumWidth(0), |
| m_popupWindow(0), |
| m_menuContentItem(0), |
| m_popupVisible(false), |
| m_containersCount(0), |
| m_xOffset(0), |
| m_yOffset(0), |
| m_triggerCount(0), |
| m_proxy(false) |
| { |
| connect(this, SIGNAL(__textChanged()), this, SIGNAL(titleChanged())); |
| |
| if (QGuiApplication::platformName() != QStringLiteral("xcb")) { // QTBUG-51372 |
| m_platformMenu = QGuiApplicationPrivate::platformTheme()->createPlatformMenu(); |
| if (m_platformMenu) { |
| connect(m_platformMenu, SIGNAL(aboutToShow()), this, SIGNAL(aboutToShow())); |
| connect(m_platformMenu, SIGNAL(aboutToHide()), this, SLOT(hideMenu())); |
| if (platformItem()) |
| platformItem()->setMenu(m_platformMenu); |
| } |
| } |
| if (const QFont *font = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::MenuItemFont)) |
| m_font = *const_cast<QFont*>(font); |
| } |
| |
| QQuickMenu1::~QQuickMenu1() |
| { |
| while (!m_menuItems.empty()) { |
| QQuickMenuBase1 *item = m_menuItems.takeFirst(); |
| if (item) |
| item->setParentMenu(0); |
| } |
| |
| if (platformItem()) |
| platformItem()->setMenu(0); |
| |
| delete m_platformMenu; |
| m_platformMenu = 0; |
| } |
| |
| void QQuickMenu1::syncParentMenuBar() |
| { |
| QQuickMenuBar1 *menubar = qobject_cast<QQuickMenuBar1 *>(parent()); |
| if (menubar && menubar->platformMenuBar()) |
| menubar->platformMenuBar()->syncMenu(m_platformMenu); |
| } |
| |
| void QQuickMenu1::setVisible(bool v) |
| { |
| QQuickMenuBase1::setVisible(v); |
| if (m_platformMenu) { |
| m_platformMenu->setVisible(v); |
| syncParentMenuBar(); |
| } |
| } |
| |
| void QQuickMenu1::setEnabled(bool e) |
| { |
| QQuickMenuText1::setEnabled(e); |
| if (m_platformMenu) { |
| m_platformMenu->setEnabled(e); |
| syncParentMenuBar(); |
| } |
| } |
| |
| void QQuickMenu1::updateText() |
| { |
| if (m_platformMenu) |
| m_platformMenu->setText(this->text()); |
| QQuickMenuText1::updateText(); |
| } |
| |
| void QQuickMenu1::setMinimumWidth(int w) |
| { |
| if (w == m_minimumWidth) |
| return; |
| |
| m_minimumWidth = w; |
| if (m_platformMenu) |
| m_platformMenu->setMinimumWidth(w); |
| |
| emit minimumWidthChanged(); |
| } |
| |
| void QQuickMenu1::setFont(const QFont &arg) |
| { |
| if (arg == m_font) |
| return; |
| |
| m_font = arg; |
| if (m_platformMenu) |
| m_platformMenu->setFont(arg); |
| } |
| |
| void QQuickMenu1::setXOffset(qreal x) |
| { |
| m_xOffset = x; |
| } |
| |
| void QQuickMenu1::setYOffset(qreal y) |
| { |
| m_yOffset = y; |
| } |
| |
| void QQuickMenu1::setSelectedIndex(int index) |
| { |
| if (m_selectedIndex == index) |
| return; |
| |
| m_selectedIndex = index; |
| emit __selectedIndexChanged(); |
| } |
| |
| void QQuickMenu1::updateSelectedIndex() |
| { |
| if (QQuickMenuItem1 *menuItem = qobject_cast<QQuickMenuItem1*>(sender())) { |
| int index = indexOfMenuItem(menuItem); |
| setSelectedIndex(index); |
| } |
| } |
| |
| QQuickMenuItems QQuickMenu1::menuItems() |
| { |
| return QQuickMenuItems(this, 0, &QQuickMenu1::append_menuItems, &QQuickMenu1::count_menuItems, |
| &QQuickMenu1::at_menuItems, &QQuickMenu1::clear_menuItems); |
| } |
| |
| QQuickWindow *QQuickMenu1::findParentWindow() |
| { |
| if (!m_parentWindow) { |
| QQuickItem *parentAsItem = qobject_cast<QQuickItem *>(parent()); |
| m_parentWindow = visualItem() ? visualItem()->window() : // Menu as menu item case |
| parentAsItem ? parentAsItem->window() : 0; //Menu as context menu/popup case |
| } |
| return m_parentWindow; |
| } |
| |
| void QQuickMenu1::popup() |
| { |
| QQuickWindow *quickWindow = findParentWindow(); |
| QPoint renderOffset; |
| QWindow *renderWindow = QQuickRenderControl::renderWindowFor(quickWindow, &renderOffset); |
| QWindow *parentWindow = renderWindow ? renderWindow : quickWindow; |
| QScreen *screen = parentWindow ? parentWindow->screen() : qGuiApp->primaryScreen(); |
| QPoint mousePos = QCursor::pos(screen); |
| |
| if (mousePos.x() == int(qInf())) { |
| // ### fixme: no mouse pos registered. Get pos from touch... |
| mousePos = screen->availableGeometry().center(); |
| } |
| |
| if (parentWindow) |
| mousePos = parentWindow->mapFromGlobal(mousePos); |
| |
| __popup(QRectF(mousePos.x() - renderOffset.x(), mousePos.y() - renderOffset.y(), 0, 0)); |
| } |
| |
| void QQuickMenu1::__popup(const QRectF &targetRect, int atItemIndex, MenuType menuType) |
| { |
| if (popupVisible()) { |
| hideMenu(); |
| // Mac and Windows would normally move the menu under the cursor, so we should not |
| // return here. However, very often we want to re-contextualize the menu, and this |
| // has to be done at the application level. |
| return; |
| } |
| |
| setPopupVisible(true); |
| |
| QQuickMenuBase1 *atItem = menuItemAtIndex(atItemIndex); |
| |
| QQuickWindow *quickWindow = findParentWindow(); |
| QPoint renderOffset; |
| QWindow *renderWindow = QQuickRenderControl::renderWindowFor(quickWindow, &renderOffset); |
| QWindow *parentWindow = renderWindow ? renderWindow : quickWindow; |
| // parentWindow may not be a QQuickWindow (happens when using QQuickWidget) |
| |
| if (m_platformMenu) { |
| if (m_windowConnection) |
| QObject::disconnect(m_windowConnection); |
| m_windowConnection = connect(parentWindow, &QWindow::visibleChanged, this, |
| &QQuickMenu1::platformMenuWindowVisibleChanged, Qt::UniqueConnection); |
| QRectF globalTargetRect = targetRect.translated(m_xOffset, m_yOffset); |
| if (visualItem()) { |
| if (qGuiApp->isRightToLeft()) { |
| qreal w = qMax(static_cast<qreal>(m_minimumWidth), m_menuContentItem->width()); |
| globalTargetRect.moveLeft(w - targetRect.x() - targetRect.width()); |
| } |
| globalTargetRect = visualItem()->mapRectToScene(globalTargetRect); |
| } |
| globalTargetRect.translate(renderOffset); |
| m_platformMenu->setMenuType(QPlatformMenu::MenuType(menuType)); |
| m_platformMenu->showPopup(parentWindow, |
| QHighDpi::toNativePixels(globalTargetRect.toRect(), parentWindow), |
| atItem ? atItem->platformItem() : 0); |
| } else { |
| m_popupWindow = new QQuickMenuPopupWindow1(this); |
| if (visualItem()) |
| m_popupWindow->setParentItem(visualItem()); |
| else |
| m_popupWindow->setParentWindow(parentWindow, quickWindow); |
| m_popupWindow->setPopupContentItem(m_menuContentItem); |
| m_popupWindow->setItemAt(atItem ? atItem->visualItem() : 0); |
| |
| connect(m_popupWindow, SIGNAL(visibleChanged(bool)), this, SLOT(windowVisibleChanged(bool))); |
| connect(m_popupWindow, SIGNAL(geometryChanged()), this, SIGNAL(__popupGeometryChanged())); |
| connect(m_popupWindow, SIGNAL(willBeDeletedLater()), this, SLOT(clearPopupWindow())); |
| |
| m_popupWindow->setPosition(targetRect.x() + m_xOffset + renderOffset.x(), |
| targetRect.y() + targetRect.height() + m_yOffset + renderOffset.y()); |
| emit aboutToShow(); |
| m_popupWindow->show(); |
| } |
| } |
| |
| void QQuickMenu1::setMenuContentItem(QQuickItem *item) |
| { |
| if (m_menuContentItem != item) { |
| m_menuContentItem = item; |
| emit menuContentItemChanged(); |
| } |
| } |
| |
| void QQuickMenu1::setPopupVisible(bool v) |
| { |
| if (m_popupVisible != v) { |
| m_popupVisible = v; |
| emit popupVisibleChanged(); |
| } |
| } |
| |
| QRect QQuickMenu1::popupGeometry() const |
| { |
| if (!m_popupWindow || !m_popupVisible) |
| return QRect(); |
| |
| return m_popupWindow->geometry(); |
| } |
| |
| void QQuickMenu1::prepareItemTrigger(QQuickMenuItem1 *) |
| { |
| m_triggerCount++; |
| __dismissMenu(); |
| } |
| |
| void QQuickMenu1::concludeItemTrigger(QQuickMenuItem1 *) |
| { |
| if (--m_triggerCount == 0) |
| destroyAllMenuPopups(); |
| } |
| |
| /*! |
| * \internal |
| * Close this menu's popup window. Emits aboutToHide and sets __popupVisible to false. |
| */ |
| void QQuickMenu1::hideMenu() |
| { |
| if (m_popupVisible) { |
| emit aboutToHide(); |
| setPopupVisible(false); |
| } |
| if (m_popupWindow && m_popupWindow->isVisible()) |
| m_popupWindow->hide(); |
| m_parentWindow = 0; |
| } |
| |
| QQuickMenuPopupWindow1 *QQuickMenu1::topMenuPopup() const |
| { |
| QQuickMenuPopupWindow1 *topMenuWindow = m_popupWindow; |
| while (topMenuWindow) { |
| QQuickMenuPopupWindow1 *pw = qobject_cast<QQuickMenuPopupWindow1 *>(topMenuWindow->transientParent()); |
| if (!pw) |
| return topMenuWindow; |
| topMenuWindow = pw; |
| } |
| |
| return 0; |
| } |
| |
| /*! |
| * \internal |
| * Dismiss all the menus this menu is attached to, bottom-up. |
| * In QQuickPopupWindow, before closing, dismissPopup() emits popupDismissed() |
| * which is connected to dismissPopup() on any child popup. |
| */ |
| void QQuickMenu1::__dismissMenu() |
| { |
| if (m_platformMenu) { |
| m_platformMenu->dismiss(); |
| } else if (QQuickMenuPopupWindow1 *topPopup = topMenuPopup()) { |
| topPopup->dismissPopup(); |
| } |
| } |
| |
| /*! |
| * \internal |
| * Called when the popup window visible property changes. |
| */ |
| void QQuickMenu1::windowVisibleChanged(bool v) |
| { |
| if (!v) { |
| if (m_popupWindow) { |
| QQuickMenuPopupWindow1 *parentMenuPopup = qobject_cast<QQuickMenuPopupWindow1 *>(m_popupWindow->transientParent()); |
| if (parentMenuPopup) { |
| parentMenuPopup->setMouseGrabEnabled(true); |
| parentMenuPopup->setKeyboardGrabEnabled(true); |
| } |
| } |
| if (m_popupVisible) |
| __closeAndDestroy(); |
| } |
| } |
| |
| void QQuickMenu1::platformMenuWindowVisibleChanged(bool visible) |
| { |
| if (!visible) { |
| if (m_windowConnection) { |
| QObject::disconnect(m_windowConnection); |
| m_windowConnection = QMetaObject::Connection(); |
| } |
| if (m_platformMenu) { |
| m_platformMenu->dismiss(); |
| } |
| } |
| } |
| |
| void QQuickMenu1::clearPopupWindow() |
| { |
| m_popupWindow = 0; |
| emit __menuPopupDestroyed(); |
| } |
| |
| void QQuickMenu1::destroyMenuPopup() |
| { |
| if (m_triggerCount > 0) |
| return; |
| if (m_popupWindow) |
| m_popupWindow->setToBeDeletedLater(); |
| } |
| |
| void QQuickMenu1::destroyAllMenuPopups() { |
| if (m_triggerCount > 0) |
| return; |
| QQuickMenuPopupWindow1 *popup = topMenuPopup(); |
| if (popup) |
| popup->setToBeDeletedLater(); |
| } |
| |
| QQuickMenuBar1 *QQuickMenu1::menuBar() |
| { |
| QObject *pi = parentMenuOrMenuBar(); |
| while (pi) { |
| if (QQuickMenuBar1 *menuBar = qobject_cast<QQuickMenuBar1*>(pi)) |
| return menuBar; |
| else if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1*>(pi)) |
| pi = menu->parentMenuOrMenuBar(); |
| else |
| return 0; |
| } |
| return 0; |
| } |
| |
| void QQuickMenu1::__closeAndDestroy() |
| { |
| hideMenu(); |
| destroyMenuPopup(); |
| } |
| |
| void QQuickMenu1::__dismissAndDestroy() |
| { |
| if (m_platformMenu) |
| return; |
| |
| __dismissMenu(); |
| destroyAllMenuPopups(); |
| } |
| |
| void QQuickMenu1::itemIndexToListIndex(int itemIndex, int *listIndex, int *containerIndex) const |
| { |
| *listIndex = -1; |
| QQuickMenuItemContainer1 *container = 0; |
| while (itemIndex >= 0 && ++*listIndex < m_menuItems.count()) |
| if ((container = qobject_cast<QQuickMenuItemContainer1 *>(m_menuItems[*listIndex]))) |
| itemIndex -= container->items().count(); |
| else |
| --itemIndex; |
| |
| if (container) |
| *containerIndex = container->items().count() + itemIndex; |
| else |
| *containerIndex = -1; |
| } |
| |
| int QQuickMenu1::itemIndexForListIndex(int listIndex) const |
| { |
| int index = 0; |
| int i = 0; |
| while (i < listIndex && i < m_menuItems.count()) |
| if (QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(m_menuItems[i++])) |
| index += container->items().count(); |
| else |
| ++index; |
| |
| return index; |
| } |
| |
| QQuickMenuBase1 *QQuickMenu1::nextMenuItem(QQuickMenu1::MenuItemIterator *it) const |
| { |
| if (it->containerIndex != -1) { |
| QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(m_menuItems[it->index]); |
| if (++it->containerIndex < container->items().count()) |
| return container->items()[it->containerIndex]; |
| } |
| |
| if (++it->index < m_menuItems.count()) { |
| if (QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(m_menuItems[it->index])) { |
| it->containerIndex = 0; |
| return container->items()[0]; |
| } else { |
| it->containerIndex = -1; |
| return m_menuItems[it->index]; |
| } |
| } |
| |
| return 0; |
| } |
| |
| QQuickMenuBase1 *QQuickMenu1::menuItemAtIndex(int index) const |
| { |
| if (0 <= index && index < m_itemsCount) { |
| if (!m_containersCount) { |
| return m_menuItems[index]; |
| } else if (m_containersCount == 1 && m_menuItems.count() == 1) { |
| QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(m_menuItems[0]); |
| return container->items()[index]; |
| } else { |
| int containerIndex; |
| int i; |
| itemIndexToListIndex(index, &i, &containerIndex); |
| if (containerIndex != -1) { |
| QQuickMenuItemContainer1 *container = qobject_cast<QQuickMenuItemContainer1 *>(m_menuItems[i]); |
| return container->items()[containerIndex]; |
| } else { |
| return m_menuItems[i]; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| bool QQuickMenu1::contains(QQuickMenuBase1 *item) |
| { |
| if (item->container()) |
| return item->container()->items().contains(item); |
| |
| return m_menuItems.contains(item); |
| } |
| |
| int QQuickMenu1::indexOfMenuItem(QQuickMenuBase1 *item) const |
| { |
| if (!item) |
| return -1; |
| if (item->container()) { |
| int containerIndex = m_menuItems.indexOf(item->container()); |
| if (containerIndex == -1) |
| return -1; |
| int index = item->container()->items().indexOf(item); |
| return index == -1 ? -1 : itemIndexForListIndex(containerIndex) + index; |
| } else { |
| int index = m_menuItems.indexOf(item); |
| return index == -1 ? -1 : itemIndexForListIndex(index); |
| } |
| } |
| |
| QQuickMenuItem1 *QQuickMenu1::addItem(const QString &title) |
| { |
| return insertItem(m_itemsCount, title); |
| } |
| |
| QQuickMenuItem1 *QQuickMenu1::insertItem(int index, const QString &title) |
| { |
| QQuickMenuItem1 *item = new QQuickMenuItem1(this); |
| item->setText(title); |
| insertItem(index, item); |
| return item; |
| } |
| |
| void QQuickMenu1::addSeparator() |
| { |
| insertSeparator(m_itemsCount); |
| } |
| |
| void QQuickMenu1::insertSeparator(int index) |
| { |
| QQuickMenuSeparator1 *item = new QQuickMenuSeparator1(this); |
| insertItem(index, item); |
| } |
| |
| void QQuickMenu1::insertItem(int index, QQuickMenuBase1 *menuItem) |
| { |
| if (!menuItem) |
| return; |
| int itemIndex; |
| if (m_containersCount) { |
| QQuickMenuItemContainer1 *container = menuItem->parent() != this ? m_containers[menuItem->parent()] : 0; |
| if (container) { |
| container->insertItem(index, menuItem); |
| itemIndex = itemIndexForListIndex(m_menuItems.indexOf(container)) + index; |
| } else { |
| itemIndex = itemIndexForListIndex(index); |
| m_menuItems.insert(itemIndex, menuItem); |
| } |
| } else { |
| itemIndex = index; |
| m_menuItems.insert(index, menuItem); |
| } |
| |
| setupMenuItem(menuItem, itemIndex); |
| emit itemsChanged(); |
| } |
| |
| void QQuickMenu1::removeItem(QQuickMenuBase1 *menuItem) |
| { |
| // Removes the item, but if it's a container, the container is kept |
| if (menuItem) { |
| unparentItem(menuItem); |
| emit itemsChanged(); |
| } |
| } |
| |
| void QQuickMenu1::clear() |
| { |
| if (m_itemsCount > 0) { |
| while (m_itemsCount > 0) |
| unparentItem(menuItemAtIndex(0)); |
| |
| // We can delete the containers now, as there cannot be any further items in them. |
| qDeleteAll(m_containers); |
| m_containers.clear(); |
| m_containersCount = 0; |
| |
| // The containers are also kept in m_menuItems, so we have to clear explicitly. |
| m_menuItems.clear(); |
| |
| emit itemsChanged(); |
| } |
| } |
| |
| void QQuickMenu1::unparentItem(QQuickMenuBase1 *menuItem) |
| { |
| menuItem->setParentMenu(nullptr); |
| QQuickMenuItemContainer1 *container = (menuItem->parent() != this) |
| ? m_containers[menuItem->parent()] : nullptr; |
| if (container) |
| container->removeItem(menuItem); |
| else |
| m_menuItems.removeOne(menuItem); |
| --m_itemsCount; |
| } |
| |
| void QQuickMenu1::setupMenuItem(QQuickMenuBase1 *item, int platformIndex) |
| { |
| item->setParentMenu(this); |
| if (m_platformMenu) { |
| QPlatformMenuItem *before = 0; |
| if (platformIndex != -1) |
| before = m_platformMenu->menuItemAt(platformIndex); |
| m_platformMenu->insertMenuItem(item->platformItem(), before); |
| } |
| ++m_itemsCount; |
| } |
| |
| void QQuickMenu1::append_menuItems(QQuickMenuItems *list, QObject *o) |
| { |
| if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1 *>(list->object)) { |
| if (QQuickMenuBase1 *menuItem = qobject_cast<QQuickMenuBase1 *>(o)) { |
| menu->m_menuItems.append(menuItem); |
| menu->setupMenuItem(menuItem); |
| } else { |
| QQuickMenuItemContainer1 *menuItemContainer = new QQuickMenuItemContainer1(menu); |
| menu->m_menuItems.append(menuItemContainer); |
| menu->m_containers.insert(o, menuItemContainer); |
| menuItemContainer->setParentMenu(menu); |
| ++menu->m_containersCount; |
| const auto children = o->children(); |
| for (QObject *child : children) { |
| if (QQuickMenuBase1 *item = qobject_cast<QQuickMenuBase1 *>(child)) { |
| menuItemContainer->insertItem(-1, item); |
| menu->setupMenuItem(item); |
| } |
| } |
| } |
| } |
| } |
| |
| int QQuickMenu1::count_menuItems(QQuickMenuItems *list) |
| { |
| if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1 *>(list->object)) |
| return menu->m_itemsCount; |
| |
| return 0; |
| } |
| |
| QObject *QQuickMenu1::at_menuItems(QQuickMenuItems *list, int index) |
| { |
| if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1 *>(list->object)) |
| return menu->menuItemAtIndex(index); |
| |
| return 0; |
| } |
| |
| void QQuickMenu1::clear_menuItems(QQuickMenuItems *list) |
| { |
| if (QQuickMenu1 *menu = qobject_cast<QQuickMenu1 *>(list->object)) { |
| // There may be stray containers that don't appear in m_menuItems. This is because we may |
| // remove a container with removeItem(), which will only remove it from m_menuItems. |
| // Therefore, make sure that all containers are removed from m_menuItems first. |
| for (QQuickMenuItemContainer1 *container : menu->m_containers) |
| menu->m_menuItems.removeOne(container); |
| |
| // Delete or unparent the items first. They may have references to the containers. |
| // QTBUG-48927: a proxy menu (ApplicationWindowStyle.qml) must not |
| // delete its items, because they are owned by the menubar |
| // We still do own the containers, though. We create them on append_menuItems, after all. |
| while (!menu->m_menuItems.empty()) { |
| if (menu->m_proxy) |
| menu->unparentItem(menu->m_menuItems[0]); |
| else |
| delete menu->m_menuItems.takeFirst(); |
| } |
| menu->m_menuItems.clear(); |
| |
| qDeleteAll(menu->m_containers); |
| menu->m_containers.clear(); |
| menu->m_containersCount = 0; |
| |
| menu->m_itemsCount = 0; |
| } |
| } |
| |
| QT_END_NAMESPACE |