| /**************************************************************************** |
| ** |
| ** 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 "qquickmenupopupwindow_p.h" |
| |
| #include <qguiapplication.h> |
| #include <qpa/qwindowsysteminterface.h> |
| #include <qquickitem.h> |
| #include <QtGui/QScreen> |
| #include <QtQuick/QQuickRenderControl> |
| #include "qquickmenu_p.h" |
| #include "qquickmenubar_p.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| QQuickMenuPopupWindow1::QQuickMenuPopupWindow1(QQuickMenu1 *menu) : |
| m_itemAt(0), |
| m_logicalParentWindow(0), |
| m_menu(menu) |
| { |
| } |
| |
| void QQuickMenuPopupWindow1::setParentItem(QQuickItem *item) |
| { |
| QQuickPopupWindow1::setParentItem(item); |
| if (item) { |
| QWindow *parentWindow = item->window(); |
| QWindow *renderWindow = QQuickRenderControl::renderWindowFor(static_cast<QQuickWindow *>(parentWindow)); |
| setParentWindow(renderWindow ? renderWindow : parentWindow, item->window()); |
| } |
| } |
| |
| void QQuickMenuPopupWindow1::setItemAt(QQuickItem *menuItem) |
| { |
| if (m_itemAt) { |
| disconnect(m_itemAt, SIGNAL(xChanged()), this, SLOT(updatePosition())); |
| disconnect(m_itemAt, SIGNAL(yChanged()), this, SLOT(updatePosition())); |
| } |
| |
| m_itemAt = menuItem; |
| if (menuItem) { |
| m_oldItemPos = menuItem->position().toPoint(); |
| connect(menuItem, SIGNAL(xChanged()), this, SLOT(updatePosition())); |
| connect(menuItem, SIGNAL(yChanged()), this, SLOT(updatePosition())); |
| } |
| } |
| |
| void QQuickMenuPopupWindow1::setParentWindow(QWindow *effectiveParentWindow, QQuickWindow *parentWindow) |
| { |
| while (effectiveParentWindow && effectiveParentWindow->parent()) |
| effectiveParentWindow = effectiveParentWindow->parent(); |
| if (transientParent() != effectiveParentWindow) |
| setTransientParent(effectiveParentWindow); |
| m_logicalParentWindow = parentWindow; |
| if (parentWindow) { |
| if (QQuickMenuPopupWindow1 *pw = qobject_cast<QQuickMenuPopupWindow1 *>(parentWindow)) { |
| connect(pw, SIGNAL(popupDismissed()), this, SLOT(dismissPopup())); |
| connect(pw, SIGNAL(willBeDeletedLater()), this, SLOT(setToBeDeletedLater())); |
| } else { |
| connect(parentWindow, SIGNAL(destroyed()), this, SLOT(deleteLater())); |
| } |
| } |
| } |
| |
| void QQuickMenuPopupWindow1::setToBeDeletedLater() |
| { |
| deleteLater(); |
| emit willBeDeletedLater(); |
| } |
| |
| void QQuickMenuPopupWindow1::setGeometry(int posx, int posy, int w, int h) |
| { |
| QWindow *pw = transientParent(); |
| if (!pw && parentItem()) |
| pw = parentItem()->window(); |
| if (!pw) |
| pw = this; |
| QRect g = pw->screen()->geometry(); |
| |
| if (posx + w > g.right()) { |
| if (qobject_cast<QQuickMenuPopupWindow1 *>(transientParent())) { |
| // reposition submenu window on the parent menu's left side |
| int submenuOverlap = pw->x() + pw->width() - posx; |
| posx -= pw->width() + w - 2 * submenuOverlap; |
| } else { |
| posx = g.right() - w; |
| } |
| } else { |
| posx = qMax(posx, g.left()); |
| } |
| |
| posy = qBound(g.top(), posy, g.bottom() - h); |
| |
| QQuickWindow::setGeometry(posx, posy, w, h); |
| emit geometryChanged(); |
| } |
| |
| void QQuickMenuPopupWindow1::updateSize() |
| { |
| const QQuickItem *contentItem = popupContentItem(); |
| if (!contentItem) |
| return; |
| |
| qreal x = m_initialPos.x(); |
| qreal y = m_initialPos.y(); |
| if (qGuiApp->layoutDirection() == Qt::RightToLeft) |
| x -= contentItem->width(); |
| setGeometry(x, y, contentItem->width(), contentItem->height()); |
| } |
| |
| void QQuickMenuPopupWindow1::updatePosition() |
| { |
| QPointF newPos = position() + m_oldItemPos - m_itemAt->position(); |
| m_initialPos += m_oldItemPos - m_itemAt->position(); |
| setGeometry(newPos.x(), newPos.y(), width(), height()); |
| } |
| |
| void QQuickMenuPopupWindow1::focusInEvent(QFocusEvent *e) |
| { |
| QQuickWindow::focusInEvent(e); |
| if (m_menu && m_menu->menuContentItem()) |
| m_menu->menuContentItem()->forceActiveFocus(); |
| } |
| |
| void QQuickMenuPopupWindow1::exposeEvent(QExposeEvent *e) |
| { |
| // the popup will reposition at the last moment, so its |
| // initial position must be captured for updateSize(). |
| m_initialPos = position(); |
| if (m_logicalParentWindow && m_logicalParentWindow->parent()) { |
| // This must be a QQuickWindow embedded via createWindowContainer. |
| m_initialPos += m_logicalParentWindow->geometry().topLeft(); |
| } |
| QQuickPopupWindow1::exposeEvent(e); |
| |
| if (isExposed()) |
| updateSize(); |
| } |
| |
| QQuickMenu1 *QQuickMenuPopupWindow1::menu() const |
| { |
| return m_menu; |
| } |
| |
| bool QQuickMenuPopupWindow1::shouldForwardEventAfterDismiss(QMouseEvent *e) const |
| { |
| // If the event falls inside this item the event should not be forwarded. |
| // For example for comboboxes or top menus of the menubar |
| QQuickMenuBar1 *mb = m_menu ? m_menu->menuBar() : nullptr; |
| QQuickItem *item = mb && !mb->isNative() ? mb->contentItem() : menu()->visualItem(); |
| QWindow *window = transientParent(); |
| if (item && window && item->window() == window) { |
| QPointF pos = window->mapFromGlobal(mapToGlobal(e->pos())); |
| pos = item->mapFromScene(pos); |
| if (item->contains(pos)) |
| return false; |
| } |
| |
| #ifdef Q_OS_OSX |
| if (e->button() == Qt::RightButton) |
| return true; |
| #endif |
| |
| #ifdef Q_OS_WIN |
| return true; |
| #endif |
| |
| return false; |
| } |
| |
| QT_END_NAMESPACE |