feat: abstract out scavenger scopes

This commit is contained in:
outfoxxed 2024-02-14 03:03:41 -08:00
parent 82aa7d45d3
commit d6ed717c39
Signed by untrusted user: outfoxxed
GPG key ID: 4C88A185FB89301E
4 changed files with 74 additions and 56 deletions

View file

@ -1,10 +1,16 @@
#include "scavenge.hpp" #include "scavenge.hpp"
#include <utility>
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qlogging.h> #include <qlogging.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlcomponent.h> #include <qqmlcomponent.h>
#include <qqmlengine.h> #include <qqmlengine.h>
#include <qqmllist.h>
// FIXME: there are core problems with SCAVENGE_PARENT due to the qml engine liking to set parents really late.
// this should instead be handled by proxying all property values until a possible target is ready or definitely not coming.
// The parent should probably be stable in componentComplete() but should be tested.
QObject* SCAVENGE_PARENT = nullptr; // NOLINT QObject* SCAVENGE_PARENT = nullptr; // NOLINT
@ -44,3 +50,38 @@ QObject* createComponentScavengeable(
return instance; return instance;
} }
void ScavengeableScope::earlyInit(QObject* old) {
auto* oldshell = qobject_cast<ScavengeableScope*>(old);
if (oldshell != nullptr) {
this->scavengeableData = std::move(oldshell->mData);
}
}
QObject* ScavengeableScope::scavengeTargetFor(QObject* /* child */) {
if (this->scavengeableData.length() > this->mData.length()) {
return this->scavengeableData[this->mData.length()];
}
return nullptr;
}
QQmlListProperty<QObject> ScavengeableScope::data() {
return QQmlListProperty<QObject>(
this,
nullptr,
&ScavengeableScope::appendComponent,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
);
}
void ScavengeableScope::appendComponent(QQmlListProperty<QObject>* list, QObject* component) {
auto* self = static_cast<ScavengeableScope*>(list->object); // NOLINT
component->setParent(self);
self->mData.append(component);
}

View file

@ -2,6 +2,8 @@
#include <qobject.h> #include <qobject.h>
#include <qqmlcomponent.h> #include <qqmlcomponent.h>
#include <qqmlintegration.h>
#include <qqmllist.h>
#include <qqmlparserstatus.h> #include <qqmlparserstatus.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
@ -30,7 +32,7 @@ protected:
class Scavengeable { class Scavengeable {
public: public:
Scavengeable() = default; explicit Scavengeable() = default;
virtual ~Scavengeable() = default; virtual ~Scavengeable() = default;
Scavengeable(Scavengeable&) = delete; Scavengeable(Scavengeable&) = delete;
@ -47,3 +49,31 @@ QObject* createComponentScavengeable(
QQmlComponent& component, QQmlComponent& component,
QVariantMap& initialProperties QVariantMap& initialProperties
); );
///! Reloader connection scope
/// Attempts to maintain scavengeable connections.
/// This is mostly useful to split a scavengeable component slot (e.g. `Variants`)
/// into multiple slots.
///
/// If you don't know what that means you probably don't need it.
class ScavengeableScope: public Scavenger, virtual public Scavengeable {
Q_OBJECT;
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
Q_CLASSINFO("DefaultProperty", "data");
QML_ELEMENT;
public:
explicit ScavengeableScope(QObject* parent = nullptr): Scavenger(parent) {}
void earlyInit(QObject* old) override;
QObject* scavengeTargetFor(QObject* child) override;
QQmlListProperty<QObject> data();
private:
static void appendComponent(QQmlListProperty<QObject>* list, QObject* component);
// track only the children assigned to `data` in order
QList<QObject*> mData;
QList<QObject*> scavengeableData;
};

View file

@ -1,26 +1,7 @@
#include "shell.hpp" #include "shell.hpp"
#include <utility>
#include <qobject.h>
#include <qqmllist.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
void ShellRoot::earlyInit(QObject* old) {
auto* oldshell = qobject_cast<ShellRoot*>(old);
if (oldshell != nullptr) {
this->scavengeableChildren = std::move(oldshell->children);
}
}
QObject* ShellRoot::scavengeTargetFor(QObject* /* child */) {
if (this->scavengeableChildren.length() > this->children.length()) {
return this->scavengeableChildren[this->children.length()];
}
return nullptr;
}
void ShellRoot::setConfig(ShellConfig config) { void ShellRoot::setConfig(ShellConfig config) {
this->mConfig = config; this->mConfig = config;
@ -28,22 +9,3 @@ void ShellRoot::setConfig(ShellConfig config) {
} }
ShellConfig ShellRoot::config() const { return this->mConfig; } ShellConfig ShellRoot::config() const { return this->mConfig; }
QQmlListProperty<QObject> ShellRoot::components() {
return QQmlListProperty<QObject>(
this,
nullptr,
&ShellRoot::appendComponent,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
);
}
void ShellRoot::appendComponent(QQmlListProperty<QObject>* list, QObject* component) {
auto* shell = static_cast<ShellRoot*>(list->object); // NOLINT
component->setParent(shell);
shell->children.append(component);
}

View file

@ -2,9 +2,7 @@
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qlist.h> #include <qlist.h>
#include <qobject.h>
#include <qqmlengine.h> #include <qqmlengine.h>
#include <qqmllist.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include "scavenge.hpp" #include "scavenge.hpp"
@ -18,35 +16,22 @@ public:
}; };
///! Root config element ///! Root config element
class ShellRoot: public Scavenger, virtual public Scavengeable { class ShellRoot: public ScavengeableScope {
Q_OBJECT; Q_OBJECT;
/// If `config.watchFiles` is true the configuration will be reloaded whenever it changes. /// If `config.watchFiles` is true the configuration will be reloaded whenever it changes.
/// Defaults to true. /// Defaults to true.
Q_PROPERTY(ShellConfig config READ config WRITE setConfig); Q_PROPERTY(ShellConfig config READ config WRITE setConfig);
Q_PROPERTY(QQmlListProperty<QObject> components READ components);
Q_CLASSINFO("DefaultProperty", "components");
QML_ELEMENT; QML_ELEMENT;
public: public:
explicit ShellRoot(QObject* parent = nullptr): Scavenger(parent) {} explicit ShellRoot(QObject* parent = nullptr): ScavengeableScope(parent) {}
void earlyInit(QObject* old) override;
QObject* scavengeTargetFor(QObject* child) override;
void setConfig(ShellConfig config); void setConfig(ShellConfig config);
[[nodiscard]] ShellConfig config() const; [[nodiscard]] ShellConfig config() const;
QQmlListProperty<QObject> components();
signals: signals:
void configChanged(); void configChanged();
private: private:
static void appendComponent(QQmlListProperty<QObject>* list, QObject* component);
ShellConfig mConfig; ShellConfig mConfig;
// track only the children assigned to `components` in order
QList<QObject*> children;
QList<QObject*> scavengeableChildren;
}; };