Files
z-bar-qt/Plugins/ZShell/Config/ConfigSection.hpp
T

83 lines
3.7 KiB
C++

#pragma once
#include <QObject>
#include <QJsonObject>
#include <QJsonValue>
#include <QMetaProperty>
#include <QMetaMethod>
// ---------------------------------------------------------------------------
// CFG_PROPERTY / CFG_SECTION
//
// These generate exactly the boilerplate you'd write by hand for a
// Q_PROPERTY with a backing member, getter, setter and NOTIFY signal - just
// in one line instead of ~8. Because everything they generate is a normal
// compiled getter/setter, there is *zero* extra runtime cost vs hand-written
// code: reading Config.appearance.anim.sessionGifSpeed from QML calls a
// plain inline getter. Reflection (QMetaObject) is only walked at load time,
// save time, and once at construction time to wire change signals - never
// on the read/write path itself.
//
// CFG_PROPERTY(type, name, Name)
// - type: C++ type (bool, int, qreal, QString, QVariantList, ...)
// - name: property name as it must appear in the JSON file (camelCase,
// or whatever the JSON actually uses - it MUST match exactly)
// - Name: same name, capitalized, used to build setName()/NameChanged()
//
// CFG_SECTION(Type, name, Name)
// - Declares a child ConfigSection* property (e.g. Anim inside Appearance)
// - The pointer itself is CONSTANT (never reassigned); the object it
// points to is what actually changes.
// ---------------------------------------------------------------------------
#define CFG_PROPERTY(type, name, Name) \
Q_PROPERTY(type name READ name WRITE set##Name NOTIFY Name##Changed) \
public: \
type name() const { return m_##name; } \
void set##Name(const type &value) { \
if (m_##name == value) return; \
m_##name = value; \
Q_EMIT Name##Changed(); \
} \
Q_SIGNALS: \
void Name##Changed(); \
private: \
type m_##name{};
#define CFG_SECTION(Type, name, Name) \
public: \
Q_PROPERTY(Type *name READ name CONSTANT) \
Type *name() const { return m_##name; } \
private: \
Type *m_##name = nullptr;
class ConfigSection : public QObject {
Q_OBJECT
public:
explicit ConfigSection(QObject *parent = nullptr);
// Populate this section (and all child sections) from JSON. Unknown
// keys in obj are ignored; missing keys keep their current value.
void fromJson(const QJsonObject &obj);
// Serialize this section (and all child sections) to JSON.
QJsonObject toJson() const;
Q_SIGNALS:
// Emitted whenever any property in this section, or any nested section,
// changes. Config listens on the root section to know a save is needed,
// without every leaf class needing to know about Config.
void changed();
protected:
// Call once, at the end of every subclass constructor, after all child
// ConfigSection* members have been constructed (new'd with `this` as
// parent). Sets up the reflection-based signal forwarding.
void wireSignals();
private Q_SLOTS:
void onPropertyChanged();
private:
bool m_wired = false;
};