forked from quickshell/quickshell
		
	core/proxywindow: improve QsWindowAttached robustness
Can now track window parent window changes. Added tests.
This commit is contained in:
		
							parent
							
								
									539692bc11
								
							
						
					
					
						commit
						b6a79fe99c
					
				
					 7 changed files with 145 additions and 36 deletions
				
			
		| 
						 | 
					@ -28,7 +28,6 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent)
 | 
				
			||||||
    , mContentItem(new QQuickItem()) {
 | 
					    , mContentItem(new QQuickItem()) {
 | 
				
			||||||
	QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership);
 | 
						QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership);
 | 
				
			||||||
	this->mContentItem->setParent(this);
 | 
						this->mContentItem->setParent(this);
 | 
				
			||||||
	this->mContentItem->setProperty("__qs_proxywindow", QVariant::fromValue(this));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// clang-format off
 | 
						// clang-format off
 | 
				
			||||||
	QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged);
 | 
						QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged);
 | 
				
			||||||
| 
						 | 
					@ -84,7 +83,7 @@ void ProxyWindowBase::onReload(QObject* oldInstance) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ProxyWindowBase::postCompleteWindow() { this->setVisible(this->mVisible); }
 | 
					void ProxyWindowBase::postCompleteWindow() { this->setVisible(this->mVisible); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ProxiedWindow* ProxyWindowBase::createQQuickWindow() { return new ProxiedWindow(); }
 | 
					ProxiedWindow* ProxyWindowBase::createQQuickWindow() { return new ProxiedWindow(this); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ProxyWindowBase::createWindow() {
 | 
					void ProxyWindowBase::createWindow() {
 | 
				
			||||||
	if (this->window != nullptr) return;
 | 
						if (this->window != nullptr) return;
 | 
				
			||||||
| 
						 | 
					@ -375,9 +374,29 @@ QQmlListProperty<QObject> ProxyWindowBase::data() {
 | 
				
			||||||
void ProxyWindowBase::onWidthChanged() { this->mContentItem->setWidth(this->width()); }
 | 
					void ProxyWindowBase::onWidthChanged() { this->mContentItem->setWidth(this->width()); }
 | 
				
			||||||
void ProxyWindowBase::onHeightChanged() { this->mContentItem->setHeight(this->height()); }
 | 
					void ProxyWindowBase::onHeightChanged() { this->mContentItem->setHeight(this->height()); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ProxyWindowAttached::ProxyWindowAttached(QQuickItem* parent): QsWindowAttached(parent) {
 | 
				
			||||||
 | 
						this->updateWindow();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QObject* ProxyWindowAttached::window() const { return this->mWindow; }
 | 
					QObject* ProxyWindowAttached::window() const { return this->mWindow; }
 | 
				
			||||||
QQuickItem* ProxyWindowAttached::contentItem() const { return this->mWindow->contentItem(); }
 | 
					QQuickItem* ProxyWindowAttached::contentItem() const { return this->mWindow->contentItem(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ProxyWindowAttached::updateWindow() {
 | 
				
			||||||
 | 
						auto* window = static_cast<QQuickItem*>(this->parent())->window(); // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (auto* proxy = qobject_cast<ProxiedWindow*>(window)) {
 | 
				
			||||||
 | 
							this->setWindow(proxy->proxy());
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							this->setWindow(nullptr);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ProxyWindowAttached::setWindow(ProxyWindowBase* window) {
 | 
				
			||||||
 | 
						if (window == this->mWindow) return;
 | 
				
			||||||
 | 
						this->mWindow = window;
 | 
				
			||||||
 | 
						emit this->windowChanged();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ProxiedWindow::exposeEvent(QExposeEvent* event) {
 | 
					void ProxiedWindow::exposeEvent(QExposeEvent* event) {
 | 
				
			||||||
	this->QQuickWindow::exposeEvent(event);
 | 
						this->QQuickWindow::exposeEvent(event);
 | 
				
			||||||
	emit this->exposed();
 | 
						emit this->exposed();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -145,26 +145,36 @@ class ProxyWindowAttached: public QsWindowAttached {
 | 
				
			||||||
	Q_OBJECT;
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	explicit ProxyWindowAttached(ProxyWindowBase* window)
 | 
						explicit ProxyWindowAttached(QQuickItem* parent);
 | 
				
			||||||
	    : QsWindowAttached(window)
 | 
					 | 
				
			||||||
	    , mWindow(window) {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] QObject* window() const override;
 | 
						[[nodiscard]] QObject* window() const override;
 | 
				
			||||||
	[[nodiscard]] QQuickItem* contentItem() const override;
 | 
						[[nodiscard]] QQuickItem* contentItem() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						void updateWindow() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	ProxyWindowBase* mWindow;
 | 
						ProxyWindowBase* mWindow = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setWindow(ProxyWindowBase* window);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProxiedWindow: public QQuickWindow {
 | 
					class ProxiedWindow: public QQuickWindow {
 | 
				
			||||||
	Q_OBJECT;
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	explicit ProxiedWindow(QWindow* parent = nullptr): QQuickWindow(parent) {}
 | 
						explicit ProxiedWindow(ProxyWindowBase* proxy, QWindow* parent = nullptr)
 | 
				
			||||||
 | 
						    : QQuickWindow(parent)
 | 
				
			||||||
 | 
						    , mProxy(proxy) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] ProxyWindowBase* proxy() const { return this->mProxy; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
	void exposed();
 | 
						void exposed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
	void exposeEvent(QExposeEvent* event) override;
 | 
						void exposeEvent(QExposeEvent* event) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						ProxyWindowBase* mProxy;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,3 +5,4 @@ function (qs_test name)
 | 
				
			||||||
endfunction()
 | 
					endfunction()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
qs_test(popupwindow popupwindow.cpp)
 | 
					qs_test(popupwindow popupwindow.cpp)
 | 
				
			||||||
 | 
					qs_test(windowattached windowattached.cpp)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/window/test/windowattached.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/window/test/windowattached.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					#include "windowattached.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qquickitem.h>
 | 
				
			||||||
 | 
					#include <qsignalspy.h>
 | 
				
			||||||
 | 
					#include <qtest.h>
 | 
				
			||||||
 | 
					#include <qtestcase.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../proxywindow.hpp"
 | 
				
			||||||
 | 
					#include "../windowinterface.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TestWindowAttachment::attachedAfterReload() {
 | 
				
			||||||
 | 
						auto window = ProxyWindowBase();
 | 
				
			||||||
 | 
						auto item = QQuickItem();
 | 
				
			||||||
 | 
						item.setParentItem(window.contentItem());
 | 
				
			||||||
 | 
						window.reload(nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* attached = WindowInterface::qmlAttachedProperties(&item);
 | 
				
			||||||
 | 
						QCOMPARE_NE(attached, nullptr);
 | 
				
			||||||
 | 
						QCOMPARE(attached->window(), &window);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TestWindowAttachment::attachedBeforeReload() {
 | 
				
			||||||
 | 
						auto window = ProxyWindowBase();
 | 
				
			||||||
 | 
						auto item = QQuickItem();
 | 
				
			||||||
 | 
						item.setParentItem(window.contentItem());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* attached = WindowInterface::qmlAttachedProperties(&item);
 | 
				
			||||||
 | 
						QCOMPARE_NE(attached, nullptr);
 | 
				
			||||||
 | 
						QCOMPARE(attached->window(), nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto spy = QSignalSpy(attached, &QsWindowAttached::windowChanged);
 | 
				
			||||||
 | 
						window.reload(nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QCOMPARE(attached->window(), &window);
 | 
				
			||||||
 | 
						QCOMPARE(spy.length(), 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TestWindowAttachment::owningWindowChanged() {
 | 
				
			||||||
 | 
						auto window1 = ProxyWindowBase();
 | 
				
			||||||
 | 
						auto window2 = ProxyWindowBase();
 | 
				
			||||||
 | 
						window1.reload(nullptr);
 | 
				
			||||||
 | 
						window2.reload(nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto item = QQuickItem();
 | 
				
			||||||
 | 
						item.setParentItem(window1.contentItem());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* attached = WindowInterface::qmlAttachedProperties(&item);
 | 
				
			||||||
 | 
						QCOMPARE_NE(attached, nullptr);
 | 
				
			||||||
 | 
						QCOMPARE(attached->window(), &window1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto spy = QSignalSpy(attached, &QsWindowAttached::windowChanged);
 | 
				
			||||||
 | 
						item.setParentItem(window2.contentItem());
 | 
				
			||||||
 | 
						QCOMPARE(attached->window(), &window2);
 | 
				
			||||||
 | 
						// setParentItem changes the parent to nullptr before the new window.
 | 
				
			||||||
 | 
						QCOMPARE(spy.length(), 2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TestWindowAttachment::nonItemParents() {
 | 
				
			||||||
 | 
						auto window = ProxyWindowBase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto item = QQuickItem();
 | 
				
			||||||
 | 
						item.setParentItem(window.contentItem());
 | 
				
			||||||
 | 
						auto object = QObject(&item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						window.reload(nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* attached = WindowInterface::qmlAttachedProperties(&object);
 | 
				
			||||||
 | 
						QCOMPARE_NE(attached, nullptr);
 | 
				
			||||||
 | 
						QCOMPARE(attached->window(), &window);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QTEST_MAIN(TestWindowAttachment);
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/window/test/windowattached.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/window/test/windowattached.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestWindowAttachment: public QObject {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
						static void attachedAfterReload();
 | 
				
			||||||
 | 
						static void attachedBeforeReload();
 | 
				
			||||||
 | 
						static void owningWindowChanged();
 | 
				
			||||||
 | 
						static void nonItemParents();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -2,33 +2,19 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
#include <qquickitem.h>
 | 
					#include <qquickitem.h>
 | 
				
			||||||
#include <qvariant.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "proxywindow.hpp"
 | 
					#include "proxywindow.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QsWindowAttached* WindowInterface::qmlAttachedProperties(QObject* object) {
 | 
					QsWindowAttached::QsWindowAttached(QQuickItem* parent): QObject(parent) {
 | 
				
			||||||
	auto* visualRoot = qobject_cast<QQuickItem*>(object);
 | 
						QObject::connect(parent, &QQuickItem::windowChanged, this, &QsWindowAttached::updateWindow);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
	ProxyWindowBase* proxy = nullptr;
 | 
					
 | 
				
			||||||
	while (visualRoot != nullptr) {
 | 
					QsWindowAttached* WindowInterface::qmlAttachedProperties(QObject* object) {
 | 
				
			||||||
		proxy = visualRoot->property("__qs_proxywindow").value<ProxyWindowBase*>();
 | 
						while (object && !qobject_cast<QQuickItem*>(object)) {
 | 
				
			||||||
 | 
							object = object->parent();
 | 
				
			||||||
		if (proxy) break;
 | 
						}
 | 
				
			||||||
		visualRoot = visualRoot->parentItem();
 | 
					
 | 
				
			||||||
	};
 | 
						if (!object) return nullptr;
 | 
				
			||||||
 | 
						auto* item = static_cast<QQuickItem*>(object); // NOLINT
 | 
				
			||||||
	if (!proxy) return nullptr;
 | 
						return new ProxyWindowAttached(item);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto v = proxy->property("__qs_window_attached");
 | 
					 | 
				
			||||||
	if (auto* attached = v.value<QsWindowAttached*>()) {
 | 
					 | 
				
			||||||
		return attached;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto* attached = new ProxyWindowAttached(proxy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attached) {
 | 
					 | 
				
			||||||
		proxy->setProperty("__qs_window_attached", QVariant::fromValue(attached));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return attached;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -142,14 +142,20 @@ signals:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QsWindowAttached: public QObject {
 | 
					class QsWindowAttached: public QObject {
 | 
				
			||||||
	Q_OBJECT;
 | 
						Q_OBJECT;
 | 
				
			||||||
	Q_PROPERTY(QObject* window READ window CONSTANT);
 | 
						Q_PROPERTY(QObject* window READ window NOTIFY windowChanged);
 | 
				
			||||||
	Q_PROPERTY(QQuickItem* contentItem READ contentItem CONSTANT);
 | 
						Q_PROPERTY(QQuickItem* contentItem READ contentItem NOTIFY windowChanged);
 | 
				
			||||||
	QML_ANONYMOUS;
 | 
						QML_ANONYMOUS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	[[nodiscard]] virtual QObject* window() const = 0;
 | 
						[[nodiscard]] virtual QObject* window() const = 0;
 | 
				
			||||||
	[[nodiscard]] virtual QQuickItem* contentItem() const = 0;
 | 
						[[nodiscard]] virtual QQuickItem* contentItem() const = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signals:
 | 
				
			||||||
 | 
						void windowChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected slots:
 | 
				
			||||||
 | 
						virtual void updateWindow() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
	explicit QsWindowAttached(QObject* parent): QObject(parent) {}
 | 
						explicit QsWindowAttached(QQuickItem* parent);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue