Browse Source

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

Michael Ragazzon 6 years ago
parent
commit
84e4849244

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

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

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

@@ -42,6 +42,7 @@ class DecoratorInstancer;
 class Element;
 class Element;
 class ElementDocument;
 class ElementDocument;
 class ElementInstancer;
 class ElementInstancer;
+class ElementText;
 class Event;
 class Event;
 class EventInstancer;
 class EventInstancer;
 class EventListener;
 class EventListener;
@@ -103,6 +104,10 @@ public:
 	/// @param[in] text The text to instance the element (or elements) from.
 	/// @param[in] text The text to instance the element (or elements) from.
 	/// @return True if the string was parsed without error, false otherwise.
 	/// @return True if the string was parsed without error, false otherwise.
 	static bool InstanceElementText(Element* parent, const String& text);
 	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.
 	/// Instances an element tree based on the stream.
 	/// @param[in] parent The element the stream elements will be added to.
 	/// @param[in] parent The element the stream elements will be added to.
 	/// @param[in] stream The stream to read the element RML from.
 	/// @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 XMLNodeHandler;
 class URL;
 class URL;
 class DataModel;
 class DataModel;
+class Context;
 
 
 /**
 /**
 	RmlUi's XML parsing engine. The factory creates an instance of this class for each RML parse.
 	RmlUi's XML parsing engine. The factory creates an instance of this class for each RML parse.
@@ -76,15 +77,15 @@ public:
 		String tag;
 		String tag;
 
 
 		// Element representing this frame.
 		// Element representing this frame.
-		Element* element;
+		Element* element = nullptr;
 
 
 		// Handler used for this frame.
 		// Handler used for this frame.
-		XMLNodeHandler* node_handler;
+		XMLNodeHandler* node_handler = nullptr;
 
 
 		// The default handler used for this frame's children.
 		// 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.
 	/// Pushes an element handler onto the parse stack for parsing child elements.
@@ -118,6 +119,8 @@ private:
 
 
 	DataModel* active_data_model;
 	DataModel* active_data_model;
 
 
+	Context* root_context;
+
 	// The parser stack.
 	// The parser stack.
 	using ParserStack = std::stack< ParseFrame >;
 	using ParserStack = std::stack< ParseFrame >;
 	ParserStack stack;
 	ParserStack stack;

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

@@ -163,7 +163,7 @@ form h2
 <tabset id="menu">
 <tabset id="menu">
 <tab>Welcome</tab>
 <tab>Welcome</tab>
 <panel id="welcome" data-model="my_model">
 <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>
 	<p>Data binding demo. We rate this a good old {{rating}}!</p>
 </panel>
 </panel>
 <tab>Decorators</tab>
 <tab>Decorators</tab>

+ 10 - 5
Source/Core/DataBinding.cpp

@@ -34,7 +34,7 @@ namespace Rml {
 namespace Core {
 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());
 	text.reserve(in_text.size());
 
 
@@ -101,10 +101,15 @@ bool DataViewText::Update(const DataModel& model)
 
 
 	if (entries_modified)
 	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
 		else
 		{
 		{
@@ -117,7 +122,7 @@ bool DataViewText::Update(const DataModel& model)
 	return entries_modified;
 	return entries_modified;
 }
 }
 
 
-String DataViewText::CreateText() const
+String DataViewText::BuildText() const
 {
 {
 	size_t reserve_size = text.size();
 	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;
 	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
 // Instances a element tree based on the stream
 bool Factory::InstanceElementStream(Element* parent, Stream* 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
 	// Determine the parent
 	Element* parent = parser->GetParseFrame()->element;
 	Element* parent = parser->GetParseFrame()->element;
+	
+	RMLUI_ASSERT(parent);
 
 
 	if (DataModel* data_model = parser->GetDataModel())
 	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());
 			Log::Message(Log::LT_WARNING, "Could not add data binding view to element '%s'.", parent->GetAddress().c_str());
 		}
 		}
 	}
 	}
 
 
-
 	// Parse the text into the element
 	// Parse the text into the element
 	return Factory::InstanceElementText(parent, data);
 	return Factory::InstanceElementText(parent, data);
 }
 }

+ 17 - 17
Source/Core/XMLParser.cpp

@@ -48,12 +48,10 @@ XMLParser::XMLParser(Element* root)
 
 
 	// Add the first frame.
 	// Add the first frame.
 	ParseFrame frame;
 	ParseFrame frame;
-	frame.node_handler = nullptr;
-	frame.child_handler = nullptr;
 	frame.element = root;
 	frame.element = root;
-	frame.tag = "";
 	stack.push(frame);
 	stack.push(frame);
 
 
+	root_context = (root ? root->GetContext() : nullptr);
 	active_handler = nullptr;
 	active_handler = nullptr;
 	active_data_model = 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)
 	// Store the current active handler, so we can use it through this function (as active handler may change)
 	XMLNodeHandler* node_handler = active_handler;
 	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;
 	Element* element = nullptr;
 
 
 	// Get the handler to handle the open tag
 	// 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);
 		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
 	// Push onto the stack
 	ParseFrame frame;
 	ParseFrame frame;
 	frame.node_handler = node_handler;
 	frame.node_handler = node_handler;