Explorar o código

Refactor, move the new data variable types into the library. Now working 'for' loops for structs.

Michael Ragazzon %!s(int64=6) %!d(string=hai) anos
pai
achega
e346522249

+ 2 - 0
CMake/FileList.cmake

@@ -110,6 +110,7 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Core.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataController.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataModel.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataVariable.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DataView.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Debug.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Decorator.h
@@ -197,6 +198,7 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/Core.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataController.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataModel.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/DataVariable.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataView.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Decorator.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorGradient.cpp

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

@@ -34,7 +34,6 @@
 #include "Traits.h"
 #include "Input.h"
 #include "ScriptInterface.h"
-#include "DataModel.h"
 
 namespace Rml {
 namespace Core {
@@ -44,6 +43,8 @@ class ContextInstancer;
 class ElementDocument;
 class EventListener;
 class RenderInterface;
+class DataModel;
+class DataModelHandle;
 enum class EventId : uint16_t;
 
 /**
@@ -220,15 +221,9 @@ public:
 	void SetInstancer(ContextInstancer* instancer);
 
 
-	DataModelHandle CreateDataModel(String name) 
-	{
-		auto result = data_models.emplace(name, std::make_unique<DataModel>());
-		if (result.second)
-			return DataModelHandle(result.first->second.get());
-
-		return DataModelHandle(nullptr);
-	}
+	DataModelHandle CreateDataModel(String name);
 
+	// Todo: Remove
 	DataModel* GetDataModel(const String& name)
 	{
 		auto it = data_models.find(name);

+ 5 - 6
Include/RmlUi/Core/DataController.h

@@ -33,6 +33,7 @@
 #include "Types.h"
 #include "Variant.h"
 #include "StringUtilities.h"
+#include "DataVariable.h"
 
 namespace Rml {
 namespace Core {
@@ -40,14 +41,12 @@ namespace Core {
 class Element;
 class DataModel;
 
-class RMLUICORE_API DataController {
-
-};
+class RMLUICORE_API DataController {};
 
 
 class DataControllerAttribute : public DataController {
 public:
-	DataControllerAttribute(const DataModel& model, const String& in_attribute_name, const String& in_value_name);
+	DataControllerAttribute(const DataModel& model, Element* parent, const String& in_attribute_name, const String& in_value_name);
 
 	inline operator bool() const {
 		return !attribute_name.empty();
@@ -69,11 +68,11 @@ public:
 private:
 	bool dirty = false;
 	String attribute_name;
-	String value_name;
+	Address variable_address;
 };
 
 
-class RMLUICORE_API DataControllers {
+class RMLUICORE_API DataControllers : NonCopyMoveable {
 public:
 
 	void AddController(Element* element, DataControllerAttribute&& controller) {

+ 37 - 277
Include/RmlUi/Core/DataModel.h

@@ -33,317 +33,75 @@
 #include "Types.h"
 #include "Traits.h"
 #include "Variant.h"
-#include "StringUtilities.h"
 #include "DataView.h"
 #include "DataController.h"
+#include "DataVariable.h"
 
 namespace Rml {
 namespace Core {
 
-class DataVariable;
-class DataMember;
-class DataContainer;
 class Element;
 
 
-class DataAddress {
-
-	struct Entry {
-		enum Type { Array, Struct, Variable };
-		int index;
-		String name;
-	};
-
-	std::vector<Entry> address;
-
-};
-
-
-
-class DataModel {
+class RMLUICORE_API DataModel : NonCopyMoveable {
 public:
-	bool GetValue(const String& name, Variant& out_value) const;
-	bool SetValue(const String& name, const Variant& value) const;
+	DataModel(DataTypeRegister* type_register) : type_register(type_register) {}
 
-	template<typename T>
-	bool GetValue(const String& name, T& out_value) const {
-		Variant variant;
-		return GetValue(name, variant) && variant.GetInto<T>(out_value);
-	}
-
-	String ResolveVariableName(const String& raw_name, Element* parent) const;
+	DataTypeRegister& GetTypeRegister() { return *type_register; }
 
-
-	using Variables = UnorderedMap<String, UniquePtr<DataVariable>>;
-	Variables variables;
-
-	DataControllers controllers;
-	DataViews views;
-
-	using DataMembers = SmallUnorderedMap<String, UniquePtr<DataMember>>;
-	using DataTypes = UnorderedMap<int, DataMembers>;
-	DataTypes data_types;
-
-	using DataContainers = UnorderedMap<String, UniquePtr<DataContainer>>;
-	DataContainers containers;
-
-	using ScopedAliases = UnorderedMap< Element*, SmallUnorderedMap<String, String> >;
-	mutable ScopedAliases aliases;
-};
-
-
-class DataVariable {
-public:
-	DataVariable(void* ptr) : ptr(ptr) {}
-	virtual ~DataVariable() = default;
-
-	inline bool Get(Variant& out_value) {
-		return Get(ptr, out_value);
+	template<typename T> bool BindScalar(String name, T* ptr) {
+		return Bind(name, ptr, type_register->GetOrAddScalar<T>(), VariableType::Scalar);
 	}
-	inline bool Set(const Variant& in_value) {
-		return Set(ptr, in_value);
+	template<typename T> bool BindStruct(String name, T* ptr) {
+		return Bind(name, ptr, type_register->Get<T>(), VariableType::Struct);
 	}
-
-protected:
-	virtual bool Get(const void* object, Variant& out_value) = 0;
-	virtual bool Set(void* object, const Variant& in_value) = 0;
-
-private:
-	void* ptr;
-};
-
-class DataMember {
-public:
-	virtual ~DataMember() = default;
-	virtual bool Get(const void* object, Variant& out_value) = 0;
-	virtual bool Set(void* object, const Variant& in_value) = 0;
-};
-
-
-
-template<typename T>
-class DataBindingDefault : public DataVariable {
-public:
-	DataBindingDefault(void* ptr) : DataVariable(ptr) {}
-
-private:
-	bool Get(const void* object, Variant& out_value) override {
-		out_value = *static_cast<const T*>(object);
-		return true;
-	}
-	bool Set(void* object, const Variant& in_value) override {
-		T& target = *static_cast<T*>(object);
-		return in_value.GetInto<T>(target);
-	}
-};
-
-
-
-
-class DataBindingContext {
-public:
-	struct Item {
-		String name;
-		int index = -1;
-	};
-	DataBindingContext(std::vector<Item>&& in_items) : items(std::move(in_items)), it(items.begin()) {}
-
-	operator bool() const { return it != items.end(); }
-
-	const Item& Next() {
-		RMLUI_ASSERT(it != items.end());
-		return *(it++);
+	template<typename T> bool BindArray(String name, T* ptr) {
+		return Bind(name, ptr, type_register->Get<T>(), VariableType::Array);
 	}
-private:
-	std::vector<Item> items;
-	std::vector<Item>::iterator it;
-};
-
 
-class DataContainer {
-public:
-	DataContainer(void* ptr) : ptr(ptr) {}
-	virtual ~DataContainer() = default;
+	Variant GetValue(const String& address_str) const;
+	bool SetValue(const String& address_str, const Variant& variant) const;
 
-	inline bool Get(Variant& out_value, DataBindingContext& context) {
-		return Get(ptr, out_value, context);
-	}
-	inline bool Set(const Variant& in_value, DataBindingContext& context) {
-		return Set(ptr, in_value, context);
-	}
-	inline int Size() {
-		return Size(ptr);
-	}
-
-protected:
-	virtual bool Get(const void* container_ptr, Variant& out_value, DataBindingContext& context) = 0;
-	virtual bool Set(void* container_ptr, const Variant& in_value, DataBindingContext& context) = 0;
-	virtual int Size(void* container_ptr) = 0;
-
-private:
-	void* ptr;
-};
-
-
-template<typename T>
-class DataContainerDefault : public DataContainer {
-public:
-	DataContainerDefault(void* ptr) : DataContainer(ptr) {}
-
-private:
-	bool Get(const void* container_ptr, Variant& out_value, DataBindingContext& context) override {
-		if (!context)
-		{
-			RMLUI_ERROR;
-			return false;
-		}
-		auto& item = context.Next();
-		auto& container = *static_cast<const T*>(container_ptr);
-		if (item.index < 0 || item.index >= (int)container.size())
-		{
-			Log::Message(Log::LT_WARNING, "Data container index out of bounds.");
-			return false;
-		}
-		out_value = container[item.index];
-		return true;
-	}
-	bool Set(void* container_ptr, const Variant& in_value, DataBindingContext& context) override {
-		if (!context)
-		{
-			RMLUI_ERROR;
-			return false;
-		}
-		auto& item = context.Next();
-		auto& container = *static_cast<T*>(container_ptr);
-		if (item.index < (int)container.size() || item.index >= (int)container.size())
-		{
-			Log::Message(Log::LT_WARNING, "Data container index out of bounds.");
-			return false;
-		}
-		container[item.index] = in_value.Get< typename T::value_type >();
-		return true;
-	}
-	int Size(void* container_ptr) override {
-		return (int)static_cast<T*>(container_ptr)->size();
+	template<typename T>
+	bool GetValue(const Address& address, T& out_value) const {
+		Variant variant;
+		Variable variable = GetVariable(address);
+		return variable && variable.Get(variant) && variant.GetInto<T>(out_value);
 	}
-};
 
+	Variable GetVariable(const String& address_str) const;
+	Variable GetVariable(const Address& address) const;
 
+	Address ResolveAddress(const String& address_str, Element* parent) const;
 
+	void AddView(UniquePtr<DataView> view) { views.Add(std::move(view)); }
 
-class DataBindingMember : public DataVariable {
-public:
-	DataBindingMember(void* object, DataMember* member) : DataVariable(object), member(member) {}
-
-private:
-	bool Get(const void* object, Variant& out_value) override {
-		return member->Get(object, out_value);
-	}
-	bool Set(void* object, const Variant& in_value) override {
-		return member->Set(object, in_value);
-	}
+	// Todo: remove const / mutable. 
+	bool InsertAlias(Element* element, const String& alias_name, Address replace_with_address) const;
+	bool EraseAliases(Element* element) const;
 
-	DataMember* member;
-};
-
-
-template <typename Object, typename MemberType>
-class DataMemberDefault : public DataMember {
-public:
-	DataMemberDefault(MemberType Object::* member_ptr) : member_ptr(member_ptr) {}
-
-	bool Get(const void* object, Variant& out_value) override {
-		out_value = static_cast<const Object*>(object)->*member_ptr;
-		return true;
-	}
-	bool Set(void* object, const Variant& in_value) override {
-		MemberType& target = static_cast<Object*>(object)->*member_ptr;
-		return in_value.GetInto<MemberType>(target);
-	}
+	bool UpdateViews() { return views.Update(*this); }
 
+	// Todo: Make private
+	DataControllers controllers;
 private:
-	MemberType Object::* member_ptr;
-};
+	bool Bind(String name, void* ptr, VariableDefinition* variable, VariableType type);
 
+	DataTypeRegister* type_register;
 
+	UnorderedMap<String, Variable> variables;
 
-template <typename Object>
-class DataTypeHandle {
-public:
-	DataTypeHandle(DataModel::DataMembers* members) : members(members) {}
-
-	template <typename MemberType>
-	DataTypeHandle& RegisterMember(String name, MemberType Object::* member_ptr)
-	{
-		RMLUI_ASSERT(members);
-		members->emplace(name, std::make_unique<DataMemberDefault<Object, MemberType>>(member_ptr));
-		return *this;
-	}
+	using ScopedAliases = UnorderedMap< Element*, SmallUnorderedMap<String, Address> >;
+	mutable ScopedAliases aliases;
 
-private:
-	DataModel::DataMembers* members;
+	DataViews views;
 };
 
 
-class DataModelHandle {
+class RMLUICORE_API DataModelHandle {
 public:
-	DataModelHandle() : model(nullptr) {}
 	DataModelHandle(DataModel* model) : model(model) {}
 
-	template <typename T>
-	DataModelHandle& BindValue(String name, T* object)
-	{
-		RMLUI_ASSERT(model);
-		model->variables.emplace(name, std::make_unique<DataBindingDefault<T>>( object ));
-		return *this;
-	}
-
-	template <typename Container>
-	DataModelHandle& BindContainer(String name, Container* object)
-	{
-		RMLUI_ASSERT(model);
-		//using T = Container::value_type;
-
-		model->containers.emplace(name, std::make_unique<DataContainerDefault<Container>>(object));
-		return *this;
-	}
-
-	template <typename T>
-	DataModelHandle& BindTypeValue(String name, T* object)
-	{
-		RMLUI_ASSERT(model);
-
-		int id = Family< typename std::remove_pointer< T >::type >::Id();
-
-		auto it = model->data_types.find(id);
-		if (it != model->data_types.end())
-		{
-			auto& members = it->second;
-			for (auto& pair : members)
-			{
-				const String full_name = name + '.' + pair.first;
-				DataMember* member = pair.second.get();
-				bool inserted = model->variables.emplace(full_name, std::make_unique<DataBindingMember>(object, member)).second;
-				RMLUI_ASSERT(inserted);
-			}
-		}
-
-		return *this;
-	}
-
-	template <typename T>
-	DataTypeHandle<T> RegisterType()
-	{
-		RMLUI_ASSERT(model);
-		int id = Family< T >::Id();
-		auto result = model->data_types.emplace(id, DataModel::DataMembers() );
-		if (!result.second) {
-			RMLUI_ERRORMSG("Type already registered.");
-			return DataTypeHandle<T>(nullptr);
-		}
-		return DataTypeHandle<T>(&result.first->second);
-	}
-
 	void UpdateControllers() {
 		RMLUI_ASSERT(model);
 		model->controllers.Update(*model);
@@ -351,9 +109,11 @@ public:
 
 	void UpdateViews() {
 		RMLUI_ASSERT(model);
-		model->views.Update(*model);
+		model->UpdateViews();
 	}
 
+	DataModel* GetModel() { return model; }
+
 	operator bool() { return model != nullptr; }
 
 private:

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

@@ -0,0 +1,405 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICOREDATAVARIABLE_H
+#define RMLUICOREDATAVARIABLE_H
+
+#include "Header.h"
+#include "Types.h"
+#include "Traits.h"
+#include "Variant.h"
+
+namespace Rml {
+namespace Core {
+
+class Variable;
+
+template<typename T>
+struct is_valid_scalar {
+	static constexpr bool value = std::is_convertible<T, byte>::value
+		|| std::is_convertible<T, char>::value
+		|| std::is_convertible<T, float>::value
+		|| std::is_convertible<T, int>::value
+		|| std::is_convertible<T, String>::value
+		|| std::is_convertible<T, Vector2f>::value
+		|| std::is_convertible<T, Vector3f>::value
+		|| std::is_convertible<T, Vector4f>::value
+		|| std::is_convertible<T, Colourb>::value
+		|| std::is_convertible<T, Colourf>::value
+		|| std::is_convertible<T, char*>::value
+		|| std::is_convertible<T, void*>::value;
+};
+
+enum class VariableType { Scalar, Array, Struct };
+
+struct AddressEntry {
+	AddressEntry(String name) : name(name), index(-1) { }
+	AddressEntry(int index) : index(index) { }
+	String name;
+	int index;
+};
+using Address = std::vector<AddressEntry>;
+
+
+class RMLUICORE_API VariableDefinition {
+public:
+	virtual ~VariableDefinition() = default;
+	VariableType Type() const { return type; }
+
+	virtual bool Get(void* ptr, Variant& variant);
+	virtual bool Set(void* ptr, const Variant& variant);
+	virtual int Size(void* ptr);
+	virtual Variable GetChild(void* ptr, const AddressEntry& address);
+
+protected:
+	VariableDefinition(VariableType type) : type(type) {}
+
+private:
+	VariableType type;
+};
+
+
+
+class Variable {
+public:
+	Variable() {}
+	Variable(VariableDefinition* definition, void* ptr) : definition(definition), ptr(ptr) {}
+
+	operator bool() const { return definition && ptr; }
+
+	inline bool Get(Variant& variant) {
+		return definition->Get(ptr, variant);
+	}
+	inline bool Set(const Variant& variant) {
+		return definition->Set(ptr, variant);
+	}
+	inline int Size() {
+		return definition->Size(ptr);
+	}
+	inline Variable GetChild(const AddressEntry& address) {
+		return definition->GetChild(ptr, address);
+	}
+	inline VariableType Type() const {
+		return definition->Type();
+	}
+
+private:
+	VariableDefinition* definition = nullptr;
+	void* ptr = nullptr;
+};
+
+
+
+template<typename T>
+class ScalarDefinition final : public VariableDefinition {
+public:
+	ScalarDefinition() : VariableDefinition(VariableType::Scalar) {}
+
+	bool Get(void* ptr, Variant& variant) override
+	{
+		variant = *static_cast<T*>(ptr);
+		return true;
+	}
+	bool Set(void* ptr, const Variant& variant) override
+	{
+		return variant.GetInto<T>(*static_cast<T*>(ptr));
+	}
+};
+
+
+template<typename Container>
+class ArrayDefinition final : public VariableDefinition {
+public:
+	ArrayDefinition(VariableDefinition* underlying_variable) : VariableDefinition(VariableType::Array), underlying_variable(underlying_variable) {}
+
+	int Size(void* ptr) override {
+		return int(static_cast<Container*>(ptr)->size());
+	}
+
+protected:
+	Variable GetChild(void* void_ptr, const AddressEntry& address) override
+	{
+		Container* ptr = static_cast<Container*>(void_ptr);
+		const int index = address.index;
+
+		if (index < 0 && index >= (int)ptr->size())
+		{
+			Log::Message(Log::LT_WARNING, "Data array index out of bounds.");
+			return Variable();
+		}
+
+		void* next_ptr = &((*ptr)[index]);
+		return Variable(underlying_variable, next_ptr);
+	}
+
+private:
+	VariableDefinition* underlying_variable;
+};
+
+
+class StructMember {
+public:
+	StructMember(VariableDefinition* variable) : variable(variable) {}
+	virtual ~StructMember() = default;
+
+	VariableDefinition* GetVariable() const { return variable; }
+
+	virtual void* GetPointer(void* base_ptr) = 0;
+
+private:
+	VariableDefinition* variable;
+};
+
+template <typename Object, typename MemberType>
+class StructMemberDefault final : public StructMember {
+public:
+	StructMemberDefault(VariableDefinition* variable, MemberType Object::* member_ptr) : StructMember(variable), member_ptr(member_ptr) {}
+
+	void* GetPointer(void* base_ptr) override {
+		return &(static_cast<Object*>(base_ptr)->*member_ptr);
+	}
+
+private:
+	MemberType Object::* member_ptr;
+};
+
+
+class StructDefinition final : public VariableDefinition {
+public:
+	StructDefinition() : VariableDefinition(VariableType::Struct)
+	{}
+
+	Variable GetChild(void* ptr, const AddressEntry& address) override
+	{
+		const String& name = address.name;
+		if (name.empty())
+		{
+			Log::Message(Log::LT_WARNING, "Expected a struct member name but none given.");
+			return Variable();
+		}
+
+		auto it = members.find(name);
+		if (it == members.end())
+		{
+			Log::Message(Log::LT_WARNING, "Member %s not found in data struct.", name.c_str());
+			return Variable();
+		}
+
+		void* next_ptr = it->second->GetPointer(ptr);
+		VariableDefinition* next_variable = it->second->GetVariable();
+
+		return Variable(next_variable, next_ptr);
+	}
+
+	void AddMember(const String& name, UniquePtr<StructMember> member)
+	{
+		RMLUI_ASSERT(member);
+		bool inserted = members.emplace(name, std::move(member)).second;
+		RMLUI_ASSERTMSG(inserted, "Member name already exists.");
+	}
+
+private:
+	SmallUnorderedMap<String, UniquePtr<StructMember>> members;
+};
+
+
+
+class DataTypeRegister;
+
+class DefinitionHandle {
+public:
+	operator bool() const { return type_register && GetDefinition(); }
+	virtual VariableDefinition* GetDefinition() const = 0;
+protected:
+	DefinitionHandle(DataTypeRegister* type_register) : type_register(type_register) {}
+	DataTypeRegister* type_register;
+};
+
+template <typename T>
+class ScalarHandle : public DefinitionHandle {
+public:
+	ScalarHandle(DataTypeRegister* type_register) : DefinitionHandle(type_register) {}
+	VariableDefinition* GetDefinition() const override;
+};
+
+template<typename Object>
+class StructHandle final : public DefinitionHandle {
+public:
+	StructHandle(DataTypeRegister* type_register, StructDefinition* struct_definition) : DefinitionHandle(type_register), struct_definition(struct_definition) {}
+
+	// Register scalar member type 
+	template <typename MemberType>
+	StructHandle<Object>& RegisterMember(const String& name, MemberType Object::* member_ptr) {
+		static_assert(is_valid_scalar<MemberType>::value, "Not a valid scalar member type. Did you mean to add a struct or array member? If so, provide its handle.");
+		return RegisterMember(name, member_ptr, ScalarHandle<MemberType>(type_register));
+	}
+
+	// Register struct or array member type
+	template <typename MemberType>
+	StructHandle<Object>& RegisterMember(const String& name, MemberType Object::* member_ptr, const DefinitionHandle& member_handle);
+
+	VariableDefinition* GetDefinition() const override {
+		return struct_definition;
+	}
+
+private:
+	StructDefinition* struct_definition;
+};
+
+template<typename Container>
+class ArrayHandle final : public DefinitionHandle {
+public:
+	ArrayHandle(DataTypeRegister* type_register, ArrayDefinition<Container>* array_definition) : DefinitionHandle(type_register), array_definition(array_definition) {}
+
+	VariableDefinition* GetDefinition() const override {
+		return array_definition;
+	}
+
+private:
+	ArrayDefinition<Container>* array_definition;
+};
+
+
+
+class DataTypeRegister : NonCopyMoveable {
+public:
+	template<typename T>
+	StructHandle<T> RegisterStruct()
+	{
+		static_assert(std::is_class<T>::value, "Type must be a struct or class type.");
+		FamilyId id = Family<T>::Id();
+
+		auto struct_variable = std::make_unique<StructDefinition>();
+		StructDefinition* struct_variable_raw = struct_variable.get();
+
+		bool inserted = type_register.emplace(id, std::move(struct_variable)).second;
+		if (!inserted)
+		{
+			RMLUI_ERRORMSG("Type already declared");
+			return StructHandle<T>(nullptr, nullptr);
+		}
+		
+		return StructHandle<T>(this, struct_variable_raw);
+	}
+
+	// Register array of scalars
+	template<typename Container>
+	ArrayHandle<Container> RegisterArray()
+	{
+		using value_type = typename Container::value_type;
+		static_assert(is_valid_scalar<value_type>::value, "Underlying value type of array is not a valid scalar type. Provide the type handle if adding an array of structs or arrays.");
+		VariableDefinition* value_variable = GetOrAddScalar<value_type>();
+		return RegisterArray<Container>(value_variable);
+	}
+
+	// Register array of structs or arrays
+	template<typename Container>
+	ArrayHandle<Container> RegisterArray(const DefinitionHandle& type_handle)
+	{
+		using value_type = typename Container::value_type;
+		VariableDefinition* value_variable = Get<value_type>();
+		bool correct_handle = (type_handle.GetDefinition() == value_variable);
+
+		RMLUI_ASSERTMSG(value_variable, "Underlying value type of array has not been registered.");
+		RMLUI_ASSERTMSG(correct_handle, "Improper type handle provided.");
+		if (!value_variable || !correct_handle)
+			return ArrayHandle<Container>(nullptr, nullptr);
+
+		return RegisterArray<Container>(value_variable);
+	}
+
+	template<typename T>
+	VariableDefinition* GetOrAddScalar()
+	{
+		FamilyId id = Family<T>::Id();
+
+		auto result = type_register.emplace(id, nullptr);
+		auto& it = result.first;
+		bool inserted = result.second;
+
+		if (inserted)
+		{
+			it->second = std::make_unique<ScalarDefinition<T>>();
+		}
+
+		return it->second.get();
+	}
+
+	template<typename T>
+	VariableDefinition* Get() const
+	{
+		FamilyId id = Family<T>::Id();
+		auto it = type_register.find(id);
+		if (it == type_register.end())
+			return nullptr;
+
+		return it->second.get();
+	}
+
+private:
+	template<typename Container>
+	ArrayHandle<Container> RegisterArray(VariableDefinition* value_variable)
+	{
+		FamilyId container_id = Family<Container>::Id();
+
+		auto array_variable = std::make_unique<ArrayDefinition<Container>>(value_variable);
+		ArrayDefinition<Container>* array_variable_raw = array_variable.get();
+
+		bool inserted = type_register.emplace(container_id, std::move(array_variable)).second;
+		if (!inserted)
+		{
+			RMLUI_ERRORMSG("Array type already declared.");
+			return ArrayHandle<Container>(nullptr, nullptr);
+		}
+
+		return ArrayHandle<Container>(this, array_variable_raw);
+	}
+
+	UnorderedMap<FamilyId, UniquePtr<VariableDefinition>> type_register;
+};
+
+
+
+
+template<typename T>
+inline VariableDefinition* ScalarHandle<T>::GetDefinition() const {
+	return type_register->GetOrAddScalar<T>();
+}
+
+template<typename Object>
+template<typename MemberType>
+inline StructHandle<Object>& StructHandle<Object>::RegisterMember(const String& name, MemberType Object::* member_ptr, const DefinitionHandle& member_handle) {
+	RMLUI_ASSERTMSG(member_handle.GetDefinition() == type_register->Get<MemberType>(), "Mismatch between member type and provided type handle.");
+	struct_definition->AddMember(name, std::make_unique<StructMemberDefault<Object, MemberType>>(member_handle.GetDefinition(), member_ptr));
+	return *this;
+}
+
+}
+}
+
+#endif

+ 32 - 47
Include/RmlUi/Core/DataView.h

@@ -33,6 +33,8 @@
 #include "Types.h"
 #include "Variant.h"
 #include "StringUtilities.h"
+#include "Traits.h"
+#include "DataVariable.h"
 
 namespace Rml {
 namespace Core {
@@ -41,8 +43,13 @@ class Element;
 class ElementText;
 class DataModel;
 
-class RMLUICORE_API DataView {
+class RMLUICORE_API DataView : NonCopyMoveable {
+public:
+	virtual ~DataView();
+	virtual bool Update(const DataModel& model) = 0;
 
+protected:
+	DataView() {}
 };
 
 class DataViewText : public DataView {
@@ -53,14 +60,14 @@ public:
 		return !data_entries.empty() && element;
 	}
 
-	bool Update(const DataModel& model);
+	bool Update(const DataModel& model) override;
 
 private:
 	String BuildText() const;
 
 	struct DataEntry {
 		size_t index = 0; // Index into 'text'
-		String binding_name;
+		Address variable_address;
 		String value;
 	};
 
@@ -73,48 +80,48 @@ private:
 
 class DataViewAttribute : public DataView {
 public:
-	DataViewAttribute(const DataModel& model, Element* element, const String& binding_name, const String& attribute_name);
+	DataViewAttribute(const DataModel& model, Element* element, Element* parent, const String& binding_name, const String& attribute_name);
 
 	inline operator bool() const {
 		return !attribute_name.empty() && element;
 	}
-	bool Update(const DataModel& model);
+	bool Update(const DataModel& model) override;
 
 private:
 	ObserverPtr<Element> element;
-	String binding_name;
+	Address variable_address;
 	String attribute_name;
 };
 
 
 class DataViewStyle : public DataView {
 public:
-	DataViewStyle(const DataModel& model, Element* element, const String& binding_name, const String& property_name);
+	DataViewStyle(const DataModel& model, Element* element, Element* parent, const String& binding_name, const String& property_name);
 
 	inline operator bool() const {
-		return !binding_name.empty() && !property_name.empty() && element;
+		return !variable_address.empty() && !property_name.empty() && element;
 	}
-	bool Update(const DataModel& model);
+	bool Update(const DataModel& model) override;
 
 private:
 	ObserverPtr<Element> element;
-	String binding_name;
+	Address variable_address;
 	String property_name;
 };
 
 
 class DataViewIf : public DataView {
 public:
-	DataViewIf(const DataModel& model, Element* element, const String& binding_name);
+	DataViewIf(const DataModel& model, Element* element, Element* parent, const String& binding_name);
 
 	inline operator bool() const {
-		return !binding_name.empty() && element;
+		return !variable_address.empty() && element;
 	}
-	bool Update(const DataModel& model);
+	bool Update(const DataModel& model) override;
 
 private:
 	ObserverPtr<Element> element;
-	String binding_name;
+	Address variable_address;
 };
 
 
@@ -123,13 +130,13 @@ public:
 	DataViewFor(const DataModel& model, Element* element, const String& binding_name, const String& rml_contents);
 
 	inline operator bool() const {
-		return !binding_name.empty() && element;
+		return !variable_address.empty() && element;
 	}
-	bool Update(const DataModel& model);
+	bool Update(const DataModel& model) override;
 
 private:
 	ObserverPtr<Element> element;
-	String binding_name;
+	Address variable_address;
 	String rml_contents;
 	ElementAttributes attributes;
 
@@ -137,47 +144,25 @@ private:
 };
 
 
-class RMLUICORE_API DataViews {
+class RMLUICORE_API DataViews : NonCopyMoveable {
 public:
+	DataViews();
+	~DataViews();
 
-	void AddView(DataViewText&& view) {
-		text_views.push_back(std::move(view));
-	}
-	void AddView(DataViewAttribute&& view) {
-		attribute_views.push_back(std::move(view));
-	}
-	void AddView(DataViewStyle&& view) {
-		style_views.push_back(std::move(view));
-	}
-	void AddView(DataViewIf&& view) {
-		if_views.push_back(std::move(view));
-	}
-	void AddView(DataViewFor&& view) {
-		for_views.push_back(std::move(view));
+	void Add(UniquePtr<DataView> view) {
+		views.push_back(std::move(view));
 	}
 
 	bool Update(const DataModel& model)
 	{
 		bool result = false;
-		for (auto& view : text_views)
-			result |= view.Update(model);
-		for (auto& view : attribute_views)
-			result |= view.Update(model);
-		for (auto& view : style_views)
-			result |= view.Update(model);
-		for (auto& view : if_views)
-			result |= view.Update(model);
-		for (auto& view : for_views)
-			result |= view.Update(model);
+		for (auto& view : views)
+			result |= view->Update(model);
 		return result;
 	}
 
 private:
-	std::vector<DataViewText> text_views;
-	std::vector<DataViewAttribute> attribute_views;
-	std::vector<DataViewStyle> style_views;
-	std::vector<DataViewIf> if_views;
-	std::vector<DataViewFor> for_views;
+	std::vector<UniquePtr<DataView>> views;
 };
 
 }

+ 6 - 3
Include/RmlUi/Core/Traits.h

@@ -71,6 +71,9 @@ public:
 	}
 };
 
+
+enum class FamilyId : int {};
+
 class RMLUICORE_API FamilyBase {
 protected:
 	static int GetNewId() {
@@ -78,9 +81,9 @@ protected:
 		return id++;
 	}
 	template<typename T>
-	static int GetId() {
+	static FamilyId GetId() {
 		static int id = GetNewId();
-		return id;
+		return static_cast<FamilyId>(id);
 	}
 };
 
@@ -89,7 +92,7 @@ class Family : FamilyBase {
 public:
 	// Get a unique ID for a given type.
 	// Note: IDs will be unique across DLL-boundaries even for the same type.
-	static int Id() {
+	static FamilyId Id() {
 		return GetId< typename std::remove_cv< typename std::remove_reference< T >::type >::type >();
 	}
 };

+ 4 - 4
Samples/basic/databinding/data/databinding.rml

@@ -169,13 +169,13 @@ form h2
 	<div data-if="good_rating">Thanks for the awesome rating!</div>
 	<h1>{{invader.name}}</h1>
 	<img data-attr-sprite="invader.sprite" data-style-image-color="invader.color"/>
+	<div data-for="invaders">
+		<h1>{{it.name}}</h1>
+		<img data-attr-sprite="it.sprite" data-style-image-color="it.color"/>
+	</div>
 	<div data-for="indices">
 		<h1>{{it}}</h1>
 	</div>
-	<!--<div data-for="invader : invaders">
-		<h1>{{it.name}}</h1>
-		<img data-attr-sprite="it.sprite" data-style-image-color="it.color"/>
-	</div>-->
 </panel>
 <tab>Decorators</tab>
 <panel id="decorators">

+ 27 - 729
Samples/basic/databinding/src/main.cpp

@@ -115,726 +115,18 @@ struct MyData {
 	int rating = 99;
 	bool good_rating = true;
 
-	Invader invader{ "Delightful invader", "icon-invader", "red" };
+	Invader invader{ "Delightful invader", "icon-invader", "white" };
 
-	std::vector<Invader> invaders;
+	std::vector<Invader> invaders = {
+		Invader{"Angry invader", "icon-invader", "red"},
+		Invader{"Harmless invader", "icon-flag", "blue"},
+		Invader{"Hero", "icon-game", "yellow"},
+	};
 
 	std::vector<int> indices = { 1, 2, 3, 4, 5 };
 } my_data;
 
-Rml::Core::DataModelHandle my_model;
-
-
-
-
-
-
-
-
-
-
-
-
-namespace Data {
-	using namespace Rml::Core;
-
-
-	template<typename T>
-	struct is_valid_scalar {
-		static constexpr bool value = std::is_convertible<T, byte>::value
-			|| std::is_convertible<T, char>::value
-			|| std::is_convertible<T, float>::value
-			|| std::is_convertible<T, int>::value
-			|| std::is_convertible<T, String>::value
-			|| std::is_convertible<T, Vector2f>::value
-			|| std::is_convertible<T, Vector3f>::value
-			|| std::is_convertible<T, Vector4f>::value
-			|| std::is_convertible<T, Colourb>::value
-			|| std::is_convertible<T, char*>::value
-			|| std::is_convertible<T, void*>::value;
-	};
-
-
-	enum class VariableType { Scalar, Array, Struct };
-
-	class Variable {
-	public:
-		virtual ~Variable() = default;
-		VariableType Type() const { return type; }
-
-	protected:
-		Variable(VariableType type) : type(type) {}
-
-	private:
-		VariableType type;
-	};
-
-
-	class VariableInstancer {
-	public:
-		virtual ~VariableInstancer() = default;
-		virtual UniquePtr<Variable> Instance(void* ptr) = 0;
-	};
-
-
-
-	class Scalar : public Variable {
-	public:
-		virtual bool Get(Variant& variant) = 0;
-		virtual bool Set(const Variant& variant) = 0;
-
-	protected:
-		Scalar() : Variable(VariableType::Scalar) {}
-	};
-
-	template<typename T>
-	class ScalarDefault final : public Scalar {
-	public:
-		ScalarDefault(T* ptr) : ptr(ptr) { RMLUI_ASSERT(ptr); }
-		bool Get(Variant& variant) override {
-			variant = *ptr;
-			return true;
-		}
-		bool Set(const Variant& variant) override {
-			return variant.GetInto<T>(*ptr);
-		}
-	private:
-		T* ptr;
-	};
-
-	class Array : public Variable {
-	public:
-		Variable* operator[](const int index)
-		{
-			void* ptr = GetAddress(index);
-			if (!ptr)
-			{
-				if (index >= 0)
-					items.clear();
-				return nullptr;
-			}
-
-			if (index >= (int)items.size())
-				items.resize(Size());
-
-			PtrVariable& item = items[index];
-			if (item.ptr != ptr)
-			{
-				item.ptr = ptr;
-				item.variable = item_instancer->Instance(ptr);
-			}
-
-			return item.variable.get();
-		}
-
-		virtual int Size() const = 0;
-
-	protected:
-		Array(VariableInstancer* item_instancer) : Variable(VariableType::Array), item_instancer(item_instancer) {}
-		
-		virtual void* GetAddress(int index) const = 0;
-
-	private:
-		struct PtrVariable{ void* ptr = nullptr; UniquePtr<Variable> variable; };
-		VariableInstancer* item_instancer;
-		std::vector<PtrVariable> items;
-	};
-
-	template<typename Container>
-	class ArrayDefault final : public Array {
-	public:
-		ArrayDefault(Container* ptr, VariableInstancer* item_instancer) : Array(item_instancer), ptr(ptr) {  }
-
-		int Size() const override {
-			return (int)ptr->size();
-		}
-
-	protected:
-		void* GetAddress(int index) const override
-		{
-			if (index < 0 || index >= (int)ptr->size())
-			{
-				Log::Message(Log::LT_WARNING, "Data array index out of bounds.");
-				return nullptr;
-			}
-			return &((*ptr)[index]);
-		}
-
-	private:
-		Container* ptr;
-	};
-
-
-
-	class Struct final : public Variable {
-	public:
-		Struct(SmallUnorderedMap<String, UniquePtr<Variable>>&& members) : Variable(VariableType::Struct), members(std::move(members))
-		{}
-
-		Variable* operator[](const String& name) const {
-			auto it = members.find(name);
-			if (it == members.end())
-				return nullptr;
-			return it->second.get();
-		}
-
-	private:
-		SmallUnorderedMap<String, UniquePtr<Variable>> members;
-	};
-
-
-
-	template<typename T>
-	class ScalarInstancer final : public VariableInstancer {
-	public:
-		UniquePtr<Variable> Instance(void* ptr) override {
-			return std::make_unique<ScalarDefault<T>>( static_cast<T*>(ptr) );
-		}
-	};
-
-
-	template<typename Container>
-	class ArrayInstancer final : public VariableInstancer {
-	public:
-		ArrayInstancer(VariableInstancer* item_instancer) : item_instancer(item_instancer) {}
-
-		UniquePtr<Variable> Instance(void* ptr) override {
-			return std::make_unique<ArrayDefault<Container>>(static_cast<Container*>(ptr), item_instancer);
-		}
-	private:
-		VariableInstancer* item_instancer;
-	};
-
-
-	template <typename Object>
-	class StructInstancer final : public VariableInstancer {
-	public:
-		StructInstancer() {}
-
-		template <typename MemberType>
-		void AddMember(const String& name, MemberType Object::* member_ptr, VariableInstancer* instancer) {
-			RMLUI_ASSERT(instancer && instancer != this);
-			members.emplace(name, std::make_unique< MemberInstancer<Object, MemberType> >(member_ptr, instancer));
-		}
-
-		UniquePtr<Variable> Instance(void* ptr_to_struct) override {
-			SmallUnorderedMap<String, UniquePtr<Variable>> variable_members;
-			variable_members.reserve(members.size());
-			for (auto& member : members)
-			{
-				auto variable = member.second->Instance(ptr_to_struct);
-				bool inserted = variable_members.emplace(member.first, std::move(variable)).second;
-				RMLUI_ASSERT(inserted);
-			}
-			return std::make_unique< Struct >(std::move(variable_members));
-		}
-	private:
-		class MemberInstancerBase {
-		public:
-			virtual ~MemberInstancerBase() = default;
-			virtual UniquePtr<Variable> Instance(void* base_ptr) = 0;
-		};
-
-		template <typename Object, typename MemberType>
-		class MemberInstancer final : public MemberInstancerBase {
-		public:
-			MemberInstancer(MemberType Object::* member_ptr, VariableInstancer* instancer) : member_ptr(member_ptr), instancer(instancer) {}
-
-			UniquePtr<Variable> Instance(void* base_ptr) override {
-				return instancer->Instance(&(static_cast<Object*>(base_ptr)->*member_ptr));
-			}
-
-		private:
-			MemberType Object::* member_ptr;
-			VariableInstancer* instancer;
-		};
-
-		SmallUnorderedMap<String, UniquePtr<MemberInstancerBase>> members;
-	};
-
-
-	class TypeRegister;
-
-	class TypeHandleBase {
-	public:
-		operator bool() const { return type_register && GetInstancer(); }
-
-		virtual VariableInstancer* GetInstancer() const = 0;
-
-	protected:
-		TypeHandleBase(TypeRegister* type_register) : type_register(type_register) {}
-		TypeRegister* type_register;
-	};
-
-	class StructTypeHandleBase : public TypeHandleBase {
-	protected:
-		StructTypeHandleBase(TypeRegister* type_register) : TypeHandleBase(type_register) {}
-	};
-
-	class ArrayTypeHandleBase : public TypeHandleBase {
-	protected:
-		ArrayTypeHandleBase(TypeRegister* type_register) : TypeHandleBase(type_register) {}
-	};
-
-	template<typename Object>
-	class StructTypeHandle final : public StructTypeHandleBase {
-	public:
-		StructTypeHandle(TypeRegister* type_register, StructInstancer<Object>* instancer) : StructTypeHandleBase(type_register), instancer(instancer) {}
-
-		template <typename MemberType>
-		StructTypeHandle<Object>& AddScalar(const String& name, MemberType Object::* member_ptr) {
-			static_assert(is_valid_scalar<MemberType>::value, "Not a valid scalar member type. Did you mean to add a struct member?");
-			VariableInstancer* member_instancer = type_register->GetOrAddScalar<MemberType>();
-			instancer->AddMember(name, member_ptr, member_instancer);
-			return *this;
-		}
-
-		template <typename MemberType>
-		StructTypeHandle<Object>& AddStruct(const String& name, MemberType Object::* member_ptr, const StructTypeHandleBase& struct_handle) {
-			RMLUI_ASSERTMSG(type_register->Get<MemberType>() == struct_handle.GetInstancer(), "Mismatch between member type and provided struct instancer.");
-			instancer->AddMember(name, member_ptr, struct_handle.GetInstancer());
-			return *this;
-		}
-
-		template <typename MemberType>
-		StructTypeHandle<Object>& AddArray(const String& name, MemberType Object::* member_ptr, const ArrayTypeHandleBase& array_handle) {
-			RMLUI_ASSERTMSG(type_register->Get<MemberType>() == array_handle.GetInstancer(), "Mismatch between member type and provided struct instancer.");
-			instancer->AddMember(name, member_ptr, array_handle.GetInstancer());
-			return *this;
-		}
-
-		VariableInstancer* GetInstancer() const override {
-			return instancer;
-		}
-	private:
-		StructInstancer<Object>* instancer;
-	};
-
-	template<typename Container>
-	class ArrayTypeHandle final : public ArrayTypeHandleBase {
-	public:
-		ArrayTypeHandle(TypeRegister* type_register, ArrayInstancer<Container>* instancer) : ArrayTypeHandleBase(type_register), instancer(instancer) {}
-
-		VariableInstancer* GetInstancer() const override {
-			return instancer;
-		}
-	private:
-		ArrayInstancer<Container>* instancer;
-	};
-
-
-
-
-
-
-	class TypeRegister {
-	public:
-		template<typename T>
-		StructTypeHandle<T> RegisterStruct()
-		{
-			static_assert(std::is_class<T>::value, "Type must be a struct or class type.");
-			int family_id = Family<T>::Id();
-
-			auto struct_instancer = std::make_unique<StructInstancer<T>>();
-			StructInstancer<T>* struct_instancer_raw = struct_instancer.get();
-
-			auto result = type_register.emplace(family_id, std::move(struct_instancer));
-			auto& it = result.first;
-			bool inserted = result.second;
-			if (!inserted)
-			{
-				RMLUI_ERRORMSG("Type already declared");
-				return StructTypeHandle<T>(nullptr, nullptr);
-			}
-			
-			return StructTypeHandle<T>(this, struct_instancer_raw);
-		}
-
-		template<typename Container>
-		ArrayTypeHandle<Container> RegisterArrayOfScalar()
-		{
-			using value_type = typename Container::value_type;
-			static_assert(is_valid_scalar<value_type>::value, "Underlying value type of array is not a valid scalar type. Did you mean to add a struct or array?");
-
-			VariableInstancer* value_instancer = GetOrAddScalar<value_type>();
-
-			return RegisterArray<Container>(value_instancer);
-		}
-
-		template<typename Container>
-		ArrayTypeHandle<Container> RegisterArrayOfStruct(const StructTypeHandleBase& struct_handle)
-		{
-			using value_type = typename Container::value_type;
-			VariableInstancer* value_instancer = Get<value_type>();
-			bool correct_handle = (struct_handle.GetInstancer() == value_instancer);
-			
-			RMLUI_ASSERTMSG(value_instancer, "Underlying value type of array has not been registered.");
-			RMLUI_ASSERTMSG(correct_handle, "Improper struct handle provided.");
-			if (!value_instancer || !correct_handle)
-				return ArrayTypeHandle<Container>(nullptr, nullptr);
-
-			return RegisterArray<Container>(value_instancer);
-		}
-
-		template<typename Container>
-		ArrayTypeHandle<Container> RegisterArrayOfArray(const ArrayTypeHandleBase& array_handle)
-		{
-			using value_type = typename Container::value_type;
-			VariableInstancer* value_instancer = Get<value_type>();
-			bool correct_handle = (array_handle.GetInstancer() == value_instancer);
-
-			RMLUI_ASSERTMSG(value_instancer, "Underlying value type of array has not been registered.");
-			RMLUI_ASSERTMSG(correct_handle, "Improper struct handle provided.");
-			if (!value_instancer || !correct_handle)
-				return ArrayTypeHandle<Container>(nullptr, nullptr);
-
-			return RegisterArray<Container>(value_instancer);
-			using value_type = typename Container::value_type;
-			VariableInstancer* value_instancer = Get<value_type>();
-			if (!value_instancer)
-			{
-				RMLUI_ERRORMSG("Underlying value type of array has not been registered.");
-				return ArrayTypeHandle<Container>(nullptr, nullptr);
-			}
-			return RegisterArray<Container>(value_instancer);
-		}
-
-		template<typename T>
-		VariableInstancer* GetOrAddScalar()
-		{
-			int id = Family<T>::Id();
-
-			auto result = type_register.emplace(id, nullptr);
-			auto& it = result.first;
-			bool inserted = result.second;
-
-			if (inserted)
-			{
-				it->second = std::make_unique<ScalarInstancer<T>>();
-			}
-
-			return it->second.get();
-		}
-
-		template<typename T>
-		VariableInstancer* Get() const
-		{
-			int id = Family<T>::Id();
-			auto it = type_register.find(id);
-			if (it == type_register.end())
-				return nullptr;
-
-			return it->second.get();
-		}
-
-		template<typename T>
-		UniquePtr<Variable> Instance(T* ptr) const
-		{
-			if (auto instancer = Get<T>())
-				return instancer->Instance(ptr);
-			return nullptr;
-		}
-
-	private:
-		template<typename Container>
-		ArrayTypeHandle<Container> RegisterArray(VariableInstancer* value_instancer)
-		{
-			int container_id = Family<Container>::Id();
-
-			auto result = type_register.emplace(container_id, std::make_unique<ArrayInstancer<Container>>(value_instancer));
-			auto& it = result.first;
-			bool inserted = result.second;
-			if (!inserted)
-			{
-				RMLUI_ERRORMSG("Array type already declared.");
-				return ArrayTypeHandle<Container>(nullptr, nullptr);
-			}
-
-			return ArrayTypeHandle<Container>(this, static_cast<ArrayInstancer<Container>*>(it->second.get()));
-		}
-
-		UnorderedMap<int, UniquePtr<VariableInstancer>> type_register;
-	};
-
-
-
-	class Model {
-	public:
-		Model(TypeRegister* type_register) : type_register(type_register) {}
-
-		template<typename T> bool BindScalar(String name, T* ptr) {
-			return Bind(name, ptr, type_register->GetOrAddScalar<T>());
-		}
-		template<typename T> bool BindStruct(String name, T* ptr) {
-			return Bind(name, ptr, type_register->Get<T>());
-		}
-		template<typename T> bool BindArray(String name, T* ptr) {
-			return Bind(name, ptr, type_register->Get<T>());
-		}
-
-		Variant GetValue(const String& address_str) const;
-		bool SetValue(const String& address_str, const Variant& variant) const;
-
-	private:
-		bool Bind(String name, void* ptr, VariableInstancer* instancer);
-
-		Variable* GetVariable(const String& address_str) const;
-
-		TypeRegister* type_register;
-		UnorderedMap<String, UniquePtr<Variable>> variables;
-	};
-
-
-	struct AddressEntry {
-		AddressEntry(String name) : name(name), index(-1) { }
-		AddressEntry(int index) : index(index) { }
-		String name;
-		int index;
-	};
-	using Address = std::vector<AddressEntry>;
-
-
-
-	Address ParseAddress(const String& address_str)
-	{
-		StringList list;
-		StringUtilities::ExpandString(list, address_str, '.');
-
-		Address address;
-		address.reserve(list.size() * 2);
-
-		for (const auto& item : list)
-		{
-			if (item.empty())
-				return Address();
-
-			size_t i_open = item.find('[', 0);
-			if (i_open == 0)
-				return Address();
-
-			address.emplace_back(item.substr(0, i_open));
-
-			while (i_open != String::npos)
-			{
-				size_t i_close = item.find(']', i_open + 1);
-				if (i_close == String::npos)
-					return Address();
-
-				int index = FromString<int>(item.substr(i_open + 1, i_close - i_open), -1);
-				if (index < 0)
-					return Address();
-
-				address.emplace_back(index);
-
-				i_open = item.find('[', i_close + 1);
-			}
-			// TODO: Abort on invalid characters among [ ] and after the last found bracket?
-		}
-
-		return address;
-	};
-
-	Variant Model::GetValue(const Rml::Core::String& address_str) const
-	{
-		Variable* var = GetVariable(address_str);
-		Variant result;
-
-		if (var && var->Type() == VariableType::Scalar)
-		{
-			if (!static_cast<Scalar*>(var)->Get(result))
-				Log::Message(Log::LT_WARNING, "Could not parse data value '%s'", address_str.c_str());
-		}
-
-		return result;
-	}
-
-
-	bool Model::SetValue(const String& address_str, const Variant& variant) const
-	{
-		Variable* var = GetVariable(address_str);
-		bool result;
-
-		if (var && var->Type() == VariableType::Scalar)
-		{
-			if (static_cast<Scalar*>(var)->Set(variant))
-				result = true;
-			else
-				Log::Message(Log::LT_WARNING, "Could not assign data value '%s'", address_str.c_str());
-		}
-
-		return result;
-	}
-
-	bool Model::Bind(String name, void* ptr, VariableInstancer* instancer)
-	{
-		RMLUI_ASSERT(ptr);
-		if (!instancer)
-		{
-			Log::Message(Log::LT_WARNING, "No instancer could be found for the data variable '%s'.", name.c_str());
-			return false;
-		}
-
-		UniquePtr<Variable> variable = instancer->Instance(ptr);
-		if (!variable)
-		{
-			Log::Message(Log::LT_WARNING, "Could not instance data variable '%s'.", name.c_str());
-			return false;
-		}
-
-		bool inserted = variables.emplace(name, std::move(variable)).second;
-		if (!inserted)
-		{
-			Log::Message(Log::LT_WARNING, "Data model variable with name '%s' already exists.", name.c_str());
-			return false;
-		}
-
-		return true;
-	}
-
-	Variable* Model::GetVariable(const String& address_str) const
-	{
-		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 nullptr;
-		}
-
-		auto it = variables.find(address.front().name);
-		if (it == variables.end())
-		{
-			Log::Message(Log::LT_WARNING, "Could not find the data variable '%s'.", address_str.c_str());
-			return nullptr;
-		}
-
-		Variable* var = it->second.get();
-		for (int i = 1; i < (int)address.size(); i++)
-		{
-			if (!var)
-				break;
-
-			const AddressEntry& entry = address[i];
-
-			switch (var->Type()) {
-			case VariableType::Struct:
-			{
-				Struct& the_struct = static_cast<Struct&>(*var);
-				var = the_struct[entry.name];
-				break;
-			}
-			case VariableType::Array:
-			{
-				Array& the_array = static_cast<Array&>(*var);
-				var = the_array[entry.index];
-				break;
-			}
-			case VariableType::Scalar:
-			{
-				Log::Message(Log::LT_WARNING, "Invalid data variable address '%s'. The scalar variable '%s' was encountered before the end of the address.", address_str.c_str(), (i > 0 ? address[i - 1].name.c_str() : ""));
-				var = nullptr;
-				return nullptr;
-				break;
-			}
-			default:
-				RMLUI_ERROR;
-				var = nullptr;
-				return nullptr;
-			}
-		}
-
-		if (!var)
-		{
-			Log::Message(Log::LT_WARNING, "Could not find the data variable '%s'.", address_str.c_str());
-			return nullptr;
-		}
-
-		return var;
-	}
-
-
-}
-
-
-
-void TestDataVariable()
-{
-	using namespace Rml::Core;
-	using namespace Data;
-
-	using IntVector = std::vector<int>;
-
-	struct FunData {
-		int i = 99;
-		String x = "hello";
-		IntVector magic = { 3, 5, 7, 11, 13 };
-	};
-
-	using FunArray = std::array<FunData, 3>;
-
-	struct SmartData {
-		bool valid = true;
-		FunData fun;
-		FunArray more_fun;
-	};
-
-	TypeRegister types;
-
-	{
-		auto int_vector_handle = types.RegisterArrayOfScalar<IntVector>();
-
-		auto fun_handle = types.RegisterStruct<FunData>();
-		if (fun_handle)
-		{
-			fun_handle.AddScalar("i", &FunData::i);
-			fun_handle.AddScalar("x", &FunData::x);
-			fun_handle.AddArray("magic", &FunData::magic, int_vector_handle);
-		}
-
-		auto fun_array_handle = types.RegisterArrayOfStruct<FunArray>(fun_handle);
-
-		auto smart_handle = types.RegisterStruct<SmartData>();
-		if (smart_handle)
-		{
-			smart_handle.AddScalar("valid", &SmartData::valid);
-			smart_handle.AddStruct("fun", &SmartData::fun, fun_handle);
-			smart_handle.AddArray("more_fun", &SmartData::more_fun, fun_array_handle);
-		}
-	}
-
-	Model model(&types);
-
-	SmartData data;
-	data.fun.x = "Hello, we're in SmartData!";
-
-	model.BindStruct("data", &data);
-
-	{
-		std::vector<String> test_addresses = { "data.more_fun[1].magic[3]", "data.fun.x", "data.valid" };
-		std::vector<String> expected_results = { ToString(data.more_fun[1].magic[3]), ToString(data.fun.x), ToString(data.valid) };
-
-		std::vector<String> results;
-
-		for(auto& address : test_addresses)
-		{
-			auto the_address = ParseAddress(address);
-
-			Variant variant = model.GetValue(address);
-			results.push_back(variant.Get<String>());
-		}
-
-		RMLUI_ASSERT(results == expected_results);
-
-		bool success = model.SetValue("data.more_fun[1].magic[1]", 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 };
-
-		String result = model.GetValue("data.fun.magic[8]").Get<String>();
-		RMLUI_ASSERT(result == "90");
-	}
-
-}
+Rml::Core::DataModelHandle my_model_handle(nullptr);
 
 
 
@@ -842,24 +134,30 @@ void TestDataVariable()
 
 bool SetupDataBinding(Rml::Core::Context* context)
 {
-	my_model = context->CreateDataModel("my_model");
-	if (!my_model)
+	my_model_handle = context->CreateDataModel("my_model");
+	if (!my_model_handle)
 		return false;
 
-	my_model.BindValue("hello_world", &my_data.hello_world);
-	my_model.BindValue("rating", &my_data.rating);
-	my_model.BindValue("good_rating", &my_data.good_rating);
+	Rml::Core::DataModel& my_model = *my_model_handle.GetModel();
+
+	my_model.BindScalar("hello_world", &my_data.hello_world);
+	my_model.BindScalar("rating", &my_data.rating);
+	my_model.BindScalar("good_rating", &my_data.good_rating);
+
+	Rml::Core::DataTypeRegister& types = Rml::Core::GetDataTypeRegister();
 
-	auto invader_type = my_model.RegisterType<Invader>();
-	invader_type.RegisterMember("name", &Invader::name);
-	invader_type.RegisterMember("sprite", &Invader::sprite);
-	invader_type.RegisterMember("color", &Invader::color);
+	auto invader_handle = types.RegisterStruct<Invader>();
+	invader_handle.RegisterMember("name", &Invader::name);
+	invader_handle.RegisterMember("sprite", &Invader::sprite);
+	invader_handle.RegisterMember("color", &Invader::color);
 
-	my_model.BindTypeValue("invader", &my_data.invader);
+	my_model.BindStruct("invader", &my_data.invader);
 
-	my_model.BindContainer("indices", &my_data.indices);
+	types.RegisterArray<std::vector<int>>();
+	types.RegisterArray<std::vector<Invader>>(invader_handle);
 
-	TestDataVariable();
+	my_model.BindArray("indices", &my_data.indices);
+	my_model.BindArray("invaders", &my_data.invaders);
 
 	return true;
 }
@@ -872,9 +170,9 @@ std::unique_ptr<DemoWindow> demo_window;
 
 void GameLoop()
 {
-	my_model.UpdateControllers();
+	my_model_handle.UpdateControllers();
 	my_data.good_rating = (my_data.rating > 50);
-	my_model.UpdateViews();
+	my_model_handle.UpdateViews();
 
 	demo_window->Update();
 	context->Update();

+ 10 - 0
Source/Core/Context.cpp

@@ -31,6 +31,7 @@
 #include "EventIterators.h"
 #include "PluginRegistry.h"
 #include "StreamFile.h"
+#include "../../Include/RmlUi/Core/DataModel.h"
 #include "../../Include/RmlUi/Core/StreamMemory.h"
 #include <algorithm>
 #include <iterator>
@@ -787,6 +788,15 @@ void Context::SetInstancer(ContextInstancer* _instancer)
 	instancer = _instancer;
 }
 
+DataModelHandle Context::CreateDataModel(String name)
+{
+	auto result = data_models.emplace(name, std::make_unique<DataModel>( &GetDataTypeRegister() ));
+	if (result.second)
+		return DataModelHandle(result.first->second.get());
+
+	return DataModelHandle(nullptr);
+}
+
 // Internal callback for when an element is removed from the hierarchy.
 void Context::OnElementDetach(Element* element)
 {

+ 6 - 0
Source/Core/Core.cpp

@@ -309,6 +309,12 @@ 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)
 {

+ 8 - 4
Source/Core/DataController.cpp

@@ -34,12 +34,13 @@ namespace Rml {
 namespace Core {
 
 
-DataControllerAttribute::DataControllerAttribute(const DataModel& model, const String& in_attribute_name, const String& in_value_name) : attribute_name(in_attribute_name), value_name(in_value_name)
+DataControllerAttribute::DataControllerAttribute(const DataModel& model, Element* parent, const String& in_attribute_name, const String& in_value_name) : attribute_name(in_attribute_name)
 {
-	if (model.variables.count(value_name) == 0)
+    variable_address = model.ResolveAddress(in_value_name, parent);
+    if (!model.GetVariable(variable_address))
 	{
 		attribute_name.clear();
-		value_name.clear();
+        variable_address.clear();
 	}
 }
 
@@ -49,7 +50,10 @@ bool DataControllerAttribute::Update(Element* element, const DataModel& model)
 	if (dirty)
 	{
 		if(Variant* value = element->GetAttribute(attribute_name))
-			result = model.SetValue(value_name, *value);
+        {
+            if (Variable variable = model.GetVariable(variable_address))
+                result = variable.Set(*value);
+        }
 		dirty = false;
 	}
 	return result;

+ 255 - 48
Source/Core/DataModel.cpp

@@ -33,80 +33,169 @@ namespace Rml {
 namespace Core {
 
 
-bool DataModel::GetValue(const String& name, Variant& out_value) const
+static Address ParseAddress(const String& address_str)
 {
-	bool success = true;
+	StringList list;
+	StringUtilities::ExpandString(list, address_str, '.');
 
-	auto pos = name.find('[');
-	if (pos != String::npos)
+	Address address;
+	address.reserve(list.size() * 2);
+
+	for (const auto& item : list)
 	{
-		auto pos_end = name.find(']', pos + 2);
-		if(pos_end != String::npos)
+		if (item.empty())
+			return Address();
+
+		size_t i_open = item.find('[', 0);
+		if (i_open == 0)
+			return Address();
+
+		address.emplace_back(item.substr(0, i_open));
+
+		while (i_open != String::npos)
 		{
-			const String container_name = name.substr(0, pos);
-			const int index = FromString(name.substr(pos + 1, pos_end - pos + 1), -1);
-			if (index >= 0)
-			{
-				auto it_container = containers.find(container_name);
-				if (it_container != containers.end())
-				{
-					DataBindingContext context({ DataBindingContext::Item{ "", index} });
+			size_t i_close = item.find(']', i_open + 1);
+			if (i_close == String::npos)
+				return Address();
 
-					if (it_container->second->Get(out_value, context))
-						return true;
-				}
-			}
+			int index = FromString<int>(item.substr(i_open + 1, i_close - i_open), -1);
+			if (index < 0)
+				return Address();
+
+			address.emplace_back(index);
+
+			i_open = item.find('[', i_close + 1);
 		}
-		return false;
+		// TODO: Abort on invalid characters among [ ] and after the last found bracket?
 	}
 
-	auto it = variables.find(name);
-	if (it != variables.end())
+	return address;
+};
+
+
+Variant DataModel::GetValue(const Rml::Core::String& address_str) const
+{
+	Variable variable = GetVariable(address_str);
+
+	Variant result;
+	if (!variable)
+		return result;
+
+	if (variable.Type() != VariableType::Scalar)
 	{
-		DataVariable& binding = *it->second;
+		Log::Message(Log::LT_WARNING, "Error retrieving data variable '%s': Only the values of scalar variables can be parsed.", address_str.c_str());
+		return result;
+	}
+	if (!variable.Get(result))
+		Log::Message(Log::LT_WARNING, "Could not parse data value '%s'", address_str.c_str());
+
+	return result;
+}
+
 
-		success = binding.Get(out_value);
-		if (!success)
-			Log::Message(Log::LT_WARNING, "Could not get value from '%s' in data model.", name.c_str());
+bool DataModel::SetValue(const String& address_str, const Variant& variant) const
+{
+	Variable variable = GetVariable(address_str);
+
+	if (!variable)
+		return false;
+
+	if (variable.Type() != VariableType::Scalar)
+	{
+		Log::Message(Log::LT_WARNING, "Could not assign data value '%s', variable is not a scalar type.", address_str.c_str());
+		return false;
 	}
-	else
+
+	if (!variable.Set(variant))
 	{
-		Log::Message(Log::LT_WARNING, "Could not find value named '%s' in data model.", name.c_str());
-		success = false;
+		Log::Message(Log::LT_WARNING, "Could not assign data value '%s'", address_str.c_str());
+		return false;
 	}
 
-	return success;
+	return true;
 }
 
+bool DataModel::Bind(String name, void* ptr, VariableDefinition* variable, VariableType type)
+{
+	RMLUI_ASSERT(ptr);
+	if (!variable)
+	{
+		Log::Message(Log::LT_WARNING, "No registered type could be found for the data variable '%s'.", name.c_str());
+		return false;
+	}
+
+	if (variable->Type() != type)
+	{
+		Log::Message(Log::LT_WARNING, "The registered type does not match the given type for the data variable '%s'.", name.c_str());
+		return false;
+	}
+
+	bool inserted = variables.emplace(name, Variable(variable, ptr)).second;
+	if (!inserted)
+	{
+		Log::Message(Log::LT_WARNING, "Data model variable with name '%s' already exists.", name.c_str());
+		return false;
+	}
 
-bool DataModel::SetValue(const String& name, const Variant& value) const
+	return true;
+}
+
+Variable DataModel::GetVariable(const String& address_str) const
 {
-	bool success = true;
+	Address address = ParseAddress(address_str);
 
-	auto it = variables.find(name);
-	if (it != variables.end())
+	if (address.empty() || address.front().name.empty())
 	{
-		DataVariable& binding = *it->second;
+		Log::Message(Log::LT_WARNING, "Invalid data address '%s'.", address_str.c_str());
+		return Variable();
+	}
 
-		success = binding.Set(value);
-		if (!success)
-			Log::Message(Log::LT_WARNING, "Could not set value to '%s' in data model.", name.c_str());
+	Variable instance = GetVariable(address);
+	if (!instance)
+	{
+		Log::Message(Log::LT_WARNING, "Could not find the data variable '%s'.", address_str.c_str());
+		return Variable();
 	}
-	else
+
+	return instance;
+}
+
+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;
+
+	for (int i = 1; i < (int)address.size() && variable; i++)
 	{
-		Log::Message(Log::LT_WARNING, "Could not find value named '%s' in data model.", name.c_str());
-		success = false;
+		variable = variable.GetChild(address[i]);
+		if (!variable)
+			return Variable();
 	}
 
-	return success;
+	return variable;
 }
 
-String DataModel::ResolveVariableName(const String& raw_name, Element* parent) const
+Address DataModel::ResolveAddress(const String& address_str, Element* parent) const
 {
-	auto it = variables.find(raw_name);
+	Address address = ParseAddress(address_str);
+
+	if (address.empty() || address.front().name.empty())
+		return Address();
+
+	const String& first_name = address.front().name;
+
+	auto it = variables.find(first_name);
 	if (it != variables.end())
-		return raw_name;
+		return address;
 
+	// Look for a variable alias for the first name.
+	
 	Element* ancestor = parent;
 	while (ancestor && ancestor->GetDataModel() == this)
 	{
@@ -114,20 +203,138 @@ String DataModel::ResolveVariableName(const String& raw_name, Element* parent) c
 		if (it_element != aliases.end())
 		{
 			auto& alias_names = it_element->second;
-			auto it_alias_name = alias_names.find(raw_name);
+			auto it_alias_name = alias_names.find(first_name);
 			if (it_alias_name != alias_names.end())
 			{
-				return it_alias_name->second;
+				const Address& replace_address = it_alias_name->second;
+				if (replace_address.empty() || replace_address.front().name.empty())
+				{
+					// Variable alias is invalid
+					return Address();
+				}
+
+				// Insert the full alias address, replacing the first element.
+				address[0] = std::move(replace_address[0]);
+				address.insert(address.begin() + 1, replace_address.begin() + 1, replace_address.end());
+				return address;
 			}
 		}
 
 		ancestor = ancestor->GetParentNode();
 	}
 
-	Log::Message(Log::LT_WARNING, "Could not find variable name '%s' in data model.", raw_name.c_str());
+	Log::Message(Log::LT_WARNING, "Could not find variable name '%s' in data model.", address_str.c_str());
+
+	return Address();
+}
+
+bool DataModel::InsertAlias(Element* element, const String& alias_name, Address replace_with_address) const
+{
+	if (replace_with_address.empty() || replace_with_address.front().name.empty())
+	{
+		Log::Message(Log::LT_WARNING, "Could not add alias variable '%s' to data model, replacement address invalid.", alias_name.c_str());
+		return false;
+	}
+
+	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());
+
+	map[alias_name] = std::move(replace_with_address);
 
-	return String();
+	return true;
 }
 
+bool DataModel::EraseAliases(Element* element) const
+{
+	return aliases.erase(element) == 1;
+}
+
+
+
+#ifdef RMLUI_DEBUG
+
+static struct TestDataVariables {
+	TestDataVariables() 
+	{
+		using IntVector = std::vector<int>;
+
+		struct FunData {
+			int i = 99;
+			String x = "hello";
+			IntVector magic = { 3, 5, 7, 11, 13 };
+		};
+
+		using FunArray = std::array<FunData, 3>;
+
+		struct SmartData {
+			bool valid = true;
+			FunData fun;
+			FunArray more_fun;
+		};
+
+		DataTypeRegister types;
+
+		{
+			auto int_vector_handle = types.RegisterArray<IntVector>();
+
+			auto fun_handle = types.RegisterStruct<FunData>();
+			if (fun_handle)
+			{
+				fun_handle.RegisterMember("i", &FunData::i);
+				fun_handle.RegisterMember("x", &FunData::x);
+				fun_handle.RegisterMember("magic", &FunData::magic, int_vector_handle);
+			}
+
+			auto fun_array_handle = types.RegisterArray<FunArray>(fun_handle);
+
+			auto smart_handle = types.RegisterStruct<SmartData>();
+			if (smart_handle)
+			{
+				smart_handle.RegisterMember("valid", &SmartData::valid);
+				smart_handle.RegisterMember("fun", &SmartData::fun, fun_handle);
+				smart_handle.RegisterMember("more_fun", &SmartData::more_fun, fun_array_handle);
+			}
+		}
+
+		DataModel model(&types);
+
+		SmartData data;
+		data.fun.x = "Hello, we're in SmartData!";
+
+		model.BindStruct("data", &data);
+
+		{
+			std::vector<String> test_addresses = { "data.more_fun[1].magic[3]", "data.fun.x", "data.valid" };
+			std::vector<String> expected_results = { ToString(data.more_fun[1].magic[3]), ToString(data.fun.x), ToString(data.valid) };
+
+			std::vector<String> results;
+
+			for (auto& address : test_addresses)
+			{
+				auto the_address = ParseAddress(address);
+
+				Variant variant = model.GetValue(address);
+				results.push_back(variant.Get<String>());
+			}
+
+			RMLUI_ASSERT(results == expected_results);
+
+			bool success = model.SetValue("data.more_fun[1].magic[1]", 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 };
+
+			String result = model.GetValue("data.fun.magic[8]").Get<String>();
+			RMLUI_ASSERT(result == "90");
+		}
+	}
+} test_data_variables;
+
+
+#endif
+
 }
 }

+ 53 - 0
Source/Core/DataVariable.cpp

@@ -0,0 +1,53 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "precompiled.h"
+#include "../../Include/RmlUi/Core/DataVariable.h"
+
+namespace Rml {
+namespace Core {
+
+bool VariableDefinition::Get(void* ptr, Variant& variant) {
+    Log::Message(Log::LT_WARNING, "Values can only be retrieved from scalar data types.");
+    return false;
+}
+bool VariableDefinition::Set(void* ptr, const Variant& variant) {
+    Log::Message(Log::LT_WARNING, "Values can only be assigned to scalar data types.");
+    return false;
+}
+int VariableDefinition::Size(void* ptr) {
+    Log::Message(Log::LT_WARNING, "Tried to get the size from a non-array data type.");
+    return 0;
+}
+Variable VariableDefinition::GetChild(void* ptr, const AddressEntry& address) {
+    Log::Message(Log::LT_WARNING, "Tried to get the child of a scalar type.");
+    return Variable();
+}
+
+}
+}

+ 38 - 20
Source/Core/DataView.cpp

@@ -35,6 +35,9 @@ namespace Rml {
 namespace Core {
 
 
+DataView::~DataView() {}
+
+
 DataViewText::DataViewText(const DataModel& model, ElementText* in_parent_element, const String& in_text, const size_t index_begin_search) : element(in_parent_element->GetObserverPtr())
 {
 	text.reserve(in_text.size());
@@ -58,8 +61,8 @@ DataViewText::DataViewText(const DataModel& model, ElementText* in_parent_elemen
 
 		DataEntry entry;
 		entry.index = text.size();
-		entry.binding_name = (String)StringUtilities::StripWhitespace(StringView(in_text.data() + begin_name, in_text.data() + end_name));
-		entry.binding_name = model.ResolveVariableName(entry.binding_name, in_parent_element);
+		String address_str = StringUtilities::StripWhitespace(StringView(in_text.data() + begin_name, in_text.data() + end_name));
+		entry.variable_address = model.ResolveAddress(address_str, in_parent_element);
 
 		data_entries.push_back(std::move(entry));
 
@@ -91,8 +94,7 @@ bool DataViewText::Update(const DataModel& model)
 	for (DataEntry& entry : data_entries)
 	{
 		String value;
-		bool result = model.GetValue(entry.binding_name, value);
-
+		bool result = model.GetValue(entry.variable_address, value);
 		if (result && entry.value != value)
 		{
 			entry.value = value;
@@ -146,9 +148,10 @@ String DataViewText::BuildText() const
 }
 
 
-DataViewAttribute::DataViewAttribute(const DataModel& model, Element* element, const String& binding_name, const String& attribute_name)
-	: element(element->GetObserverPtr()), binding_name(binding_name), attribute_name(attribute_name)
+DataViewAttribute::DataViewAttribute(const DataModel& model, Element* element, Element* parent, const String& binding_name, const String& attribute_name)
+	: element(element->GetObserverPtr()), attribute_name(attribute_name)
 {
+	variable_address = model.ResolveAddress(binding_name, parent);
 	Update(model);
 }
 
@@ -156,7 +159,7 @@ bool DataViewAttribute::Update(const DataModel& model)
 {
 	bool result = false;
 	String value;
-	if (model.GetValue(binding_name, value))
+	if (model.GetValue(variable_address, value))
 	{
 		Variant* attribute = element->GetAttribute(attribute_name);
 
@@ -171,9 +174,10 @@ bool DataViewAttribute::Update(const DataModel& model)
 
 
 
-DataViewStyle::DataViewStyle(const DataModel& model, Element* element, const String& binding_name, const String& property_name)
-	: element(element->GetObserverPtr()), binding_name(binding_name), property_name(property_name)
+DataViewStyle::DataViewStyle(const DataModel& model, Element* element, Element* parent, const String& binding_name, const String& property_name)
+	: element(element->GetObserverPtr()), property_name(property_name)
 {
+	variable_address = model.ResolveAddress(binding_name, parent);
 	Update(model);
 }
 
@@ -182,7 +186,7 @@ bool DataViewStyle::Update(const DataModel& model)
 {
 	bool result = false;
 	String value;
-	if (model.GetValue(binding_name, value))
+	if (model.GetValue(variable_address, value))
 	{
 		const Property* p = element->GetLocalProperty(property_name);
 		if (!p || p->Get<String>() != value)
@@ -197,8 +201,9 @@ bool DataViewStyle::Update(const DataModel& model)
 
 
 
-DataViewIf::DataViewIf(const DataModel& model, Element* element, const String& binding_name) : element(element->GetObserverPtr()), binding_name(binding_name)
+DataViewIf::DataViewIf(const DataModel& model, Element* element, Element* parent, const String& binding_name) : element(element->GetObserverPtr())
 {
+	variable_address = model.ResolveAddress(binding_name, element);
 	Update(model);
 }
 
@@ -207,7 +212,7 @@ bool DataViewIf::Update(const DataModel& model)
 {
 	bool result = false;
 	bool value = false;
-	if (model.GetValue(binding_name, value))
+	if (model.GetValue(variable_address, value))
 	{
 		bool is_visible = (element->GetLocalStyleProperties().count(PropertyId::Display) == 0);
 		if(is_visible != value)
@@ -224,9 +229,10 @@ bool DataViewIf::Update(const DataModel& model)
 
 
 
-DataViewFor::DataViewFor(const DataModel& model, Element* element, const String& binding_name, const String& in_rml_content) 
-	: element(element->GetObserverPtr()), binding_name(binding_name), rml_contents(in_rml_content)
+DataViewFor::DataViewFor(const DataModel& model, Element* element, const String& binding_name, const String& in_rml_content)
+	: element(element->GetObserverPtr()), rml_contents(in_rml_content)
 {
+	variable_address = model.ResolveAddress(binding_name, element);
 	attributes = element->GetAttributes();
 	attributes.erase("data-for");
 	element->SetProperty(PropertyId::Display, Property(Style::Display::None));
@@ -237,10 +243,12 @@ DataViewFor::DataViewFor(const DataModel& model, Element* element, const String&
 
 bool DataViewFor::Update(const DataModel& model)
 {
-	bool result = false;
-	bool value = false;
+	Variable variable = model.GetVariable(variable_address);
+	if (!variable)
+		return false;
 
-	const int size = model.containers.find(binding_name)->second->Size();
+	bool result = false;
+	const int size = variable.Size();
 	const int num_elements = (int)elements.size();
 
 	for (int i = 0; i < Math::Max(size, num_elements); i++)
@@ -252,8 +260,12 @@ bool DataViewFor::Update(const DataModel& model)
 			Element* new_element = element->GetParentNode()->InsertBefore(std::move(new_element_ptr), element.get());
 			elements.push_back(new_element);
 
-			auto& alias_map = model.aliases.emplace(new_element, SmallUnorderedMap<String, String>()).first->second;
-			alias_map["it"] = binding_name + "[" + ToString(i) + "]";
+			Address replacement_address;
+			replacement_address.reserve(variable_address.size() + 1);
+			replacement_address = variable_address;
+			replacement_address.push_back(AddressEntry(i));
+
+			model.InsertAlias(new_element, "it", replacement_address);
 
 			elements[i]->SetInnerRML(rml_contents);
 
@@ -262,7 +274,7 @@ bool DataViewFor::Update(const DataModel& model)
 		if (i >= size)
 		{
 			elements[i]->GetParentNode()->RemoveChild(elements[i]).reset();
-			model.aliases.erase(elements[i]);
+			model.EraseAliases(elements[i]);
 			elements[i] = nullptr;
 		}
 	}
@@ -273,5 +285,11 @@ bool DataViewFor::Update(const DataModel& model)
 	return result;
 }
 
+DataViews::DataViews()
+{}
+
+DataViews::~DataViews()
+{}
+
 }
 }

+ 13 - 13
Source/Core/Factory.cpp

@@ -285,13 +285,13 @@ ElementPtr Factory::InstanceElement(Element* parent, const String& instancer_nam
 							{
 								const String attr_bind_name = name.substr(5 + data_type.size() + 1);
 
-								DataViewAttribute data_view(*data_model, element.get(), value_bind_name, attr_bind_name);
-								if (data_view)
-									data_model->views.AddView(std::move(data_view));
+								auto view = std::make_unique<DataViewAttribute>(*data_model, element.get(), parent, value_bind_name, attr_bind_name);
+								if (*view)
+									data_model->AddView(std::move(view));
 								else
 									Log::Message(Log::LT_WARNING, "Could not add data-attr view to element '%s'.", parent->GetAddress().c_str());
 
-								DataControllerAttribute data_controller(*data_model, attr_bind_name, value_bind_name);
+								DataControllerAttribute data_controller(*data_model, parent, attr_bind_name, value_bind_name);
 								if (data_controller)
 									data_model->controllers.AddController(element.get(), std::move(data_controller));
 								else
@@ -301,17 +301,17 @@ ElementPtr Factory::InstanceElement(Element* parent, const String& instancer_nam
 							{
 								const String property_name = name.substr(5 + data_type.size() + 1);
 
-								DataViewStyle view(*data_model, element.get(), value_bind_name, property_name);
-								if (view)
-									data_model->views.AddView(std::move(view));
+								auto view = std::make_unique<DataViewStyle>(*data_model, element.get(), parent, value_bind_name, property_name);
+								if (*view)
+									data_model->AddView(std::move(view));
 								else
 									Log::Message(Log::LT_WARNING, "Could not add data-style view to element '%s'.", parent->GetAddress().c_str());
 							}
 							else if (data_type == "if")
 							{
-								DataViewIf view(*data_model, element.get(), value_bind_name);
-								if (view)
-									data_model->views.AddView(std::move(view));
+								auto view = std::make_unique<DataViewIf>(*data_model, element.get(), parent, value_bind_name);
+								if (*view)
+									data_model->AddView(std::move(view));
 								else
 									Log::Message(Log::LT_WARNING, "Could not add data-if view to element '%s'.", parent->GetAddress().c_str());
 							}
@@ -394,10 +394,10 @@ bool Factory::InstanceElementText(Element* parent, const String& text)
 			const size_t i_brackets = translated_data.find("{{", 0);
 			if (i_brackets != String::npos)
 			{
-				DataViewText data_view(*data_model, text_element, translated_data, i_brackets);
-				if (data_view)
+				auto view = std::make_unique<DataViewText>(*data_model, text_element, translated_data, i_brackets);
+				if (*view)
 				{
-					data_model->views.AddView(std::move(data_view));
+					data_model->AddView(std::move(view));
 					data_view_added = true;
 				}
 				else

+ 3 - 3
Source/Core/XMLNodeHandlerDefault.cpp

@@ -95,9 +95,9 @@ bool XMLNodeHandlerDefault::ElementData(XMLParser* parser, const String& data)
 		{
 			String value_bind_name = attribute->Get<String>();
 
-			DataViewFor view(*data_model, parent, value_bind_name, data);
-			if (view)
-				data_model->views.AddView(std::move(view));
+			auto view = std::make_unique<DataViewFor>(*data_model, parent, value_bind_name, data);
+			if (*view)
+				data_model->AddView(std::move(view));
 			else
 				Log::Message(Log::LT_WARNING, "Could not add data-for view to element '%s'.", parent->GetAddress().c_str());