core/proxywindow: improve QsWindowAttached robustness
Can now track window parent window changes. Added tests.
This commit is contained in:
parent
539692bc11
commit
91eb9641b5
|
@ -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();
|
||||||
|
};
|
|
@ -6,29 +6,16 @@
|
||||||
|
|
||||||
#include "proxywindow.hpp"
|
#include "proxywindow.hpp"
|
||||||
|
|
||||||
|
QsWindowAttached::QsWindowAttached(QQuickItem* parent): QObject(parent) {
|
||||||
|
QObject::connect(parent, &QQuickItem::windowChanged, this, &QsWindowAttached::updateWindow);
|
||||||
|
}
|
||||||
|
|
||||||
QsWindowAttached* WindowInterface::qmlAttachedProperties(QObject* object) {
|
QsWindowAttached* WindowInterface::qmlAttachedProperties(QObject* object) {
|
||||||
auto* visualRoot = qobject_cast<QQuickItem*>(object);
|
while (object && !qobject_cast<QQuickItem*>(object)) {
|
||||||
|
object = object->parent();
|
||||||
ProxyWindowBase* proxy = nullptr;
|
|
||||||
while (visualRoot != nullptr) {
|
|
||||||
proxy = visualRoot->property("__qs_proxywindow").value<ProxyWindowBase*>();
|
|
||||||
|
|
||||||
if (proxy) break;
|
|
||||||
visualRoot = visualRoot->parentItem();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!proxy) return nullptr;
|
|
||||||
|
|
||||||
auto v = proxy->property("__qs_window_attached");
|
|
||||||
if (auto* attached = v.value<QsWindowAttached*>()) {
|
|
||||||
return attached;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* attached = new ProxyWindowAttached(proxy);
|
if (!object) return nullptr;
|
||||||
|
auto* item = static_cast<QQuickItem*>(object); // NOLINT
|
||||||
if (attached) {
|
return new ProxyWindowAttached(item);
|
||||||
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…
Reference in a new issue