Browse Source

Render text nodes

Michael Ragazzon 4 weeks ago
parent
commit
e8c592ce47

+ 4 - 0
Source/Core/Element.cpp

@@ -221,6 +221,10 @@ void Element::Render()
 
 
 			OnRender();
 			OnRender();
 		}
 		}
+
+		// TODO(Michael): Offset is not correct. Should we make these nodes part of the stacking context?
+		for (ElementText* child : IterateChildren<ElementText>(true))
+			child->Render(this, GetAbsoluteOffset());
 	}
 	}
 
 
 	// Render all elements in our local stacking context.
 	// Render all elements in our local stacking context.

+ 9 - 6
Source/Core/Layout/BlockContainer.cpp

@@ -189,22 +189,25 @@ LayoutBox* BlockContainer::AddBlockLevelBox(UniquePtr<LayoutBox> block_level_box
 	return block_level_box;
 	return block_level_box;
 }
 }
 
 
-InlineBoxHandle BlockContainer::AddInlineElement(Element* element, const Box& child_box)
+InlineBoxHandle BlockContainer::AddInlineNode(Node* node, const Box& child_box)
 {
 {
 	RMLUI_ZoneScoped;
 	RMLUI_ZoneScoped;
 
 
 	// Inline-level elements need to be added to an inline container, open one if needed.
 	// Inline-level elements need to be added to an inline container, open one if needed.
 	InlineContainer* inline_container = EnsureOpenInlineContainer();
 	InlineContainer* inline_container = EnsureOpenInlineContainer();
 
 
-	InlineBox* inline_box = inline_container->AddInlineElement(element, child_box);
+	InlineBox* inline_box = inline_container->AddInlineNode(node, child_box);
 
 
-	if (element->GetPosition() == Style::Position::Relative)
-		AddRelativeElement(element);
+	if (Element* element = AsIf<Element*>(node))
+	{
+		if (element->GetPosition() == Style::Position::Relative)
+			AddRelativeElement(element);
+	}
 
 
 	return {inline_box};
 	return {inline_box};
 }
 }
 
 
-void BlockContainer::CloseInlineElement(InlineBoxHandle handle)
+void BlockContainer::CloseInlineNode(InlineBoxHandle handle)
 {
 {
 	// If the inline-level element did not generate an inline box, then there is no need to close anything.
 	// If the inline-level element did not generate an inline box, then there is no need to close anything.
 	if (!handle.inline_box)
 	if (!handle.inline_box)
@@ -214,7 +217,7 @@ void BlockContainer::CloseInlineElement(InlineBoxHandle handle)
 	// element in it. However, it is possible that an intermediary block-level element was placed, thereby splitting the
 	// element in it. However, it is possible that an intermediary block-level element was placed, thereby splitting the
 	// inline element into multiple inline containers around the block-level box. If we don't have an open inline
 	// inline element into multiple inline containers around the block-level box. If we don't have an open inline
 	// container at all, open a new one, even if the sole purpose of the new line is to close this inline element.
 	// container at all, open a new one, even if the sole purpose of the new line is to close this inline element.
-	EnsureOpenInlineContainer()->CloseInlineElement(handle.inline_box);
+	EnsureOpenInlineContainer()->CloseInlineNode(handle.inline_box);
 }
 }
 
 
 void BlockContainer::AddBreak()
 void BlockContainer::AddBreak()

+ 8 - 8
Source/Core/Layout/BlockContainer.h

@@ -88,15 +88,15 @@ public:
 	// Adds an element to this block container to be handled as a floating element.
 	// Adds an element to this block container to be handled as a floating element.
 	void AddFloatElement(Element* element, Vector2f visible_overflow_size);
 	void AddFloatElement(Element* element, Vector2f visible_overflow_size);
 
 
-	/// Adds a new inline-level element to this block container.
-	/// @param[in] element The new inline element.
-	/// @param[in] box The box defining the element's bounds.
-	/// @return A handle for the inline element, which must later be submitted to 'CloseInlineElement()'.
+	/// Adds a new inline-level node to this block container.
+	/// @param[in] node The new inline node.
+	/// @param[in] box The box defining the node's bounds.
+	/// @return A handle for the inline box, which must later be submitted to 'CloseInlineNode()'.
 	/// @note Adds a new inline container to this box if needed, which starts a new inline formatting context.
 	/// @note Adds a new inline container to this box if needed, which starts a new inline formatting context.
