core/window: add parentWindow property to FloatingWindow

This commit is contained in:
bbedward 2025-11-26 10:29:51 -05:00 committed by outfoxxed
parent ceac3c6cfa
commit b4e71cb2c0
No known key found for this signature in database
GPG key ID: 4C88A185FB89301E
5 changed files with 161 additions and 2 deletions

View file

@ -4,6 +4,7 @@ project(quickshell VERSION "0.2.1" LANGUAGES CXX C)
set(UNRELEASED_FEATURES set(UNRELEASED_FEATURES
"network.2" "network.2"
"colorquant-imagerect" "colorquant-imagerect"
"window-parent"
) )
set(QT_MIN_VERSION "6.6.0") set(QT_MIN_VERSION "6.6.0")

View file

@ -30,6 +30,7 @@ set shell id.
- Added ext-background-effect window blur support. - Added ext-background-effect window blur support.
- Added per-corner radius support to Region. - Added per-corner radius support to Region.
- Added ColorQuantizer region selection. - Added ColorQuantizer region selection.
- Added dialog window support to FloatingWindow.
## Other Changes ## Other Changes

View file

@ -3,7 +3,7 @@
#include <qnamespace.h> #include <qnamespace.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlengine.h> #include <qqmlengine.h>
#include <qqmllist.h> #include <qqmlinfo.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
#include <qwindow.h> #include <qwindow.h>
@ -11,6 +11,27 @@
#include "proxywindow.hpp" #include "proxywindow.hpp"
#include "windowinterface.hpp" #include "windowinterface.hpp"
ProxyFloatingWindow::ProxyFloatingWindow(QObject* parent): ProxyWindowBase(parent) {
this->bTargetVisible.setBinding([this] {
if (!this->bWantsVisible) return false;
auto* parent = this->bParentProxyWindow.value();
if (!parent) return true;
return parent->bindableBackerVisibility().value();
});
}
void ProxyFloatingWindow::targetVisibleChanged() {
if (this->window && this->bParentProxyWindow) {
auto* bw = this->bParentProxyWindow.value()->backingWindow();
if (bw != this->window->transientParent()) {
this->window->setTransientParent(bw);
}
}
this->ProxyWindowBase::setVisible(this->bTargetVisible);
}
void ProxyFloatingWindow::connectWindow() { void ProxyFloatingWindow::connectWindow() {
this->ProxyWindowBase::connectWindow(); this->ProxyWindowBase::connectWindow();
@ -19,6 +40,25 @@ void ProxyFloatingWindow::connectWindow() {
this->window->setMaximumSize(this->bMaximumSize); this->window->setMaximumSize(this->bMaximumSize);
} }
void ProxyFloatingWindow::completeWindow() {
this->ProxyWindowBase::completeWindow();
auto* parent = this->bParentProxyWindow.value();
this->window->setTransientParent(parent ? parent->backingWindow() : nullptr);
}
void ProxyFloatingWindow::postCompleteWindow() {
this->ProxyWindowBase::setVisible(this->bTargetVisible);
}
void ProxyFloatingWindow::onParentDestroyed() {
this->mParentWindow = nullptr;
this->bParentProxyWindow = nullptr;
emit this->parentWindowChanged();
}
void ProxyFloatingWindow::setVisible(bool visible) { this->bWantsVisible = visible; }
void ProxyFloatingWindow::trySetWidth(qint32 implicitWidth) { void ProxyFloatingWindow::trySetWidth(qint32 implicitWidth) {
if (!this->window->isVisible()) { if (!this->window->isVisible()) {
this->ProxyWindowBase::trySetWidth(implicitWidth); this->ProxyWindowBase::trySetWidth(implicitWidth);
@ -46,6 +86,42 @@ void ProxyFloatingWindow::onMaximumSizeChanged() {
emit this->maximumSizeChanged(); 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->bParentProxyWindow) {
QObject::disconnect(this->bParentProxyWindow, nullptr, this, nullptr);
}
if (this->mParentWindow) {
QObject::disconnect(this->mParentWindow, nullptr, this, nullptr);
}
this->mParentWindow = nullptr;
this->bParentProxyWindow = nullptr;
if (auto* proxy = ProxyWindowBase::forObject(window)) {
this->mParentWindow = window;
this->bParentProxyWindow = proxy;
QObject::connect(
this->mParentWindow,
&QObject::destroyed,
this,
&ProxyFloatingWindow::onParentDestroyed
);
}
emit this->parentWindowChanged();
}
// FloatingWindowInterface // FloatingWindowInterface
FloatingWindowInterface::FloatingWindowInterface(QObject* parent) FloatingWindowInterface::FloatingWindowInterface(QObject* parent)
@ -57,6 +133,7 @@ FloatingWindowInterface::FloatingWindowInterface(QObject* parent)
QObject::connect(this->window, &ProxyFloatingWindow::titleChanged, this, &FloatingWindowInterface::titleChanged); QObject::connect(this->window, &ProxyFloatingWindow::titleChanged, this, &FloatingWindowInterface::titleChanged);
QObject::connect(this->window, &ProxyFloatingWindow::minimumSizeChanged, this, &FloatingWindowInterface::minimumSizeChanged); QObject::connect(this->window, &ProxyFloatingWindow::minimumSizeChanged, this, &FloatingWindowInterface::minimumSizeChanged);
QObject::connect(this->window, &ProxyFloatingWindow::maximumSizeChanged, this, &FloatingWindowInterface::maximumSizeChanged); QObject::connect(this->window, &ProxyFloatingWindow::maximumSizeChanged, this, &FloatingWindowInterface::maximumSizeChanged);
QObject::connect(this->window, &ProxyFloatingWindow::parentWindowChanged, this, &FloatingWindowInterface::parentWindowChanged);
QObject::connect(this->window, &ProxyWindowBase::windowConnected, this, &FloatingWindowInterface::onWindowConnected); QObject::connect(this->window, &ProxyWindowBase::windowConnected, this, &FloatingWindowInterface::onWindowConnected);
// clang-format on // clang-format on
} }
@ -169,3 +246,9 @@ bool FloatingWindowInterface::startSystemResize(Qt::Edges edges) const {
if (!qw) return false; if (!qw) return false;
return qw->startSystemResize(edges); return qw->startSystemResize(edges);
} }
QObject* FloatingWindowInterface::parentWindow() const { return this->window->parentWindow(); }
void FloatingWindowInterface::setParentWindow(QObject* window) {
this->window->setParentWindow(window);
}

View file

@ -16,9 +16,15 @@ class ProxyFloatingWindow: public ProxyWindowBase {
Q_OBJECT; Q_OBJECT;
public: public:
explicit ProxyFloatingWindow(QObject* parent = nullptr): ProxyWindowBase(parent) {} explicit ProxyFloatingWindow(QObject* parent = nullptr);
void connectWindow() override; void connectWindow() override;
void completeWindow() override;
void postCompleteWindow() 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 // 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. // which is awful so we disable it for floating windows.
@ -29,11 +35,28 @@ signals:
void minimumSizeChanged(); void minimumSizeChanged();
void maximumSizeChanged(); void maximumSizeChanged();
void titleChanged(); void titleChanged();
void parentWindowChanged();
private slots:
void onParentDestroyed();
private: private:
void onMinimumSizeChanged(); void onMinimumSizeChanged();
void onMaximumSizeChanged(); void onMaximumSizeChanged();
void onTitleChanged(); void onTitleChanged();
void targetVisibleChanged();
QObject* mParentWindow = nullptr;
Q_OBJECT_BINDABLE_PROPERTY(ProxyFloatingWindow, ProxyWindowBase*, bParentProxyWindow);
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(ProxyFloatingWindow, bool, bWantsVisible, true);
Q_OBJECT_BINDABLE_PROPERTY(
ProxyFloatingWindow,
bool,
bTargetVisible,
&ProxyFloatingWindow::targetVisibleChanged
);
public: public:
Q_OBJECT_BINDABLE_PROPERTY( Q_OBJECT_BINDABLE_PROPERTY(
@ -75,6 +98,11 @@ class FloatingWindowInterface: public WindowInterface {
Q_PROPERTY(bool maximized READ isMaximized WRITE setMaximized NOTIFY maximizedChanged); Q_PROPERTY(bool maximized READ isMaximized WRITE setMaximized NOTIFY maximizedChanged);
/// Whether the window is currently fullscreen. /// Whether the window is currently fullscreen.
Q_PROPERTY(bool fullscreen READ isFullscreen WRITE setFullscreen NOTIFY fullscreenChanged); 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 NOTIFY parentWindowChanged);
// clang-format on // clang-format on
QML_NAMED_ELEMENT(FloatingWindow); QML_NAMED_ELEMENT(FloatingWindow);
@ -101,6 +129,9 @@ public:
/// Start a system resize operation. Must be called during a pointer press/drag. /// Start a system resize operation. Must be called during a pointer press/drag.
Q_INVOKABLE [[nodiscard]] bool startSystemResize(Qt::Edges edges) const; Q_INVOKABLE [[nodiscard]] bool startSystemResize(Qt::Edges edges) const;
[[nodiscard]] QObject* parentWindow() const;
void setParentWindow(QObject* window);
signals: signals:
void minimumSizeChanged(); void minimumSizeChanged();
void maximumSizeChanged(); void maximumSizeChanged();
@ -108,6 +139,7 @@ signals:
void minimizedChanged(); void minimizedChanged();
void maximizedChanged(); void maximizedChanged();
void fullscreenChanged(); void fullscreenChanged();
void parentWindowChanged();
private slots: private slots:
void onWindowConnected(); void onWindowConnected();

View file

@ -0,0 +1,42 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Fusion
import Quickshell
Scope {
FloatingWindow {
id: control
color: contentItem.palette.window
ColumnLayout {
CheckBox {
id: parentCb
text: "Show parent"
}
CheckBox {
id: dialogCb
text: "Show dialog"
}
}
}
FloatingWindow {
id: parentw
Text {
text: "parent"
}
visible: parentCb.checked
color: contentItem.palette.window
FloatingWindow {
id: dialog
parentWindow: parentw
visible: dialogCb.checked
color: contentItem.palette.window
Text {
text: "dialog"
}
}
}
}