From 86dae5d036608da9f762ee0b09dc6947043da37d Mon Sep 17 00:00:00 2001 From: bbedward Date: Wed, 26 Nov 2025 10:29:51 -0500 Subject: [PATCH 1/3] core/window: add parentWindow property to FloatingWindow --- src/window/floatingwindow.cpp | 37 ++++++++++++++++++++++++++++++++++- src/window/floatingwindow.hpp | 15 ++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/window/floatingwindow.cpp b/src/window/floatingwindow.cpp index a0c9fdda..98aee916 100644 --- a/src/window/floatingwindow.cpp +++ b/src/window/floatingwindow.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -19,6 +19,13 @@ void ProxyFloatingWindow::connectWindow() { this->window->setMaximumSize(this->bMaximumSize); } +void ProxyFloatingWindow::postCompleteWindow() { + auto* parentBacking = + this->mParentProxyWindow ? this->mParentProxyWindow->backingWindow() : nullptr; + + this->window->setTransientParent(parentBacking); +} + void ProxyFloatingWindow::trySetWidth(qint32 implicitWidth) { if (!this->window->isVisible()) { this->ProxyWindowBase::trySetWidth(implicitWidth); @@ -46,6 +53,28 @@ void ProxyFloatingWindow::onMaximumSizeChanged() { emit this->maximumSizeChanged(); } +QObject* ProxyFloatingWindow::parentWindow() const { return this->mParentWindow; } + +void ProxyFloatingWindow::setParentWindow(QObject* window) { + if (window == this->mParentWindow) return; + + if (window) { + if (auto* proxy = qobject_cast(window)) { + this->mParentProxyWindow = proxy; + } else if (auto* interface = qobject_cast(window)) { + this->mParentProxyWindow = interface->proxyWindow(); + } else { + qmlWarning(this) << "parentWindow must be a quickshell window."; + return; + } + + this->mParentWindow = window; + } else { + this->mParentWindow = nullptr; + this->mParentProxyWindow = nullptr; + } +} + // FloatingWindowInterface FloatingWindowInterface::FloatingWindowInterface(QObject* parent) @@ -169,3 +198,9 @@ bool FloatingWindowInterface::startSystemResize(Qt::Edges edges) const { if (!qw) return false; return qw->startSystemResize(edges); } + +QObject* FloatingWindowInterface::parentWindow() const { return this->window->parentWindow(); } + +void FloatingWindowInterface::setParentWindow(QObject* window) { + this->window->setParentWindow(window); +} diff --git a/src/window/floatingwindow.hpp b/src/window/floatingwindow.hpp index 06b5b9e2..d34f1cc3 100644 --- a/src/window/floatingwindow.hpp +++ b/src/window/floatingwindow.hpp @@ -19,6 +19,10 @@ class ProxyFloatingWindow: public ProxyWindowBase { explicit ProxyFloatingWindow(QObject* parent = nullptr): ProxyWindowBase(parent) {} void connectWindow() override; + void postCompleteWindow() override; + + [[nodiscard]] QObject* parentWindow() const; + void setParentWindow(QObject* window); // Setting geometry while the window is visible makes the content item shrink but not the window // which is awful so we disable it for floating windows. @@ -35,6 +39,9 @@ class ProxyFloatingWindow: public ProxyWindowBase { void onMaximumSizeChanged(); void onTitleChanged(); + QObject* mParentWindow = nullptr; + ProxyWindowBase* mParentProxyWindow = nullptr; + public: Q_OBJECT_BINDABLE_PROPERTY( ProxyFloatingWindow, @@ -75,6 +82,11 @@ class FloatingWindowInterface: public WindowInterface { Q_PROPERTY(bool maximized READ isMaximized WRITE setMaximized NOTIFY maximizedChanged); /// Whether the window is currently fullscreen. Q_PROPERTY(bool fullscreen READ isFullscreen WRITE setFullscreen NOTIFY fullscreenChanged); + /// The parent window of this window. Setting this makes the window a child of the parent, + /// which affects window stacking behavior. + /// + /// > [!NOTE] This property cannot be changed after the window is visible. + Q_PROPERTY(QObject* parentWindow READ parentWindow WRITE setParentWindow); // clang-format on QML_NAMED_ELEMENT(FloatingWindow); @@ -101,6 +113,9 @@ class FloatingWindowInterface: public WindowInterface { /// Start a system resize operation. Must be called during a pointer press/drag. Q_INVOKABLE [[nodiscard]] bool startSystemResize(Qt::Edges edges) const; + [[nodiscard]] QObject* parentWindow() const; + void setParentWindow(QObject* window); + signals: void minimumSizeChanged(); void maximumSizeChanged(); From 26887fab9b2b8d497779af9541adf1933209ddc6 Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 22 Dec 2025 12:33:24 -0500 Subject: [PATCH 2/3] core/window: fix FloatingWindow parentWindow to wait for parent visibility --- src/window/floatingwindow.cpp | 53 ++++++++++++++++++++++++++++------- src/window/floatingwindow.hpp | 3 +- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/window/floatingwindow.cpp b/src/window/floatingwindow.cpp index 98aee916..bbf5e52d 100644 --- a/src/window/floatingwindow.cpp +++ b/src/window/floatingwindow.cpp @@ -19,11 +19,39 @@ void ProxyFloatingWindow::connectWindow() { this->window->setMaximumSize(this->bMaximumSize); } -void ProxyFloatingWindow::postCompleteWindow() { - auto* parentBacking = - this->mParentProxyWindow ? this->mParentProxyWindow->backingWindow() : nullptr; +void ProxyFloatingWindow::setVisible(bool visible) { + if (!visible) { + QObject::disconnect(this->mParentVisibleConn); + this->ProxyWindowBase::setVisible(false); + return; + } + + // If there's a parent, set transient before showing + if (this->mParentProxyWindow) { + auto* pw = this->mParentProxyWindow->backingWindow(); + if (pw && pw->isVisible()) { + if (this->window) this->window->setTransientParent(pw); + } else { + // Parent not visible yet - wait for it. + QObject::disconnect(this->mParentVisibleConn); + this->mParentVisibleConn = QObject::connect( + this->mParentProxyWindow, + &ProxyWindowBase::backerVisibilityChanged, + this, + [this]() { + auto* pw = + this->mParentProxyWindow ? this->mParentProxyWindow->backingWindow() : nullptr; + if (!pw || !pw->isVisible() || !this->window) return; + this->window->setTransientParent(pw); + QObject::disconnect(this->mParentVisibleConn); + this->ProxyWindowBase::setVisible(true); + } + ); + return; + } + } - this->window->setTransientParent(parentBacking); + this->ProxyWindowBase::setVisible(true); } void ProxyFloatingWindow::trySetWidth(qint32 implicitWidth) { @@ -58,20 +86,25 @@ QObject* ProxyFloatingWindow::parentWindow() const { return this->mParentWindow; void ProxyFloatingWindow::setParentWindow(QObject* window) { if (window == this->mParentWindow) return; + if (this->window && this->window->isVisible()) { + qmlWarning(this) << "parentWindow cannot be changed after the window is visible."; + return; + } + + QObject::disconnect(this->mParentVisibleConn); + this->mParentWindow = nullptr; + this->mParentProxyWindow = nullptr; + if (window) { if (auto* proxy = qobject_cast(window)) { this->mParentProxyWindow = proxy; - } else if (auto* interface = qobject_cast(window)) { - this->mParentProxyWindow = interface->proxyWindow(); + } else if (auto* iface = qobject_cast(window)) { + this->mParentProxyWindow = iface->proxyWindow(); } else { qmlWarning(this) << "parentWindow must be a quickshell window."; return; } - this->mParentWindow = window; - } else { - this->mParentWindow = nullptr; - this->mParentProxyWindow = nullptr; } } diff --git a/src/window/floatingwindow.hpp b/src/window/floatingwindow.hpp index d34f1cc3..441bbbd2 100644 --- a/src/window/floatingwindow.hpp +++ b/src/window/floatingwindow.hpp @@ -19,7 +19,7 @@ class ProxyFloatingWindow: public ProxyWindowBase { explicit ProxyFloatingWindow(QObject* parent = nullptr): ProxyWindowBase(parent) {} void connectWindow() override; - void postCompleteWindow() override; + void setVisible(bool visible) override; [[nodiscard]] QObject* parentWindow() const; void setParentWindow(QObject* window); @@ -41,6 +41,7 @@ class ProxyFloatingWindow: public ProxyWindowBase { QObject* mParentWindow = nullptr; ProxyWindowBase* mParentProxyWindow = nullptr; + QMetaObject::Connection mParentVisibleConn; public: Q_OBJECT_BINDABLE_PROPERTY( From 90314d31d0e84d1d233a7bab85897e13e44110d5 Mon Sep 17 00:00:00 2001 From: bbedward Date: Tue, 23 Dec 2025 16:04:34 -0500 Subject: [PATCH 3/3] core/window: add missing destroyed handler --- src/window/floatingwindow.cpp | 57 +++++++++++++++++++++++++++-------- src/window/floatingwindow.hpp | 5 ++- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/window/floatingwindow.cpp b/src/window/floatingwindow.cpp index bbf5e52d..d41dd241 100644 --- a/src/window/floatingwindow.cpp +++ b/src/window/floatingwindow.cpp @@ -19,9 +19,35 @@ void ProxyFloatingWindow::connectWindow() { this->window->setMaximumSize(this->bMaximumSize); } +void ProxyFloatingWindow::onParentDestroyed() { + this->mParentWindow = nullptr; + this->mParentProxyWindow = nullptr; +} + +void ProxyFloatingWindow::onParentVisible() { + auto* pw = this->mParentProxyWindow ? this->mParentProxyWindow->backingWindow() : nullptr; + if (!pw || !pw->isVisible() || !this->window) return; + + this->window->setTransientParent(pw); + QObject::disconnect( + this->mParentProxyWindow, + &ProxyWindowBase::backerVisibilityChanged, + this, + &ProxyFloatingWindow::onParentVisible + ); + this->ProxyWindowBase::setVisible(true); +} + void ProxyFloatingWindow::setVisible(bool visible) { if (!visible) { - QObject::disconnect(this->mParentVisibleConn); + if (this->mParentProxyWindow) { + QObject::disconnect( + this->mParentProxyWindow, + &ProxyWindowBase::backerVisibilityChanged, + this, + &ProxyFloatingWindow::onParentVisible + ); + } this->ProxyWindowBase::setVisible(false); return; } @@ -32,20 +58,11 @@ void ProxyFloatingWindow::setVisible(bool visible) { if (pw && pw->isVisible()) { if (this->window) this->window->setTransientParent(pw); } else { - // Parent not visible yet - wait for it. - QObject::disconnect(this->mParentVisibleConn); - this->mParentVisibleConn = QObject::connect( + QObject::connect( this->mParentProxyWindow, &ProxyWindowBase::backerVisibilityChanged, this, - [this]() { - auto* pw = - this->mParentProxyWindow ? this->mParentProxyWindow->backingWindow() : nullptr; - if (!pw || !pw->isVisible() || !this->window) return; - this->window->setTransientParent(pw); - QObject::disconnect(this->mParentVisibleConn); - this->ProxyWindowBase::setVisible(true); - } + &ProxyFloatingWindow::onParentVisible ); return; } @@ -91,7 +108,14 @@ void ProxyFloatingWindow::setParentWindow(QObject* window) { return; } - QObject::disconnect(this->mParentVisibleConn); + if (this->mParentProxyWindow) { + QObject::disconnect(this->mParentProxyWindow, nullptr, this, nullptr); + } + + if (this->mParentWindow) { + QObject::disconnect(this->mParentWindow, nullptr, this, nullptr); + } + this->mParentWindow = nullptr; this->mParentProxyWindow = nullptr; @@ -104,7 +128,14 @@ void ProxyFloatingWindow::setParentWindow(QObject* window) { qmlWarning(this) << "parentWindow must be a quickshell window."; return; } + this->mParentWindow = window; + QObject::connect( + this->mParentWindow, + &QObject::destroyed, + this, + &ProxyFloatingWindow::onParentDestroyed + ); } } diff --git a/src/window/floatingwindow.hpp b/src/window/floatingwindow.hpp index 441bbbd2..f289c6a1 100644 --- a/src/window/floatingwindow.hpp +++ b/src/window/floatingwindow.hpp @@ -34,6 +34,10 @@ class ProxyFloatingWindow: public ProxyWindowBase { void maximumSizeChanged(); void titleChanged(); +private slots: + void onParentDestroyed(); + void onParentVisible(); + private: void onMinimumSizeChanged(); void onMaximumSizeChanged(); @@ -41,7 +45,6 @@ class ProxyFloatingWindow: public ProxyWindowBase { QObject* mParentWindow = nullptr; ProxyWindowBase* mParentProxyWindow = nullptr; - QMetaObject::Connection mParentVisibleConn; public: Q_OBJECT_BINDABLE_PROPERTY(