Просмотр исходного кода

Some refactoring of DataViews. Move the default DataViews to separate files.

Michael Ragazzon 5 лет назад
Родитель
Сommit
20c1dbc363

+ 2 - 0
CMake/FileList.cmake

@@ -5,6 +5,7 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/ComputeProperty.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ContextInstancerDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DataParser.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/DataViewDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorGradient.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorNinePatch.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiled.h
@@ -202,6 +203,7 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/DataParser.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataVariable.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DataView.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/DataViewDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Decorator.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorGradient.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorInstancer.cpp

+ 1 - 1
Include/RmlUi/Core/DataModel.h

@@ -47,7 +47,7 @@ class RMLUICORE_API DataModel : NonCopyMoveable {
 public:
 	DataModel(const TransformFuncRegister* transform_register = nullptr) : transform_register(transform_register) {}
 
-	void AddView(UniquePtr<DataView> view) { views.Add(std::move(view)); }
+	void AddView(DataViewPtr view) { views.Add(std::move(view)); }
 	void AddController(UniquePtr<DataController> controller) { controllers.Add(std::move(controller)); }
 
 	bool BindVariable(const String& name, Variable variable);

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

@@ -31,168 +31,96 @@
 
 #include "Header.h"
 #include "Types.h"
-#include "Variant.h"
-#include "StringUtilities.h"
 #include "Traits.h"
-#include "DataVariable.h"
 #include <unordered_map>
 
 namespace Rml {
 namespace Core {
 
 class Element;
-class ElementText;
 class DataModel;
-class DataExpression;
-using DataExpressionPtr = UniquePtr<DataExpression>;
 
-class RMLUICORE_API DataView : NonCopyMoveable {
-public:
-	virtual ~DataView();
-	virtual bool Update(DataModel& model) = 0;
-	virtual StringList GetVariableNameList() const = 0;
-
-	bool IsValid() const { return (bool)attached_element; }
-	explicit operator bool() const { return IsValid(); }
-
-	Element* GetElement() const;
-	int GetElementDepth() const { return element_depth; }
-	
-protected:
-	DataView(Element* element);
-
-	void InvalidateView() { attached_element.reset(); }
-
-private:
-	ObserverPtr<Element> attached_element;
-	int element_depth;
-};
 
-class DataViewText final : public DataView {
+class RMLUICORE_API DataViewInstancer : public NonCopyMoveable {
 public:
-	DataViewText(DataModel& model, ElementText* in_element, const String& in_text);
-	~DataViewText();
-
-	bool Update(DataModel& model) override;
-	StringList GetVariableNameList() const override;
-
-private:
-	String BuildText() const;
-
-	struct DataEntry {
-		size_t index = 0; // Index into 'text'
-		DataExpressionPtr data_expression;
-		String value;
-	};
-
-	String text;
-	std::vector<DataEntry> data_entries;
+	DataViewInstancer() {}
+	virtual ~DataViewInstancer() {}
+	virtual DataViewPtr InstanceView(Element* element) = 0;
 };
 
-
-
-class DataViewAttribute final : public DataView {
+template<typename T>
+class DataViewInstancerDefault final : public DataViewInstancer {
 public:
-	DataViewAttribute(DataModel& model, Element* element, const String& binding_name, const String& attribute_name);
-	~DataViewAttribute();
-
-	bool Update(DataModel& model) override;
-
-	StringList GetVariableNameList() const override;
-private:
-	String attribute_name;
-	DataExpressionPtr data_expression;
-};
-
-
-class DataViewStyle final : public DataView {
-public:
-	DataViewStyle(DataModel& model, Element* element, const String& binding_name, const String& property_name);
-	~DataViewStyle();
-
-	bool Update(DataModel& model) override;
-
-	StringList GetVariableNameList() const override;
-private:
-	String property_name;
-	DataExpressionPtr data_expression;
+	DataViewPtr InstanceView(Element* element) override {
+		return DataViewPtr(new T(element));
+	}
 };
 
-class DataViewClass final : public DataView {
-public:
-	DataViewClass(DataModel& model, Element* element, const String& binding_name, const String& class_name);
-	~DataViewClass();
-
-	bool Update(DataModel& model) override;
+/**
+	Data view.
 
-	StringList GetVariableNameList() const override;
+	Data views are used to present a data variable in the document by different means.
+	A data view is declared in the document by the element attribute:
+	
+	    data-[type]-[modifier]="[expression]"
 
-private:
-	String class_name;
-	DataExpressionPtr data_expression;
-};
+	The modifier may or may not be required depending on the data view.
+ */
 
-class DataViewRml final : public DataView {
+class RMLUICORE_API DataView : public Releasable {
 public:
-	DataViewRml(DataModel& model, Element* element, const String& binding_name, const String& rml_contents);
-	~DataViewRml();
+	virtual ~DataView();
 
-	bool Update(DataModel& model) override;
+	// Initialize the data view.
+	// @param[in] model The data model the view will be attached to.
+	// @param[in] element The element which spawned the view.
+	// @param[in] expression The value of the element's 'data-' attribute which spawned the view (see above).
+	// @param[in] modifier_or_inner_rml The modifier for the given view type (see above), or the inner rml contents for structural data views.
+	// @return True on success.
+	virtual bool Initialize(DataModel& model, Element* element, const String& expression, const String& modifier_or_inner_rml) = 0;
 
-	StringList GetVariableNameList() const override;
+	// Update the data view.
+	// Returns true if the update resulted in a change.
+	virtual bool Update(DataModel& model) = 0;
 
-private:
-	String previous_rml;
-	DataExpressionPtr data_expression;
-};
+	// Returns the list of data variable name(s) which can modify this view.
+	virtual StringList GetVariableNameList() const = 0;
 
+	// Returns the attached element if it still exists.
+	Element* GetElement() const;
 
-class DataViewIf final : public DataView {
-public:
-	DataViewIf(DataModel& model, Element* element, const String& binding_name);
-	~DataViewIf();
+	// Returns the depth of the attached element in the document tree.
+	int GetElementDepth() const;
+	
+	// Returns true if the element still exists
+	bool IsValid() const;
+	
+protected:
+	DataView(Element* element);
 
-	bool Update(DataModel& model) override;
+	// Delete this
+	void Release() override;
 
-	StringList GetVariableNameList() const override;
 private:
-	DataExpressionPtr data_expression;
+	ObserverPtr<Element> attached_element;
+	int element_depth;
 };
 
 
-class DataViewFor final : public DataView {
-public:
-	DataViewFor(DataModel& model, Element* element, const String& binding_name, const String& rml_contents);
-
-	bool Update(DataModel& model) override;
-
-	StringList GetVariableNameList() const override {
-		return variable_address.empty() ? StringList() : StringList{ variable_address.front().name };
-	}
-
-private:
-	DataAddress variable_address;
-	String alias_name;
-	String rml_contents;
-	ElementAttributes attributes;
-
-	ElementList elements;
-};
-
 
 class RMLUICORE_API DataViews : NonCopyMoveable {
 public:
 	DataViews();
 	~DataViews();
 
-	void Add(UniquePtr<DataView> view);
+	void Add(DataViewPtr view);
 
 	void OnElementRemove(Element* element);
 
 	bool Update(DataModel& model, const SmallUnorderedSet< String >& dirty_variables);
 
 private:
-	using DataViewList = std::vector<UniquePtr<DataView>>;
+	using DataViewList = std::vector<DataViewPtr>;
 
 	DataViewList views;
 	

+ 6 - 0
Include/RmlUi/Core/Factory.h

@@ -37,6 +37,7 @@ namespace Core {
 
 class Context;
 class ContextInstancer;
+class DataViewInstancer;
 class Decorator;
 class DecoratorInstancer;
 class Element;
@@ -173,6 +174,11 @@ public:
 	/// @return The instanced event listener.
 	static EventListener* InstanceEventListener(const String& value, Element* element);
 
+	// TODO documentation
+	static void RegisterDataViewInstancer(DataViewInstancer* instancer, const String& name, bool is_structural_view = false);
+
+	static DataViewPtr InstanceDataView(const String& type_name, Element* element, bool is_structural_view);
+
 private:
 	Factory();
 	~Factory();

+ 1 - 0
Include/RmlUi/Core/TypeConverter.h

@@ -34,6 +34,7 @@
 #include "StringUtilities.h"
 #include <stdlib.h>
 #include <stdio.h>
+#include <cinttypes>
 
 namespace Rml {
 namespace Core {

+ 2 - 2
Include/RmlUi/Core/TypeConverter.inl

@@ -174,7 +174,7 @@ class TypeConverter< String, int64_t >
 public:
 	static bool Convert(const String& src, int64_t& dest)
 	{
-		return sscanf(src.c_str(), "%lld", &dest) == 1;
+		return sscanf(src.c_str(), "%" SCNd64, &dest) == 1;
 	}
 };
 
@@ -294,7 +294,7 @@ class TypeConverter< int64_t, String >
 public:
 	static bool Convert(const int64_t& src, String& dest)
 	{
-		return FormatString(dest, 32, "%lld", src) > 0;
+		return FormatString(dest, 32, "%" PRId64, src) > 0;
 	}
 };
 

+ 4 - 0
Include/RmlUi/Core/Types.h

@@ -186,6 +186,10 @@ using TransformPtr = SharedPtr< Transform >;
 using DecoratorsPtr = SharedPtr<const Decorators>;
 using FontEffectsPtr = SharedPtr<const FontEffects>;
 
+// Data binding types
+class DataView;
+using DataViewPtr = UniqueReleaserPtr<DataView>;
+
 }
 }
 

+ 6 - 3
Source/Core/BaseXMLParser.cpp

@@ -59,10 +59,13 @@ void BaseXMLParser::Parse(Stream* stream)
 	source_url = &stream->GetSourceURL();
 
 	xml_source.clear();
-	const size_t source_size = stream->Length();
-	const size_t read_size = stream->Read(xml_source, source_size);
 
-	RMLUI_ASSERT(source_size == read_size);
+	// We read in the whole XML file here.
+	// TODO: It doesn't look like the Stream interface is used for anything useful. We
+	//   might as well just use a span or StringView, and get completely rid of it.
+	// @performance Otherwise, use the temporary allocator.
+	const size_t source_size = stream->Length();
+	stream->Read(xml_source, source_size);
 
 	xml_index = 0;
 	line_number = 1;

+ 13 - 412
Source/Core/DataView.cpp

@@ -27,9 +27,8 @@
  */
 
 #include "precompiled.h"
-#include "../../Include/RmlUi/Core/DataModel.h"
 #include "../../Include/RmlUi/Core/DataView.h"
-#include "DataParser.h"
+#include "../../Include/RmlUi/Core/Element.h"
 
 namespace Rml {
 namespace Core {
@@ -44,433 +43,35 @@ Element* DataView::GetElement() const
 	return result;
 }
 
-DataView::DataView(Element* element) : attached_element(element->GetObserverPtr()), element_depth(0) {
-	if (element)
-	{
-		for (Element* parent = element->GetParentNode(); parent; parent = parent->GetParentNode())
-			element_depth += 1;
-	}
-}
-
-
-DataViewText::DataViewText(DataModel& model, ElementText* parent_element, const String& in_text) : DataView(parent_element)
-{
-	text.reserve(in_text.size());
-
-	DataExpressionInterface expression_interface(&model, parent_element);
-	bool success = true;
-
-	size_t previous_close_brackets = 0;
-	size_t begin_brackets = 0;
-	while ((begin_brackets = in_text.find("{{", begin_brackets)) != String::npos)
-	{
-		text.insert(text.end(), in_text.begin() + previous_close_brackets, in_text.begin() + begin_brackets);
-
-		const size_t begin_name = begin_brackets + 2;
-		const size_t end_name = in_text.find("}}", begin_name);
-
-		if (end_name == String::npos)
-		{
-			success = false;
-			break;
-		}
-
-		DataEntry entry;
-		entry.index = text.size();
-		entry.data_expression = std::make_unique<DataExpression>(String(in_text.begin() + begin_name, in_text.begin() + end_name));
-
-		if (entry.data_expression->Parse(expression_interface))
-			data_entries.push_back(std::move(entry));
-
-		previous_close_brackets = end_name + 2;
-		begin_brackets = previous_close_brackets;
-	}
-
-	if (data_entries.empty())
-		success = false;
-
-	if (success && previous_close_brackets < in_text.size())
-		text.insert(text.end(), in_text.begin() + previous_close_brackets, in_text.end());
-
-	if (!success)
-	{
-		text.clear();
-		data_entries.clear();
-		InvalidateView();
-	}
-}
-
-DataViewText::~DataViewText()
-{}
-
-bool DataViewText::Update(DataModel& model)
-{
-	bool entries_modified = false;
-	Element* element = GetElement();
-	DataExpressionInterface expression_interface(&model, element);
-
-	for (DataEntry& entry : data_entries)
-	{
-		RMLUI_ASSERT(entry.data_expression);
-		Variant variant;
-		bool result = entry.data_expression->Run(expression_interface, variant);
-		const String value = variant.Get<String>();
-		if (result && entry.value != value)
-		{
-			entry.value = value;
-			entries_modified = true;
-		}
-	}
-
-	if (entries_modified)
-	{
-		if (Element* element = GetElement())
-		{
-			RMLUI_ASSERTMSG(rmlui_dynamic_cast<ElementText*>(element), "Somehow the element type was changed from ElementText since construction of the view. Should not be possible?");
-
-			if(auto text_element = static_cast<ElementText*>(element))
-			{
-				String new_text = BuildText();
-				text_element->SetText(new_text);
-			}
-		}
-		else
-		{
-			Log::Message(Log::LT_WARNING, "Could not update data view text, element no longer valid. Was it destroyed?");
-		}
-	}
-
-	return entries_modified;
-}
-
-StringList DataViewText::GetVariableNameList() const
-{
-	StringList full_list;
-	full_list.reserve(data_entries.size());
-	
-	for (const DataEntry& entry : data_entries)
-	{
-		RMLUI_ASSERT(entry.data_expression);
-
-		StringList entry_list = entry.data_expression->GetVariableNameList();
-		full_list.insert(full_list.end(),
-			std::make_move_iterator(entry_list.begin()),
-			std::make_move_iterator(entry_list.end())
-		);
-	}
-
-	return full_list;
-}
-
-String DataViewText::BuildText() const
-{
-	size_t reserve_size = text.size();
-
-	for (const DataEntry& entry : data_entries)
-		reserve_size += entry.value.size();
-
-	String result;
-	result.reserve(reserve_size);
-
-	size_t previous_index = 0;
-	for (const DataEntry& entry : data_entries)
-	{
-		result += text.substr(previous_index, entry.index - previous_index);
-		result += entry.value;
-		previous_index = entry.index;
-	}
-
-	if (previous_index < text.size())
-		result += text.substr(previous_index);
-
-	return result;
-}
-
-
-DataViewAttribute::DataViewAttribute(DataModel& model, Element* element, const String& binding_name, const String& attribute_name)
-	: DataView(element), attribute_name(attribute_name)
-{
-	data_expression = std::make_unique<DataExpression>(binding_name);
-	DataExpressionInterface interface(&model, element);
-
-	if (!data_expression->Parse(interface))
-		InvalidateView();
-}
-DataViewAttribute::~DataViewAttribute()
-{}
-
-bool DataViewAttribute::Update(DataModel& model)
-{
-	bool result = false;
-	Variant variant;
-	Element* element = GetElement();
-	DataExpressionInterface interface(&model, element);
-
-	if (element && data_expression->Run(interface, variant))
-	{
-		const String value = variant.Get<String>();
-		const Variant* attribute = element->GetAttribute(attribute_name);
-		
-		if (!attribute || (attribute && attribute->Get<String>() != value))
-		{
-			element->SetAttribute(attribute_name, value);
-			result = true;
-		}
-	}
-	return result;
-}
-
-StringList DataViewAttribute::GetVariableNameList() const {
-	return data_expression ? data_expression->GetVariableNameList() : StringList();
-}
-
-
-DataViewStyle::DataViewStyle(DataModel& model, Element* element, const String& binding_name, const String& property_name)
-	: DataView(element), property_name(property_name)
-{
-	data_expression = std::make_unique<DataExpression>(binding_name);
-	DataExpressionInterface interface(&model, element);
-
-	if(!data_expression->Parse(interface))
-		InvalidateView();
-}
-
-DataViewStyle::~DataViewStyle()
-{
-}
-
-
-bool DataViewStyle::Update(DataModel& model)
-{
-	bool result = false;
-	Variant variant;
-	Element* element = GetElement();
-	DataExpressionInterface interface(&model, element);
-	
-	if (element && data_expression->Run(interface, variant))
-	{
-		const String value = variant.Get<String>();
-		const Property* p = element->GetLocalProperty(property_name);
-		if (!p || p->Get<String>() != value)
-		{
-			element->SetProperty(property_name, value);
-			result = true;
-		}
-	}
-	return result;
+int DataView::GetElementDepth() const {
+	return element_depth;
 }
 
-StringList DataViewStyle::GetVariableNameList() const {
-	return data_expression ? data_expression->GetVariableNameList() : StringList();
+bool DataView::IsValid() const {
+	return (bool)attached_element;
 }
 
-
-DataViewClass::DataViewClass(DataModel& model, Element* element, const String& binding_name, const String& class_name) 
-	: DataView(element), class_name(class_name)
-{
-	data_expression = std::make_unique<DataExpression>(binding_name);
-	DataExpressionInterface interface(&model, element);
-
-	if (!data_expression->Parse(interface))
-		InvalidateView();
-}
-
-DataViewClass::~DataViewClass() = default;
-
-bool DataViewClass::Update(DataModel& model)
-{
-	bool result = false;
-	Variant variant;
-	Element* element = GetElement();
-	DataExpressionInterface interface(&model, element);
-
-	if (element && data_expression->Run(interface, variant))
-	{
-		const bool activate = variant.Get<bool>();
-		const bool is_set = element->IsClassSet(class_name);
-		if (activate != is_set)
-		{
-			element->SetClass(class_name, activate);
-			result = true;
-		}
-	}
-	return result;
-}
-
-StringList DataViewClass::GetVariableNameList() const
-{
-	return data_expression ? data_expression->GetVariableNameList() : StringList();
-}
-
-
-
-DataViewRml::DataViewRml(DataModel& model, Element* element, const String& binding_name, const String& /*unused*/)
-	: DataView(element)
-{
-	data_expression = std::make_unique<DataExpression>(binding_name);
-	DataExpressionInterface interface(&model, element);
-
-	if (!data_expression->Parse(interface))
-		InvalidateView();
-}
-
-DataViewRml::~DataViewRml() = default;
-
-bool DataViewRml::Update(DataModel & model)
-{
-	bool result = false;
-	Variant variant;
-	Element* element = GetElement();
-	DataExpressionInterface interface(&model, element);
-
-	if (element && data_expression->Run(interface, variant))
-	{
-		String new_rml = variant.Get<String>();
-		if (new_rml != previous_rml)
-		{
-			element->SetInnerRML(new_rml);
-			previous_rml = std::move(new_rml);
-			result = true;
-		}
-	}
-	return result;
-}
-
-StringList DataViewRml::GetVariableNameList() const
-{
-	return data_expression ? data_expression->GetVariableNameList() : StringList();
-}
-
-
-
-
-DataViewIf::DataViewIf(DataModel& model, Element* element, const String& binding_name) : DataView(element)
-{
-	data_expression = std::make_unique<DataExpression>(binding_name);
-	DataExpressionInterface interface(&model, element);
-
-	if (!data_expression->Parse(interface))
-		InvalidateView();
-}
-
-DataViewIf::~DataViewIf()
-{}
-
-
-bool DataViewIf::Update(DataModel& model)
-{
-	bool result = false;
-	Variant variant;
-	Element* element = GetElement();
-	DataExpressionInterface interface(&model, element);
-
-	if (element && data_expression->Run(interface, variant))
+DataView::DataView(Element* element) : attached_element(element->GetObserverPtr()), element_depth(0) {
+	if (element)
 	{
-		const bool value = variant.Get<bool>();
-		const bool is_visible = (element->GetLocalStyleProperties().count(PropertyId::Display) == 0);
-		if(is_visible != value)
-		{
-			if (value)
-				element->RemoveProperty(PropertyId::Display);
-			else
-				element->SetProperty(PropertyId::Display, Property(Style::Display::None));
-			result = true;
-		}
+		for (Element* parent = element->GetParentNode(); parent; parent = parent->GetParentNode())
+			element_depth += 1;
 	}
-	return result;
 }
-StringList DataViewIf::GetVariableNameList() const {
-	return data_expression ? data_expression->GetVariableNameList() : StringList();
-}
-
-
 
-DataViewFor::DataViewFor(DataModel& model, Element* element, const String& in_binding_name, const String& in_rml_content)
-	: DataView(element), rml_contents(in_rml_content)
+void DataView::Release()
 {
-	StringList binding_list;
-	StringUtilities::ExpandString(binding_list, in_binding_name, ':');
-
-	if (binding_list.empty() || binding_list.size() > 2 || binding_list.front().empty() || binding_list.back().empty())
-	{
-		Log::Message(Log::LT_WARNING, "Invalid syntax in data-for '%s'", in_binding_name.c_str());
-		InvalidateView();
-		return;
-	}
-
-	if (binding_list.size() == 2)
-		alias_name = binding_list.front();
-	else
-		alias_name = "it";
-
-	const String& binding_name = binding_list.back();
-
-	variable_address = model.ResolveAddress(binding_name, element);
-	if (variable_address.empty())
-	{
-		InvalidateView();
-		return;
-	}
-
-	attributes = element->GetAttributes();
-	attributes.erase("data-for");
-	element->SetProperty(PropertyId::Display, Property(Style::Display::None));
+	delete this;
 }
 
 
-
-bool DataViewFor::Update(DataModel& model)
-{
-	Variable variable = model.GetVariable(variable_address);
-	if (!variable)
-		return false;
-
-	bool result = false;
-	const int size = variable.Size();
-	const int num_elements = (int)elements.size();
-	Element* element = GetElement();
-
-	for (int i = 0; i < Math::Max(size, num_elements); i++)
-	{
-		if (i >= num_elements)
-		{
-			ElementPtr new_element_ptr = Factory::InstanceElement(nullptr, element->GetTagName(), element->GetTagName(), attributes);
-
-			DataAddress replacement_address;
-			replacement_address.reserve(variable_address.size() + 1);
-			replacement_address = variable_address;
-			replacement_address.push_back(AddressEntry(i));
-
-			model.InsertAlias(new_element_ptr.get(), alias_name, replacement_address);
-
-			Element* new_element = element->GetParentNode()->InsertBefore(std::move(new_element_ptr), element);
-			elements.push_back(new_element);
-
-			elements[i]->SetInnerRML(rml_contents);
-
-			RMLUI_ASSERT(i < (int)elements.size());
-		}
-		if (i >= size)
-		{
-			model.EraseAliases(elements[i]);
-			elements[i]->GetParentNode()->RemoveChild(elements[i]).reset();
-			elements[i] = nullptr;
-		}
-	}
-
-	if (num_elements > size)
-		elements.resize(size);
-
-	return result;
-}
-
 DataViews::DataViews()
 {}
 
 DataViews::~DataViews()
 {}
 
-void DataViews::Add(UniquePtr<DataView> view) {
+void DataViews::Add(DataViewPtr view) {
 	views_to_add.push_back(std::move(view));
 }
 
@@ -489,7 +90,7 @@ void DataViews::OnElementRemove(Element* element)
 	}
 }
 
-bool DataViews::Update(DataModel & model, const SmallUnorderedSet< String >& dirty_variables)
+bool DataViews::Update(DataModel& model, const SmallUnorderedSet< String >& dirty_variables)
 {
 	bool result = false;
 

+ 447 - 0
Source/Core/DataViewDefault.cpp

@@ -0,0 +1,447 @@
+/*
+ * 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 "DataViewDefault.h"
+#include "DataParser.h"
+#include "../../Include/RmlUi/Core/Variant.h"
+#include "../../Include/RmlUi/Core/DataModel.h"
+
+namespace Rml {
+namespace Core {
+
+
+DataViewCommon::DataViewCommon(Element* element) : DataView(element)
+{}
+
+DataViewCommon::~DataViewCommon()
+{}
+
+bool DataViewCommon::Initialize(DataModel& model, Element* element, const String& expression_str, const String& in_modifier)
+{
+	modifier = in_modifier;
+	expression = std::make_unique<DataExpression>(expression_str);
+	DataExpressionInterface interface(&model, element);
+
+	bool result = expression->Parse(interface);
+	return result;
+}
+
+StringList DataViewCommon::GetVariableNameList() const {
+	RMLUI_ASSERT(expression);
+	return expression->GetVariableNameList();
+}
+
+const String& DataViewCommon::GetModifier() const {
+	return modifier;
+}
+
+DataExpression& DataViewCommon::GetExpression() {
+	RMLUI_ASSERT(expression);
+	return *expression;
+}
+
+
+
+DataViewAttribute::DataViewAttribute(Element* element) : DataViewCommon(element)
+{}
+
+DataViewAttribute::~DataViewAttribute()
+{}
+
+bool DataViewAttribute::Update(DataModel& model)
+{
+	const String& attribute_name = GetModifier();
+	bool result = false;
+	Variant variant;
+	Element* element = GetElement();
+	DataExpressionInterface interface(&model, element);
+
+	if (element && GetExpression().Run(interface, variant))
+	{
+		const String value = variant.Get<String>();
+		const Variant* attribute = element->GetAttribute(attribute_name);
+		
+		if (!attribute || (attribute && attribute->Get<String>() != value))
+		{
+			element->SetAttribute(attribute_name, value);
+			result = true;
+		}
+	}
+	return result;
+}
+
+
+DataViewStyle::DataViewStyle(Element* element) : DataViewCommon(element)
+{}
+
+DataViewStyle::~DataViewStyle()
+{
+}
+
+
+bool DataViewStyle::Update(DataModel& model)
+{
+	const String& property_name = GetModifier();
+	bool result = false;
+	Variant variant;
+	Element* element = GetElement();
+	DataExpressionInterface interface(&model, element);
+	
+	if (element && GetExpression().Run(interface, variant))
+	{
+		const String value = variant.Get<String>();
+		const Property* p = element->GetLocalProperty(property_name);
+		if (!p || p->Get<String>() != value)
+		{
+			element->SetProperty(property_name, value);
+			result = true;
+		}
+	}
+	return result;
+}
+
+
+DataViewClass::DataViewClass(Element* element) : DataViewCommon(element)
+{}
+
+DataViewClass::~DataViewClass()
+{}
+
+bool DataViewClass::Update(DataModel& model)
+{
+	const String& class_name = GetModifier();
+	bool result = false;
+	Variant variant;
+	Element* element = GetElement();
+	DataExpressionInterface interface(&model, element);
+
+	if (element && GetExpression().Run(interface, variant))
+	{
+		const bool activate = variant.Get<bool>();
+		const bool is_set = element->IsClassSet(class_name);
+		if (activate != is_set)
+		{
+			element->SetClass(class_name, activate);
+			result = true;
+		}
+	}
+	return result;
+}
+
+
+DataViewRml::DataViewRml(Element* element) : DataViewCommon(element)
+{}
+
+DataViewRml::~DataViewRml()
+{}
+
+bool DataViewRml::Update(DataModel & model)
+{
+	bool result = false;
+	Variant variant;
+	Element* element = GetElement();
+	DataExpressionInterface interface(&model, element);
+
+	if (element && GetExpression().Run(interface, variant))
+	{
+		String new_rml = variant.Get<String>();
+		if (new_rml != previous_rml)
+		{
+			element->SetInnerRML(new_rml);
+			previous_rml = std::move(new_rml);
+			result = true;
+		}
+	}
+	return result;
+}
+
+
+DataViewIf::DataViewIf(Element* element) : DataViewCommon(element)
+{}
+
+DataViewIf::~DataViewIf()
+{}
+
+
+bool DataViewIf::Update(DataModel& model)
+{
+	bool result = false;
+	Variant variant;
+	Element* element = GetElement();
+	DataExpressionInterface interface(&model, element);
+
+	if (element && GetExpression().Run(interface, variant))
+	{
+		const bool value = variant.Get<bool>();
+		const bool is_visible = (element->GetLocalStyleProperties().count(PropertyId::Display) == 0);
+		if(is_visible != value)
+		{
+			if (value)
+				element->RemoveProperty(PropertyId::Display);
+			else
+				element->SetProperty(PropertyId::Display, Property(Style::Display::None));
+			result = true;
+		}
+	}
+	return result;
+}
+
+
+
+
+
+DataViewText::DataViewText(Element* element) : DataView(element)
+{
+
+}
+
+DataViewText::~DataViewText()
+{}
+
+bool DataViewText::Initialize(DataModel& model, Element* element, const String& RMLUI_UNUSED_PARAMETER(expression), const String& RMLUI_UNUSED_PARAMETER(modifier))
+{
+	RMLUI_UNUSED(expression);
+	RMLUI_UNUSED(modifier);
+
+	ElementText* element_text = rmlui_dynamic_cast<ElementText*>(element);
+	if (!element_text)
+		return false;
+
+	const String& in_text = element_text->GetText();
+	
+	text.reserve(in_text.size());
+
+	DataExpressionInterface expression_interface(&model, element);
+
+	size_t previous_close_brackets = 0;
+	size_t begin_brackets = 0;
+	while ((begin_brackets = in_text.find("{{", begin_brackets)) != String::npos)
+	{
+		text.insert(text.end(), in_text.begin() + previous_close_brackets, in_text.begin() + begin_brackets);
+
+		const size_t begin_name = begin_brackets + 2;
+		const size_t end_name = in_text.find("}}", begin_name);
+
+		if (end_name == String::npos)
+			return false;
+
+		DataEntry entry;
+		entry.index = text.size();
+		entry.data_expression = std::make_unique<DataExpression>(String(in_text.begin() + begin_name, in_text.begin() + end_name));
+
+		if (entry.data_expression->Parse(expression_interface))
+			data_entries.push_back(std::move(entry));
+
+		previous_close_brackets = end_name + 2;
+		begin_brackets = previous_close_brackets;
+	}
+
+	if (data_entries.empty())
+		return false;
+
+	if (previous_close_brackets < in_text.size())
+		text.insert(text.end(), in_text.begin() + previous_close_brackets, in_text.end());
+
+	return true;
+}
+
+bool DataViewText::Update(DataModel& model)
+{
+	bool entries_modified = false;
+	Element* element = GetElement();
+	DataExpressionInterface expression_interface(&model, element);
+
+	for (DataEntry& entry : data_entries)
+	{
+		RMLUI_ASSERT(entry.data_expression);
+		Variant variant;
+		bool result = entry.data_expression->Run(expression_interface, variant);
+		const String value = variant.Get<String>();
+		if (result && entry.value != value)
+		{
+			entry.value = value;
+			entries_modified = true;
+		}
+	}
+
+	if (entries_modified)
+	{
+		if (Element* element = GetElement())
+		{
+			RMLUI_ASSERTMSG(rmlui_dynamic_cast<ElementText*>(element), "Somehow the element type was changed from ElementText since construction of the view. Should not be possible?");
+
+			if (auto text_element = static_cast<ElementText*>(element))
+			{
+				String new_text = BuildText();
+				text_element->SetText(new_text);
+			}
+		}
+		else
+		{
+			Log::Message(Log::LT_WARNING, "Could not update data view text, element no longer valid. Was it destroyed?");
+		}
+	}
+
+	return entries_modified;
+}
+
+StringList DataViewText::GetVariableNameList() const
+{
+	StringList full_list;
+	full_list.reserve(data_entries.size());
+
+	for (const DataEntry& entry : data_entries)
+	{
+		RMLUI_ASSERT(entry.data_expression);
+
+		StringList entry_list = entry.data_expression->GetVariableNameList();
+		full_list.insert(full_list.end(),
+			std::make_move_iterator(entry_list.begin()),
+			std::make_move_iterator(entry_list.end())
+		);
+	}
+
+	return full_list;
+}
+
+String DataViewText::BuildText() const
+{
+	size_t reserve_size = text.size();
+
+	for (const DataEntry& entry : data_entries)
+		reserve_size += entry.value.size();
+
+	String result;
+	result.reserve(reserve_size);
+
+	size_t previous_index = 0;
+	for (const DataEntry& entry : data_entries)
+	{
+		result += text.substr(previous_index, entry.index - previous_index);
+		result += entry.value;
+		previous_index = entry.index;
+	}
+
+	if (previous_index < text.size())
+		result += text.substr(previous_index);
+
+	return result;
+}
+
+
+
+DataViewFor::DataViewFor(Element* element) : DataView(element)
+{}
+
+DataViewFor::~DataViewFor()
+{}
+
+bool DataViewFor::Initialize(DataModel& model, Element* element, const String& in_expression, const String& in_rml_content)
+{
+	rml_contents = in_rml_content;
+
+	StringList iterator_container_pair;
+	StringUtilities::ExpandString(iterator_container_pair, in_expression, ':');
+
+	if (iterator_container_pair.empty() || iterator_container_pair.size() > 2 || iterator_container_pair.front().empty() || iterator_container_pair.back().empty())
+	{
+		Log::Message(Log::LT_WARNING, "Invalid syntax in data-for '%s'", in_expression.c_str());
+		return false;
+	}
+
+	if (iterator_container_pair.size() == 2)
+		iterator_name = iterator_container_pair.front();
+	else
+		iterator_name = "it";
+
+	const String& container_name = iterator_container_pair.back();
+
+	container_address = model.ResolveAddress(container_name, element);
+	if (container_address.empty())
+		return false;
+
+	element->SetProperty(PropertyId::Display, Property(Style::Display::None));
+
+	return true;
+}
+
+
+bool DataViewFor::Update(DataModel& model)
+{
+	Variable variable = model.GetVariable(container_address);
+	if (!variable)
+		return false;
+
+	bool result = false;
+	const int size = variable.Size();
+	const int num_elements = (int)elements.size();
+	Element* element = GetElement();
+
+	for (int i = 0; i < Math::Max(size, num_elements); i++)
+	{
+		if (i >= num_elements)
+		{
+			ElementPtr new_element_ptr = Factory::InstanceElement(nullptr, element->GetTagName(), element->GetTagName(), attributes);
+
+			DataAddress iterator_address;
+			iterator_address.reserve(container_address.size() + 1);
+			iterator_address = container_address;
+			iterator_address.push_back(AddressEntry(i));
+
+			model.InsertAlias(new_element_ptr.get(), iterator_name, std::move(iterator_address));
+
+			Element* new_element = element->GetParentNode()->InsertBefore(std::move(new_element_ptr), element);
+			elements.push_back(new_element);
+
+			elements[i]->SetInnerRML(rml_contents);
+
+			RMLUI_ASSERT(i < (int)elements.size());
+		}
+		if (i >= size)
+		{
+			model.EraseAliases(elements[i]);
+			elements[i]->GetParentNode()->RemoveChild(elements[i]).reset();
+			elements[i] = nullptr;
+		}
+	}
+
+	if (num_elements > size)
+		elements.resize(size);
+
+	return result;
+}
+
+StringList DataViewFor::GetVariableNameList() const {
+	RMLUI_ASSERT(!container_address.empty());
+	return StringList{ container_address.front().name };
+}
+
+
+}
+}

+ 158 - 0
Source/Core/DataViewDefault.h

@@ -0,0 +1,158 @@
+/*
+ * 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 RMLUICOREDATAVIEWDEFAULT_H
+#define RMLUICOREDATAVIEWDEFAULT_H
+
+#include "../../Include/RmlUi/Core/Header.h"
+#include "../../Include/RmlUi/Core/Types.h"
+#include "../../Include/RmlUi/Core/DataView.h"
+
+namespace Rml {
+namespace Core {
+
+class Element;
+class DataExpression;
+using DataExpressionPtr = UniquePtr<DataExpression>;
+
+
+class DataViewCommon : public DataView {
+public:
+	DataViewCommon(Element* element);
+	~DataViewCommon();
+
+	bool Initialize(DataModel& model, Element* element, const String& expression, const String& modifier) override;
+
+	StringList GetVariableNameList() const override;
+
+protected:
+	const String& GetModifier() const;
+	DataExpression& GetExpression();
+
+private:
+	String modifier;
+	DataExpressionPtr expression;
+};
+
+
+class DataViewAttribute final : public DataViewCommon {
+public:
+	DataViewAttribute(Element* element);
+	~DataViewAttribute();
+
+	bool Update(DataModel& model) override;
+};
+
+
+class DataViewStyle final : public DataViewCommon {
+public:
+	DataViewStyle(Element* element);
+	~DataViewStyle();
+
+	bool Update(DataModel& model) override;
+};
+
+
+class DataViewClass final : public DataViewCommon {
+public:
+	DataViewClass(Element* element);
+	~DataViewClass();
+
+	bool Update(DataModel& model) override;
+};
+
+
+class DataViewRml final : public DataViewCommon {
+public:
+	DataViewRml(Element* element);
+	~DataViewRml();
+
+	bool Update(DataModel& model) override;
+
+private:
+	String previous_rml;
+};
+
+
+class DataViewIf final : public DataViewCommon {
+public:
+	DataViewIf(Element* element);
+	~DataViewIf();
+
+	bool Update(DataModel& model) override;
+};
+
+
+class DataViewText final : public DataView {
+public:
+	DataViewText(Element* in_element);
+	~DataViewText();
+
+	bool Initialize(DataModel& model, Element* element, const String& expression, const String& modifier) override;
+
+	bool Update(DataModel& model) override;
+	StringList GetVariableNameList() const override;
+
+private:
+	String BuildText() const;
+
+	struct DataEntry {
+		size_t index = 0; // Index into 'text'
+		DataExpressionPtr data_expression;
+		String value;
+	};
+
+	String text;
+	std::vector<DataEntry> data_entries;
+};
+
+
+class DataViewFor final : public DataView {
+public:
+	DataViewFor(Element* element);
+	~DataViewFor();
+
+	bool Initialize(DataModel& model, Element* element, const String& expression, const String& inner_rml) override;
+
+	bool Update(DataModel& model) override;
+
+	StringList GetVariableNameList() const override;
+
+private:
+	DataAddress container_address;
+	String iterator_name;
+	String rml_contents;
+	ElementAttributes attributes;
+
+	ElementList elements;
+};
+
+}
+}
+
+#endif

+ 38 - 74
Source/Core/ElementUtilities.cpp

@@ -397,85 +397,36 @@ void ElementUtilities::ApplyDataViewsControllers(Element* element)
 
 			if (name.size() > 5 && name[0] == 'd' && name[1] == 'a' && name[2] == 't' && name[3] == 'a' && name[4] == '-')
 			{
-				constexpr size_t data_str_size = sizeof("data");
-				const size_t data_type_end = name.find('-', 5);
-				const size_t count = (data_type_end == String::npos ? String::npos : data_type_end - 5);
-				const String data_type = name.substr(5, count);
+				const size_t type_end = name.find('-', 5);
+				const size_t type_size = (type_end == String::npos ? String::npos : type_end - 5);
+				const String type_name = name.substr(5, type_size);
+
 				const String data_expression = attribute.second.Get<String>();
 
-				if (data_type == "attr")
+				if (DataViewPtr view = Factory::InstanceDataView(type_name, element, false))
 				{
-					const String attr_bind_name = name.substr(data_str_size + data_type.size() + 1);
+					String modifier;
+					const size_t label_offset = sizeof("data") + type_name.size() + 1;
+					if (label_offset < name.size())
+						modifier = name.substr(label_offset);
 
-					auto view = std::make_unique<DataViewAttribute>(*data_model, element, data_expression, attr_bind_name);
-					if (*view)
+					bool success = view->Initialize(*data_model, element, data_expression, modifier);
+					if (success)
 						data_model->AddView(std::move(view));
 					else
-						Log::Message(Log::LT_WARNING, "Could not add data-attr view to element: %s", element->GetAddress().c_str());
+						Log::Message(Log::LT_WARNING, "Could not add data-%s view to element: %s", type_name.c_str(), element->GetAddress().c_str());
 				}
-				else if (data_type == "value")
-				{
-					const String attr_bind_name = "value";
-					auto view = std::make_unique<DataViewAttribute>(*data_model, element, data_expression, attr_bind_name);
-					if (*view)
-						data_model->AddView(std::move(view));
-					else
-						Log::Message(Log::LT_WARNING, "Could not add data-value view to element: %s", element->GetAddress().c_str());
 
+				if (type_name == "value")
+				{
+					// TODO: Make the same abstraction for controllers as for views (or maybe make them into views instead if possible?)
 					auto controller = std::make_unique<DataControllerValue>(*data_model, element, data_expression);
 					if (controller)
 						data_model->AddController(std::move(controller));
 					else
 						Log::Message(Log::LT_WARNING, "Could not add data-value controller to element: %s", element->GetAddress().c_str());
 				}
-				else if (data_type == "style")
-				{
-					const String property_name = name.substr(data_str_size + data_type.size() + 1);
 
-					auto view = std::make_unique<DataViewStyle>(*data_model, element, data_expression, 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", element->GetAddress().c_str());
-				}
-				else if (data_type == "class")
-				{
-					const String class_name = name.substr(data_str_size + data_type.size() + 1);
-
-					auto view = std::make_unique<DataViewClass>(*data_model, element, data_expression, class_name);
-					if (*view)
-						data_model->AddView(std::move(view));
-					else
-						Log::Message(Log::LT_WARNING, "Could not add data-class view to element: %s", element->GetAddress().c_str());
-				}
-				else if (data_type == "rml")
-				{
-					auto view = std::make_unique<DataViewRml>(*data_model, element, data_expression, String());
-					if (*view)
-						data_model->AddView(std::move(view));
-					else
-						Log::Message(Log::LT_WARNING, "Could not add data-rml view to element: %s", element->GetAddress().c_str());
-				}
-				else if (data_type == "if")
-				{
-					auto view = std::make_unique<DataViewIf>(*data_model, element, data_expression);
-					if (*view)
-						data_model->AddView(std::move(view));
-					else
-						Log::Message(Log::LT_WARNING, "Could not add data-if view to element: %s", element->GetAddress().c_str());
-				}
-				else if (data_type == "text")
-				{
-					// This attribute is automatically added by the Factory when double brackets '{{' are encountered in the text.
-					if (ElementText* element_text = rmlui_dynamic_cast<ElementText*>(element))
-					{
-						auto view = std::make_unique<DataViewText>(*data_model, element_text, element_text->GetText());
-						if (*view)
-							data_model->AddView(std::move(view));
-						else
-							Log::Message(Log::LT_WARNING, "Could not add data binding view to element: %s", element->GetAddress().c_str());
-					}
-				}
 			}
 		}
 
@@ -485,26 +436,39 @@ void ElementUtilities::ApplyDataViewsControllers(Element* element)
 bool ElementUtilities::ApplyStructuralDataViews(Element* element, const String& inner_xml)
 {
 	RMLUI_ASSERT(element);
-	bool result = false;
+	bool success = false;
 
 	if (DataModel* data_model = element->GetDataModel())
 	{
-		if (const Variant* attribute = element->GetAttribute("data-for"))
+		// TODO: Almost copy-paste of above, combine the two functions.
+		// TODO: Iterate over the attributes BEFORE we create our views, as they may possibly change
+		//   the element's attributes, thereby invalidating the iterators -> undefined behavior.
+
+		for (auto& attribute : element->GetAttributes())
 		{
-			String value_bind_name = attribute->Get<String>();
+			const String& name = attribute.first;
 
-			auto view = std::make_unique<DataViewFor>(*data_model, element, value_bind_name, inner_xml);
-			if (*view)
+			if (name.size() > 5 && name[0] == 'd' && name[1] == 'a' && name[2] == 't' && name[3] == 'a' && name[4] == '-')
 			{
-				data_model->AddView(std::move(view));
-				result = true;
+
+				const size_t data_type_end = name.find('-', 5);
+				const size_t count = (data_type_end == String::npos ? String::npos : data_type_end - 5);
+				const String view_type = name.substr(5, count);
+				const String data_expression = attribute.second.Get<String>();
+
+				if (DataViewPtr view = Factory::InstanceDataView(view_type, element, true))
+				{
+					success = view->Initialize(*data_model, element, data_expression, inner_xml);
+					if (success)
+						data_model->AddView(std::move(view));
+					else
+						Log::Message(Log::LT_WARNING, "Could not add data-%s view to element: %s", view_type.c_str(), element->GetAddress().c_str());
+				}
 			}
-			else
-				Log::Message(Log::LT_WARNING, "Could not add data-for view to element: %s", element->GetAddress().c_str());
 		}
 	}
 
-	return result;
+	return success;
 }
 
 }

+ 75 - 3
Source/Core/Factory.cpp

@@ -31,6 +31,7 @@
 #include "../../Include/RmlUi/Core.h"
 
 #include "ContextInstancerDefault.h"
+#include "DataViewDefault.h"
 #include "DecoratorTiledBoxInstancer.h"
 #include "DecoratorTiledHorizontalInstancer.h"
 #include "DecoratorTiledImageInstancer.h"
@@ -60,17 +61,28 @@ namespace Rml {
 namespace Core {
 
 // Element instancers.
-typedef UnorderedMap< String, ElementInstancer* > ElementInstancerMap;
+using ElementInstancerMap = UnorderedMap< String, ElementInstancer* >;
 static ElementInstancerMap element_instancers;
 
 // Decorator instancers.
-typedef UnorderedMap< String, DecoratorInstancer* > DecoratorInstancerMap;
+using DecoratorInstancerMap = UnorderedMap< String, DecoratorInstancer* >;
 static DecoratorInstancerMap decorator_instancers;
 
 // Font effect instancers.
-typedef UnorderedMap< String, FontEffectInstancer* > FontEffectInstancerMap;
+using FontEffectInstancerMap = UnorderedMap< String, FontEffectInstancer* >;
 static FontEffectInstancerMap font_effect_instancers;
 
+// Data view instancers.
+using DataViewInstancerMap = UnorderedMap< String, DataViewInstancer* >;
+static DataViewInstancerMap data_view_instancers;
+
+// Structural data view instancers.
+using StructuralDataViewInstancerMap = SmallUnorderedMap< String, DataViewInstancer* >;
+static StructuralDataViewInstancerMap structural_data_view_instancers;
+
+// Structural data view names.
+static StringList structural_data_view_names;
+
 // The context instancer.
 static ContextInstancer* context_instancer = nullptr;;
 
@@ -105,6 +117,15 @@ struct DefaultInstancers {
 	Ptr<FontEffectInstancer> font_effect_glow = std::make_unique<FontEffectGlowInstancer>();
 	Ptr<FontEffectInstancer> font_effect_outline = std::make_unique<FontEffectOutlineInstancer>();
 	Ptr<FontEffectInstancer> font_effect_shadow = std::make_unique<FontEffectShadowInstancer>();
+
+	Ptr<DataViewInstancer> data_view_attribute = std::make_unique<DataViewInstancerDefault< DataViewAttribute >>();
+	Ptr<DataViewInstancer> data_view_class     = std::make_unique<DataViewInstancerDefault< DataViewClass >>();
+	Ptr<DataViewInstancer> data_view_if        = std::make_unique<DataViewInstancerDefault< DataViewIf >>();
+	Ptr<DataViewInstancer> data_view_rml       = std::make_unique<DataViewInstancerDefault< DataViewRml >>();
+	Ptr<DataViewInstancer> data_view_style     = std::make_unique<DataViewInstancerDefault< DataViewStyle >>();
+	Ptr<DataViewInstancer> data_view_text      = std::make_unique<DataViewInstancerDefault< DataViewText >>();
+
+	Ptr<DataViewInstancer> structural_data_view_for = std::make_unique<DataViewInstancerDefault< DataViewFor >>();
 };
 
 static UniquePtr<DefaultInstancers> default_instancers;
@@ -167,6 +188,16 @@ bool Factory::Initialise()
 	XMLParser::RegisterNodeHandler("head", std::make_shared<XMLNodeHandlerHead>());
 	XMLParser::RegisterNodeHandler("template", std::make_shared<XMLNodeHandlerTemplate>());
 
+	// Register the default data views
+	RegisterDataViewInstancer(default_instancers->data_view_attribute.get(), "attr", false);
+	RegisterDataViewInstancer(default_instancers->data_view_class.get(),     "class", false);
+	RegisterDataViewInstancer(default_instancers->data_view_if.get(),        "if", false);
+	RegisterDataViewInstancer(default_instancers->data_view_rml.get(),       "rml", false);
+	RegisterDataViewInstancer(default_instancers->data_view_style.get(),     "style", false);
+	RegisterDataViewInstancer(default_instancers->data_view_text.get(),      "text", false);
+
+	RegisterDataViewInstancer(default_instancers->structural_data_view_for.get(), "for", true);
+
 	return true;
 }
 
@@ -178,6 +209,10 @@ void Factory::Shutdown()
 
 	font_effect_instancers.clear();
 
+	data_view_instancers.clear();
+	structural_data_view_instancers.clear();
+	structural_data_view_names.clear();
+
 	context_instancer = nullptr;
 
 	event_listener_instancer = nullptr;
@@ -493,5 +528,42 @@ EventListener* Factory::InstanceEventListener(const String& value, Element* elem
 	return nullptr;
 }
 
+void Factory::RegisterDataViewInstancer(DataViewInstancer* instancer, const String& name, bool is_structural_view)
+{
+	bool inserted = false;
+	if (is_structural_view)
+	{
+		inserted = structural_data_view_instancers.emplace(name, instancer).second;
+		if (inserted)
+			structural_data_view_names.push_back(name);
+	}
+	else
+	{
+		inserted = data_view_instancers.emplace(name, instancer).second;
+	}
+	
+	if (!inserted)
+		Log::Message(Log::LT_WARNING, "Could not register data view instancer '%s'. The given name is already registered.", name.c_str());
+}
+
+DataViewPtr Factory::InstanceDataView(const String& type_name, Element* element, bool is_structural_view)
+{
+	RMLUI_ASSERT(element);
+
+	if (is_structural_view)
+	{
+		auto it = structural_data_view_instancers.find(type_name);
+		if (it != structural_data_view_instancers.end())
+			return it->second->InstanceView(element);
+	}
+	else
+	{
+		auto it = data_view_instancers.find(type_name);
+		if (it != data_view_instancers.end())
+			return it->second->InstanceView(element);
+	}
+	return nullptr;
+}
+
 }
 }

+ 2 - 0
Source/Core/XMLNodeHandlerDefault.cpp

@@ -73,6 +73,8 @@ bool XMLNodeHandlerDefault::ElementEnd(XMLParser* RMLUI_UNUSED_PARAMETER(parser)
 	RMLUI_UNUSED(parser);
 	RMLUI_UNUSED(name);
 
+	// TODO: Perhaps it's better to submit views from here...
+
 	return true;
 }