瀏覽代碼

Set values on data view constructions. Add attribute view.

Michael Ragazzon 6 年之前
父節點
當前提交
4d681b0167

+ 27 - 8
Include/RmlUi/Core/DataBinding.h

@@ -44,16 +44,12 @@ class ElementText;
 
 class DataViewText {
 public:
-	DataViewText(ElementText* in_element, const String& in_text, size_t index_begin_search = 0);
+	DataViewText(const DataModel& model, ElementText* in_element, const String& in_text, size_t index_begin_search = 0);
 
 	inline operator bool() const {
 		return !data_entries.empty() && element;
 	}
 
-	inline bool IsDirty() const {
-		return is_dirty;
-	}
-
 	bool Update(const DataModel& model);
 
 private:
@@ -68,15 +64,35 @@ private:
 	ObserverPtr<Element> element;
 	String text;
 	std::vector<DataEntry> data_entries;
-	bool is_dirty = false;
+};
+
+
+
+class DataViewAttribute {
+public:
+	DataViewAttribute(const DataModel& model, Element* element, const String& attribute_name, const String& value_name);
+
+	inline operator bool() const {
+		return !attribute_name.empty() && element;
+	}
+	bool Update(const DataModel& model);
+
+private:
+	ObserverPtr<Element> element;
+	String attribute_name;
+	String value_name;
 };
 
 
 class DataViews {
 public:
 
-	void AddTextView(DataViewText&& text_view) {
-		text_views.push_back(std::move(text_view));
+	void AddView(DataViewText&& view) {
+		text_views.push_back(std::move(view));
+	}
+
+	void AddView(DataViewAttribute&& view) {
+		attribute_views.push_back(std::move(view));
 	}
 
 	bool Update(const DataModel& model)
@@ -84,11 +100,14 @@ public:
 		bool result = false;
 		for (auto& view : text_views)
 			result |= view.Update(model);
+		for (auto& view : attribute_views)
+			result |= view.Update(model);
 		return result;
 	}
 
 private:
 	std::vector<DataViewText> text_views;
+	std::vector<DataViewAttribute> attribute_views;
 };
 
 

+ 5 - 0
Include/RmlUi/Core/Element.h

@@ -44,6 +44,7 @@ namespace Rml {
 namespace Core {
 
 class Context;
+class DataModel;
 class Decorator;
 class ElementInstancer;
 class EventDispatcher;
@@ -55,6 +56,7 @@ class ElementDefinition;
 class ElementDocument;
 class ElementScroll;
 class ElementStyle;
+class Factory;
 class PropertiesIteratorView;
 class FontFaceHandleDefault;
 class PropertyDictionary;
@@ -672,6 +674,8 @@ private:
 	// The owning document
 	ElementDocument* owner_document;
 
+	// Active data model for this element.
+	DataModel* data_model;
 	// Attributes on this element.
 	ElementAttributes attributes;
 
@@ -738,6 +742,7 @@ private:
 	friend class LayoutInlineBox;
 	friend struct ElementDeleter;
 	friend class ElementScroll;
+	friend class Factory;
 };
 
 }

+ 1 - 0
Samples/basic/databinding/data/databinding.rml

@@ -165,6 +165,7 @@ form h2
 <panel id="welcome" data-model="my_model">
 	<p class="title" style="margin-top: 1.8em;">{{hello_world}}<span style="color: blue;"> Rated: {{rating}}</span></p>
 	<p>Data binding demo. We rate this a good old {{rating}}!</p>
+	<input type="range" name="rating" min="0" max="100" step="1" value="50" data-attr-value="rating"/> 
 </panel>
 <tab>Decorators</tab>
 <panel id="decorators">

+ 0 - 2
Samples/basic/databinding/src/main.cpp

@@ -241,8 +241,6 @@ int main(int RMLUI_UNUSED_PARAMETER(argc), char** RMLUI_UNUSED_PARAMETER(argv))
 	demo_window->GetDocument()->AddEventListener(Rml::Core::EventId::Keydown, demo_window.get());
 	demo_window->GetDocument()->AddEventListener(Rml::Core::EventId::Keyup, demo_window.get());
 
-	my_model.UpdateViews();
-
 	Shell::EventLoop(GameLoop);
 
 	demo_window->Shutdown();

+ 27 - 7
Source/Core/DataBinding.cpp

@@ -34,7 +34,7 @@ namespace Rml {
 namespace Core {
 
 
-DataViewText::DataViewText(ElementText* in_parent_element, const String& in_text, const size_t index_begin_search) : element(in_parent_element->GetObserverPtr())
+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());
 
@@ -72,20 +72,18 @@ DataViewText::DataViewText(ElementText* in_parent_element, const String& in_text
 
 	if (success)
 	{
-		is_dirty = true;
+		Update(model);
 	}
 	else
 	{
 		text.clear();
 		data_entries.clear();
 	}
-
-
 }
 
 bool DataViewText::Update(const DataModel& model)
 {
-	bool entries_modified = is_dirty;
+	bool entries_modified = false;
 
 	for (DataEntry& entry : data_entries)
 	{
@@ -117,8 +115,6 @@ bool DataViewText::Update(const DataModel& model)
 		}
 	}
 
-	is_dirty = false;
-
 	return entries_modified;
 }
 
