forked from quickshell/quickshell
feat: implement soft reloading
This commit is contained in:
parent
ba1e18a125
commit
362789fc46
|
@ -27,6 +27,9 @@ qt_add_executable(qtshell
|
||||||
src/cpp/shell.cpp
|
src/cpp/shell.cpp
|
||||||
src/cpp/variants.cpp
|
src/cpp/variants.cpp
|
||||||
src/cpp/rootwrapper.cpp
|
src/cpp/rootwrapper.cpp
|
||||||
|
src/cpp/proxywindow.cpp
|
||||||
|
src/cpp/scavenge.cpp
|
||||||
|
src/cpp/rootwrapper.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(qtshell URI QtShell)
|
qt_add_qml_module(qtshell URI QtShell)
|
||||||
|
|
114
src/cpp/proxywindow.cpp
Normal file
114
src/cpp/proxywindow.cpp
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#include "proxywindow.hpp"
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmllist.h>
|
||||||
|
#include <qquickitem.h>
|
||||||
|
#include <qquickwindow.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
ProxyWindowBase::~ProxyWindowBase() {
|
||||||
|
if (this->window != nullptr) {
|
||||||
|
this->window->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::earlyInit(QObject* old) {
|
||||||
|
auto* oldpw = qobject_cast<ProxyWindowBase*>(old);
|
||||||
|
|
||||||
|
if (oldpw == nullptr || oldpw->window == nullptr) {
|
||||||
|
this->window = new QQuickWindow();
|
||||||
|
} else {
|
||||||
|
this->window = oldpw->disownWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickWindow* ProxyWindowBase::disownWindow() {
|
||||||
|
auto data = this->data();
|
||||||
|
ProxyWindowBase::dataClear(&data);
|
||||||
|
data.clear(&data);
|
||||||
|
|
||||||
|
auto* window = this->window;
|
||||||
|
this->window = nullptr;
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE
|
||||||
|
#define PROXYPROP(type, get, set) \
|
||||||
|
type ProxyWindowBase::get() { return this->window->get(); } \
|
||||||
|
void ProxyWindowBase::set(type value) { this->window->set(value); }
|
||||||
|
|
||||||
|
PROXYPROP(bool, isVisible, setVisible);
|
||||||
|
PROXYPROP(qint32, width, setWidth);
|
||||||
|
PROXYPROP(qint32, height, setHeight);
|
||||||
|
PROXYPROP(QColor, color, setColor);
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
return QQmlListProperty<QObject>(
|
||||||
|
this,
|
||||||
|
nullptr,
|
||||||
|
ProxyWindowBase::dataAppend,
|
||||||
|
ProxyWindowBase::dataCount,
|
||||||
|
ProxyWindowBase::dataAt,
|
||||||
|
ProxyWindowBase::dataClear,
|
||||||
|
ProxyWindowBase::dataReplace,
|
||||||
|
ProxyWindowBase::dataRemoveLast
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
QQmlListProperty<QObject> ProxyWindowBase::dataBacker(QQmlListProperty<QObject>* prop) {
|
||||||
|
auto* that = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
|
||||||
|
return that->window->property("data").value<QQmlListProperty<QObject>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::dataAppend(QQmlListProperty<QObject>* prop, QObject* obj) {
|
||||||
|
auto backer = dataBacker(prop);
|
||||||
|
backer.append(&backer, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
qsizetype ProxyWindowBase::dataCount(QQmlListProperty<QObject>* prop) {
|
||||||
|
auto backer = dataBacker(prop);
|
||||||
|
return backer.count(&backer);
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject* ProxyWindowBase::dataAt(QQmlListProperty<QObject>* prop, qsizetype i) {
|
||||||
|
auto backer = dataBacker(prop);
|
||||||
|
return backer.at(&backer, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::dataClear(QQmlListProperty<QObject>* prop) {
|
||||||
|
auto backer = dataBacker(prop);
|
||||||
|
backer.clear(&backer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj) {
|
||||||
|
auto backer = dataBacker(prop);
|
||||||
|
backer.replace(&backer, i, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::dataRemoveLast(QQmlListProperty<QObject>* prop) {
|
||||||
|
auto backer = dataBacker(prop);
|
||||||
|
backer.removeLast(&backer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyFloatingWindow::setVisible(bool value) {
|
||||||
|
this->geometryLocked |= value;
|
||||||
|
ProxyWindowBase::setVisible(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyFloatingWindow::setWidth(qint32 value) {
|
||||||
|
if (!this->geometryLocked) {
|
||||||
|
ProxyWindowBase::setWidth(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyFloatingWindow::setHeight(qint32 value) {
|
||||||
|
if (!this->geometryLocked) {
|
||||||
|
ProxyWindowBase::setHeight(value);
|
||||||
|
}
|
||||||
|
}
|
82
src/cpp/proxywindow.hpp
Normal file
82
src/cpp/proxywindow.hpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qcolor.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmllist.h>
|
||||||
|
#include <qqmlparserstatus.h>
|
||||||
|
#include <qquickwindow.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "scavenge.hpp"
|
||||||
|
|
||||||
|
// Proxy to an actual window exposing a limited property set with the ability to
|
||||||
|
// transfer it to a new window.
|
||||||
|
// Detaching a window and touching any property is a use after free.
|
||||||
|
//
|
||||||
|
// NOTE: setting an `id` in qml will point to the proxy window and not the real window so things
|
||||||
|
// like anchors dont work
|
||||||
|
class ProxyWindowBase: public Scavenger {
|
||||||
|
Q_OBJECT;
|
||||||
|
Q_PROPERTY(bool visible READ isVisible WRITE setVisible);
|
||||||
|
Q_PROPERTY(qint32 width READ width WRITE setWidth);
|
||||||
|
Q_PROPERTY(qint32 height READ height WRITE setHeight);
|
||||||
|
Q_PROPERTY(QColor color READ color WRITE setColor);
|
||||||
|
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
|
||||||
|
Q_CLASSINFO("DefaultProperty", "data");
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void earlyInit(QObject* old) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ProxyWindowBase(QObject* parent = nullptr): Scavenger(parent) {}
|
||||||
|
~ProxyWindowBase() override;
|
||||||
|
|
||||||
|
ProxyWindowBase(ProxyWindowBase&) = delete;
|
||||||
|
ProxyWindowBase(ProxyWindowBase&&) = delete;
|
||||||
|
void operator=(ProxyWindowBase&) = delete;
|
||||||
|
void operator=(ProxyWindowBase&&) = delete;
|
||||||
|
|
||||||
|
// Disown the backing window and delete all its children.
|
||||||
|
QQuickWindow* disownWindow();
|
||||||
|
|
||||||
|
bool isVisible();
|
||||||
|
virtual void setVisible(bool value);
|
||||||
|
|
||||||
|
qint32 width();
|
||||||
|
virtual void setWidth(qint32 value);
|
||||||
|
|
||||||
|
qint32 height();
|
||||||
|
virtual void setHeight(qint32 value);
|
||||||
|
|
||||||
|
QColor color();
|
||||||
|
void setColor(QColor value);
|
||||||
|
|
||||||
|
QQmlListProperty<QObject> data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QQmlListProperty<QObject> dataBacker(QQmlListProperty<QObject>* prop);
|
||||||
|
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);
|
||||||
|
|
||||||
|
QQuickWindow* window = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// qt attempts to resize the window but fails because wayland
|
||||||
|
// and only resizes the graphics context which looks terrible.
|
||||||
|
class ProxyFloatingWindow: public ProxyWindowBase {
|
||||||
|
Q_OBJECT;
|
||||||
|
QML_ELEMENT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setVisible(bool value) override;
|
||||||
|
void setWidth(qint32 value) override;
|
||||||
|
void setHeight(qint32 value) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool geometryLocked = false;
|
||||||
|
};
|
|
@ -8,11 +8,12 @@
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
|
|
||||||
|
#include "scavenge.hpp"
|
||||||
#include "shell.hpp"
|
#include "shell.hpp"
|
||||||
|
|
||||||
RootWrapper::RootWrapper(QUrl rootUrl):
|
RootWrapper::RootWrapper(QUrl rootUrl):
|
||||||
QObject(nullptr), rootUrl(std::move(rootUrl)), engine(this) {
|
QObject(nullptr), rootUrl(std::move(rootUrl)), engine(this) {
|
||||||
this->reloadGraph();
|
this->reloadGraph(true);
|
||||||
|
|
||||||
if (this->activeRoot == nullptr) {
|
if (this->activeRoot == nullptr) {
|
||||||
qCritical() << "could not create scene graph, exiting";
|
qCritical() << "could not create scene graph, exiting";
|
||||||
|
@ -20,20 +21,24 @@ RootWrapper::RootWrapper(QUrl rootUrl):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWrapper::reloadGraph() {
|
void RootWrapper::reloadGraph(bool hard) {
|
||||||
if (this->activeRoot != nullptr) {
|
if (this->activeRoot != nullptr) {
|
||||||
this->engine.clearComponentCache();
|
this->engine.clearComponentCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto component = QQmlComponent(&this->engine, this->rootUrl);
|
auto component = QQmlComponent(&this->engine, this->rootUrl);
|
||||||
|
|
||||||
|
SCAVENGE_PARENT = hard ? nullptr : this;
|
||||||
auto* obj = component.beginCreate(this->engine.rootContext());
|
auto* obj = component.beginCreate(this->engine.rootContext());
|
||||||
|
SCAVENGE_PARENT = nullptr;
|
||||||
|
|
||||||
if (obj == nullptr) {
|
if (obj == nullptr) {
|
||||||
qWarning() << "failed to create root component";
|
qWarning() << "failed to create root component";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* qtsobj = qobject_cast<QtShell*>(obj);
|
auto* newRoot = qobject_cast<QtShell*>(obj);
|
||||||
if (qtsobj == nullptr) {
|
if (newRoot == nullptr) {
|
||||||
qWarning() << "root component was not a QtShell";
|
qWarning() << "root component was not a QtShell";
|
||||||
delete obj;
|
delete obj;
|
||||||
return;
|
return;
|
||||||
|
@ -46,7 +51,7 @@ void RootWrapper::reloadGraph() {
|
||||||
this->activeRoot = nullptr;
|
this->activeRoot = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->activeRoot = qtsobj;
|
this->activeRoot = newRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWrapper::changeRoot(QtShell* newRoot) {
|
void RootWrapper::changeRoot(QtShell* newRoot) {
|
||||||
|
@ -61,4 +66,6 @@ void RootWrapper::changeRoot(QtShell* newRoot) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QObject* RootWrapper::scavengeTargetFor(QObject* /* child */) { return this->activeRoot; }
|
||||||
|
|
||||||
void RootWrapper::destroy() { this->deleteLater(); }
|
void RootWrapper::destroy() { this->deleteLater(); }
|
||||||
|
|
|
@ -6,17 +6,20 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
|
|
||||||
|
#include "scavenge.hpp"
|
||||||
#include "shell.hpp"
|
#include "shell.hpp"
|
||||||
|
|
||||||
class RootWrapper: public QObject {
|
class RootWrapper: public QObject, virtual public Scavengeable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RootWrapper(QUrl rootUrl);
|
explicit RootWrapper(QUrl rootUrl);
|
||||||
|
|
||||||
void reloadGraph();
|
void reloadGraph(bool hard);
|
||||||
void changeRoot(QtShell* newRoot);
|
void changeRoot(QtShell* newRoot);
|
||||||
|
|
||||||
|
QObject* scavengeTargetFor(QObject* child) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
|
|
40
src/cpp/scavenge.cpp
Normal file
40
src/cpp/scavenge.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#include "scavenge.hpp"
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlcomponent.h>
|
||||||
|
#include <qqmlengine.h>
|
||||||
|
|
||||||
|
QObject* SCAVENGE_PARENT = nullptr; // NOLINT
|
||||||
|
|
||||||
|
void Scavenger::classBegin() {
|
||||||
|
// prayers
|
||||||
|
if (this->parent() == nullptr) {
|
||||||
|
this->setParent(SCAVENGE_PARENT);
|
||||||
|
SCAVENGE_PARENT = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* parent = dynamic_cast<Scavengeable*>(this->parent());
|
||||||
|
|
||||||
|
QObject* old = nullptr;
|
||||||
|
if (parent != nullptr) {
|
||||||
|
old = parent->scavengeTargetFor(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->earlyInit(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject* createComponentScavengeable(
|
||||||
|
QObject& parent,
|
||||||
|
QQmlComponent& component,
|
||||||
|
QVariantMap& initialProperties
|
||||||
|
) {
|
||||||
|
SCAVENGE_PARENT = &parent;
|
||||||
|
auto* instance = component.beginCreate(QQmlEngine::contextForObject(&parent));
|
||||||
|
SCAVENGE_PARENT = nullptr;
|
||||||
|
if (instance == nullptr) return nullptr;
|
||||||
|
if (instance->parent() != nullptr) instance->setParent(&parent);
|
||||||
|
component.setInitialProperties(instance, initialProperties);
|
||||||
|
component.completeCreate();
|
||||||
|
return instance;
|
||||||
|
}
|
49
src/cpp/scavenge.hpp
Normal file
49
src/cpp/scavenge.hpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlcomponent.h>
|
||||||
|
#include <qqmlparserstatus.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
extern QObject* SCAVENGE_PARENT; // NOLINT
|
||||||
|
|
||||||
|
class Scavenger: public QObject, public QQmlParserStatus {
|
||||||
|
Q_OBJECT;
|
||||||
|
Q_INTERFACES(QQmlParserStatus);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Scavenger(QObject* parent = nullptr): QObject(parent) {}
|
||||||
|
~Scavenger() override = default;
|
||||||
|
|
||||||
|
Scavenger(Scavenger&) = delete;
|
||||||
|
Scavenger(Scavenger&&) = delete;
|
||||||
|
void operator=(Scavenger&) = delete;
|
||||||
|
void operator=(Scavenger&&) = delete;
|
||||||
|
|
||||||
|
void classBegin() override;
|
||||||
|
void componentComplete() override {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// do early init, sometimes with a scavengeable target
|
||||||
|
virtual void earlyInit(QObject* old) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Scavengeable {
|
||||||
|
public:
|
||||||
|
Scavengeable() = default;
|
||||||
|
virtual ~Scavengeable() = default;
|
||||||
|
|
||||||
|
Scavengeable(Scavengeable&) = delete;
|
||||||
|
Scavengeable(Scavengeable&&) = delete;
|
||||||
|
void operator=(Scavengeable&) = delete;
|
||||||
|
void operator=(Scavengeable&&) = delete;
|
||||||
|
|
||||||
|
// return an old object that might have salvageable resources
|
||||||
|
virtual QObject* scavengeTargetFor(QObject* child) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
QObject* createComponentScavengeable(
|
||||||
|
QObject& parent,
|
||||||
|
QQmlComponent& component,
|
||||||
|
QVariantMap& initialProperties
|
||||||
|
);
|
|
@ -1,4 +1,5 @@
|
||||||
#include "shell.hpp"
|
#include "shell.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
@ -8,7 +9,7 @@
|
||||||
|
|
||||||
#include "rootwrapper.hpp"
|
#include "rootwrapper.hpp"
|
||||||
|
|
||||||
void QtShell::reload() {
|
void QtShell::reload(bool hard) {
|
||||||
auto* rootobj = QQmlEngine::contextForObject(this)->engine()->parent();
|
auto* rootobj = QQmlEngine::contextForObject(this)->engine()->parent();
|
||||||
auto* root = qobject_cast<RootWrapper*>(rootobj);
|
auto* root = qobject_cast<RootWrapper*>(rootobj);
|
||||||
|
|
||||||
|
@ -17,7 +18,23 @@ void QtShell::reload() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
root->reloadGraph();
|
root->reloadGraph(hard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtShell::earlyInit(QObject* old) {
|
||||||
|
auto* oldshell = qobject_cast<QtShell*>(old);
|
||||||
|
|
||||||
|
if (oldshell != nullptr) {
|
||||||
|
this->scavengeableChildren = std::move(oldshell->children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject* QtShell::scavengeTargetFor(QObject* /* child */) {
|
||||||
|
if (this->scavengeableChildren.length() > this->children.length()) {
|
||||||
|
return this->scavengeableChildren[this->children.length()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<QObject> QtShell::components() {
|
QQmlListProperty<QObject> QtShell::components() {
|
||||||
|
@ -36,4 +53,5 @@ QQmlListProperty<QObject> QtShell::components() {
|
||||||
void QtShell::appendComponent(QQmlListProperty<QObject>* list, QObject* component) {
|
void QtShell::appendComponent(QQmlListProperty<QObject>* list, QObject* component) {
|
||||||
auto* shell = static_cast<QtShell*>(list->object); // NOLINT
|
auto* shell = static_cast<QtShell*>(list->object); // NOLINT
|
||||||
component->setParent(shell);
|
component->setParent(shell);
|
||||||
|
shell->children.append(component);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,37 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qlist.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qqmllist.h>
|
#include <qqmllist.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
class QtShell: public QObject {
|
#include "scavenge.hpp"
|
||||||
|
|
||||||
|
class QtShell: public Scavenger, virtual public Scavengeable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
Q_PROPERTY(QQmlListProperty<QObject> components READ components FINAL);
|
Q_PROPERTY(QQmlListProperty<QObject> components READ components FINAL);
|
||||||
Q_CLASSINFO("DefaultProperty", "components");
|
Q_CLASSINFO("DefaultProperty", "components");
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QtShell(QObject* parent = nullptr): QObject(parent) {}
|
explicit QtShell(QObject* parent = nullptr): Scavenger(parent) {}
|
||||||
|
|
||||||
|
void earlyInit(QObject* old) override;
|
||||||
|
QObject* scavengeTargetFor(QObject* child) override;
|
||||||
|
|
||||||
QQmlListProperty<QObject> components();
|
QQmlListProperty<QObject> components();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void reload();
|
void reload(bool hard = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void appendComponent(QQmlListProperty<QObject>* list, QObject* component);
|
static void appendComponent(QQmlListProperty<QObject>* list, QObject* component);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// track only the children assigned to `components` in order
|
||||||
|
QList<QObject*> children;
|
||||||
|
QList<QObject*> scavengeableChildren;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,17 +4,33 @@
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
|
||||||
|
#include "scavenge.hpp"
|
||||||
|
|
||||||
|
void Variants::earlyInit(QObject* old) {
|
||||||
|
auto* oldv = qobject_cast<Variants*>(old);
|
||||||
|
if (oldv != nullptr) {
|
||||||
|
this->scavengeableInstances = std::move(oldv->instances);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject* Variants::scavengeTargetFor(QObject* /* child */) {
|
||||||
|
if (this->activeScavengeVariant != nullptr) {
|
||||||
|
auto* r = this->scavengeableInstances.get(*this->activeScavengeVariant);
|
||||||
|
if (r != nullptr) return *r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Variants::setVariants(QVariantList variants) {
|
void Variants::setVariants(QVariantList variants) {
|
||||||
this->mVariants = std::move(variants);
|
this->mVariants = std::move(variants);
|
||||||
qDebug() << "configurations updated:" << this->mVariants;
|
|
||||||
|
|
||||||
this->updateVariants();
|
this->updateVariants();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Variants::componentComplete() {
|
void Variants::componentComplete() {
|
||||||
qDebug() << "configure ready";
|
Scavenger::componentComplete();
|
||||||
|
|
||||||
this->updateVariants();
|
this->updateVariants();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,14 +69,14 @@ void Variants::updateVariants() {
|
||||||
continue; // we dont need to recreate this one
|
continue; // we dont need to recreate this one
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* instance = this->mComponent->createWithInitialProperties(variant, nullptr);
|
this->activeScavengeVariant = &variant;
|
||||||
|
auto* instance = createComponentScavengeable(*this, *this->mComponent, variant);
|
||||||
|
|
||||||
if (instance == nullptr) {
|
if (instance == nullptr) {
|
||||||
qWarning() << "failed to create variant with object" << variant;
|
qWarning() << "failed to create variant with object" << variant;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
instance->setParent(this);
|
|
||||||
this->instances.insert(variant, instance);
|
this->instances.insert(variant, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +91,17 @@ bool AwfulMap<K, V>::contains(const K& key) const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename K, typename V>
|
||||||
|
V* AwfulMap<K, V>::get(const K& key) {
|
||||||
|
for (auto& [k, v]: this->values) {
|
||||||
|
if (key == k) {
|
||||||
|
return &v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename K, typename V>
|
template <typename K, typename V>
|
||||||
void AwfulMap<K, V>::insert(K key, V value) {
|
void AwfulMap<K, V>::insert(K key, V value) {
|
||||||
this->values.push_back(QPair<K, V>(key, value));
|
this->values.push_back(QPair<K, V>(key, value));
|
||||||
|
|
|
@ -2,22 +2,26 @@
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qlist.h>
|
#include <qlist.h>
|
||||||
|
#include <qlogging.h>
|
||||||
#include <qmap.h>
|
#include <qmap.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlcomponent.h>
|
#include <qqmlcomponent.h>
|
||||||
#include <qqmlparserstatus.h>
|
#include <qqmlparserstatus.h>
|
||||||
|
|
||||||
|
#include "scavenge.hpp"
|
||||||
|
|
||||||
// extremely inefficient map
|
// extremely inefficient map
|
||||||
template <typename K, typename V>
|
template <typename K, typename V>
|
||||||
class AwfulMap {
|
class AwfulMap {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] bool contains(const K& key) const;
|
[[nodiscard]] bool contains(const K& key) const;
|
||||||
|
[[nodiscard]] V* get(const K& key);
|
||||||
void insert(K key, V value); // assumes no duplicates
|
void insert(K key, V value); // assumes no duplicates
|
||||||
bool remove(const K& key); // returns true if anything was removed
|
bool remove(const K& key); // returns true if anything was removed
|
||||||
QList<QPair<K, V>> values;
|
QList<QPair<K, V>> values;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Variants: public QObject, public QQmlParserStatus {
|
class Variants: public Scavenger, virtual public Scavengeable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
Q_PROPERTY(QQmlComponent* component MEMBER mComponent);
|
Q_PROPERTY(QQmlComponent* component MEMBER mComponent);
|
||||||
Q_PROPERTY(QVariantList variants MEMBER mVariants WRITE setVariants);
|
Q_PROPERTY(QVariantList variants MEMBER mVariants WRITE setVariants);
|
||||||
|
@ -25,9 +29,11 @@ class Variants: public QObject, public QQmlParserStatus {
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Variants(QObject* parent = nullptr): QObject(parent) {}
|
explicit Variants(QObject* parent = nullptr): Scavenger(parent) {}
|
||||||
|
|
||||||
|
void earlyInit(QObject* old) override;
|
||||||
|
QObject* scavengeTargetFor(QObject* child) override;
|
||||||
|
|
||||||
void classBegin() override {};
|
|
||||||
void componentComplete() override;
|
void componentComplete() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -37,4 +43,8 @@ private:
|
||||||
QQmlComponent* mComponent = nullptr;
|
QQmlComponent* mComponent = nullptr;
|
||||||
QVariantList mVariants;
|
QVariantList mVariants;
|
||||||
AwfulMap<QVariantMap, QObject*> instances;
|
AwfulMap<QVariantMap, QObject*> instances;
|
||||||
|
|
||||||
|
// pointers may die post componentComplete.
|
||||||
|
AwfulMap<QVariantMap, QObject*> scavengeableInstances;
|
||||||
|
QVariantMap* activeScavengeVariant = nullptr;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue