Browse Source

Fix content injection for sibling inline templates, see #838

Previously, the content was always injected into the first of the siblings.
Michael Ragazzon 2 months ago
parent
commit
a214e7124e
2 changed files with 123 additions and 6 deletions
  1. 15 5
      Source/Core/Template.cpp
  2. 108 1
      Tests/Source/UnitTests/Template.cpp

+ 15 - 5
Source/Core/Template.cpp

@@ -111,16 +111,26 @@ Element* Template::ParseTemplate(Element* element)
 {
 	body->Seek(0, SEEK_SET);
 
+	const int num_children_before = element->GetNumChildren();
+
 	XMLParser parser(element);
 	parser.Parse(body.get());
 
-	// If theres an inject attribute on the template,
-	// attempt to find the required element
+	// If there's an inject attribute on the template, attempt to find the required element.
 	if (!content.empty())
 	{
-		Element* content_element = ElementUtilities::GetElementById(element, content);
-		if (content_element)
-			element = content_element;
+		const int num_children_after = element->GetNumChildren();
+
+		// We look through the newly added elements (and only those), and look for the desired id.
+		for (int i = num_children_before; i < num_children_after; ++i)
+		{
+			Element* content_element = ElementUtilities::GetElementById(element->GetChild(i), content);
+			if (content_element)
+			{
+				element = content_element;
+				break;
+			}
+		}
 	}
 
 	return element;

+ 108 - 1
Tests/Source/UnitTests/Template.cpp

@@ -174,7 +174,7 @@ TEST_CASE("template.inline")
 	TestsShell::ShutdownShell();
 }
 
-TEST_CASE("template.inline+inline")
+TEST_CASE("template.inline+inline.unique")
 {
 	static const String document_rml = R"(
 <rml>
@@ -228,3 +228,110 @@ TEST_CASE("template.inline+inline")
 	document->Close();
 	TestsShell::ShutdownShell();
 }
+
+TEST_CASE("template.inline+inline.identical.wrapped")
+{
+	static const String document_rml = R"(
+<rml>
+<head>
+	<link type="text/template" href="/assets/window.rml"/>
+	<link type="text/template" href="/../Tests/Data/UnitTests/template_basic.rml"/>
+	<style>
+		body
+		{
+			top: 100px;
+			left: 200px;
+			width: 600px;
+			height: 450px;
+		}
+		p {
+			border: 1px aqua;
+			padding: 5px;
+			margin: 10px;
+		}
+	</style>
+</head>
+
+<body id="body" class="inline">
+<div id="wrap_t1">
+	<template src="basic">
+		Enable<span id="s1">X</span>
+	</template>
+</div>
+<div id="wrap_t2">
+	<template src="basic">
+		Disable<span id="s2">Y</span>
+	</template>
+</div>
+</body>
+</rml>
+)";
+
+	static const String s1_address = "span#s1 < p#text < div#wrap_t1 < body#body.inline < #root#main";
+	static const String s2_address = "span#s2 < p#text < div#wrap_t2 < body#body.inline < #root#main";
+
+	Context* context = TestsShell::GetContext();
+	REQUIRE(context);
+
+	ElementDocument* document = context->LoadDocumentFromMemory(document_rml);
+	document->Show();
+	TestsShell::RenderLoop();
+
+	CHECK(document->GetElementById("s1")->GetAddress() == s1_address);
+	CHECK(document->GetElementById("s2")->GetAddress() == s2_address);
+	CHECK(StringUtilities::StripWhitespace(document->QuerySelector("#wrap_t1 p#text")->GetInnerRML()) == R"(Enable<span id="s1">X</span>)");
+
+	document->Close();
+	TestsShell::ShutdownShell();
+}
+
+TEST_CASE("template.inline+inline.identical.siblings")
+{
+	static const String document_rml = R"(
+<rml>
+<head>
+	<link type="text/template" href="/assets/window.rml"/>
+	<link type="text/template" href="/../Tests/Data/UnitTests/template_basic.rml"/>
+	<style>
+		body
+		{
+			top: 100px;
+			left: 200px;
+			width: 600px;
+			height: 450px;
+		}
+		p {
+			border: 1px aqua;
+			padding: 5px;
+			margin: 10px;
+		}
+	</style>
+</head>
+
+<body id="body" class="inline">
+<template src="basic">
+	Enable<span id="s1">X</span>
+</template>
+<template src="basic">
+	Disable<span id="s2">Y</span>
+</template>
+</body>
+</rml>
+)";
+
+	Context* context = TestsShell::GetContext();
+	REQUIRE(context);
+
+	ElementDocument* document = context->LoadDocumentFromMemory(document_rml);
+	document->Show();
+	TestsShell::RenderLoop();
+
+	ElementList p_elements;
+	document->GetElementsByTagName(p_elements, "p");
+	REQUIRE(p_elements.size() == 2);
+	CHECK(StringUtilities::StripWhitespace(p_elements[0]->GetInnerRML()) == R"(Enable<span id="s1">X</span>)");
+	CHECK(StringUtilities::StripWhitespace(p_elements[1]->GetInnerRML()) == R"(Disable<span id="s2">Y</span>)");
+
+	document->Close();
+	TestsShell::ShutdownShell();
+}