-	InlineBoxHandle AddInlineElement(Element* element, const Box& box);
-	/// Closes a previously added inline element. This must be called after all its children have been added.
-	/// @param[in] handle A handle previously returned from 'AddInlineElement()'.
-	void CloseInlineElement(InlineBoxHandle handle);
+	InlineBoxHandle AddInlineNode(Node* node, const Box& box);
+	/// Closes a previously added inline node. This must be called after all its children have been added.
+	/// @param[in] handle A handle previously returned from 'AddInlineNode()'.
+	void CloseInlineNode(InlineBoxHandle handle);
 
 
 	// Adds a line-break to this block box.
 	// Adds a line-break to this block box.
 	void AddBreak();
 	void AddBreak();

+ 25 - 15
Source/Core/Layout/BlockFormattingContext.cpp

@@ -144,9 +144,9 @@ UniquePtr<LayoutBox> BlockFormattingContext::Format(ContainerBox* parent_contain
 	for (int layout_iteration = 0; layout_iteration < 3; layout_iteration++)
 	for (int layout_iteration = 0; layout_iteration < 3; layout_iteration++)
 	{
 	{
 		bool all_children_formatted = true;
 		bool all_children_formatted = true;
-		for (int i = 0; i < element->GetNumChildren() && all_children_formatted; i++)
+		for (int i = 0; i < element->GetNumChildNodes() && all_children_formatted; i++)
 		{
 		{
-			if (!FormatBlockContainerChild(container.get(), element->GetChild(i)))
+			if (!FormatBlockContainerChild(container.get(), element->GetChildNode(i)))
 				all_children_formatted = false;
 				all_children_formatted = false;
 		}
 		}
 
 
@@ -177,9 +177,9 @@ bool BlockFormattingContext::FormatBlockBox(BlockContainer* parent_container, El
 
 
 	// Format our children. This may result in scrollbars being added to our formatting context root, then we need to
 	// Format our children. This may result in scrollbars being added to our formatting context root, then we need to
 	// bail out and restart formatting for the current block formatting context.
 	// bail out and restart formatting for the current block formatting context.
-	for (int i = 0; i < element->GetNumChildren(); i++)
+	for (int i = 0; i < element->GetNumChildNodes(); i++)
 	{
 	{
-		if (!FormatBlockContainerChild(container, element->GetChild(i)))
+		if (!FormatBlockContainerChild(container, element->GetChildNode(i)))
 			return false;
 			return false;
 	}
 	}
 
 
@@ -189,28 +189,28 @@ bool BlockFormattingContext::FormatBlockBox(BlockContainer* parent_container, El
 	return true;
 	return true;
 }
 }
 
 
-bool BlockFormattingContext::FormatInlineBox(BlockContainer* parent_container, Element* element)
+bool BlockFormattingContext::FormatInlineBox(BlockContainer* parent_container, Node* node, Element* element_for_style)
 {
 {
 	RMLUI_ZoneScopedC(0x3F6F6F);
 	RMLUI_ZoneScopedC(0x3F6F6F);
-	const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent_container, element->GetPosition()).size;
+	const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent_container, element_for_style->GetPosition()).size;
 
 
 	Box box;
 	Box box;
-	LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::Inline);
-	auto inline_box_handle = parent_container->AddInlineElement(element, box);
+	LayoutDetails::BuildBox(box, containing_block, element_for_style, BuildBoxMode::Inline);
+	InlineBoxHandle inline_box_handle = parent_container->AddInlineNode(node, box);
 
 
 	// Format the element's children.
 	// Format the element's children.
-	for (int i = 0; i < element->GetNumChildren(); i++)
+	for (int i = 0; i < node->GetNumChildNodes(); i++)
 	{
 	{
-		if (!FormatBlockContainerChild(parent_container, element->GetChild(i)))
+		if (!FormatBlockContainerChild(parent_container, node->GetChildNode(i)))
 			return false;
 			return false;
 	}
 	}
 
 
-	parent_container->CloseInlineElement(inline_box_handle);
+	parent_container->CloseInlineNode(inline_box_handle);
 
 
 	return true;
 	return true;
 }
 }
 
 
