feat: use an intermediary content item for proxy windows (fixes in desc)

fix: combo boxes not opening after a soft reload
fix: anchors breaking after a soft reload
This commit is contained in:
outfoxxed 2024-02-17 04:13:37 -08:00
parent 5e58f0ba9f
commit ed62193978
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
2 changed files with 20 additions and 116 deletions

View file

@ -12,6 +12,14 @@
#include "region.hpp" #include "region.hpp"
#include "reload.hpp" #include "reload.hpp"
ProxyWindowBase::ProxyWindowBase(QObject* parent): Reloadable(parent) {
this->contentItem = new QQuickItem(); // NOLINT
this->contentItem->setParent(this);
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged);
QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onHeightChanged);
}
ProxyWindowBase::~ProxyWindowBase() { ProxyWindowBase::~ProxyWindowBase() {
if (this->window != nullptr) { if (this->window != nullptr) {
this->window->deleteLater(); this->window->deleteLater();
@ -29,20 +37,12 @@ void ProxyWindowBase::onReload(QObject* oldInstance) {
this->setupWindow(); this->setupWindow();
for (auto* child: this->pendingChildren) { Reloadable::reloadRecursive(this->contentItem, oldInstance);
Reloadable::reloadRecursive(child, oldInstance);
}
auto backer = this->dataBacker(); this->contentItem->setParentItem(this->window->contentItem());
for (auto* child: this->pendingChildren) {
// Reparent QQuickItems to the content element,
// while leaving QObjects parented to the proxy window.
if (qobject_cast<QQuickItem*>(child) != nullptr) {
backer.append(&backer, child);
}
}
this->pendingChildren.clear(); this->contentItem->setWidth(this->width());
this->contentItem->setHeight(this->height());
emit this->windowConnected(); emit this->windowConnected();
this->window->setVisible(this->mVisible); this->window->setVisible(this->mVisible);
@ -69,9 +69,7 @@ void ProxyWindowBase::setupWindow() {
QQuickWindow* ProxyWindowBase::disownWindow() { QQuickWindow* ProxyWindowBase::disownWindow() {
QObject::disconnect(this->window, nullptr, this, nullptr); QObject::disconnect(this->window, nullptr, this, nullptr);
auto data = this->data(); this->contentItem->setParentItem(nullptr);
ProxyWindowBase::dataClear(&data);
data.clear(&data);
auto* window = this->window; auto* window = this->window;
this->window = nullptr; this->window = nullptr;
@ -162,100 +160,13 @@ void ProxyWindowBase::updateMask() {
this->window->setMask(mask); this->window->setMask(mask);
} }
// see:
// https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/quick/items/qquickwindow.cpp
// https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/quick/items/qquickitem.cpp
//
// relevant functions are private so we call them via the property
QQmlListProperty<QObject> ProxyWindowBase::data() { QQmlListProperty<QObject> ProxyWindowBase::data() {
return QQmlListProperty<QObject>( return this->contentItem->property("data").value<QQmlListProperty<QObject>>();
this,
nullptr,
ProxyWindowBase::dataAppend,
ProxyWindowBase::dataCount,
ProxyWindowBase::dataAt,
ProxyWindowBase::dataClear,
ProxyWindowBase::dataReplace,
ProxyWindowBase::dataRemoveLast
);
} }
QQmlListProperty<QObject> ProxyWindowBase::dataBacker() { void ProxyWindowBase::onWidthChanged() { this->contentItem->setWidth(this->width()); }
return this->window->property("data").value<QQmlListProperty<QObject>>();
}
void ProxyWindowBase::dataAppend(QQmlListProperty<QObject>* prop, QObject* obj) { void ProxyWindowBase::onHeightChanged() { this->contentItem->setHeight(this->height()); }
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
if (self->window == nullptr) {
if (obj != nullptr) {
obj->setParent(self);
self->pendingChildren.append(obj);
}
} else {
auto backer = self->dataBacker();
backer.append(&backer, obj);
}
}
qsizetype ProxyWindowBase::dataCount(QQmlListProperty<QObject>* prop) {
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
if (self->window == nullptr) {
return self->pendingChildren.count();
} else {
auto backer = self->dataBacker();
return backer.count(&backer);
}
}
QObject* ProxyWindowBase::dataAt(QQmlListProperty<QObject>* prop, qsizetype i) {
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
if (self->window == nullptr) {
return self->pendingChildren.at(i);
} else {
auto backer = self->dataBacker();
return backer.at(&backer, i);
}
}
void ProxyWindowBase::dataClear(QQmlListProperty<QObject>* prop) {
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
if (self->window == nullptr) {
self->pendingChildren.clear();
} else {
auto backer = self->dataBacker();
backer.clear(&backer);
}
}
void ProxyWindowBase::dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj) {
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
if (self->window == nullptr) {
if (obj != nullptr) {
obj->setParent(self);
self->pendingChildren.replace(i, obj);
}
} else {
auto backer = self->dataBacker();
backer.replace(&backer, i, obj);
}
}
void ProxyWindowBase::dataRemoveLast(QQmlListProperty<QObject>* prop) {
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
if (self->window == nullptr) {
self->pendingChildren.removeLast();
} else {
auto backer = self->dataBacker();
backer.removeLast(&backer);
}
}
void ProxyFloatingWindow::setWidth(qint32 width) { void ProxyFloatingWindow::setWidth(qint32 width) {
if (this->window == nullptr || !this->window->isVisible()) this->ProxyWindowBase::setWidth(width); if (this->window == nullptr || !this->window->isVisible()) this->ProxyWindowBase::setWidth(width);

View file

@ -100,7 +100,7 @@ class ProxyWindowBase: public Reloadable {
Q_CLASSINFO("DefaultProperty", "data"); Q_CLASSINFO("DefaultProperty", "data");
public: public:
explicit ProxyWindowBase(QObject* parent = nullptr): Reloadable(parent) {} explicit ProxyWindowBase(QObject* parent = nullptr);
~ProxyWindowBase() override; ~ProxyWindowBase() override;
ProxyWindowBase(ProxyWindowBase&) = delete; ProxyWindowBase(ProxyWindowBase&) = delete;
@ -144,6 +144,8 @@ signals:
private slots: private slots:
void onMaskChanged(); void onMaskChanged();
void onWidthChanged();
void onHeightChanged();
protected: protected:
bool mVisible = false; bool mVisible = false;
@ -152,19 +154,10 @@ protected:
QColor mColor = Qt::white; QColor mColor = Qt::white;
PendingRegion* mMask = nullptr; PendingRegion* mMask = nullptr;
QQuickWindow* window = nullptr; QQuickWindow* window = nullptr;
QQuickItem* contentItem = nullptr;
private: private:
void updateMask(); void updateMask();
QQmlListProperty<QObject> dataBacker();
static void dataAppend(QQmlListProperty<QObject>* prop, QObject* obj);
static qsizetype dataCount(QQmlListProperty<QObject>* prop);
static QObject* dataAt(QQmlListProperty<QObject>* prop, qsizetype i);
static void dataClear(QQmlListProperty<QObject>* prop);
static void dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj);
static void dataRemoveLast(QQmlListProperty<QObject>* prop);
QVector<QObject*> pendingChildren;
}; };
// qt attempts to resize the window but fails because wayland // qt attempts to resize the window but fails because wayland