Browse Source

Data types now associated with the Context. Clean up data model.

Michael Ragazzon 5 years ago
parent
commit
742f01592d

+ 4 - 1
Include/RmlUi/Core/Context.h

@@ -45,6 +45,7 @@ class EventListener;
 class RenderInterface;
 class DataModel;
 class DataModelHandle;
+class DataTypeRegister;
 enum class EventId : uint16_t;
 
 /**
@@ -221,7 +222,7 @@ public:
 	void SetInstancer(ContextInstancer* instancer);
 
 
-	DataModelHandle CreateDataModel(String name);
+	DataModelHandle CreateDataModel(const String& name);
 
 	// Todo: Remove
 	DataModel* GetDataModel(const String& name)
@@ -303,6 +304,8 @@ private:
 	using DataModels = UnorderedMap<String, UniquePtr<DataModel>>;
 	DataModels data_models;
 
+	UniquePtr<DataTypeRegister> data_type_register;
+
 	// Internal callback for when an element is detached or removed from the hierarchy.
 	void OnElementDetach(Element* element);
 	// Internal callback for when a new element gains focus.

+ 8 - 4
Include/RmlUi/Core/DataController.h

@@ -44,15 +44,19 @@ class RMLUICORE_API DataController {
 public:
 	bool UpdateVariable(DataModel& model);
 
-	const String& GetVariableName() const;
+    String GetVariableName() const {
+        return address.empty() ? String() : address.front().name;
+    }
+
+    Element* GetElement() const {
+        return attached_element.get();
+    }
 
 	explicit operator bool() const {
 		return !address.empty() && attached_element;
 	}
 
-    Element* GetElement() const {
-        return attached_element.get();
-    }
+    virtual ~DataController();
 
 protected:
 	DataController(Element* element);

+ 27 - 26
Include/RmlUi/Core/DataModel.h

@@ -45,9 +45,16 @@ class Element;
 
 class RMLUICORE_API DataModel : NonCopyMoveable {
 public:
-	bool Bind(const String& name, void* ptr, VariableDefinition* variable);
+	void AddView(UniquePtr<DataView> view) { views.Add(std::move(view)); }
+	void AddController(UniquePtr<DataController> controller) { controllers.Add(std::move(controller)); }
+
+	bool BindVariable(const String& name, Variable variable);
+
+	bool InsertAlias(Element* element, const String& alias_name, Address replace_with_address);
+	bool EraseAliases(Element* element);
+
+	Address ResolveAddress(const String& address_str, Element* parent) const;
 
-	Variable GetVariable(const String& address_str) const;
 	Variable GetVariable(const Address& address) const;
 
 	template<typename T>
@@ -60,33 +67,20 @@ public:
 	void DirtyVariable(const String& variable_name);
 	bool IsVariableDirty(const String& variable_name) const;
 
-	Address ResolveAddress(const String& address_str, Element* parent) const;
-
-	void AddView(UniquePtr<DataView> view) { views.Add(std::move(view)); }
-	void AddController(UniquePtr<DataController> controller) { controllers.Add(std::move(controller)); }
-
-	// Todo: remove const / mutable. 
-	bool InsertAlias(Element* element, const String& alias_name, Address replace_with_address) const;
-	bool EraseAliases(Element* element) const;
-
-	bool Update();
-
-	void DirtyController(Element* element) { controllers.DirtyElement(*this, element); }
 	void OnElementRemove(Element* element);
+	void DirtyController(Element* element);
 
-
+	bool Update();
 
 private:
-	UnorderedMap<String, Variable> variables;
-
-	using ScopedAliases = UnorderedMap< Element*, SmallUnorderedMap<String, Address> >;
-	mutable ScopedAliases aliases;
-
 	DataViews views;
+	DataControllers controllers;
 
-	SmallUnorderedSet< String > dirty_variables;
+	UnorderedMap<String, Variable> variables;
+	SmallUnorderedSet<String> dirty_variables;
 
-	DataControllers controllers;
+	using ScopedAliases = UnorderedMap<Element*, SmallUnorderedMap<String, Address>>;
+	ScopedAliases aliases;
 };
 
 
@@ -109,28 +103,35 @@ public:
 	}
 
 	// Bind a data variable.
-	// Note: For non-scalar types make sure they first have been registered with the appropriate 'Register...()' functions.
+	// @note For non-scalar types make sure they first have been registered with the appropriate 'Register...()' functions.
 	template<typename T> bool Bind(const String& name, T* ptr) {
-		return model->Bind(name, ptr, type_register->GetOrAddScalar<T>());
+		RMLUI_ASSERTMSG(ptr, "Invalid pointer to data variable");
+		return model->BindVariable(name, Variable(type_register->GetOrAddScalar<T>(), ptr));
 	}
 	// Bind a get/set function pair.
 	bool BindFunc(const String& name, DataGetFunc get_func, DataSetFunc set_func = {}) {
 		VariableDefinition* func_definition = type_register->RegisterFunc(std::move(get_func), std::move(set_func));
-		bool result = model->Bind(name, nullptr, func_definition);
+		bool result = model->BindVariable(name, Variable(func_definition, nullptr));
 		return result;
 	}
 
+	// Register a struct type.
+	// @note The type applies to every data model associated with the current Context.
+	// @return A handle which can be used to register struct members.
 	template<typename T>
 	StructHandle<T> RegisterStruct() {
 		return type_register->RegisterStruct<T>();
 	}
 
+	// Register an array type.
+	// @note The type applies to every data model associated with the current Context.
+	// @note If 'Container::value_type' represents a non-scalar type, that type must already have been registered with the appropriate 'Register...()' functions.
+	// @note Container requires the following functions implemented: size() and operator[]. This includes std::vector and std::array.
 	template<typename Container>
 	bool RegisterArray() {
 		return type_register->RegisterArray<Container>();
 	}
 
-
 	explicit operator bool() { return model && type_register; }
 
 private:

+ 2 - 0
Include/RmlUi/Core/DataVariable.h

@@ -255,6 +255,7 @@ public:
 		RMLUI_ASSERT(member);
 		bool inserted = members.emplace(name, std::move(member)).second;
 		RMLUI_ASSERTMSG(inserted, "Member name already exists.");
+		(void)inserted;
 	}
 
 private:
@@ -365,6 +366,7 @@ public:
 		auto& it = result.first;
 		bool inserted = result.second;
 		RMLUI_ASSERT(inserted);
+		(void)inserted;
 
 		it->second = std::make_unique<FuncDefinition>(std::move(get_func), std::move(set_func));
 

+ 12 - 12
Include/RmlUi/Core/DataView.h

@@ -47,7 +47,7 @@ class DataModel;
 class RMLUICORE_API DataView : NonCopyMoveable {
 public:
 	virtual ~DataView();
-	virtual bool Update(const DataModel& model) = 0;
+	virtual bool Update(DataModel& model) = 0;
 	virtual StringList GetVariableNameList() const = 0;
 
 	bool IsValid() const { return (bool)attached_element; }
@@ -68,9 +68,9 @@ private:
 
 class DataViewText final : public DataView {
 public:
-	DataViewText(const DataModel& model, ElementText* in_element, const String& in_text, size_t index_begin_search = 0);
+	DataViewText(DataModel& model, ElementText* in_element, const String& in_text, size_t index_begin_search = 0);
 
-	bool Update(const DataModel& model) override;
+	bool Update(DataModel& model) override;
 
 	StringList GetVariableNameList() const override {
 		StringList list;
@@ -98,9 +98,9 @@ private:
 
 class DataViewAttribute final : public DataView {
 public:
-	DataViewAttribute(const DataModel& model, Element* element, Element* parent, const String& binding_name, const String& attribute_name);
+	DataViewAttribute(DataModel& model, Element* element, Element* parent, const String& binding_name, const String& attribute_name);
 
-	bool Update(const DataModel& model) override;
+	bool Update(DataModel& model) override;
 
 	StringList GetVariableNameList() const override {
 		return variable_address.empty() ? StringList() : StringList{ variable_address.front().name };
@@ -113,9 +113,9 @@ private:
 
 class DataViewStyle final : public DataView {
 public:
-	DataViewStyle(const DataModel& model, Element* element, Element* parent, const String& binding_name, const String& property_name);
+	DataViewStyle(DataModel& model, Element* element, Element* parent, const String& binding_name, const String& property_name);
 
-	bool Update(const DataModel& model) override;
+	bool Update(DataModel& model) override;
 
 	StringList GetVariableNameList() const override {
 		return variable_address.empty() ? StringList() : StringList{ variable_address.front().name };
@@ -128,9 +128,9 @@ private:
 
 class DataViewIf final : public DataView {
 public:
-	DataViewIf(const DataModel& model, Element* element, Element* parent, const String& binding_name);
+	DataViewIf(DataModel& model, Element* element, Element* parent, const String& binding_name);
 
-	bool Update(const DataModel& model) override;
+	bool Update(DataModel& model) override;
 
 	StringList GetVariableNameList() const override {
 		return variable_address.empty() ? StringList() : StringList{ variable_address.front().name };
@@ -142,9 +142,9 @@ private:
 
 class DataViewFor final : public DataView {
 public:
-	DataViewFor(const DataModel& model, Element* element, const String& binding_name, const String& rml_contents);
+	DataViewFor(DataModel& model, Element* element, const String& binding_name, const String& rml_contents);
 
-	bool Update(const DataModel& model) override;
+	bool Update(DataModel& model) override;
 
 	StringList GetVariableNameList() const override {
 		return variable_address.empty() ? StringList() : StringList{ variable_address.front().name };
@@ -169,7 +169,7 @@ public:
 
 	void OnElementRemove(Element* element);
 
-	bool Update(const DataModel& model, const SmallUnorderedSet< String >& dirty_variables);
+	bool Update(DataModel& model, const SmallUnorderedSet< String >& dirty_variables);
 
 private:
 	using DataViewList = std::vector<UniquePtr<DataView>>;

+ 11 - 3
Source/Core/Context.cpp

@@ -788,11 +788,19 @@ void Context::SetInstancer(ContextInstancer* _instancer)
 	instancer = _instancer;
 }
 
-DataModelHandle Context::CreateDataModel(String name)
+DataModelHandle Context::CreateDataModel(const String& name)
 {
 	auto result = data_models.emplace(name, std::make_unique<DataModel>());
-	if (result.second)
-		return DataModelHandle(result.first->second.get(), &GetDataTypeRegister());
+	bool inserted = result.second;
+	if (inserted)
+	{
+		if (!data_type_register)
+			data_type_register = std::make_unique<DataTypeRegister>();
+
+		return DataModelHandle(result.first->second.get(), data_type_register.get());
+	}
+
+	RMLUI_ERRORMSG("Data model name already exists.")
 
 	return DataModelHandle();
 }

+ 0 - 6
Source/Core/Core.cpp

@@ -309,12 +309,6 @@ bool LoadFontFace(const byte* data, int data_size, const String& font_family, St
 	return font_interface->LoadFontFace(data, data_size, font_family, style, weight, fallback_face);
 }
 
-DataTypeRegister& GetDataTypeRegister()
-{
-	static DataTypeRegister data_types;
-	return data_types;
-}
-
 // Registers a generic rmlui plugin
 void RegisterPlugin(Plugin* plugin)
 {

+ 3 - 6
Source/Core/DataController.cpp

@@ -36,6 +36,9 @@ namespace Core {
 DataController::DataController(Element* element) : attached_element(element->GetObserverPtr())
 {}
 
+DataController::~DataController()
+{}
+
 bool DataController::UpdateVariable(DataModel& model)
 {
     Element* element = attached_element.get();
@@ -52,12 +55,6 @@ bool DataController::UpdateVariable(DataModel& model)
     return variable_changed;
 }
 
-const String& DataController::GetVariableName() const {
-    static const String empty_string;
-    return address.empty() ? empty_string : address.front().name;
-}
-
-
 DataControllerValue::DataControllerValue(DataModel& model, Element* element, const String& in_value_name) : DataController(element)
 {
     Address variable_address = model.ResolveAddress(in_value_name, element);

+ 51 - 65
Source/Core/DataModel.cpp

@@ -72,16 +72,15 @@ static Address ParseAddress(const String& address_str)
 	return address;
 };
 
-bool DataModel::Bind(const String& name, void* ptr, VariableDefinition* variable)
+bool DataModel::BindVariable(const String& name, Variable variable)
 {
 	if (!variable)
 	{
-		Log::Message(Log::LT_WARNING, "No registered type could be found for the data variable '%s'.", name.c_str());
+		Log::Message(Log::LT_WARNING, "Could not bind variable '%s' to data model, data type not registered.", name.c_str());
 		return false;
 	}
-	RMLUI_ASSERT(ptr || variable->Type() == VariableType::Function);
 
-	bool inserted = variables.emplace(name, Variable(variable, ptr)).second;
+	bool inserted = variables.emplace(name, variable).second;
 	if (!inserted)
 	{
 		Log::Message(Log::LT_WARNING, "Data model variable with name '%s' already exists.", name.c_str());
@@ -91,58 +90,33 @@ bool DataModel::Bind(const String& name, void* ptr, VariableDefinition* variable
 	return true;
 }
 
-Variable DataModel::GetVariable(const String& address_str) const
+bool DataModel::InsertAlias(Element* element, const String& alias_name, Address replace_with_address)
 {
-	Address address = ParseAddress(address_str);
-
-	if (address.empty() || address.front().name.empty())
-	{
-		Log::Message(Log::LT_WARNING, "Invalid data address '%s'.", address_str.c_str());
-		return Variable();
-	}
-
-	Variable instance = GetVariable(address);
-	if (!instance)
+	if (replace_with_address.empty() || replace_with_address.front().name.empty())
 	{
-		Log::Message(Log::LT_WARNING, "Could not find the data variable '%s'.", address_str.c_str());
-		return Variable();
+		Log::Message(Log::LT_WARNING, "Could not add alias variable '%s' to data model, replacement address invalid.", alias_name.c_str());
+		return false;
 	}
 
-	return instance;
-}
+	if (variables.count(alias_name) == 1)
+		Log::Message(Log::LT_WARNING, "Alias variable '%s' is shadowed by a global variable.", alias_name.c_str());
 
-Variable DataModel::GetVariable(const Address& address) const
-{
-	if (address.empty() || address.front().name.empty())
-		return Variable();
-
-	auto it = variables.find(address.front().name);
-	if (it == variables.end())
-		return Variable();
-
-	Variable variable = it->second;
+	auto& map = aliases.emplace(element, SmallUnorderedMap<String, Address>()).first->second;
+	
+	auto it = map.find(alias_name);
+	if (it != map.end())
+		Log::Message(Log::LT_WARNING, "Alias name '%s' in data model already exists, replaced.", alias_name.c_str());
 
-	for (int i = 1; i < (int)address.size() && variable; i++)
-	{
-		variable = variable.GetChild(address[i]);
-		if (!variable)
-			return Variable();
-	}
+	map[alias_name] = std::move(replace_with_address);
 
-	return variable;
+	return true;
 }
 
-void DataModel::DirtyVariable(const String& variable_name)
+bool DataModel::EraseAliases(Element* element)
 {
-	RMLUI_ASSERTMSG(variables.count(variable_name) == 1, "Variable name not found among added variables.");
-	dirty_variables.insert(variable_name);
-}
-
-bool DataModel::IsVariableDirty(const String& variable_name) const {
-	return (dirty_variables.count(variable_name) == 1);
+	return aliases.erase(element) == 1;
 }
 
-
 Address DataModel::ResolveAddress(const String& address_str, Element* parent) const
 {
 	Address address = ParseAddress(address_str);
@@ -190,28 +164,46 @@ Address DataModel::ResolveAddress(const String& address_str, Element* parent) co
 	return Address();
 }
 
-bool DataModel::InsertAlias(Element* element, const String& alias_name, Address replace_with_address) const
+Variable DataModel::GetVariable(const Address& address) const
 {
-	if (replace_with_address.empty() || replace_with_address.front().name.empty())
+	if (address.empty() || address.front().name.empty())
+		return Variable();
+
+	auto it = variables.find(address.front().name);
+	if (it == variables.end())
+		return Variable();
+
+	Variable variable = it->second;
+
+	for (int i = 1; i < (int)address.size() && variable; i++)
 	{
-		Log::Message(Log::LT_WARNING, "Could not add alias variable '%s' to data model, replacement address invalid.", alias_name.c_str());
-		return false;
+		variable = variable.GetChild(address[i]);
+		if (!variable)
+			return Variable();
 	}
 
-	auto& map = aliases.emplace(element, SmallUnorderedMap<String, Address>()).first->second;
-	
-	auto it = map.find(alias_name);
-	if (it != map.end())
-		Log::Message(Log::LT_WARNING, "Alias name '%s' in data model already exists, replaced.", alias_name.c_str());
+	return variable;
+}
 
-	map[alias_name] = std::move(replace_with_address);
+void DataModel::DirtyVariable(const String& variable_name)
+{
+	RMLUI_ASSERTMSG(variables.count(variable_name) == 1, "Variable name not found among added variables.");
+	dirty_variables.insert(variable_name);
+}
 
-	return true;
+bool DataModel::IsVariableDirty(const String& variable_name) const {
+	return (dirty_variables.count(variable_name) == 1);
 }
 
-bool DataModel::EraseAliases(Element* element) const
+void DataModel::OnElementRemove(Element* element)
 {
-	return aliases.erase(element) == 1;
+	EraseAliases(element);
+	views.OnElementRemove(element);
+}
+
+void DataModel::DirtyController(Element* element) 
+{
+	controllers.DirtyElement(*this, element);
 }
 
 bool DataModel::Update() 
@@ -221,12 +213,6 @@ bool DataModel::Update()
 	return result;
 }
 
-void DataModel::OnElementRemove(Element* element)
-{
-	EraseAliases(element);
-	views.OnElementRemove(element);
-}
-
 
 
 #ifdef RMLUI_DEBUG
@@ -297,13 +283,13 @@ static struct TestDataVariables {
 
 			RMLUI_ASSERT(results == expected_results);
 
-			bool success = model.GetVariable("data.more_fun[1].magic[1]").Set(Variant(String("199")));
+			bool success = model.GetVariable(ParseAddress("data.more_fun[1].magic[1]")).Set(Variant(String("199")));
 			RMLUI_ASSERT(success && data.more_fun[1].magic[1] == 199);
 
 			data.fun.magic = { 99, 190, 55, 2000, 50, 60, 70, 80, 90 };
 
 			Variant test_get_result;
-			bool test_get_success = model.GetVariable("data.fun.magic[8]").Get(test_get_result);
+			bool test_get_success = model.GetVariable(ParseAddress("data.fun.magic[8]")).Get(test_get_result);
 			RMLUI_ASSERT(test_get_success && test_get_result.Get<String>() == "90");
 		}
 	}

+ 11 - 11
Source/Core/DataView.cpp

@@ -54,7 +54,7 @@ DataView::DataView(Element* element) : attached_element(element->GetObserverPtr(
 }
 
 
-DataViewText::DataViewText(const DataModel& model, ElementText* in_parent_element, const String& in_text, const size_t index_begin_search) : DataView(in_parent_element)
+DataViewText::DataViewText(DataModel& model, ElementText* in_parent_element, const String& in_text, const size_t index_begin_search) : DataView(in_parent_element)
 {
 	text.reserve(in_text.size());
 
@@ -104,7 +104,7 @@ DataViewText::DataViewText(const DataModel& model, ElementText* in_parent_elemen
 	}
 }
 
-bool DataViewText::Update(const DataModel& model)
+bool DataViewText::Update(DataModel& model)
 {
 	bool entries_modified = false;
 
@@ -165,7 +165,7 @@ String DataViewText::BuildText() const
 }
 
 
-DataViewAttribute::DataViewAttribute(const DataModel& model, Element* element, Element* parent, const String& binding_name, const String& attribute_name)
+DataViewAttribute::DataViewAttribute(DataModel& model, Element* element, Element* parent, const String& binding_name, const String& attribute_name)
 	: DataView(element), attribute_name(attribute_name)
 {
 	variable_address = model.ResolveAddress(binding_name, parent);
@@ -176,7 +176,7 @@ DataViewAttribute::DataViewAttribute(const DataModel& model, Element* element, E
 		Update(model);
 }
 
-bool DataViewAttribute::Update(const DataModel& model)
+bool DataViewAttribute::Update(DataModel& model)
 {
 	bool result = false;
 	String value;
@@ -197,7 +197,7 @@ bool DataViewAttribute::Update(const DataModel& model)
 
 
 
-DataViewStyle::DataViewStyle(const DataModel& model, Element* element, Element* parent, const String& binding_name, const String& property_name)
+DataViewStyle::DataViewStyle(DataModel& model, Element* element, Element* parent, const String& binding_name, const String& property_name)
 	: DataView(element), property_name(property_name)
 {
 	variable_address = model.ResolveAddress(binding_name, parent);
@@ -209,7 +209,7 @@ DataViewStyle::DataViewStyle(const DataModel& model, Element* element, Element*
 }
 
 
-bool DataViewStyle::Update(const DataModel& model)
+bool DataViewStyle::Update(DataModel& model)
 {
 	bool result = false;
 	String value;
@@ -230,7 +230,7 @@ bool DataViewStyle::Update(const DataModel& model)
 
 
 
-DataViewIf::DataViewIf(const DataModel& model, Element* element, Element* parent, const String& binding_name) : DataView(element)
+DataViewIf::DataViewIf(DataModel& model, Element* element, Element* parent, const String& binding_name) : DataView(element)
 {
 	variable_address = model.ResolveAddress(binding_name, element);
 	if (variable_address.empty())
@@ -240,7 +240,7 @@ DataViewIf::DataViewIf(const DataModel& model, Element* element, Element* parent
 }
 
 
-bool DataViewIf::Update(const DataModel& model)
+bool DataViewIf::Update(DataModel& model)
 {
 	bool result = false;
 	bool value = false;
@@ -263,7 +263,7 @@ bool DataViewIf::Update(const DataModel& model)
 
 
 
-DataViewFor::DataViewFor(const DataModel& model, Element* element, const String& in_binding_name, const String& in_rml_content)
+DataViewFor::DataViewFor(DataModel& model, Element* element, const String& in_binding_name, const String& in_rml_content)
 	: DataView(element), rml_contents(in_rml_content)
 {
 	StringList binding_list;
@@ -298,7 +298,7 @@ DataViewFor::DataViewFor(const DataModel& model, Element* element, const String&
 
 
 
-bool DataViewFor::Update(const DataModel& model)
+bool DataViewFor::Update(DataModel& model)
 {
 	Variable variable = model.GetVariable(variable_address);
 	if (!variable)
@@ -368,7 +368,7 @@ void DataViews::OnElementRemove(Element* element)
 	}
 }
 
-bool DataViews::Update(const DataModel & model, const SmallUnorderedSet< String >& dirty_variables)
+bool DataViews::Update(DataModel & model, const SmallUnorderedSet< String >& dirty_variables)
 {
 	bool result = false;