-bool BlockFormattingContext::FormatBlockContainerChild(BlockContainer* parent_container, Element* element)
+bool BlockFormattingContext::FormatBlockContainerChild(BlockContainer* parent_container, Node* node)
 {
 {
 #ifdef RMLUI_TRACY_PROFILING
 #ifdef RMLUI_TRACY_PROFILING
 	RMLUI_ZoneScoped;
 	RMLUI_ZoneScoped;
@@ -218,6 +218,16 @@ bool BlockFormattingContext::FormatBlockContainerChild(BlockContainer* parent_co
 	RMLUI_ZoneName(name.c_str(), name.size());
 	RMLUI_ZoneName(name.c_str(), name.size());
 #endif
 #endif
 
 
+	if (ElementText* text_node = AsIf<ElementText*>(node))
+	{
+		RMLUI_ASSERTMSG(node->GetParentElement(), "Text nodes must have a parent element.");
+		FormatInlineBox(parent_container, text_node, node->GetParentElement());
+		return true;
+	}
+
+	Element* element = AsIf<Element*>(node);
+	RMLUI_ASSERTMSG(element, "Block formatting contexts can only contain element and text nodes.");
+
 	// Check for special formatting tags.
 	// Check for special formatting tags.
 	if (element->GetTagName() == "br")
 	if (element->GetTagName() == "br")
 	{
 	{
@@ -268,8 +278,8 @@ bool BlockFormattingContext::FormatBlockContainerChild(BlockContainer* parent_co
 		else
 		else
 		{
 		{
 			RMLUI_ASSERT(outer_display == OuterDisplayType::InlineLevel);
 			RMLUI_ASSERT(outer_display == OuterDisplayType::InlineLevel);
-			auto inline_box_handle = parent_container->AddInlineElement(element, element->GetBox());
-			parent_container->CloseInlineElement(inline_box_handle);
+			auto inline_box_handle = parent_container->AddInlineNode(element, element->GetBox());
+			parent_container->CloseInlineNode(inline_box_handle);
 		}
 		}
 
 
 		return true;
 		return true;
@@ -279,7 +289,7 @@ bool BlockFormattingContext::FormatBlockContainerChild(BlockContainer* parent_co
 	switch (display)
 	switch (display)
 	{
 	{
 	case Style::Display::Block: return FormatBlockBox(parent_container, element);
 	case Style::Display::Block: return FormatBlockBox(parent_container, element);
-	case Style::Display::Inline: return FormatInlineBox(parent_container, element);
+	case Style::Display::Inline: return FormatInlineBox(parent_container, element, element);
 	default:
 	default:
 		RMLUI_ERROR; // Should have been handled above.
 		RMLUI_ERROR; // Should have been handled above.
 		break;
 		break;

+ 2 - 2
Source/Core/Layout/BlockFormattingContext.h

@@ -60,11 +60,11 @@ private:
 
 
 	// Format the element as an inline box, including its children.
 	// Format the element as an inline box, including its children.
 	// @return False if the box caused an automatic vertical scrollbar to appear in the block formatting context root, forcing it to be reformatted.
 	// @return False if the box caused an automatic vertical scrollbar to appear in the block formatting context root, forcing it to be reformatted.
-	static bool FormatInlineBox(BlockContainer* parent_container, Element* element);
+	static bool FormatInlineBox(BlockContainer* parent_container, Node* node, Element* element_for_style);
 
 
 	// Determine how to format a child element of a block container, and format it accordingly, possibly including any children.
 	// Determine how to format a child element of a block container, and format it accordingly, possibly including any children.
 	// @return False if the box caused an automatic vertical scrollbar to appear in the block formatting context root, forcing it to be reformatted.
 	// @return False if the box caused an automatic vertical scrollbar to appear in the block formatting context root, forcing it to be reformatted.
-	static bool FormatBlockContainerChild(BlockContainer* parent_container, Element* element);
+	static bool FormatBlockContainerChild(BlockContainer* parent_container, Node* node);
 };
 };
 
 
 } // namespace Rml
 } // namespace Rml

+ 17 - 10
Source/Core/Layout/InlineContainer.cpp

@@ -58,27 +58,34 @@ InlineContainer::InlineContainer(BlockContainer* _parent, float _available_width
 
 
 InlineContainer::~InlineContainer() {}
 InlineContainer::~InlineContainer() {}
 
 
-InlineBox* InlineContainer::AddInlineElement(Element* element, const Box& box)
+InlineBox* InlineContainer::AddInlineNode(Node* node, const Box& box)
 {
 {
-	RMLUI_ASSERT(element);
+	RMLUI_ASSERT(node);
 
 
 	InlineBox* inline_box = nullptr;
 	InlineBox* inline_box = nullptr;
 	InlineLevelBox* inline_level_box = nullptr;
 	InlineLevelBox* inline_level_box = nullptr;
 	InlineBoxBase* parent_box = GetOpenInlineBox();
 	InlineBoxBase* parent_box = GetOpenInlineBox();
 
 
-	if (auto text_element = rmlui_dynamic_cast<ElementText*>(element))
+	if (auto text_node = AsIf<ElementText*>(node))
 	{
 	{
-		inline_level_box = parent_box->AddChild(MakeUnique<InlineLevelBox_Text>(text_element));
+		inline_level_box = parent_box->AddChild(MakeUnique<InlineLevelBox_Text>(text_node));
 	}
 	}
-	else if (box.GetSize().x >= 0.f)
+	else if (auto element = AsIf<Element*>(node))
 	{
 	{
-		inline_level_box = parent_box->AddChild(MakeUnique<InlineLevelBox_Atomic>(parent_box, element, box));
+		if (box.GetSize().x >= 0.f)
+		{
+			inline_level_box = parent_box->AddChild(MakeUnique<InlineLevelBox_Atomic>(parent_box, element, box));
+		}
+		else
+		{
+			auto inline_box_ptr = MakeUnique<InlineBox>(parent_box, element, box);
+			inline_box = inline_box_ptr.get();
+			inline_level_box = parent_box->AddChild(std::move(inline_box_ptr));
+		}
 	}
 	}
 	else
 	else
 	{
 	{
-		auto inline_box_ptr = MakeUnique<InlineBox>(parent_box, element, box);
-		inline_box = inline_box_ptr.get();
-		inline_level_box = parent_box->AddChild(std::move(inline_box_ptr));
+		RMLUI_ERRORMSG("Expected Element or Text node in InlineContainer::AddInlineNode");
 	}
 	}
 
 
 	const float minimum_line_height =
 	const float minimum_line_height =
@@ -114,7 +121,7 @@ InlineBox* InlineContainer::AddInlineElement(Element* element, const Box& box)
 	return inline_box;
 	return inline_box;
 }
 }
 
 
-void InlineContainer::CloseInlineElement(InlineBox* inline_box)
+void InlineContainer::CloseInlineNode(InlineBox* inline_box)
 {
 {
 	if (LineBox* line_box = GetOpenLineBox())
 	if (LineBox* line_box = GetOpenLineBox())
 	{
 	{

+ 8 - 8
Source/Core/Layout/InlineContainer.h

@@ -52,17 +52,17 @@ public:
 	InlineContainer(BlockContainer* parent, float available_width);
 	InlineContainer(BlockContainer* parent, float available_width);
 	~InlineContainer();
 	~InlineContainer();
 
 
-	/// Adds a new inline-level element to this inline-context box.
-	/// @param[in] element The new inline-level element.
-	/// @param[in] box The box defining the element's bounds.
-	/// @return The inline box if one was generated for the elmeent, otherwise nullptr.
+	/// Adds a new inline-level node to this inline-context box.
+	/// @param[in] node The new inline-level node.
+	/// @param[in] box The box defining the node's bounds.
+	/// @return The inline box if one was generated for the node, otherwise nullptr.
 	/// @note Any non-null return value must be closed with a call to CloseInlineElement().
 	/// @note Any non-null return value must be closed with a call to CloseInlineElement().
-	InlineBox* AddInlineElement(Element* element, const Box& box);
+	InlineBox* AddInlineNode(Node* node, const Box& box);
 
 
-	/// Closes the previously added inline box.
+	/// Closes the previously added inline node.
 	/// @param[in] inline_box The box to close.
 	/// @param[in] inline_box The box to close.
-	/// @note Calls to this function should be submitted in reverse order to AddInlineElement().
-	void CloseInlineElement(InlineBox* inline_box);
+	/// @note Calls to this function should be submitted in reverse order to AddInlineNode().
+	void CloseInlineNode(InlineBox* inline_box);
 
 
 	/// Add a break to the last line.
 	/// Add a break to the last line.
 	void AddBreak(float line_height);
 	void AddBreak(float line_height);

+ 1 - 1
Source/Core/Layout/InlineLevelBox.cpp

@@ -127,7 +127,7 @@ void InlineLevelBox::SetInlineBoxSpacing(float _spacing_left, float _spacing_rig
 
 
 String InlineLevelBox::DebugDumpTree(int depth) const
 String InlineLevelBox::DebugDumpTree(int depth) const
 {
 {
-	String value = String(depth * 2, ' ') + DebugDumpNameValue() + " | " + LayoutDetails::GetDebugElementName(GetElement()) + '\n';
+	String value = String(depth * 2, ' ') + DebugDumpNameValue() + " | " + LayoutDetails::GetDebugElementName(GetNode()) + '\n';
 	return value;
 	return value;
 }
 }
 
 

+ 5 - 2
Source/Core/Layout/LayoutDetails.cpp

@@ -309,10 +309,13 @@ void LayoutDetails::GetEdgeSizes(float& margin_a, float& margin_b, float& paddin
 	padding_border_b = Math::Max(0.0f, ResolveValue(computed_size.padding_b, base_value)) + Math::Max(0.0f, computed_size.border_b);
 	padding_border_b = Math::Max(0.0f, ResolveValue(computed_size.padding_b, base_value)) + Math::Max(0.0f, computed_size.border_b);
 }
 }
 
 
-String LayoutDetails::GetDebugElementName(Element* element)
+String LayoutDetails::GetDebugElementName(Node* node)
 {
 {
-	if (!element)
+	if (!node)
 		return "nullptr";
 		return "nullptr";
+	if (AsIf<ElementText*>(node))
+		return "#text";
+	Element* element = As<Element*>(node);
 	if (!element->GetId().empty())
 	if (!element->GetId().empty())
 		return '#' + element->GetId();
 		return '#' + element->GetId();
 	if (auto element_text = rmlui_dynamic_cast<ElementText*>(element))
 	if (auto element_text = rmlui_dynamic_cast<ElementText*>(element))

+ 1 - 1
Source/Core/Layout/LayoutDetails.h

@@ -117,7 +117,7 @@ public:
 	static void GetEdgeSizes(float& margin_a, float& margin_b, float& padding_border_a, float& padding_border_b,
 	static void GetEdgeSizes(float& margin_a, float& margin_b, float& padding_border_a, float& padding_border_b,
 		const ComputedAxisSize& computed_size, float base_value);
 		const ComputedAxisSize& computed_size, float base_value);
 
 
-	static String GetDebugElementName(Element* element);
+	static String GetDebugElementName(Node* node);
 
 
 	static bool IsScrollContainer(Style::Overflow overflow_x, Style::Overflow overflow_y)
 	static bool IsScrollContainer(Style::Overflow overflow_x, Style::Overflow overflow_y)
 	{
 	{

+ 6 - 1
Source/Core/StyleSheetSelector.cpp

@@ -352,7 +352,12 @@ bool IsSelectorApplicable(const Element* element, const StructuralSelector& sele
 	break;
 	break;
 	case StructuralSelectorType::Empty:
 	case StructuralSelectorType::Empty:
 	{
 	{
-		return element->GetNumChildren() == 0;
+		for (Node* node : element->IterateChildren<Node>())
+		{
+			if (AsIf<Element*>(node) || AsIf<ElementText*>(node))
+				return false;
+		}
+		return true;
 	}
 	}
 	break;
 	break;
 	case StructuralSelectorType::Not:
 	case StructuralSelectorType::Not:

+ 1 - 1
Tests/Source/UnitTests/Element.cpp

@@ -167,7 +167,7 @@ TEST_CASE("Element")
 	context->Render();
 	context->Render();
 
 
 	Element* div = document->GetFirstElementChild();
 	Element* div = document->GetFirstElementChild();
-	Element* span = div->GetChild(1);
+	Element* span = div->GetFirstElementChild();
 	REQUIRE(div);
 	REQUIRE(div);
 	REQUIRE(div->GetTagName() == "div");
 	REQUIRE(div->GetTagName() == "div");
 	REQUIRE(span);
 	REQUIRE(span);

+ 3 - 3
Tests/Source/UnitTests/XMLParser.cpp

@@ -136,10 +136,10 @@ TEST_CASE("XMLParser.escaping_tags")
 
 
 	TestsShell::RenderLoop();
 	TestsShell::RenderLoop();
 
 
-	CHECK(document->GetNumChildren() == 1);
-	CHECK(document->GetFirstElementChild()->GetTagName() == "#text");
+	CHECK(document->GetNumChildNodes() == 1);
+	CHECK(AsIf<ElementText*>(document->GetFirstChild()) != nullptr);
 	// Text-access should yield decoded value, while RML-access should yield encoded value
 	// Text-access should yield decoded value, while RML-access should yield encoded value
-	CHECK(rmlui_static_cast<ElementText*>(document->GetFirstChild())->GetText() == "<p>&lt;span/&gt;</p>");
+	CHECK(As<ElementText*>(document->GetFirstChild())->GetText() == "<p>&lt;span/&gt;</p>");
 	CHECK(document->GetInnerRML() == "&lt;p&gt;&amp;lt;span/&amp;gt;&lt;/p&gt;");
 	CHECK(document->GetInnerRML() == "&lt;p&gt;&amp;lt;span/&amp;gt;&lt;/p&gt;");
 
 
 	document->Close();
 	document->Close();