diff options
4 files changed, 164 insertions, 0 deletions
diff --git a/src/compositor/extensions/qwaylandquickwlshellsurfaceitem.cpp b/src/compositor/extensions/qwaylandquickwlshellsurfaceitem.cpp index 21b5c75a..393a054c 100644 --- a/src/compositor/extensions/qwaylandquickwlshellsurfaceitem.cpp +++ b/src/compositor/extensions/qwaylandquickwlshellsurfaceitem.cpp @@ -39,6 +39,7 @@ #include <QtWaylandCompositor/QWaylandCompositor> #include <QtWaylandCompositor/QWaylandInputDevice> +#include <QGuiApplication> QT_BEGIN_NAMESPACE @@ -108,11 +109,15 @@ void QWaylandQuickWlShellSurfaceItem::setShellSurface(QWaylandWlShellSurface *sh if (d->shellSurface) { disconnect(d->shellSurface, &QWaylandWlShellSurface::startMove, this, &QWaylandQuickWlShellSurfaceItem::handleStartMove); disconnect(d->shellSurface, &QWaylandWlShellSurface::startResize, this, &QWaylandQuickWlShellSurfaceItem::handleStartResize); + disconnect(d->shellSurface, &QWaylandWlShellSurface::setPopup, this, &QWaylandQuickWlShellSurfaceItem::handleSetPopup); + disconnect(d->shellSurface, &QWaylandWlShellSurface::destroyed, this, &QWaylandQuickWlShellSurfaceItem::handleShellSurfaceDestroyed); } d->shellSurface = shellSurface; if (d->shellSurface) { connect(d->shellSurface, &QWaylandWlShellSurface::startMove, this, &QWaylandQuickWlShellSurfaceItem::handleStartMove); connect(d->shellSurface, &QWaylandWlShellSurface::startResize, this, &QWaylandQuickWlShellSurfaceItem::handleStartResize); + connect(d->shellSurface, &QWaylandWlShellSurface::setPopup, this, &QWaylandQuickWlShellSurfaceItem::handleSetPopup); + connect(d->shellSurface, &QWaylandWlShellSurface::destroyed, this, &QWaylandQuickWlShellSurfaceItem::handleShellSurfaceDestroyed); } setSurface(shellSurface ? shellSurface->surface() : nullptr); emit shellSurfaceChanged(); @@ -166,6 +171,48 @@ void QWaylandQuickWlShellSurfaceItem::handleStartResize(QWaylandInputDevice *inp /*! * \internal */ +void QWaylandQuickWlShellSurfaceItem::handleSetPopup(QWaylandInputDevice *inputDevice, QWaylandSurface *parent, const QPoint &relativeToParent) +{ + Q_UNUSED(inputDevice); + Q_D(QWaylandQuickWlShellSurfaceItem); + + QWaylandQuickWlShellSurfaceItem* parentItem = qobject_cast<QWaylandQuickWlShellSurfaceItem*>(parent->views().first()->renderObject()); + if (parentItem) { + // Clear all the transforms for this ShellSurfaceItem. They are not + // applicable when the item becomes a child to a surface that has its + // own transforms. Otherwise the transforms would be applied twice. + QQmlListProperty<QQuickTransform> t = transform(); + t.clear(&t); + setRotation(0); + setScale(1.0); + setX(relativeToParent.x()); + setY(relativeToParent.y()); + setParentItem(parentItem); + } + + d->setIsPopup(true); +} + +/*! + * \internal + */ +void QWaylandQuickWlShellSurfaceItem::handleShellSurfaceDestroyed() { + Q_D(QWaylandQuickWlShellSurfaceItem); + d->setIsPopup(false); + d->shellSurface = NULL; +} + +void QWaylandQuickWlShellSurfaceItem::handleSurfaceUnmapped() +{ + Q_D(QWaylandQuickWlShellSurfaceItem); + if (!d->shellSurface || !d->shellSurface->surface()->size().isEmpty()) + return; + d->setIsPopup(false); +} + +/*! + * \internal + */ void QWaylandQuickWlShellSurfaceItem::adjustOffsetForNextFrame(const QPointF &offset) { Q_D(QWaylandQuickWlShellSurfaceItem); @@ -231,4 +278,101 @@ void QWaylandQuickWlShellSurfaceItem::surfaceChangedEvent(QWaylandSurface *newSu connect(newSurface, &QWaylandSurface::offsetForNextFrame, this, &QWaylandQuickWlShellSurfaceItem::adjustOffsetForNextFrame); } +QVector<QWaylandWlShellSurface*> QWaylandQuickWlShellSurfaceItemPrivate::popupShellSurfaces; +bool QWaylandQuickWlShellSurfaceItemPrivate::eventFilterInstalled = false; +bool QWaylandQuickWlShellSurfaceItemPrivate::waitForRelease = false; + +/*! + * \internal + */ +void QWaylandQuickWlShellSurfaceItemPrivate::closePopups() +{ + if (!popupShellSurfaces.isEmpty()) { + Q_FOREACH (QWaylandWlShellSurface* shellSurface, popupShellSurfaces) { + shellSurface->sendPopupDone(); + } + popupShellSurfaces.clear(); + } +} + +bool QWaylandQuickWlShellSurfaceItem::eventFilter(QObject *receiver, QEvent *e) +{ + Q_D(QWaylandQuickWlShellSurfaceItem); + 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); + QWaylandQuickWlShellSurfaceItem *shellSurfaceItem = qobject_cast<QWaylandQuickWlShellSurfaceItem*>(item); + bool press = event->type() == QEvent::MouseButtonPress; + bool finalRelease = (event->type() == QEvent::MouseButtonRelease) && (event->buttons() == Qt::NoButton); + bool popupClient = shellSurfaceItem && shellSurfaceItem->shellSurface()->surface()->client() == shellSurface()->surface()->client(); + + if (d->waitForRelease) { + // We are eating events until all mouse buttons are released + if (finalRelease) { + d->waitForRelease = false; + d->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. + d->waitForRelease = true; + d->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. + d->setFilterEnabled(false); + } + } + + return false; +} + +void QWaylandQuickWlShellSurfaceItemPrivate::setIsPopup(bool popup) +{ + Q_Q(QWaylandQuickWlShellSurfaceItem); + isPopup = popup; + if (popup) { + if (!eventFilterInstalled) + setFilterEnabled(true); + + if (!popupShellSurfaces.contains(shellSurface)) { + popupShellSurfaces.append(shellSurface); + QObject::connect(shellSurface->surface(), &QWaylandSurface::mappedChanged, + q, &QWaylandQuickWlShellSurfaceItem::handleSurfaceUnmapped); + } + } else { + if (shellSurface) { + popupShellSurfaces.removeOne(shellSurface); + QObject::disconnect(shellSurface->surface(), &QWaylandSurface::mappedChanged, + q, &QWaylandQuickWlShellSurfaceItem::handleSurfaceUnmapped); + } + if (!waitForRelease && eventFilterInstalled && popupShellSurfaces.isEmpty()) + setFilterEnabled(false); + } +} + +void QWaylandQuickWlShellSurfaceItemPrivate::setFilterEnabled(bool enabled) +{ + Q_Q(QWaylandQuickWlShellSurfaceItem); + static QPointer<QObject> filter; + + if (enabled && filter.isNull()) { + qGuiApp->installEventFilter(q); + filter = q; + } else if (!enabled && !filter.isNull()){ + qGuiApp->removeEventFilter(filter); + filter = nullptr; + } + eventFilterInstalled = enabled; +} + QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandquickwlshellsurfaceitem.h b/src/compositor/extensions/qwaylandquickwlshellsurfaceitem.h index 7f32c9f3..0b24e43a 100644 --- a/src/compositor/extensions/qwaylandquickwlshellsurfaceitem.h +++ b/src/compositor/extensions/qwaylandquickwlshellsurfaceitem.h @@ -69,7 +69,11 @@ Q_SIGNALS: private Q_SLOTS: void handleStartMove(QWaylandInputDevice *inputDevice); void handleStartResize(QWaylandInputDevice *inputDevice, QWaylandWlShellSurface::ResizeEdge edges); + void handleSetPopup(QWaylandInputDevice *inputDevice, QWaylandSurface *parent, const QPoint &relativeToParent); + void handleShellSurfaceDestroyed(); void adjustOffsetForNextFrame(const QPointF &offset); + + void handleSurfaceUnmapped(); protected: QWaylandQuickWlShellSurfaceItem(QWaylandQuickWlShellSurfaceItemPrivate &dd, QQuickItem *parent); @@ -77,6 +81,8 @@ protected: void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void surfaceChangedEvent(QWaylandSurface *newSurface, QWaylandSurface *oldSurface) Q_DECL_OVERRIDE; + + bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; }; QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandquickwlshellsurfaceitem_p.h b/src/compositor/extensions/qwaylandquickwlshellsurfaceitem_p.h index 3e683477..c4adb28d 100644 --- a/src/compositor/extensions/qwaylandquickwlshellsurfaceitem_p.h +++ b/src/compositor/extensions/qwaylandquickwlshellsurfaceitem_p.h @@ -52,8 +52,11 @@ QT_BEGIN_NAMESPACE // We mean it. // +class QWaylandQuickWlShellSurfaceItem; + class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickWlShellSurfaceItemPrivate : public QWaylandQuickItemPrivate { + Q_DECLARE_PUBLIC(QWaylandQuickWlShellSurfaceItem) public: enum GrabberState { DefaultState, @@ -66,8 +69,13 @@ public: , shellSurface(Q_NULLPTR) , moveItem(Q_NULLPTR) , grabberState(DefaultState) + , isPopup(false) {} + void setIsPopup(bool popup); + void setFilterEnabled(bool enabled); + static void closePopups(); + QWaylandWlShellSurface *shellSurface; QQuickItem *moveItem; @@ -85,6 +93,11 @@ public: QPointF initialMousePos; bool initialized; } resizeState; + + static QVector<QWaylandWlShellSurface*> popupShellSurfaces; + static bool eventFilterInstalled; + static bool waitForRelease; + bool isPopup; }; QT_END_NAMESPACE diff --git a/src/imports/compositor/qwaylandmousetracker.cpp b/src/imports/compositor/qwaylandmousetracker.cpp index e54321f1..a77d6b1a 100644 --- a/src/imports/compositor/qwaylandmousetracker.cpp +++ b/src/imports/compositor/qwaylandmousetracker.cpp @@ -87,6 +87,7 @@ QWaylandMouseTracker::QWaylandMouseTracker(QQuickItem *parent) Q_D(QWaylandMouseTracker); setFiltersChildMouseEvents(true); setAcceptHoverEvents(true); + setAcceptedMouseButtons(Qt::AllButtons); setCursor(QCursor(d->cursorPixmap)); } |