feat: implement type-safe ReferenceProperty for SmartPointer fields and add UI support for object selection via context-aware dropdowns
This commit is contained in:
@@ -15,6 +15,15 @@
|
||||
#include "Core/Archives.h"
|
||||
#include "Core/Signal.h"
|
||||
#include "Core/Object.h"
|
||||
#include "Core/SmartPointer.h"
|
||||
|
||||
// Type traits for detecting SmartPointer<T>
|
||||
namespace uLib {
|
||||
template<typename T> struct is_smart_pointer : std::false_type {};
|
||||
template<typename T> struct is_smart_pointer<SmartPointer<T>> : std::true_type {};
|
||||
template<typename T> struct smart_pointer_element { using type = void; };
|
||||
template<typename T> struct smart_pointer_element<SmartPointer<T>> { using type = T; };
|
||||
} // namespace uLib
|
||||
|
||||
namespace uLib {
|
||||
|
||||
@@ -216,6 +225,109 @@ private:
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief Base class for reference properties (SmartPointer<T> fields).
|
||||
* Provides a type-erased interface for getting/setting object references
|
||||
* and checking type compatibility.
|
||||
*/
|
||||
class ReferencePropertyBase : public PropertyBase {
|
||||
public:
|
||||
virtual ~ReferencePropertyBase() {}
|
||||
virtual Object* GetReferencedObject() const = 0;
|
||||
virtual void SetReferencedObject(Object* obj) = 0;
|
||||
virtual bool IsCompatible(Object* obj) const = 0;
|
||||
virtual const char* GetReferenceTypeName() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Typed reference property for SmartPointer<T> fields.
|
||||
* Filters context objects by dynamic_cast compatibility with T.
|
||||
*/
|
||||
template <typename T>
|
||||
class ReferenceProperty : public ReferencePropertyBase {
|
||||
public:
|
||||
ReferenceProperty(Object* owner, const std::string& name, SmartPointer<T>& ref,
|
||||
const std::string& units = "", const std::string& group = "")
|
||||
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_ref(ref), m_ReadOnly(false) {
|
||||
if (m_owner) m_owner->RegisterProperty(this);
|
||||
}
|
||||
|
||||
virtual ~ReferenceProperty() {}
|
||||
|
||||
// PropertyBase interface
|
||||
virtual const std::string& GetName() const override { return m_name; }
|
||||
virtual const char* GetTypeName() const override { return typeid(SmartPointer<T>).name(); }
|
||||
virtual std::type_index GetTypeIndex() const override { return std::type_index(typeid(ReferencePropertyBase)); }
|
||||
virtual const std::string& GetUnits() const override { return m_units; }
|
||||
virtual void SetUnits(const std::string& units) override { m_units = units; }
|
||||
virtual const std::string& GetGroup() const override { return m_group; }
|
||||
virtual void SetGroup(const std::string& group) override { m_group = group; }
|
||||
virtual bool IsReadOnly() const override { return m_ReadOnly; }
|
||||
void SetReadOnly(bool ro) { m_ReadOnly = ro; }
|
||||
|
||||
virtual std::string GetValueAsString() const override {
|
||||
T* ptr = m_ref.Get();
|
||||
if (!ptr) return "(none)";
|
||||
Object* obj = dynamic_cast<Object*>(ptr);
|
||||
if (obj) {
|
||||
std::string iname = obj->GetInstanceName();
|
||||
if (!iname.empty()) return iname;
|
||||
return obj->GetClassName();
|
||||
}
|
||||
return "(set)";
|
||||
}
|
||||
|
||||
// ReferencePropertyBase interface
|
||||
virtual Object* GetReferencedObject() const override {
|
||||
return dynamic_cast<Object*>(m_ref.Get());
|
||||
}
|
||||
|
||||
virtual void SetReferencedObject(Object* obj) override {
|
||||
if (!obj) {
|
||||
m_ref = SmartPointer<T>(nullptr);
|
||||
this->Updated();
|
||||
if (m_owner) m_owner->Updated();
|
||||
return;
|
||||
}
|
||||
T* casted = dynamic_cast<T*>(obj);
|
||||
if (casted) {
|
||||
m_ref = SmartPointer<T>(casted);
|
||||
this->Updated();
|
||||
if (m_owner) m_owner->Updated();
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool IsCompatible(Object* obj) const override {
|
||||
return dynamic_cast<T*>(obj) != nullptr;
|
||||
}
|
||||
|
||||
virtual const char* GetReferenceTypeName() const override {
|
||||
return typeid(T).name();
|
||||
}
|
||||
|
||||
// Serialization stubs
|
||||
virtual void serialize(Archive::xml_oarchive & ar, const unsigned int v) override {}
|
||||
virtual void serialize(Archive::xml_iarchive & ar, const unsigned int v) override {}
|
||||
virtual void serialize(Archive::text_oarchive & ar, const unsigned int v) override {}
|
||||
virtual void serialize(Archive::text_iarchive & ar, const unsigned int v) override {}
|
||||
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int v) override {}
|
||||
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int v) override {}
|
||||
virtual void serialize(Archive::log_archive & ar, const unsigned int v) override {}
|
||||
virtual void serialize(Archive::property_register_archive & ar, const unsigned int v) override {}
|
||||
|
||||
private:
|
||||
Object* m_owner;
|
||||
std::string m_name;
|
||||
std::string m_units;
|
||||
std::string m_group;
|
||||
SmartPointer<T>& m_ref;
|
||||
bool m_ReadOnly;
|
||||
};
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
namespace uLib {
|
||||
namespace Archive {
|
||||
|
||||
@@ -267,7 +379,20 @@ public:
|
||||
}
|
||||
|
||||
template<class T> void save_property_impl(const char* name, T& val, const char* units, bool hasRange, const T& minVal, const T& maxVal, bool isReadOnly) {
|
||||
if (m_Object) {
|
||||
if (!m_Object) return;
|
||||
if constexpr (is_smart_pointer<T>::value) {
|
||||
// SmartPointer<U> field: create a ReferenceProperty<U> for type-safe selection
|
||||
using ElementT = typename smart_pointer_element<T>::type;
|
||||
auto* p = new ReferenceProperty<ElementT>(m_Object, name, val, units ? units : "", GetCurrentGroup());
|
||||
p->SetReadOnly(isReadOnly);
|
||||
if (m_DisplayOnly) {
|
||||
m_Object->RegisterDisplayProperty(p);
|
||||
Object* obj = m_Object;
|
||||
Object::connect(p, &Object::Updated, [obj]() { obj->Updated(); });
|
||||
} else {
|
||||
m_Object->RegisterDynamicProperty(p);
|
||||
}
|
||||
} else {
|
||||
Property<T>* p = new Property<T>(m_Object, name, &val, units ? units : "", GetCurrentGroup());
|
||||
set_range_helper(p, hasRange, minVal, maxVal, typename std::is_arithmetic<T>::type());
|
||||
p->SetReadOnly(isReadOnly);
|
||||
|
||||
Reference in New Issue
Block a user