more unfinished changes, process tab for dashboard
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
#pragma once
|
||||
|
||||
#include <qmetatype.h>
|
||||
#include <qobject.h>
|
||||
#include <qvariant.h>
|
||||
|
||||
#include "listnode.hpp" // IWYU pragma: keep
|
||||
#include "schema.hpp" // IWYU pragma: keep
|
||||
|
||||
namespace ZShell::settings {
|
||||
|
||||
inline QVariantMap vmap(std::initializer_list<std::pair<QString, QVariant>> entries) {
|
||||
QVariantMap map;
|
||||
for (const auto& [key, value] : entries)
|
||||
map.insert(key, value);
|
||||
return map;
|
||||
}
|
||||
|
||||
template <typename... Ts> inline QList<QMetaType> unionTypes() {
|
||||
static_assert(sizeof...(Ts) >= 2, "A union needs at least two types");
|
||||
// If the max size is changed, common.cpp `mismatchStr` must be updated
|
||||
static_assert(sizeof...(Ts) <= 4, "A union cannot have more than 4 types");
|
||||
static_assert((!std::is_same_v<Ts, QVariant> && ...), "A union cannot contain QVariant");
|
||||
|
||||
return { QMetaType::fromType<Ts>()... };
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T> inline bool compare(const T& a, const T& b) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
template <std::floating_point T> bool compare(const T& a, const T& b) {
|
||||
return qFuzzyCompare(a + T(1), b + T(1));
|
||||
}
|
||||
|
||||
template <std::floating_point T> bool compare(const QList<T>& a, const QList<T>& b) {
|
||||
if (a.size() != b.size())
|
||||
return false;
|
||||
|
||||
for (qsizetype i = 0; i < a.size(); ++i)
|
||||
if (!compare(a.at(i), b.at(i)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace ZShell::settings
|
||||
|
||||
// Helper macro to prevent splitting initialiser lists
|
||||
#define DEFAULT_ARG(...) __VA_ARGS__
|
||||
|
||||
// Declares a class to be a node class. This replaces the Q_OBJECT call at the top of the class.
|
||||
#define SETTINGS_NODE_NO_CTOR(Class, Base) \
|
||||
Q_OBJECT \
|
||||
\
|
||||
public: \
|
||||
[[nodiscard]] const ZShell::settings::Schema& schema() const override { \
|
||||
static const auto schema = \
|
||||
ZShell::settings::Schema::build(&staticMetaObject, Base::staticMetaObject.propertyCount()); \
|
||||
return schema; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
using Self = Class; // For use in the below macros
|
||||
|
||||
#define SETTINGS_NODE(Class, Base) \
|
||||
SETTINGS_NODE_NO_CTOR(Class, Base) \
|
||||
QML_ANONYMOUS \
|
||||
\
|
||||
public: \
|
||||
explicit Class(Class* fallback = nullptr, QObject* parent = nullptr, bool globalOnly = false) \
|
||||
: Base(fallback, parent, globalOnly) {} \
|
||||
\
|
||||
private:
|
||||
|
||||
// Defines a property on a node.
|
||||
#define SETTINGS_PROPERTY_IMPL(Type, name, global, defaultVal, ...) \
|
||||
Q_PROPERTY(Type name READ name WRITE set_##name NOTIFY name##Changed) \
|
||||
\
|
||||
public: \
|
||||
[[nodiscard]] Type name() const { \
|
||||
if (global || m_globalOnly) \
|
||||
warnGlobalRead(QStringLiteral(#name)); \
|
||||
return m_##name; \
|
||||
} \
|
||||
\
|
||||
void set_##name(const Type& value) { \
|
||||
if (!true /* TODO: validation */) \
|
||||
return; \
|
||||
\
|
||||
if (rejectInvalidWrite(QStringLiteral(#name), value)) \
|
||||
return; /* Skip writes of the wrong type */ \
|
||||
\
|
||||
if (rejectGlobalWrite(QStringLiteral(#name))) \
|
||||
return; /* Skip writes to global only keys, they should be sent to the global layer */ \
|
||||
\
|
||||
const auto needsNotify = !ZShell::settings::detail::compare(value, m_##name); \
|
||||
m_##name = value; \
|
||||
if (recordWrite(QStringLiteral(#name), needsNotify)) \
|
||||
Q_EMIT name##Changed(); \
|
||||
} \
|
||||
\
|
||||
Q_SIGNAL void name##Changed(); \
|
||||
\
|
||||
private: \
|
||||
Type m_##name = fallbackValue(&Self::m_##name, defaultVal); \
|
||||
inline static const bool s_register_##name = \
|
||||
(ZShell::settings::Schema::annotate(&staticMetaObject, QStringLiteral(#name), \
|
||||
{ .defaultValue = QVariant::fromValue<Type>(defaultVal), .globalOnly = global, __VA_ARGS__ }), \
|
||||
true);
|
||||
|
||||
#define SETTINGS_PROPERTY(Type, name, defaultVal, ...) \
|
||||
SETTINGS_PROPERTY_IMPL(Type, name, false, DEFAULT_ARG(defaultVal), __VA_ARGS__)
|
||||
|
||||
// Defines a global property on a node. Shorthand for .globalOnly = true.
|
||||
#define SETTINGS_GLOBAL_PROPERTY(Type, name, defaultVal, ...) \
|
||||
SETTINGS_PROPERTY_IMPL(Type, name, true, DEFAULT_ARG(defaultVal), __VA_ARGS__)
|
||||
|
||||
// Defines a subobject property on a node. Subobject properties are CONSTANT.
|
||||
#define SETTINGS_SUBOBJECT_IMPL(Type, name, global) \
|
||||
Q_PROPERTY(Type* name READ name CONSTANT) \
|
||||
\
|
||||
public: \
|
||||
[[nodiscard]] Type* name() const { \
|
||||
if (global || m_globalOnly) \
|
||||
warnGlobalRead(QStringLiteral(#name)); \
|
||||
return m_##name; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
Type* m_##name = new Type(fallbackValue(&Self::m_##name, nullptr), this, global); \
|
||||
inline static const bool s_register_##name = \
|
||||
(ZShell::settings::Schema::annotate( \
|
||||
&staticMetaObject, QStringLiteral(#name), { .defaultValue = {}, .globalOnly = global }), \
|
||||
true);
|
||||
|
||||
#define SETTINGS_SUBOBJECT(Type, name) SETTINGS_SUBOBJECT_IMPL(Type, name, false)
|
||||
|
||||
// Defines a global subobject on a node. Everything inside it is global only.
|
||||
#define SETTINGS_GLOBAL_SUBOBJECT(Type, name) SETTINGS_SUBOBJECT_IMPL(Type, name, true)
|
||||
|
||||
// Defines a list type for use with SETTINGS_LIST.
|
||||
#define SETTINGS_LIST_TYPE(Element, Name) \
|
||||
class Name : public ZShell::settings::ListNode { \
|
||||
Q_OBJECT \
|
||||
QML_ANONYMOUS \
|
||||
\
|
||||
public: \
|
||||
explicit Name(Name* fallback = nullptr, QObject* parent = nullptr, bool globalOnly = false) \
|
||||
: ZShell::settings::ListNode(fallback, parent, globalOnly) {} \
|
||||
\
|
||||
[[nodiscard]] Q_INVOKABLE Element* at(qsizetype index) const { /* Format ugh */ \
|
||||
return static_cast<Element*>(elementAt(index)); \
|
||||
} \
|
||||
[[nodiscard]] Q_INVOKABLE Element* insert(const QVariantMap& props, qsizetype index = -1) { \
|
||||
return static_cast<Element*>(insertElement(props, index)); \
|
||||
} \
|
||||
\
|
||||
protected: \
|
||||
[[nodiscard]] ZShell::settings::Node* createElement(ZShell::settings::Node* fallback) override { \
|
||||
return new Element(static_cast<Element*>(fallback), this); \
|
||||
} \
|
||||
};
|
||||
|
||||
// Defines a list property on a node. List properties are CONSTANT.
|
||||
#define SETTINGS_LIST_IMPL(Type, name, global, defaultVal, ...) \
|
||||
Q_PROPERTY(Type* name READ name CONSTANT) \
|
||||
\
|
||||
public: \
|
||||
[[nodiscard]] Type* name() const { \
|
||||
if (global || m_globalOnly) \
|
||||
warnGlobalRead(QStringLiteral(#name)); \
|
||||
return m_##name; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
Type* m_##name = new Type(fallbackValue(&Self::m_##name, nullptr), this, global); \
|
||||
inline static const bool s_register_##name = \
|
||||
(ZShell::settings::Schema::annotate(&staticMetaObject, QStringLiteral(#name), \
|
||||
{ .defaultValue = QVariant::fromValue<QList<QVariantMap>>(defaultVal), \
|
||||
.globalOnly = global, \
|
||||
__VA_ARGS__ }), \
|
||||
true);
|
||||
|
||||
#define SETTINGS_LIST(Type, name, defaultVal, ...) \
|
||||
SETTINGS_LIST_IMPL(Type, name, false, DEFAULT_ARG(defaultVal), __VA_ARGS__)
|
||||
|
||||
// Defines a global list on a node. Everything inside it is global only.
|
||||
#define SETTINGS_GLOBAL_LIST(Type, name, defaultVal, ...) \
|
||||
SETTINGS_LIST_IMPL(Type, name, true, DEFAULT_ARG(defaultVal), __VA_ARGS__)
|
||||
Reference in New Issue
Block a user