@@ -147,6 +143,30 @@ String DataViewText::BuildText() const
 }
 
 
+DataViewAttribute::DataViewAttribute(const DataModel& model, Element* element, const String& attribute_name, const String& value_name)
+	: element(element->GetObserverPtr()), attribute_name(attribute_name), value_name(value_name)
+{
+	Update(model);
+}
+
+bool DataViewAttribute::Update(const DataModel& model)
+{
+	bool result = false;
+	String value;
+	if (model.GetValue(value_name, value))
+	{
+		Variant* variant = element->GetAttribute(attribute_name);
+
+		if (!variant || (variant && *variant != value))
+		{
+			element->SetAttribute(attribute_name, value);
+			result = true;
+		}
+	}
+	return result;
+}
+
+
 bool DataModel::GetValue(const String& name, String& out_value) const
 {
 	auto it = bindings.find(name);

+ 1 - 0
Source/Core/Element.cpp

@@ -147,6 +147,7 @@ transform_state(), dirty_transform(false), dirty_perspective(false), dirty_anima
 	clipping_state_dirty = true;
 
 	meta = element_meta_chunk_pool.AllocateAndConstruct(this);
+	data_model = nullptr;
 }
 
 Element::~Element()

+ 84 - 1
Source/Core/Factory.cpp

@@ -239,6 +239,68 @@ ElementPtr Factory::InstanceElement(Element* parent, const String& instancer_nam
 			element->SetAttributes(attributes);
 			ElementUtilities::BindEventAttributes(element.get());
 
+
+			{
+				// Look for the data-model attribute or otherwise copy it from the parent element
+				auto it = attributes.find("data-model");
+				if (it != attributes.end())
+				{
+					String error_msg;
+
+					if (auto context = parent->GetContext())
+					{
+						String name = it->second.Get<String>();
+						if (auto model = context->GetDataModel(name))
+							element->data_model = model;
+						else
+							Log::Message(Log::LT_WARNING, "Could not locate data model '%s'.", name.c_str());
+					}
+					else
+					{
+						Log::Message(Log::LT_WARNING, "Could not add data model to element '%s' because the context is not available.", element->GetAddress().c_str());
+					}
+				}
+				else if (parent && parent->data_model)
+				{
+					element->data_model = parent->data_model;
+				}
+
+				// If we have an active data model, check the attributes for any data bindings
+				if (DataModel* data_model = element->data_model)
+				{
+					for (auto& attribute : attributes)
+					{
+						auto& name = attribute.first;
+
+						if (name.size() > 5 && name[0] == 'd' && name[1] == 'a' && name[2] == 't' && name[3] == 'a' && name[4] == '-')
+						{
+							const size_t data_type_end = name.find('-', 5);
+							if (data_type_end != String::npos)
+							{
+								const String data_type = name.substr(5, data_type_end - 5);
+
+								if (data_type == "attr")
+								{
+									const String attr_bind_name = name.substr(5 + data_type.size() + 1);
+									const String value_bind_name = attribute.second.Get<String>();
+
+									DataViewAttribute data_view(*data_model, element.get(), attr_bind_name, value_bind_name);
+									if (data_view)
+									{
+										data_model->views.AddView(std::move(data_view));
+									}
+									else
+									{
+										Log::Message(Log::LT_WARNING, "Could not add data binding view to element '%s'.", parent->GetAddress().c_str());
+									}
+									
+								}
+							}
+						}
+					}
+				}
+			}
+
 			PluginRegistry::NotifyElementCreate(element.get());
 		}
 
@@ -303,7 +365,28 @@ bool Factory::InstanceElementText(Element* parent, const String& text)
 			return false;
 		}
 
-		text_element->SetText(translated_data);
+		// See if this text element uses data bindings.
+		bool data_view_added = false;
+		if (DataModel* data_model = element->data_model)
+		{
+			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)
+				{
+					data_model->views.AddView(std::move(data_view));
+					data_view_added = true;
+				}
+				else
+				{
+					Log::Message(Log::LT_WARNING, "Could not add data binding view to element '%s'.", parent->GetAddress().c_str());
+				}
+			}
+		}
+
+		if(!data_view_added)
+			text_element->SetText(translated_data);
 
 		// Add to active node.
 		parent->AppendChild(std::move(element));

+ 0 - 21
Source/Core/XMLNodeHandlerDefault.cpp

@@ -84,27 +84,6 @@ bool XMLNodeHandlerDefault::ElementData(XMLParser* parser, const String& data)
 	
 	RMLUI_ASSERT(parent);
 
-	if (DataModel* data_model = parser->GetDataModel())
-	{
-		const size_t i_brackets = data.find("{{", 0);
-		if (i_brackets != String::npos)
-		{
-			ElementText* text_element = Factory::InstanceElementText(parent);
-
-			if(text_element)
-			{
-				DataViewText data_view(text_element, data, i_brackets);
-				if (data_view)
-				{
-					data_model->views.AddTextView(std::move(data_view));
-					return true;
-				}
-			}
-
-			Log::Message(Log::LT_WARNING, "Could not add data binding view to element '%s'.", parent->GetAddress().c_str());
-		}
-	}
-
 	// Parse the text into the element
 	return Factory::InstanceElementText(parent, data);
 }