Browse Source

Better parsing in data-for: Nested for loops now work.

Michael Ragazzon 6 years ago
parent
commit
f9565deb88

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

@@ -84,7 +84,7 @@ class RMLUICORE_API BaseXMLParser
 		bool ReadOpenTag();
 		bool ReadCloseTag();
 		bool ReadAttributes(XMLAttributes& attributes);
-		bool ReadCDATA(const char* terminator = nullptr);
+		bool ReadCDATA(const char* tag_terminator = nullptr, bool only_terminate_at_same_xml_depth = false);
 
 		// Reads from the stream until a complete word is found.
 		// @param[out] word Word thats been found

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

@@ -147,7 +147,7 @@ protected:
 		Container* ptr = static_cast<Container*>(void_ptr);
 		const int index = address.index;
 
-		if (index < 0 && index >= int(ptr->size()))
+		if (index < 0 || index >= int(ptr->size()))
 		{
 			Log::Message(Log::LT_WARNING, "Data array index out of bounds.");
 			return Variable();

+ 6 - 3
Samples/basic/databinding/data/databinding.rml

@@ -169,12 +169,15 @@ form h2
 	<div data-if="good_rating">Thanks for the awesome rating!</div>
 	<h1>{{delightful_invader.name}}</h1>
 	<img data-attr-sprite="delightful_invader.sprite" data-style-image-color="delightful_invader.color"/>
+	<p>
+		<span data-for="i : indices"> {{i}} </span>
+	</p>
 	<div data-for="invader : invaders">
 		<h1>{{invader.name}}</h1>
 		<img data-attr-sprite="invader.sprite" data-style-image-color="invader.color"/>
-	</div>
-	<div data-for="indices">
-		<h1>{{it}}</h1>
+		<p>
+			Numbers: <span data-for="invader.numbers"> {{it}} </span>
+		</p>
 	</div>
 </panel>
 <tab>Decorators</tab>

+ 8 - 5
Samples/basic/databinding/src/main.cpp

@@ -107,6 +107,7 @@ struct Invader {
 	Rml::Core::String name;
 	Rml::Core::String sprite;
 	Rml::Core::String color;
+	std::vector<int> numbers = { 1, 2, 3, 4, 5 };
 };
 
 
@@ -118,9 +119,9 @@ struct MyData {
 	Invader delightful_invader{ "Delightful invader", "icon-invader", "white" };
 
 	std::vector<Invader> invaders = {
-		Invader{"Angry invader", "icon-invader", "red"},
-		Invader{"Harmless invader", "icon-flag", "blue"},
-		Invader{"Hero", "icon-game", "yellow"},
+		Invader{"Angry invader", "icon-invader", "red", {3, 6, 7}},
+		Invader{"Harmless invader", "icon-flag", "blue", {5, 0}},
+		Invader{"Hero", "icon-game", "yellow", {10, 11, 12, 13, 14}},
 	};
 
 	std::vector<int> indices = { 1, 2, 3, 4, 5 };
@@ -146,15 +147,17 @@ bool SetupDataBinding(Rml::Core::Context* context)
 
 	Rml::Core::DataTypeRegister& types = Rml::Core::GetDataTypeRegister();
 
+	auto vector_int_handle = types.RegisterArray<std::vector<int>>();
+
 	auto invader_handle = types.RegisterStruct<Invader>();
 	invader_handle.RegisterMember("name", &Invader::name);
 	invader_handle.RegisterMember("sprite", &Invader::sprite);
 	invader_handle.RegisterMember("color", &Invader::color);
+	invader_handle.RegisterMember("numbers", &Invader::numbers, vector_int_handle);
 
 	my_model.BindStruct("delightful_invader", &my_data.delightful_invader);
 
-	types.RegisterArray<std::vector<int>>();
-	types.RegisterArray<std::vector<Invader>>(invader_handle);
+	auto vector_invader_handle = types.RegisterArray<std::vector<Invader>>(invader_handle);
 
 	my_model.BindArray("indices", &my_data.indices);
 	my_model.BindArray("invaders", &my_data.invaders);

+ 44 - 26
Source/Core/BaseXMLParser.cpp

@@ -246,9 +246,11 @@ bool BaseXMLParser::ReadOpenTag()
 	if (section_opened)
 	{
 		String lcase_tag_name = StringUtilities::ToLower(tag_name);
-		if (treat_content_as_cdata || cdata_tags.find(lcase_tag_name) != cdata_tags.end())
+		bool is_cdata_tag = (cdata_tags.find(lcase_tag_name) != cdata_tags.end());
+
+		if (treat_content_as_cdata || is_cdata_tag)
 		{
-			if (ReadCDATA(lcase_tag_name.c_str()))
+			if (ReadCDATA(lcase_tag_name.c_str(), !is_cdata_tag))
 			{
 				open_tag_depth--;
 				if (!data.empty())
@@ -330,10 +332,10 @@ bool BaseXMLParser::ReadAttributes(XMLAttributes& attributes)
 	}
 }
 
-bool BaseXMLParser::ReadCDATA(const char* terminator)
+bool BaseXMLParser::ReadCDATA(const char* tag_terminator, bool only_terminate_at_same_xml_depth)
 {
 	String cdata;
-	if (terminator == nullptr)
+	if (tag_terminator == nullptr)
 	{
 		FindString((const unsigned char*) "]]>", cdata);
 		data += cdata;
@@ -341,36 +343,52 @@ bool BaseXMLParser::ReadCDATA(const char* terminator)
 	}
 	else
 	{
+		int tag_depth = 1;
+
 		for (;;)
 		{
 			// Search for the next tag opening.
-			if (!FindString((const unsigned char*) "<", cdata))
+			if (!FindString((const unsigned char*)"<", cdata))
+				return false;
+
+			String node_raw;
+			if (!FindString((const unsigned char*)">", node_raw))
 				return false;
 
-			if (PeekString((const unsigned char*) "/", false))
+			String node_stripped = StringUtilities::StripWhitespace(node_raw);
+			bool close_begin = false;
+			bool close_end = false;
+
+			if (!node_stripped.empty())
+			{
+				if (node_stripped.front() == '/')
+					close_begin = true;
+				else if (node_stripped.back() == '/')
+					close_end = true;
+			}
+
+			if (!close_begin && !close_end)
+				tag_depth += 1;
+			else if (close_begin && !close_end)
+				tag_depth -= 1;
+
+			if (close_begin && !close_end && (!only_terminate_at_same_xml_depth || tag_depth == 0))
 			{
-				String tag;
-				if (FindString((const unsigned char*) ">", tag))
+				String tag_name = StringUtilities::StripWhitespace(node_stripped.substr(1));
+
+				if (StringUtilities::ToLower(tag_name) == tag_terminator)
 				{
-					size_t slash_pos = tag.find('/');
-					String tag_name = StringUtilities::StripWhitespace(slash_pos == String::npos ? tag : tag.substr(slash_pos + 1));
-					if (StringUtilities::ToLower(tag_name) == terminator)
-					{
-						data += cdata;
-						return true;
-					}
-					else
-					{
-						cdata += "<";
-						cdata += tag;
-						cdata += ">";
-					}
+					data += cdata;
+					return true;
 				}
-				else
-					cdata += "<";
 			}
-			else
-				cdata += "<";
+
+			if (only_terminate_at_same_xml_depth && tag_depth <= 0)
+			{
+				return false;
+			}
+
+			cdata += '<' + node_raw + '>';
 		}
 	}
 }
@@ -467,7 +485,7 @@ bool BaseXMLParser::PeekString(const unsigned char* string, bool consume)
 
 			if (peek_read - buffer + i >= buffer_used)
 			{
-				// Wierd, seems our buffer is too small, realloc it bigger.
+				// Weird, seems our buffer is too small, realloc it bigger.
 				buffer_size *= 2;
 				int read_offset = (int)(read - buffer);
 				unsigned char* new_buffer = (unsigned char*) realloc(buffer, buffer_size);

+ 1 - 1
Source/Core/DataView.cpp

@@ -237,7 +237,7 @@ DataViewFor::DataViewFor(const DataModel& model, Element* element, const String&
 
 	if (binding_list.empty() || binding_list.size() > 2 || binding_list.front().empty() || binding_list.back().empty())
 	{
-		Log::Message(Log::LT_WARNING, "Invalid syntax for data-for '%s'", in_binding_name.c_str());
+		Log::Message(Log::LT_WARNING, "Invalid syntax in data-for '%s'", in_binding_name.c_str());
 		return;
 	}