Forráskód Böngészése

Make DataViewText take a text element, replacing its text contents with the data binding.

Michael Ragazzon 6 éve
szülő
commit
84e4849244

+ 5 - 4
Include/RmlUi/Core/DataBinding.h

@@ -39,14 +39,15 @@ namespace Core {
 
 class Element;
 class DataModel;
+class ElementText;
 
 
 class DataViewText {
 public:
-	DataViewText(Element* in_parent_element, const String& in_text, size_t index_begin_search = 0);
+	DataViewText(ElementText* in_element, const String& in_text, size_t index_begin_search = 0);
 
 	inline operator bool() const {
-		return !data_entries.empty() && parent_element;
+		return !data_entries.empty() && element;
 	}
 
 	inline bool IsDirty() const {
@@ -56,7 +57,7 @@ public:
 	bool Update(const DataModel& model);
 
 private:
-	String CreateText() const;
+	String BuildText() const;
 
 	struct DataEntry {
 		size_t index = 0; // Index into 'text'
@@ -64,7 +65,7 @@ private:
 		String value;
 	};
 
-	ObserverPtr<Element> parent_element;
+	ObserverPtr<Element> element;
 	String text;
 	std::vector<DataEntry> data_entries;
 	bool is_dirty = false;

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

@@ -42,6 +42,7 @@ class DecoratorInstancer;
 class Element;
 class ElementDocument;
 class ElementInstancer;
+class ElementText;
 class Event;
 class EventInstancer;
 class EventListener;
@@ -103,6 +104,10 @@ public:
 	/// @param[in] text The text to instance the element (or elements) from.
 	/// @return True if the string was parsed without error, false otherwise.
 	static bool InstanceElementText(Element* parent, const String& text);
+	/// Instances a single text element with no text contents.
+	/// @param[in] parent The element any instanced elements will be parented to.
+	/// @return The instanced text element if successful, nullptr otherwise.
+	static ElementText* InstanceElementText(Element* parent);
 	/// Instances an element tree based on the stream.
 	/// @param[in] parent The element the stream elements will be added to.
 	/// @param[in] stream The stream to read the element RML from.

+ 7 - 4
Include/RmlUi/Core/XMLParser.h

@@ -41,6 +41,7 @@ class Element;
 class XMLNodeHandler;
 class URL;
 class DataModel;
+class Context;
 
 /**
 	RmlUi's XML parsing engine. The factory creates an instance of this class for each RML parse.
@@ -76,15 +77,15 @@ public:
 		String tag;
 
 		// Element representing this frame.
-		Element* element;
+		Element* element = nullptr;
 
 		// Handler used for this frame.
-		XMLNodeHandler* node_handler;
+		XMLNodeHandler* node_handler = nullptr;
 
 		// The default handler used for this frame's children.
-		XMLNodeHandler* child_handler;
+		XMLNodeHandler* child_handler = nullptr;
 
-		DataModel* data_model;
+		DataModel* data_model = nullptr;
 	};
 
 	/// Pushes an element handler onto the parse stack for parsing child elements.
@@ -118,6 +119,8 @@ private:
 
 	DataModel* active_data_model;
 
+	Context* root_context;
+
 	// The parser stack.
 	using ParserStack = std::stack< ParseFrame >;
 	ParserStack stack;

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

@@ -163,7 +163,7 @@ form h2
 <tabset id="menu">
 <tab>Welcome</tab>
 <panel id="welcome" data-model="my_model">
-	<p class="title" style="margin-top: 1.8em;">{{hello_world}}</p>
+	<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>
 </panel>
 <tab>Decorators</tab>

+ 10 - 5
Source/Core/DataBinding.cpp

@@ -34,7 +34,7 @@ namespace Rml {
 namespace Core {
 
 
-DataViewText::DataViewText(Element* in_parent_element, const String& in_text, const size_t index_begin_search) : parent_element(in_parent_element->GetObserverPtr())
+DataViewText::DataViewText(ElementText* in_parent_element, const String& in_text, const size_t index_begin_search) : element(in_parent_element->GetObserverPtr())
 {
 	text.reserve(in_text.size());
 
@@ -101,10 +101,15 @@ bool DataViewText::Update(const DataModel& model)
 
 	if (entries_modified)
 	{
-		if (parent_element)
+		if (element)
 		{
-			String rml = CreateText();
-			parent_element->SetInnerRML(rml);
+			RMLUI_ASSERTMSG(rmlui_dynamic_cast<ElementText*>(element.get()), "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.get()))
+			{
+				String new_text = BuildText();
+				text_element->SetText(new_text);
+			}
 		}
 		else
 		{
@@ -117,7 +122,7 @@ bool DataViewText::Update(const DataModel& model)
 	return entries_modified;
 }
 
-String DataViewText::CreateText() const
+String DataViewText::BuildText() const
 {
 	size_t reserve_size = text.size();
 

+ 22 - 0
Source/Core/Factory.cpp

@@ -312,6 +312,28 @@ bool Factory::InstanceElementText(Element* parent, const String& text)
 	return true;
 }
 
+ElementText* Factory::InstanceElementText(Element* parent)
+{
+	XMLAttributes attributes;
+	ElementPtr element = Factory::InstanceElement(parent, "#text", "#text", attributes);
+	if (!element)
+	{
+		Log::Message(Log::LT_ERROR, "Failed to instance text element, instancer returned nullptr.");
+		return nullptr;
+	}
+
+	ElementText* text_element = rmlui_dynamic_cast<ElementText*>(element.get());
+	if (!text_element)
+	{
+		Log::Message(Log::LT_ERROR, "Failed to instance text element. Found type '%s', was expecting a derivative of ElementText.", rmlui_type_name(*element));
+		return nullptr;
+	}
+
+	parent->AppendChild(std::move(element));
+
+	return text_element;
+}
+
 // Instances a element tree based on the stream
 bool Factory::InstanceElementStream(Element* parent, Stream* stream)
 {

+ 13 - 8
Source/Core/XMLNodeHandlerDefault.cpp

@@ -81,25 +81,30 @@ bool XMLNodeHandlerDefault::ElementData(XMLParser* parser, const String& data)
 
 	// Determine the parent
 	Element* parent = parser->GetParseFrame()->element;
+	
+	RMLUI_ASSERT(parent);
 
 	if (DataModel* data_model = parser->GetDataModel())
 	{
-		size_t i_open = data.find("{{", 0);
-
-		if (parent && i_open != String::npos)
+		const size_t i_brackets = data.find("{{", 0);
+		if (i_brackets != String::npos)
 		{
-			DataViewText data_view(parent, data, i_open);
-			if (data_view)
+			ElementText* text_element = Factory::InstanceElementText(parent);
+
+			if(text_element)
 			{
-				data_model->views.AddTextView(std::move(data_view));
-				return true;
+				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);
 }

+ 17 - 17
Source/Core/XMLParser.cpp

@@ -48,12 +48,10 @@ XMLParser::XMLParser(Element* root)
 
 	// Add the first frame.
 	ParseFrame frame;
-	frame.node_handler = nullptr;
-	frame.child_handler = nullptr;
 	frame.element = root;
-	frame.tag = "";
 	stack.push(frame);
 
+	root_context = (root ? root->GetContext() : nullptr);
 	active_handler = nullptr;
 	active_data_model = nullptr;
 
@@ -140,6 +138,22 @@ void XMLParser::HandleElementStart(const String& _name, const XMLAttributes& att
 	// Store the current active handler, so we can use it through this function (as active handler may change)
 	XMLNodeHandler* node_handler = active_handler;
 
+	// Look for the data model attribute
+	if (root_context)
+	{
+		static const String data_model = "data-model";
+		auto it = attributes.find(data_model);
+		if (it != attributes.end())
+		{
+			String data_model = it->second.Get<String>();
+
+			active_data_model = root_context->GetDataModel(data_model);
+
+			if (!active_data_model)
+				Log::Message(Log::LT_WARNING, "Could not locate data model '%s'.", data_model.c_str());
+		}
+	}
+
 	Element* element = nullptr;
 
 	// Get the handler to handle the open tag
@@ -148,20 +162,6 @@ void XMLParser::HandleElementStart(const String& _name, const XMLAttributes& att
 		element = node_handler->ElementStart(this, name, attributes);
 	}
 
-	static const String data_model = "data-model";
-	auto it = attributes.find(data_model);
-	if (element && it != attributes.end())
-	{
-		String data_model = it->second.Get<String>();
-
-		active_data_model = nullptr;
-		if (auto context = element->GetContext())
-			active_data_model = context->GetDataModel( data_model );
-
-		if(!active_data_model)
-			Log::Message(Log::LT_WARNING, "Could not locate data model '%s'.", data_model.c_str());
-	}
-
 	// Push onto the stack
 	ParseFrame frame;
 	frame.node_handler = node_handler;