#pragma once #include #include #include #include #include #include #include namespace ZShell::settings { class ValueCodec; struct Annotation { QVariant defaultValue; bool globalOnly = false; // NOLINTNEXTLINE(readability-redundant-member-init) callers will get missing field init warnings without QList allowedTypes = {}; // For QVariant properties // NOLINTNEXTLINE(readability-redundant-member-init) QString identityKey = {}; }; namespace detail { // Return trivially copyable types (e.g. bool) by value and others (e.g. QVariant) by const ref template using AnnotationReturn = std::conditional_t< std::is_trivially_copyable_v && sizeof(T) <= sizeof(void*), T, const T&>; } // namespace detail #define ANNOTATION(Type, name) \ Q_PROPERTY(Type name READ name) \ \ public: \ [[nodiscard]] detail::AnnotationReturn name() const { \ return annotation.name; \ } \ \ private: struct Descriptor { Q_GADGET QML_VALUE_TYPE(descriptor) Q_PROPERTY(QString key MEMBER key) Q_PROPERTY(QString type READ typeString) Q_PROPERTY(int metaIndex MEMBER metaIndex) Q_PROPERTY(bool isNode MEMBER isNode) ANNOTATION(QVariant, defaultValue) ANNOTATION(bool, globalOnly) ANNOTATION(QString, identityKey) public: QString key; QMetaType type; int metaIndex; bool isNode; Annotation annotation; const ValueCodec* codec = nullptr; // Null for nodes and unsupported types [[nodiscard]] QString typeString() const; [[nodiscard]] bool accepts(const QMetaType& valueType) const; }; #undef ANNOTATION class Schema { public: static Schema build( const QMetaObject* meta, int baseOffset, bool includeReadOnly = false); static void annotate( const QMetaObject* meta, const QString& key, Annotation annotation); [[nodiscard]] const QList& descriptors() const; [[nodiscard]] const Descriptor* get(const QString& key) const; private: explicit Schema() = default; QList m_descriptors; QHash m_keyToIndex; }; } // namespace ZShell::settings