Files
z-bar-qt/Plugins/ZShell/Settings/macros.hpp
T

209 lines
12 KiB
C++

#pragma once
#include <qmetatype.h>
#include <qobject.h>
#include <qvariant.h>
#include "listnode.hpp"
#include "schema.hpp"
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");
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
#define DEFAULT_ARG(...) __VA_ARGS__
#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:
#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 */ \
\
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)
#define SETTINGS_GLOBAL_SUBOBJECT(Type, name) \
SETTINGS_SUBOBJECT_IMPL(Type, name, true)
#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); \
} \
};
#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__)
#define SETTINGS_GLOBAL_LIST(Type, name, defaultVal, ...) \
SETTINGS_LIST_IMPL(Type, name, true, DEFAULT_ARG(defaultVal), __VA_ARGS__)