diff --git a/src/window/floatingwindow.cpp b/src/window/floatingwindow.cpp index a0c9fdda..d41dd241 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,58 @@ 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) { + if (this->mParentProxyWindow) { + QObject::disconnect( + this->mParentProxyWindow, + &ProxyWindowBase::backerVisibilityChanged, + this, + &ProxyFloatingWindow::onParentVisible + ); + } + 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 { + QObject::connect( + this->mParentProxyWindow, + &ProxyWindowBase::backerVisibilityChanged, + this, + &ProxyFloatingWindow::onParentVisible + ); + return; + } + } + + this->ProxyWindowBase::setVisible(true); +} + void ProxyFloatingWindow::trySetWidth(qint32 implicitWidth) { if (!this->window->isVisible()) { this->ProxyWindowBase::trySetWidth(implicitWidth); @@ -46,6 +98,47 @@ void ProxyFloatingWindow::onMaximumSizeChanged() { emit this->maximumSizeChanged(); } +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; + } + + 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; + + if (window) { + if (auto* proxy = qobject_cast(window)) { + this->mParentProxyWindow = proxy; + } else if (auto* iface = qobject_cast(window)) { + this->mParentProxyWindow = iface->proxyWindow(); + } else { + qmlWarning(this) << "parentWindow must be a quickshell window."; + return; + } + + this->mParentWindow = window; + QObject::connect( + this->mParentWindow, + &QObject::destroyed, + this, + &ProxyFloatingWindow::onParentDestroyed + ); + } +} + // FloatingWindowInterface FloatingWindowInterface::FloatingWindowInterface(QObject* parent) @@ -169,3 +262,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..f289c6a1 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 setVisible(bool visible) 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. @@ -30,11 +34,18 @@ class ProxyFloatingWindow: public ProxyWindowBase { void maximumSizeChanged(); void titleChanged(); +private slots: + void onParentDestroyed(); + void onParentVisible(); + private: void onMinimumSizeChanged(); void onMaximumSizeChanged(); void onTitleChanged(); + QObject* mParentWindow = nullptr; + ProxyWindowBase* mParentProxyWindow = nullptr; + public: Q_OBJECT_BINDABLE_PROPERTY( ProxyFloatingWindow, @@ -75,6 +86,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 +117,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();