Skip to content

Commit 91eb964

Browse files
committed
core/proxywindow: improve QsWindowAttached robustness
Can now track window parent window changes. Added tests.
1 parent 539692b commit 91eb964

File tree

7 files changed

+142
-32
lines changed

7 files changed

+142
-32
lines changed

src/window/proxywindow.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent)
2828
, mContentItem(new QQuickItem()) {
2929
QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership);
3030
this->mContentItem->setParent(this);
31-
this->mContentItem->setProperty("__qs_proxywindow", QVariant::fromValue(this));
3231

3332
// clang-format off
3433
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged);
@@ -84,7 +83,7 @@ void ProxyWindowBase::onReload(QObject* oldInstance) {
8483

8584
void ProxyWindowBase::postCompleteWindow() { this->setVisible(this->mVisible); }
8685

87-
ProxiedWindow* ProxyWindowBase::createQQuickWindow() { return new ProxiedWindow(); }
86+
ProxiedWindow* ProxyWindowBase::createQQuickWindow() { return new ProxiedWindow(this); }
8887

8988
void ProxyWindowBase::createWindow() {
9089
if (this->window != nullptr) return;
@@ -375,9 +374,29 @@ QQmlListProperty<QObject> ProxyWindowBase::data() {
375374
void ProxyWindowBase::onWidthChanged() { this->mContentItem->setWidth(this->width()); }
376375
void ProxyWindowBase::onHeightChanged() { this->mContentItem->setHeight(this->height()); }
377376

377+
ProxyWindowAttached::ProxyWindowAttached(QQuickItem* parent): QsWindowAttached(parent) {
378+
this->updateWindow();
379+
}
380+
378381
QObject* ProxyWindowAttached::window() const { return this->mWindow; }
379382
QQuickItem* ProxyWindowAttached::contentItem() const { return this->mWindow->contentItem(); }
380383

384+
void ProxyWindowAttached::updateWindow() {
385+
auto* window = static_cast<QQuickItem*>(this->parent())->window(); // NOLINT
386+
387+
if (auto* proxy = qobject_cast<ProxiedWindow*>(window)) {
388+
this->setWindow(proxy->proxy());
389+
} else {
390+
this->setWindow(nullptr);
391+
}
392+
}
393+
394+
void ProxyWindowAttached::setWindow(ProxyWindowBase* window) {
395+
if (window == this->mWindow) return;
396+
this->mWindow = window;
397+
emit this->windowChanged();
398+
}
399+
381400
void ProxiedWindow::exposeEvent(QExposeEvent* event) {
382401
this->QQuickWindow::exposeEvent(event);
383402
emit this->exposed();

src/window/proxywindow.hpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,26 +145,36 @@ class ProxyWindowAttached: public QsWindowAttached {
145145
Q_OBJECT;
146146

147147
public:
148-
explicit ProxyWindowAttached(ProxyWindowBase* window)
149-
: QsWindowAttached(window)
150-
, mWindow(window) {}
148+
explicit ProxyWindowAttached(QQuickItem* parent);
151149

152150
[[nodiscard]] QObject* window() const override;
153151
[[nodiscard]] QQuickItem* contentItem() const override;
154152

153+
protected:
154+
void updateWindow() override;
155+
155156
private:
156-
ProxyWindowBase* mWindow;
157+
ProxyWindowBase* mWindow = nullptr;
158+
159+
void setWindow(ProxyWindowBase* window);
157160
};
158161

159162
class ProxiedWindow: public QQuickWindow {
160163
Q_OBJECT;
161164

162165
public:
163-
explicit ProxiedWindow(QWindow* parent = nullptr): QQuickWindow(parent) {}
166+
explicit ProxiedWindow(ProxyWindowBase* proxy, QWindow* parent = nullptr)
167+
: QQuickWindow(parent)
168+
, mProxy(proxy) {}
169+
170+
[[nodiscard]] ProxyWindowBase* proxy() const { return this->mProxy; }
164171

165172
signals:
166173
void exposed();
167174

168175
protected:
169176
void exposeEvent(QExposeEvent* event) override;
177+
178+
private:
179+
ProxyWindowBase* mProxy;
170180
};

src/window/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ function (qs_test name)
55
endfunction()
66

77
qs_test(popupwindow popupwindow.cpp)
8+
qs_test(windowattached windowattached.cpp)

src/window/test/windowattached.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "windowattached.hpp"
2+
3+
#include <qobject.h>
4+
#include <qquickitem.h>
5+
#include <qsignalspy.h>
6+
#include <qtest.h>
7+
#include <qtestcase.h>
8+
9+
#include "../proxywindow.hpp"
10+
#include "../windowinterface.hpp"
11+
12+
void TestWindowAttachment::attachedAfterReload() {
13+
auto window = ProxyWindowBase();
14+
auto item = QQuickItem();
15+
item.setParentItem(window.contentItem());
16+
window.reload(nullptr);
17+
18+
auto* attached = WindowInterface::qmlAttachedProperties(&item);
19+
QCOMPARE_NE(attached, nullptr);
20+
QCOMPARE(attached->window(), &window);
21+
}
22+
23+
void TestWindowAttachment::attachedBeforeReload() {
24+
auto window = ProxyWindowBase();
25+
auto item = QQuickItem();
26+
item.setParentItem(window.contentItem());
27+
28+
auto* attached = WindowInterface::qmlAttachedProperties(&item);
29+
QCOMPARE_NE(attached, nullptr);
30+
QCOMPARE(attached->window(), nullptr);
31+
32+
auto spy = QSignalSpy(attached, &QsWindowAttached::windowChanged);
33+
window.reload(nullptr);
34+
35+
QCOMPARE(attached->window(), &window);
36+
QCOMPARE(spy.length(), 1);
37+
}
38+
39+
void TestWindowAttachment::owningWindowChanged() {
40+
auto window1 = ProxyWindowBase();
41+
auto window2 = ProxyWindowBase();
42+
window1.reload(nullptr);
43+
window2.reload(nullptr);
44+
45+
auto item = QQuickItem();
46+
item.setParentItem(window1.contentItem());
47+
48+
auto* attached = WindowInterface::qmlAttachedProperties(&item);
49+
QCOMPARE_NE(attached, nullptr);
50+
QCOMPARE(attached->window(), &window1);
51+
52+
auto spy = QSignalSpy(attached, &QsWindowAttached::windowChanged);
53+
item.setParentItem(window2.contentItem());
54+
QCOMPARE(attached->window(), &window2);
55+
// setParentItem changes the parent to nullptr before the new window.
56+
QCOMPARE(spy.length(), 2);
57+
}
58+
59+
void TestWindowAttachment::nonItemParents() {
60+
auto window = ProxyWindowBase();
61+
62+
auto item = QQuickItem();
63+
item.setParentItem(window.contentItem());
64+
auto object = QObject(&item);
65+
66+
window.reload(nullptr);
67+
68+
auto* attached = WindowInterface::qmlAttachedProperties(&object);
69+
QCOMPARE_NE(attached, nullptr);
70+
QCOMPARE(attached->window(), &window);
71+
}
72+
73+
QTEST_MAIN(TestWindowAttachment);

src/window/test/windowattached.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
3+
#include <qobject.h>
4+
#include <qtmetamacros.h>
5+
6+
class TestWindowAttachment: public QObject {
7+
Q_OBJECT;
8+
9+
private slots:
10+
static void attachedAfterReload();
11+
static void attachedBeforeReload();
12+
static void owningWindowChanged();
13+
static void nonItemParents();
14+
};

src/window/windowinterface.cpp

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,16 @@
66

77
#include "proxywindow.hpp"
88

9-
QsWindowAttached* WindowInterface::qmlAttachedProperties(QObject* object) {
10-
auto* visualRoot = qobject_cast<QQuickItem*>(object);
11-
12-
ProxyWindowBase* proxy = nullptr;
13-
while (visualRoot != nullptr) {
14-
proxy = visualRoot->property("__qs_proxywindow").value<ProxyWindowBase*>();
15-
16-
if (proxy) break;
17-
visualRoot = visualRoot->parentItem();
18-
};
19-
20-
if (!proxy) return nullptr;
21-
22-
auto v = proxy->property("__qs_window_attached");
23-
if (auto* attached = v.value<QsWindowAttached*>()) {
24-
return attached;
25-
}
26-
27-
auto* attached = new ProxyWindowAttached(proxy);
9+
QsWindowAttached::QsWindowAttached(QQuickItem* parent): QObject(parent) {
10+
QObject::connect(parent, &QQuickItem::windowChanged, this, &QsWindowAttached::updateWindow);
11+
}
2812

29-
if (attached) {
30-
proxy->setProperty("__qs_window_attached", QVariant::fromValue(attached));
13+
QsWindowAttached* WindowInterface::qmlAttachedProperties(QObject* object) {
14+
while (object && !qobject_cast<QQuickItem*>(object)) {
15+
object = object->parent();
3116
}
3217

33-
return attached;
18+
if (!object) return nullptr;
19+
auto* item = static_cast<QQuickItem*>(object); // NOLINT
20+
return new ProxyWindowAttached(item);
3421
}

src/window/windowinterface.hpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,20 @@ class WindowInterface: public Reloadable {
142142

143143
class QsWindowAttached: public QObject {
144144
Q_OBJECT;
145-
Q_PROPERTY(QObject* window READ window CONSTANT);
146-
Q_PROPERTY(QQuickItem* contentItem READ contentItem CONSTANT);
145+
Q_PROPERTY(QObject* window READ window NOTIFY windowChanged);
146+
Q_PROPERTY(QQuickItem* contentItem READ contentItem NOTIFY windowChanged);
147147
QML_ANONYMOUS;
148148

149149
public:
150150
[[nodiscard]] virtual QObject* window() const = 0;
151151
[[nodiscard]] virtual QQuickItem* contentItem() const = 0;
152152

153+
signals:
154+
void windowChanged();
155+
156+
protected slots:
157+
virtual void updateWindow() = 0;
158+
153159
protected:
154-
explicit QsWindowAttached(QObject* parent): QObject(parent) {}
160+
explicit QsWindowAttached(QQuickItem* parent);
155161
};

0 commit comments

Comments
 (0)