diff options
author | Paul Olav Tvete <paul.tvete@theqtcompany.com> | 2016-05-25 16:28:52 +0200 |
---|---|---|
committer | Paul Olav Tvete <paul.tvete@theqtcompany.com> | 2016-05-31 08:03:10 +0000 |
commit | fd9eefba8089ff9cd0bfe986dc527bbdb0630b02 (patch) | |
tree | 8da1ee5f02d49b641bf1be2eaf73537e0ea5e5bf /src/compositor | |
parent | 1f1db8e85eb088f7aaf42c2e15b31e3bb84bfb0d (diff) |
Fix for popups
Introduce a singleton event filter that keeps working if
the popup is deleted. Move the event filter class to
qwaylandquickshellsurfaceitem, so it can be used by the
XDG shell.
Change-Id: I58998a7e62697bf6f54536d2c2e2732ceb4d970e
Reviewed-by: Johan Helsing <johan.helsing@qt.io>
Reviewed-by: Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
Diffstat (limited to 'src/compositor')
4 files changed, 104 insertions, 66 deletions
diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp index 4b087183f..bfad242cc 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp @@ -38,6 +38,7 @@ #include "qwaylandquickshellsurfaceitem_p.h" #include <QtWaylandCompositor/QWaylandShellSurface> +#include <QGuiApplication> QT_BEGIN_NAMESPACE @@ -148,4 +149,84 @@ void QWaylandQuickShellSurfaceItem::mouseReleaseEvent(QMouseEvent *event) QWaylandQuickItem::mouseReleaseEvent(event); } +/*! +\class QWaylandQuickShellEventFilter +\brief QWaylandQuickShellEventFilter implements a Wayland popup grab +\internal +*/ + +void QWaylandQuickShellEventFilter::startFilter(QWaylandClient *client, CallbackFunction closePopups) +{ + if (!self) + self = new QWaylandQuickShellEventFilter(qGuiApp); + if (!self->eventFilterInstalled) { + qGuiApp->installEventFilter(self); + self->eventFilterInstalled = true; + self->client = client; + self->closePopups = closePopups; + } +} + +void QWaylandQuickShellEventFilter::cancelFilter() +{ + if (!self) + return; + if (self->eventFilterInstalled && !self->waitForRelease) + self->stopFilter(); +} + +void QWaylandQuickShellEventFilter::stopFilter() +{ + if (eventFilterInstalled) { + qGuiApp->removeEventFilter(this); + eventFilterInstalled = false; + } +} +QWaylandQuickShellEventFilter *QWaylandQuickShellEventFilter::self = nullptr; + +QWaylandQuickShellEventFilter::QWaylandQuickShellEventFilter(QObject *parent) + : QObject(parent), eventFilterInstalled(false), waitForRelease(false), closePopups(nullptr) +{ +} + +bool QWaylandQuickShellEventFilter::eventFilter(QObject *receiver, QEvent *e) +{ + if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease) { + QQuickItem *item = qobject_cast<QQuickItem*>(receiver); + if (!item) + return false; + + QMouseEvent *event = static_cast<QMouseEvent*>(e); + QWaylandQuickShellSurfaceItem *shellSurfaceItem = qobject_cast<QWaylandQuickShellSurfaceItem*>(item); + bool press = event->type() == QEvent::MouseButtonPress; + bool finalRelease = (event->type() == QEvent::MouseButtonRelease) && (event->buttons() == Qt::NoButton); + bool popupClient = shellSurfaceItem && shellSurfaceItem->surface()->client() == client; + + if (waitForRelease) { + // We are eating events until all mouse buttons are released + if (finalRelease) { + waitForRelease = false; + stopFilter(); + } + return true; + } + + if (press && !popupClient) { + // The user clicked outside the active popup's client. The popups should + // be closed, but the event filter will stay to catch the release- + // event before removing itself. + waitForRelease = true; + closePopups(); + return true; + } else if (press) { + // There is a surface belonging to this client at this coordinate, so we can + // remove the event filter and let the normal event handler handle + // this event. + stopFilter(); + } + } + + return false; +} + QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h index b2d4780ab..c2f46917e 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h @@ -78,6 +78,26 @@ public: virtual bool mouseReleaseEvent(QMouseEvent *) { return false; } }; +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellEventFilter : public QObject +{ + Q_OBJECT +public: + typedef void (*CallbackFunction)(void); + static void startFilter(QWaylandClient *client, CallbackFunction closePopupCallback); + static void cancelFilter(); + +private: + void stopFilter(); + + QWaylandQuickShellEventFilter(QObject *parent = nullptr); + bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; + bool eventFilterInstalled; + bool waitForRelease; + QPointer<QWaylandClient> client; + CallbackFunction closePopups; + static QWaylandQuickShellEventFilter *self; +}; + QT_END_NAMESPACE #endif // QWAYLANDQUICKSHELLSURFACEITEM_P_H diff --git a/src/compositor/extensions/qwaylandwlshellintegration.cpp b/src/compositor/extensions/qwaylandwlshellintegration.cpp index ef4e1efc6..5c0fe7c9d 100644 --- a/src/compositor/extensions/qwaylandwlshellintegration.cpp +++ b/src/compositor/extensions/qwaylandwlshellintegration.cpp @@ -40,7 +40,6 @@ #include <QtWaylandCompositor/QWaylandWlShellSurface> #include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem> #include <QtWaylandCompositor/QWaylandInputDevice> -#include <QGuiApplication> QT_BEGIN_NAMESPACE @@ -158,8 +157,6 @@ bool WlShellIntegration::mouseReleaseEvent(QMouseEvent *event) } QVector<QWaylandWlShellSurface*> WlShellIntegration::popupShellSurfaces; -bool WlShellIntegration::eventFilterInstalled = false; -bool WlShellIntegration::waitForRelease = false; void WlShellIntegration::closePopups() { @@ -171,52 +168,11 @@ void WlShellIntegration::closePopups() } } -bool WlShellIntegration::eventFilter(QObject *receiver, QEvent *e) -{ - if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease) { - QQuickItem *item = qobject_cast<QQuickItem*>(receiver); - if (!item) - return false; - - QMouseEvent *event = static_cast<QMouseEvent*>(e); - QWaylandQuickShellSurfaceItem *shellSurfaceItem = qobject_cast<QWaylandQuickShellSurfaceItem*>(item); - bool press = event->type() == QEvent::MouseButtonPress; - bool finalRelease = (event->type() == QEvent::MouseButtonRelease) && (event->buttons() == Qt::NoButton); - bool popupClient = shellSurfaceItem && shellSurfaceItem->surface()->client() == m_shellSurface->surface()->client(); - - if (waitForRelease) { - // We are eating events until all mouse buttons are released - if (finalRelease) { - waitForRelease = false; - setFilterEnabled(false); - } - return true; - } - - if (press && !popupClient) { - // The user clicked outside the active popup's client. The popups should - // be closed, but the event filter will stay to catch the release- - // event before removing itself. - waitForRelease = true; - closePopups(); - return true; - } else if (press) { - // There is a surface belonging to this client at this coordinate, so we can - // remove the event filter and let the normal event handler handle - // this event. - setFilterEnabled(false); - } - } - - return false; -} - void WlShellIntegration::setIsPopup(bool popup) { isPopup = popup; if (popup) { - if (!eventFilterInstalled) - setFilterEnabled(true); + QWaylandQuickShellEventFilter::startFilter(m_shellSurface->surface()->client(), &closePopups); if (!popupShellSurfaces.contains(m_shellSurface)) { popupShellSurfaces.append(m_shellSurface); @@ -229,23 +185,9 @@ void WlShellIntegration::setIsPopup(bool popup) QObject::disconnect(m_shellSurface->surface(), &QWaylandSurface::mappedChanged, this, &WlShellIntegration::handleSurfaceUnmapped); } - if (!waitForRelease && eventFilterInstalled && popupShellSurfaces.isEmpty()) - setFilterEnabled(false); - } -} - -void WlShellIntegration::setFilterEnabled(bool enabled) -{ - static QPointer<QObject> filter; - - if (enabled && filter.isNull()) { - qGuiApp->installEventFilter(this); - filter = this; - } else if (!enabled && !filter.isNull()){ - qGuiApp->removeEventFilter(filter); - filter = nullptr; + if (popupShellSurfaces.isEmpty()) + QWaylandQuickShellEventFilter::cancelFilter(); } - eventFilterInstalled = enabled; } } diff --git a/src/compositor/extensions/qwaylandwlshellintegration_p.h b/src/compositor/extensions/qwaylandwlshellintegration_p.h index 486077200..07bbc63cb 100644 --- a/src/compositor/extensions/qwaylandwlshellintegration_p.h +++ b/src/compositor/extensions/qwaylandwlshellintegration_p.h @@ -79,10 +79,7 @@ private: Move }; - bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; - void setIsPopup(bool popup); - void setFilterEnabled(bool enabled); static void closePopups(); QWaylandQuickShellSurfaceItem *m_item; @@ -102,8 +99,6 @@ private: } resizeState; static QVector<QWaylandWlShellSurface*> popupShellSurfaces; - static bool eventFilterInstalled; - static bool waitForRelease; bool isPopup; }; |