diff --git a/src/wayland/autotests/client/test_wayland_shell.cpp b/src/wayland/autotests/client/test_wayland_shell.cpp index 89101f4443..7bbdbb1a34 100644 --- a/src/wayland/autotests/client/test_wayland_shell.cpp +++ b/src/wayland/autotests/client/test_wayland_shell.cpp @@ -47,6 +47,7 @@ private Q_SLOTS: void testFullscreen(); void testMaximize(); void testToplevel(); + void testTransient(); void testPing(); void testTitle(); void testWindowClass(); @@ -334,6 +335,61 @@ void TestWaylandShell::testToplevel() QVERIFY(!toplevelSpy.first().first().toBool()); } +void TestWaylandShell::testTransient() +{ + using namespace KWayland::Server; + using namespace KWayland::Client; + QScopedPointer s(m_compositor->createSurface()); + QVERIFY(!s.isNull()); + QVERIFY(s->isValid()); + ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); + + QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); + QVERIFY(serverSurfaceSpy.isValid()); + QVERIFY(serverSurfaceSpy.wait()); + ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); + QVERIFY(serverSurface); + QCOMPARE(serverSurface->isToplevel(), true); + QCOMPARE(serverSurface->isPopup(), false); + QCOMPARE(serverSurface->isTransient(), false); + QCOMPARE(serverSurface->transientFor(), QPointer()); + QCOMPARE(serverSurface->transientOffset(), QPoint()); + + QSignalSpy transientSpy(serverSurface, &ShellSurfaceInterface::transientChanged); + QVERIFY(transientSpy.isValid()); + QSignalSpy transientOffsetSpy(serverSurface, &ShellSurfaceInterface::transientOffsetChanged); + QVERIFY(transientOffsetSpy.isValid()); + QSignalSpy transientForChangedSpy(serverSurface, &ShellSurfaceInterface::transientForChanged); + QVERIFY(transientForChangedSpy.isValid()); + + QScopedPointer s2(m_compositor->createSurface()); + m_shell->createSurface(s2.data(), m_shell); + serverSurfaceSpy.clear(); + QVERIFY(serverSurfaceSpy.wait()); + ShellSurfaceInterface *serverSurface2 = serverSurfaceSpy.first().first().value(); + QVERIFY(serverSurface2 != serverSurface); + QVERIFY(serverSurface2); + + surface->setTransient(s2.data(), QPoint(10, 20)); + QVERIFY(transientSpy.wait()); + QCOMPARE(transientSpy.count(), 1); + QCOMPARE(transientSpy.first().first().toBool(), true); + QCOMPARE(transientOffsetSpy.count(), 1); + QCOMPARE(transientOffsetSpy.first().first().toPoint(), QPoint(10, 20)); + QCOMPARE(transientForChangedSpy.count(), 1); + QCOMPARE(serverSurface->isToplevel(), false); + QCOMPARE(serverSurface->isPopup(), false); + QCOMPARE(serverSurface->isTransient(), true); + QCOMPARE(serverSurface->transientFor(), QPointer(serverSurface2->surface())); + QCOMPARE(serverSurface->transientOffset(), QPoint(10, 20)); + + QCOMPARE(serverSurface2->isToplevel(), true); + QCOMPARE(serverSurface2->isPopup(), false); + QCOMPARE(serverSurface2->isTransient(), false); + QCOMPARE(serverSurface2->transientFor(), QPointer()); + QCOMPARE(serverSurface2->transientOffset(), QPoint()); +} + void TestWaylandShell::testPing() { using namespace KWayland::Server; diff --git a/src/wayland/server/shell_interface.cpp b/src/wayland/server/shell_interface.cpp index a2d241f5d9..0b6b4312ea 100644 --- a/src/wayland/server/shell_interface.cpp +++ b/src/wayland/server/shell_interface.cpp @@ -78,9 +78,13 @@ public: enum class WindowMode { Fullscreen, Toplevel, - Maximized + Maximized, + Transient, + Popup }; WindowMode windowMode = WindowMode::Toplevel; + QPoint transientOffset; + QPointer transientFor; void setWindowMode(WindowMode newWindowMode); private: @@ -274,13 +278,15 @@ void ShellSurfaceInterface::Private::setToplevelCallback(wl_client *client, wl_r void ShellSurfaceInterface::Private::setTransientCallback(wl_client *client, wl_resource *resource, wl_resource *parent, int32_t x, int32_t y, uint32_t flags) { - Q_UNUSED(parent) - Q_UNUSED(x) - Q_UNUSED(y) Q_UNUSED(flags) auto s = cast(resource); Q_ASSERT(client == *s->client); - // TODO: implement + s->transientFor = QPointer(SurfaceInterface::get(parent)); + s->transientOffset = QPoint(x, y); + s->setWindowMode(WindowMode::Transient); + // TODO: flags + emit s->q_func()->transientOffsetChanged(s->transientOffset); + emit s->q_func()->transientForChanged(); } void ShellSurfaceInterface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, uint32_t method, @@ -312,6 +318,13 @@ void ShellSurfaceInterface::Private::setWindowMode(WindowMode newWindowMode) if (oldWindowMode == WindowMode::Maximized || newWindowMode == WindowMode::Maximized) { emit q->maximizedChanged(windowMode == WindowMode::Maximized); } + if (oldWindowMode == WindowMode::Popup || newWindowMode == WindowMode::Popup) { + emit q->popupChanged(windowMode == WindowMode::Popup); + emit q->transientChanged(windowMode == WindowMode::Popup || windowMode == WindowMode::Transient); + } + if (oldWindowMode == WindowMode::Transient || newWindowMode == WindowMode::Transient) { + emit q->transientChanged(windowMode == WindowMode::Popup || windowMode == WindowMode::Transient); + } } void ShellSurfaceInterface::Private::setPopupCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, @@ -319,13 +332,16 @@ void ShellSurfaceInterface::Private::setPopupCallback(wl_client *client, wl_reso { Q_UNUSED(seat) Q_UNUSED(serial) - Q_UNUSED(parent) - Q_UNUSED(x) - Q_UNUSED(y) Q_UNUSED(flags) auto s = cast(resource); Q_ASSERT(client == *s->client); - // TODO: implement + // TODO: what about seat and serial? + s->transientFor = QPointer(SurfaceInterface::get(parent)); + s->transientOffset = QPoint(x, y); + s->setWindowMode(WindowMode::Popup); + // TODO: flags + emit s->q_func()->transientOffsetChanged(s->transientOffset); + emit s->q_func()->transientForChanged(); } void ShellSurfaceInterface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource, wl_resource *output) @@ -405,6 +421,30 @@ bool ShellSurfaceInterface::isMaximized() const { return d->windowMode == Private::WindowMode::Maximized; } +bool ShellSurfaceInterface::isPopup() const +{ + Q_D(); + return d->windowMode == Private::WindowMode::Popup; +} + +bool ShellSurfaceInterface::isTransient() const +{ + Q_D(); + return isPopup() || d->windowMode == Private::WindowMode::Transient; +} + +QPoint ShellSurfaceInterface::transientOffset() const +{ + Q_D(); + return d->transientOffset; +} + +QPointer< SurfaceInterface > ShellSurfaceInterface::transientFor() const +{ + Q_D(); + return d->transientFor; +} + ShellSurfaceInterface::Private *ShellSurfaceInterface::d_func() const { return reinterpret_cast(d.data()); diff --git a/src/wayland/server/shell_interface.h b/src/wayland/server/shell_interface.h index dfbc1f0776..2bf0a61d3b 100644 --- a/src/wayland/server/shell_interface.h +++ b/src/wayland/server/shell_interface.h @@ -97,6 +97,25 @@ class KWAYLANDSERVER_EXPORT ShellSurfaceInterface : public Resource * Whether the window is maximized. **/ Q_PROPERTY(bool maximized READ isMaximized NOTIFY maximizedChanged) + /** + * Whether the ShellSurfaceInterface is a popup for another SurfaceInterface. + * + * Popup implies transient. + * @since 5.5 + **/ + Q_PROPERTY(bool popup READ isPopup NOTIFY popupChanged) + /** + * Whether the ShellSurfaceInterface is a transient for another SurfaceInterface. + * + * Popup implies transient. + * @since 5.5 + **/ + Q_PROPERTY(bool transient READ isTransient NOTIFY transientChanged) + /** + * Offset of the upper left corner in the parent SurfaceInterface's coordinate system. + * @since 5.5 + **/ + Q_PROPERTY(QPoint transientOffset READ transientOffset NOTIFY transientOffsetChanged) public: virtual ~ShellSurfaceInterface(); @@ -144,6 +163,47 @@ public: bool isFullscreen() const; bool isToplevel() const; bool isMaximized() const; + /** + * @returns @c true if the ShellSurfaceInterface is a popup. + * @see isTransient + * @see transientOffset + * @see transientFor + * @since 5.5 + **/ + bool isPopup() const; + /** + * @returns @c true if the ShellSurfaceInterface is a transient or popup for another SurfaceInterface. + * @see isPopup + * @see transientOffset + * @see transientFor + * @since 5.5 + **/ + bool isTransient() const; + /** + * In case the ShellSurfaceInterface is a transient this is the offset of the ShellSurfaceInterface + * in the coordinate system of the SurfaceInterface this surface is a transient for. + * + * @returns offset in parent coordinate system. + * @see isTransient + * @see transientFor + * @since 5.5 + **/ + QPoint transientOffset() const; + /** + * The SurfaceInterface for which this ShellSurfaceInterface is a transient. + * This is only relevant if the ShellSurfaceInterface is either a transient or a + * popup. + * + * The transientOffset is in the local coordinate system of the SurfaceInterface + * returned by this method. + * + * @returns The SurfaceInterface for which this Surface is a transient + * @see isTransient + * @see isPopup + * @see transientOffset + * @since 5.5 + **/ + QPointer transientFor() const; Q_SIGNALS: /** @@ -168,6 +228,22 @@ Q_SIGNALS: void fullscreenChanged(bool); void toplevelChanged(bool); void maximizedChanged(bool); + /** + * @since 5.5 + **/ + void popupChanged(bool); + /** + * @since 5.5 + **/ + void transientChanged(bool); + /** + * @since 5.5 + **/ + void transientOffsetChanged(const QPoint&); + /** + * @since 5.5 + **/ + void transientForChanged(); private: friend class ShellInterface;