From cbab4b46c17195d7a2835c47626ce8bec8562386 Mon Sep 17 00:00:00 2001 From: David Redondo Date: Tue, 11 Jul 2023 08:49:59 +0200 Subject: [PATCH] Implement xdg-dialog-v1 --- autotests/integration/CMakeLists.txt | 7 + autotests/integration/kwin_wayland_test.h | 16 ++ autotests/integration/test_helpers.cpp | 34 ++++ autotests/integration/xdgshellwindow_test.cpp | 134 ++++++++++++++- src/wayland/CMakeLists.txt | 6 + src/wayland/xdgdialog_v1.cpp | 152 ++++++++++++++++++ src/wayland/xdgdialog_v1.h | 58 +++++++ src/wayland_server.cpp | 11 ++ src/wayland_server.h | 3 + src/xdgshellwindow.cpp | 15 ++ src/xdgshellwindow.h | 3 + 11 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 src/wayland/xdgdialog_v1.cpp create mode 100644 src/wayland/xdgdialog_v1.h diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt index 64d3c296a0..f180b1070d 100644 --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -25,6 +25,13 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework ${PLASMA_WAYLAND_PROTOCOLS_DIR}/fake-input.xml ) +# the qtwaylandscanner macro cannot handle the mismatched file name and #include "qwayland-cursor-shape-v1.h" +#include "qwayland-dialog-v1.h" #include "qwayland-fake-input.h" #include "qwayland-fractional-scale-v1.h" #include "qwayland-idle-inhibit-unstable-v1.h" @@ -547,6 +548,19 @@ public: ~SecurityContextManagerV1() override; }; +class XdgWmDialogV1 : public QtWayland::xdg_wm_dialog_v1 +{ +public: + ~XdgWmDialogV1() override; +}; + +class XdgDialogV1 : public QtWayland::xdg_dialog_v1 +{ +public: + XdgDialogV1(XdgWmDialogV1 *wm, XdgToplevel *toplevel); + ~XdgDialogV1() override; +}; + enum class AdditionalWaylandInterface { Seat = 1 << 0, PlasmaShell = 1 << 2, @@ -568,6 +582,7 @@ enum class AdditionalWaylandInterface { CursorShapeV1 = 1 << 18, FakeInput = 1 << 19, SecurityContextManagerV1 = 1 << 20, + XdgDialogV1 = 1 << 21, }; Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface) @@ -714,6 +729,7 @@ std::unique_ptr createXdgToplevelDecorationV1(XdgToplev std::unique_ptr createIdleInhibitorV1(KWayland::Client::Surface *surface); std::unique_ptr createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border); std::unique_ptr createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer); +std::unique_ptr createXdgDialogV1(XdgToplevel *toplevel); /** * Creates a shared memory buffer of @p size in @p color and attaches it to the @p surface. diff --git a/autotests/integration/test_helpers.cpp b/autotests/integration/test_helpers.cpp index ede59cf27a..3a3ee315a9 100644 --- a/autotests/integration/test_helpers.cpp +++ b/autotests/integration/test_helpers.cpp @@ -269,6 +269,21 @@ SecurityContextManagerV1::~SecurityContextManagerV1() destroy(); } +XdgWmDialogV1::~XdgWmDialogV1() +{ + destroy(); +} + +XdgDialogV1::XdgDialogV1(XdgWmDialogV1 *wm, XdgToplevel *toplevel) + : QtWayland::xdg_dialog_v1(wm->get_xdg_dialog(toplevel->object())) +{ +} + +XdgDialogV1::~XdgDialogV1() +{ + destroy(); +} + static struct { KWayland::Client::ConnectionThread *connection = nullptr; @@ -302,6 +317,7 @@ static struct CursorShapeManagerV1 *cursorShapeManagerV1 = nullptr; FakeInput *fakeInput = nullptr; SecurityContextManagerV1 *securityContextManagerV1 = nullptr; + XdgWmDialogV1 *xdgWmDialogV1; } s_waylandConnection; MockInputMethod *inputMethod() @@ -517,6 +533,12 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags) s_waylandConnection.securityContextManagerV1->init(*registry, name, version); } } + if (flags & AdditionalWaylandInterface::XdgDialogV1) { + if (interface == xdg_wm_dialog_v1_interface.name) { + s_waylandConnection.xdgWmDialogV1 = new XdgWmDialogV1(); + s_waylandConnection.xdgWmDialogV1->init(*registry, name, version); + } + } }); QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced); @@ -642,6 +664,8 @@ void destroyWaylandConnection() s_waylandConnection.fakeInput = nullptr; delete s_waylandConnection.securityContextManagerV1; s_waylandConnection.securityContextManagerV1 = nullptr; + delete s_waylandConnection.xdgWmDialogV1; + s_waylandConnection.xdgWmDialogV1 = nullptr; delete s_waylandConnection.queue; // Must be destroyed last s_waylandConnection.queue = nullptr; @@ -1098,6 +1122,16 @@ std::unique_ptr createCursorShapeDeviceV1(KWayland::Client: return std::make_unique(manager, pointer); } +std::unique_ptr createXdgDialogV1(XdgToplevel *toplevel) +{ + XdgWmDialogV1 *wm = s_waylandConnection.xdgWmDialogV1; + if (!wm) { + qWarning() << "Could not create a xdg_dialog_v1 because xdg_wm_dialog_v1 global is not bound"; + return nullptr; + } + return std::make_unique(wm, toplevel); +} + bool waitForWindowClosed(Window *window) { QSignalSpy closedSpy(window, &Window::closed); diff --git a/autotests/integration/xdgshellwindow_test.cpp b/autotests/integration/xdgshellwindow_test.cpp index f4156c90a3..2b5a38fac9 100644 --- a/autotests/integration/xdgshellwindow_test.cpp +++ b/autotests/integration/xdgshellwindow_test.cpp @@ -104,6 +104,9 @@ private Q_SLOTS: void testMaximizeAndChangeDecorationModeAfterInitialCommit(); void testFullScreenAndChangeDecorationModeAfterInitialCommit(); void testChangeDecorationModeAfterInitialCommit(); + void testModal(); + void testCloseModal(); + void testCloseInactiveModal(); }; void TestXdgShellWindow::testXdgPopupReactive_data() @@ -206,7 +209,7 @@ void TestXdgShellWindow::initTestCase() void TestXdgShellWindow::init() { - QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::AppMenu)); + QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::AppMenu | Test::AdditionalWaylandInterface::XdgDialogV1)); QVERIFY(Test::waitForWaylandPointer()); workspace()->setActiveOutput(QPoint(640, 512)); @@ -2190,5 +2193,134 @@ void TestXdgShellWindow::testChangeDecorationModeAfterInitialCommit() QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value(), Test::XdgToplevelDecorationV1::mode_client_side); } +void TestXdgShellWindow::testModal() +{ + auto parentSurface = Test::createSurface(); + auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get()); + auto parentWindow = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan); + QVERIFY(parentWindow); + + auto childSurface = Test::createSurface(); + auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) { + toplevel->set_parent(parentToplevel->object()); + }); + auto childWindow = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow); + QVERIFY(childWindow); + QVERIFY(!childWindow->isModal()); + QCOMPARE(childWindow->transientFor(), parentWindow); + + auto dialog = Test::createXdgDialogV1(childToplevel.get()); + QVERIFY(Test::waylandSync()); + QVERIFY(dialog); + QVERIFY(!childWindow->isModal()); + + QSignalSpy modalChangedSpy(childWindow, &Window::modalChanged); + + dialog->set_modal(); + Test::flushWaylandConnection(); + QVERIFY(modalChangedSpy.wait()); + QVERIFY(childWindow->isModal()); + + dialog->unset_modal(); + Test::flushWaylandConnection(); + QVERIFY(modalChangedSpy.wait()); + QVERIFY(!childWindow->isModal()); + + dialog->set_modal(); + Test::flushWaylandConnection(); + QVERIFY(modalChangedSpy.wait()); + Workspace::self()->activateWindow(parentWindow); + QCOMPARE(Workspace::self()->activeWindow(), childWindow); + + dialog.reset(); + Test::flushWaylandConnection(); + QVERIFY(modalChangedSpy.wait()); + QVERIFY(!childWindow->isModal()); +} + +void TestXdgShellWindow::testCloseModal() +{ + // This test verifies that the parent window will be activated when an active modal dialog is closed. + + // Create a parent and a child windows. + auto parentSurface = Test::createSurface(); + auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get()); + auto parent = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan); + QVERIFY(parent); + + auto childSurface = Test::createSurface(); + auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) { + toplevel->set_parent(parentToplevel->object()); + }); + auto child = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow); + QVERIFY(child); + QVERIFY(!child->isModal()); + QCOMPARE(child->transientFor(), parent); + + // Set modal state. + auto dialog = Test::createXdgDialogV1(childToplevel.get()); + QSignalSpy modalChangedSpy(child, &Window::modalChanged); + dialog->set_modal(); + Test::flushWaylandConnection(); + QVERIFY(modalChangedSpy.wait()); + QVERIFY(child->isModal()); + QCOMPARE(workspace()->activeWindow(), child); + + // Close the child. + QSignalSpy childClosedSpy(child, &Window::closed); + childToplevel.reset(); + childSurface.reset(); + dialog.reset(); + Test::flushWaylandConnection(); + QVERIFY(childClosedSpy.wait()); + QCOMPARE(workspace()->activeWindow(), parent); +} + +void TestXdgShellWindow::testCloseInactiveModal() +{ + // This test verifies that the parent window will not be activated when an inactive modal dialog is closed. + + // Create a parent and a child windows. + auto parentSurface = Test::createSurface(); + auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get()); + auto parent = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan); + QVERIFY(parent); + + auto childSurface = Test::createSurface(); + auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) { + toplevel->set_parent(parentToplevel->object()); + }); + auto child = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow); + QVERIFY(child); + QVERIFY(!child->isModal()); + QCOMPARE(child->transientFor(), parent); + + // Set modal state. + auto dialog = Test::createXdgDialogV1(childToplevel.get()); + QSignalSpy modalChangedSpy(child, &Window::modalChanged); + dialog->set_modal(); + Test::flushWaylandConnection(); + QVERIFY(modalChangedSpy.wait()); + QVERIFY(child->isModal()); + QCOMPARE(workspace()->activeWindow(), child); + + // Show another window. + auto otherSurface = Test::createSurface(); + auto otherToplevel = Test::createXdgToplevelSurface(otherSurface.get()); + auto otherWindow = Test::renderAndWaitForShown(otherSurface.get(), {200, 200}, Qt::magenta); + QVERIFY(otherWindow); + workspace()->setActiveWindow(otherWindow); + QCOMPARE(workspace()->activeWindow(), otherWindow); + + // Close the child. + QSignalSpy childClosedSpy(child, &Window::closed); + childToplevel.reset(); + childSurface.reset(); + dialog.reset(); + Test::flushWaylandConnection(); + QVERIFY(childClosedSpy.wait()); + QCOMPARE(workspace()->activeWindow(), otherWindow); +} + WAYLANDTEST_MAIN(TestXdgShellWindow) #include "xdgshellwindow_test.moc" diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index 73f7924859..169ced11bf 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -231,6 +231,10 @@ ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/xx-color-management-v2.xml BASENAME xx-color-management-v2 ) +ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml + PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml + BASENAME dialog-v1 +) target_sources(kwin PRIVATE abstract_data_source.cpp @@ -306,6 +310,7 @@ target_sources(kwin PRIVATE viewporter.cpp xdgactivation_v1.cpp xdgdecoration_v1.cpp + xdgdialog_v1.cpp xdgforeign_v2.cpp xdgoutput_v1.cpp xdgshell.cpp @@ -381,6 +386,7 @@ install(FILES viewporter.h xdgactivation_v1.h xdgdecoration_v1.h + xdgdialog_v1.h xdgforeign_v2.h xdgoutput_v1.h xdgshell.h diff --git a/src/wayland/xdgdialog_v1.cpp b/src/wayland/xdgdialog_v1.cpp new file mode 100644 index 0000000000..4df6aafc84 --- /dev/null +++ b/src/wayland/xdgdialog_v1.cpp @@ -0,0 +1,152 @@ +/* + SPDX-FileCopyrightText: 2024 David Redondo + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "xdgdialog_v1.h" + +#include "display.h" +#include "xdgshell.h" + +#include "qwayland-server-dialog-v1.h" + +#include + +namespace KWin +{ + +constexpr int version = 1; + +class XdgDialogWmV1InterfacePrivate : public QtWaylandServer::xdg_wm_dialog_v1 +{ +public: + XdgDialogWmV1InterfacePrivate(Display *display, XdgDialogWmV1Interface *q); + + XdgDialogWmV1Interface *const q; + QHash m_dialogs; + +protected: + void xdg_wm_dialog_v1_get_xdg_dialog(Resource *resource, uint32_t id, wl_resource *toplevel) override; + void xdg_wm_dialog_v1_destroy(Resource *resource) override; +}; + +class XdgDialogV1InterfacePrivate : public QtWaylandServer::xdg_dialog_v1 +{ +public: + XdgDialogV1InterfacePrivate(wl_resource *resource, XdgDialogV1Interface *q); + + XdgDialogV1Interface *const q; + XdgToplevelInterface *m_toplevel; + bool modal = false; + +protected: + void xdg_dialog_v1_destroy(Resource *resource) override; + void xdg_dialog_v1_destroy_resource(Resource *resource) override; + void xdg_dialog_v1_set_modal(Resource *resource) override; + void xdg_dialog_v1_unset_modal(Resource *resource) override; +}; + +XdgDialogWmV1InterfacePrivate::XdgDialogWmV1InterfacePrivate(Display *display, XdgDialogWmV1Interface *q) + : xdg_wm_dialog_v1(*display, version) + , q(q) +{ +} + +void XdgDialogWmV1InterfacePrivate::xdg_wm_dialog_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void XdgDialogWmV1InterfacePrivate::xdg_wm_dialog_v1_get_xdg_dialog(Resource *resource, uint32_t id, wl_resource *toplevel_resource) +{ + auto toplevel = XdgToplevelInterface::get(toplevel_resource); + if (!toplevel) { + wl_resource_post_error(resource->handle, 0, "Invalid surface"); + return; + } + if (m_dialogs.value(toplevel)) { + wl_resource_post_error(resource->handle, error::error_already_used, "xdg_toplevel already already used to a xdg_dialog_v1"); + return; + } + auto dialogResource = wl_resource_create(resource->client(), &xdg_dialog_v1_interface, resource->version(), id); + auto dialog = new XdgDialogV1Interface(dialogResource, toplevel); + m_dialogs.insert(toplevel, dialog); + + auto removeDialog = [this, toplevel, dialog] { + m_dialogs.removeIf([toplevel, dialog](const std::pair &entry) { + return entry.first == toplevel && entry.second == dialog; + }); + }; + QObject::connect(dialog, &XdgDialogV1Interface::destroyed, q, removeDialog); + QObject::connect(toplevel, &XdgDialogV1Interface::destroyed, q, removeDialog); + + Q_EMIT q->dialogCreated(dialog); +} + +XdgDialogWmV1Interface::XdgDialogWmV1Interface(Display *display, QObject *parent) + : d(std::make_unique(display, this)) +{ +} + +XdgDialogWmV1Interface::~XdgDialogWmV1Interface() = default; + +XdgDialogV1Interface *XdgDialogWmV1Interface::dialogForToplevel(XdgToplevelInterface *toplevel) const +{ + return d->m_dialogs.value(toplevel); +} + +XdgDialogV1InterfacePrivate::XdgDialogV1InterfacePrivate(wl_resource *wl_resource, XdgDialogV1Interface *q) + : xdg_dialog_v1(wl_resource) + , q(q) +{ +} + +void XdgDialogV1InterfacePrivate::xdg_dialog_v1_destroy_resource(Resource *resource) +{ + delete q; +} + +void XdgDialogV1InterfacePrivate::xdg_dialog_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void XdgDialogV1InterfacePrivate::xdg_dialog_v1_set_modal(Resource *resource) +{ + if (modal) { + return; + } + modal = true; + Q_EMIT q->modalChanged(true); +} + +void XdgDialogV1InterfacePrivate::xdg_dialog_v1_unset_modal(Resource *resource) +{ + if (!modal) { + return; + } + modal = false; + Q_EMIT q->modalChanged(false); +} + +XdgDialogV1Interface::XdgDialogV1Interface(wl_resource *resource, XdgToplevelInterface *toplevel) + : d(std::make_unique(resource, this)) +{ + d->m_toplevel = toplevel; +} + +XdgDialogV1Interface::~XdgDialogV1Interface() +{ +} + +bool XdgDialogV1Interface::isModal() const +{ + return d->modal; +} + +XdgToplevelInterface *XdgDialogV1Interface::toplevel() const +{ + return d->m_toplevel; +} +} diff --git a/src/wayland/xdgdialog_v1.h b/src/wayland/xdgdialog_v1.h new file mode 100644 index 0000000000..5b139d055e --- /dev/null +++ b/src/wayland/xdgdialog_v1.h @@ -0,0 +1,58 @@ +/* + SPDX-FileCopyrightText: 2024 David Redondo + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#pragma once + +#include + +#include + +struct wl_resource; + +namespace KWin +{ + +class Display; +class XdgDialogV1Interface; +class XdgDialogV1InterfacePrivate; +class XdgDialogWmV1InterfacePrivate; +class XdgToplevelInterface; + +class XdgDialogWmV1Interface : public QObject +{ + Q_OBJECT +public: + XdgDialogWmV1Interface(Display *display, QObject *parent = nullptr); + ~XdgDialogWmV1Interface() override; + + XdgDialogV1Interface *dialogForToplevel(XdgToplevelInterface *toplevel) const; + +Q_SIGNALS: + void dialogCreated(XdgDialogV1Interface *dialog); + +private: + std::unique_ptr d; +}; + +class XdgDialogV1Interface : public QObject +{ + Q_OBJECT +public: + ~XdgDialogV1Interface() override; + + bool isModal() const; + XdgToplevelInterface *toplevel() const; + +Q_SIGNALS: + void modalChanged(bool modal); + +private: + XdgDialogV1Interface(wl_resource *resource, XdgToplevelInterface *toplevel); + friend class XdgDialogWmV1InterfacePrivate; + std::unique_ptr d; +}; + +} diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp index 416910b111..2605e1d71e 100644 --- a/src/wayland_server.cpp +++ b/src/wayland_server.cpp @@ -67,6 +67,7 @@ #include "wayland/viewporter.h" #include "wayland/xdgactivation_v1.h" #include "wayland/xdgdecoration_v1.h" +#include "wayland/xdgdialog_v1.h" #include "wayland/xdgforeign_v2.h" #include "wayland/xdgoutput_v1.h" #include "wayland/xdgshell.h" @@ -242,6 +243,9 @@ void WaylandServer::registerXdgToplevelWindow(XdgToplevelWindow *window) if (auto palette = m_paletteManager->paletteForSurface(surface)) { window->installPalette(palette); } + if (auto dialog = m_xdgDialogWm->dialogForToplevel(window->shellSurface())) { + window->installXdgDialogV1(dialog); + } connect(m_XdgForeign, &XdgForeignV2Interface::transientChanged, window, [this](SurfaceInterface *child) { Q_EMIT foreignTransientChanged(child); @@ -498,6 +502,13 @@ bool WaylandServer::init(InitializationFlags flags) if (qEnvironmentVariableIntValue("KWIN_ENABLE_XX_COLOR_MANAGEMENT")) { m_xxColorManager = new XXColorManagerV2(m_display, m_display); } + m_xdgDialogWm = new KWin::XdgDialogWmV1Interface(m_display, m_display); + connect(m_xdgDialogWm, &KWin::XdgDialogWmV1Interface::dialogCreated, this, [this](KWin::XdgDialogV1Interface *dialog) { + if (auto window = findXdgToplevelWindow(dialog->toplevel()->surface())) { + window->installXdgDialogV1(dialog); + } + }); + return true; } diff --git a/src/wayland_server.h b/src/wayland_server.h index d4dc7d1e41..df8bdfbaec 100644 --- a/src/wayland_server.h +++ b/src/wayland_server.h @@ -50,6 +50,8 @@ class DrmLeaseManagerV1; class TearingControlManagerV1Interface; class XwaylandShellV1Interface; class OutputOrderV1Interface; +class XdgDialogWmV1Interface; + class Window; class Output; class XdgActivationV1Integration; @@ -297,6 +299,7 @@ private: DrmLeaseManagerV1 *m_leaseManager = nullptr; OutputOrderV1Interface *m_outputOrder = nullptr; XXColorManagerV2 *m_xxColorManager = nullptr; + XdgDialogWmV1Interface *m_xdgDialogWm = nullptr; KWIN_SINGLETON(WaylandServer) }; diff --git a/src/xdgshellwindow.cpp b/src/xdgshellwindow.cpp index f454ba8e32..da1c9aa15b 100644 --- a/src/xdgshellwindow.cpp +++ b/src/xdgshellwindow.cpp @@ -30,6 +30,7 @@ #include "wayland/surface.h" #include "wayland/tablet_v2.h" #include "wayland/xdgdecoration_v1.h" +#include "wayland/xdgdialog_v1.h" #include "wayland_server.h" #include "workspace.h" @@ -488,6 +489,9 @@ void XdgToplevelWindow::handleRoleDestroyed() if (m_serverDecoration) { m_serverDecoration->disconnect(this); } + if (m_xdgDialog) { + m_xdgDialog->disconnect(this); + } m_shellSurface->disconnect(this); @@ -1442,6 +1446,17 @@ void XdgToplevelWindow::installPalette(ServerSideDecorationPaletteInterface *pal updateColorScheme(); } +void XdgToplevelWindow::installXdgDialogV1(XdgDialogV1Interface *dialog) +{ + m_xdgDialog = dialog; + + connect(dialog, &XdgDialogV1Interface::modalChanged, this, &Window::setModal); + connect(dialog, &QObject::destroyed, this, [this] { + setModal(false); + }); + setModal(dialog->isModal()); +} + void XdgToplevelWindow::setFullScreen(bool set) { if (!isFullScreenable()) { diff --git a/src/xdgshellwindow.h b/src/xdgshellwindow.h index a0e5ad7fe2..796cea95fa 100644 --- a/src/xdgshellwindow.h +++ b/src/xdgshellwindow.h @@ -27,6 +27,7 @@ class KillPrompt; class PlasmaShellSurfaceInterface; class ServerSideDecorationInterface; class ServerSideDecorationPaletteInterface; +class XdgDialogV1Interface; class XdgToplevelDecorationV1Interface; class Output; @@ -159,6 +160,7 @@ public: void installServerDecoration(ServerSideDecorationInterface *decoration); void installPalette(ServerSideDecorationPaletteInterface *palette); void installXdgDecoration(XdgToplevelDecorationV1Interface *decoration); + void installXdgDialogV1(XdgDialogV1Interface *dialog); protected: XdgSurfaceConfigure *sendRoleConfigure() const override; @@ -213,6 +215,7 @@ private: QPointer m_paletteInterface; QPointer m_serverDecoration; QPointer m_xdgDecoration; + QPointer m_xdgDialog; XdgToplevelInterface *m_shellSurface; XdgToplevelInterface::States m_nextStates; XdgToplevelInterface::States m_acknowledgedStates;