Files
z-bar-qt/Plugins/ZShell/Settings/macros.hpp
T
zach 9ea1f846ae
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Successful in 34s
Rust / build (pull_request) Successful in 59s
Python / verify (pull_request) Successful in 1m48s
Rust / fmt (pull_request) Successful in 47s
Rust / clippy (pull_request) Successful in 1m6s
more unfinished changes, process tab for dashboard
2026-09-25 16:51:28 +02:00

194 lines
15 KiB
C++

#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__)