diff options
-rw-r--r-- | src/quicktemplates/qquickmenu.cpp | 3 | ||||
-rw-r--r-- | src/quicktemplates/qquickpopup_p.h | 1 | ||||
-rw-r--r-- | src/quicktemplates/qquickpopupwindow.cpp | 110 |
3 files changed, 104 insertions, 10 deletions
diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp index d5f1a72f5b..3ab99ecd87 100644 --- a/src/quicktemplates/qquickmenu.cpp +++ b/src/quicktemplates/qquickmenu.cpp @@ -952,6 +952,9 @@ bool QQuickMenuPrivate::blockInput(QQuickItem *item, const QPointF &point) const */ bool QQuickMenuPrivate::handleReleaseWithoutGrab(const QEventPoint &eventPoint) { + if (!contains(eventPoint.scenePosition())) + return false; + QQuickMenuItem *menuItem = nullptr; // Usually, hover events have occurred, and currentIndex is set. // If not, use eventPoint.position() for picking. diff --git a/src/quicktemplates/qquickpopup_p.h b/src/quicktemplates/qquickpopup_p.h index 56287d4a03..1ca4a18621 100644 --- a/src/quicktemplates/qquickpopup_p.h +++ b/src/quicktemplates/qquickpopup_p.h @@ -451,6 +451,7 @@ private: Q_DISABLE_COPY(QQuickPopup) Q_DECLARE_PRIVATE(QQuickPopup) + friend class QQuickPopupWindow; friend class QQuickPopupItem; friend class QQuickOverlay; friend class QQuickOverlayPrivate; diff --git a/src/quicktemplates/qquickpopupwindow.cpp b/src/quicktemplates/qquickpopupwindow.cpp index 03d7472bf1..d65be0ebb4 100644 --- a/src/quicktemplates/qquickpopupwindow.cpp +++ b/src/quicktemplates/qquickpopupwindow.cpp @@ -5,9 +5,13 @@ #include "qquickcombobox_p.h" #include "qquickpopup_p.h" #include "qquickpopup_p_p.h" +#include "qquickmenu_p_p.h" +#include "qquickmenubar_p_p.h" #include "qquickpopupitem_p_p.h" +#include <QtGui/private/qguiapplication_p.h> #include <QtCore/qloggingcategory.h> +#include <QtGui/private/qeventpoint_p.h> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquickwindowmodule_p.h> #include <QtQuick/private/qquickwindowmodule_p_p.h> @@ -24,6 +28,8 @@ class QQuickPopupWindowPrivate : public QQuickWindowQmlImplPrivate public: QPointer<QQuickItem> m_popupItem; QPointer<QQuickPopup> m_popup; + + void forwardEventToParentMenuOrMenuBar(QEvent *event); }; QQuickPopupWindow::QQuickPopupWindow(QQuickPopup *popup, QWindow *parent) @@ -99,19 +105,103 @@ void QQuickPopupWindow::resizeEvent(QResizeEvent *e) d->m_popupItem->setHeight(e->size().height()); } +/*! \internal + We want to handle menus specially, compared to other popups. For menus, we + want all open parent menus and sub menus that belong together to almost + act as a single popup WRT hover event delivery. This will allow the user to + hover and highlight MenuItems inside all of them, not just the leaf menu. + This function will therefore find the menu, or menu bar, under the event's + position, and forward the event to it. But note that this forwarding will + happen in parallel with normal event delivery (we don't eat the events), as + we don't want to break the delivery to e.g grabbers. + */ +void QQuickPopupWindowPrivate::forwardEventToParentMenuOrMenuBar(QEvent *event) +{ + Q_Q(QQuickPopupWindow); + + if (!event->isPointerEvent()) + return; + auto menu = qobject_cast<QQuickMenu *>(q->popup()); + if (!menu) + return; + + auto *pe = static_cast<QPointerEvent *>(event); + const QPointF globalPos = pe->points().first().globalPosition(); + + // If there is a Menu or a MenuBar under the mouse, resolve its window. + QQuickPopupWindow *targetPopupWindow = nullptr; + QQuickWindow *targetWindow = nullptr; + + QObject *menuParent = menu; + while (menuParent) { + if (auto parentMenu = qobject_cast<QQuickMenu *>(menuParent)) { + QQuickPopupWindow *popupWindow = QQuickMenuPrivate::get(parentMenu)->popupWindow; + auto popup_d = QQuickPopupPrivate::get(popupWindow->popup()); + QPointF scenePos = popupWindow->contentItem()->mapFromGlobal(globalPos); + if (popup_d->contains(scenePos)) { + targetPopupWindow = popupWindow; + targetWindow = popupWindow; + break; + } + } else if (auto menuBar = qobject_cast<QQuickMenuBar *>(menuParent)) { + const QPointF menuBarPos = menuBar->mapFromGlobal(globalPos); + if (menuBar->contains(menuBarPos)) + targetWindow = menuBar->window(); + break; + } + + menuParent = menuParent->parent(); + } + + if (!targetPopupWindow) { + if (pe->isBeginEvent()) { + // A QQuickPopupWindow can be bigger than the Popup itself, to make room + // for a drop-shadow. Close all popups if the user clicks either on the + // shadow or outside the window. + // QGuiApplicationPrivate::closeAllPopups(); // TODO as soon as dependency update is done + return; + } + } + + if (!targetWindow) + return; + + if (pe->isUpdateEvent()){ + // Forward move events to the target window + const auto scenePos = pe->point(0).scenePosition(); + const auto translatedScenePos = targetWindow->mapFromGlobal(globalPos); + QMutableEventPoint::setScenePosition(pe->point(0), translatedScenePos); + auto *grabber = pe->exclusiveGrabber(pe->point(0)); + + if (grabber) { + // Temporarily disable the grabber, to stop the delivery agent inside + // targetWindow from forwarding the event to an item outside the menu + // or menubar. This is especially important to support a press on e.g + // a MenuBarItem, followed by a drag-and-release on top of a MenuItem. + pe->setExclusiveGrabber(pe->point(0), nullptr); + } + + qCDebug(lcPopupWindow) << "forwarding" << pe << "to popup menu:" << targetWindow; + QQuickWindowPrivate::get(targetWindow)->deliveryAgent->event(pe); + + // Restore the event before we return + QMutableEventPoint::setScenePosition(pe->point(0), scenePos); + if (grabber) + pe->setExclusiveGrabber(pe->point(0), grabber); + } else if (pe->isEndEvent()) { + // To support opening a Menu on press (e.g on a MenuBarItem), followed by + // a drag and release on a MenuItem inside the Menu, we ask the Menu to + // perform a click on the active MenuItem, if any. + if (targetPopupWindow) + QQuickPopupPrivate::get(targetPopupWindow->popup())->handleReleaseWithoutGrab(pe->point(0)); + } +} + bool QQuickPopupWindow::event(QEvent *e) { Q_D(QQuickPopupWindow); - if (e->isPointerEvent()) { - auto *pe = static_cast<QPointerEvent *>(e); - const auto &eventPoint = pe->points().first(); - const auto *grabber = qmlobject_cast<QQuickItem *>(pe->exclusiveGrabber(eventPoint)); - if (pe->isEndEvent() && (!grabber || grabber->window() != this)) - QQuickPopupPrivate::get(d->m_popup)->handleReleaseWithoutGrab(eventPoint); - // handleReleaseWithoutGrab() returns true if the popup handled eventPoint. - // Nevertheless, the component that opened the menu needs to see the release too, - // to avoid getting stuck in "pressed" state; so we don't return early. - } + d->forwardEventToParentMenuOrMenuBar(e); + return QQuickWindowQmlImpl::event(e); } |