aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quicktemplates/qquickmenu.cpp3
-rw-r--r--src/quicktemplates/qquickpopup_p.h1
-rw-r--r--src/quicktemplates/qquickpopupwindow.cpp110
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);
}