// This file Copyright © Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #pragma once #include "Utils.h" #include #include #include #include #include #include template class DynamicPropertyStore { public: using ObjectType = ObjectT; using PropertyType = PropertyT; using PropertyIdType = guint; static_assert(std::is_same_v, PropertyIdType>); struct PropertyInfo { template using ValueType = std::invoke_result_t; PropertyIdType id = 0; GParamSpec* spec = nullptr; std::function getter; PropertyInfo() = default; template PropertyInfo(PropertyType index, char const* name, char const* nick, char const* blurb, MethodT getter_method) : id(static_cast(index)) , spec(gtr_get_param_spec>(name, nick, blurb)) , getter([getter_method](ObjectType const& object, Glib::ValueBase& value) { static_cast>&>(value).set((object.*getter_method)()); }) { } }; static inline auto const PropertyCount = static_cast(PropertyType::N_PROPS); public: static DynamicPropertyStore& get() noexcept { static auto instance = DynamicPropertyStore(); return instance; } void install(GObjectClass* cls, std::initializer_list properties) { cls->get_property = &DynamicPropertyStore::get_property_vfunc; g_assert(properties_.size() == properties.size() + 1); std::move(properties.begin(), properties.end(), properties_.begin() + 1); for (auto id = PropertyIdType{ 1 }; id < PropertyCount; ++id) { g_assert(id == properties_[id].id); g_object_class_install_property(cls, id, properties_[id].spec); } } void get_value(ObjectType const& object, PropertyType index, Glib::ValueBase& value) const { get_property(index).getter(object, value); } void notify_changed(ObjectType& object, PropertyType index) const { g_object_notify_by_pspec(object.gobj(), get_property(index).spec); } private: [[nodiscard]] PropertyInfo const& get_property(PropertyType index) const noexcept { auto const id = static_cast(index); g_assert(id > 0); g_assert(id < PropertyCount); return properties_[id]; } static void get_property_vfunc(GObject* object, PropertyIdType id, GValue* value, GParamSpec* /*param_spec*/) { if (id <= 0 || id >= PropertyCount) { return; } if (auto const* const typed_object = dynamic_cast(Glib::wrap_auto(object)); typed_object != nullptr) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) get().get_value(*typed_object, PropertyType{ id }, *reinterpret_cast(value)); } } private: std::array properties_ = {}; };