فهرست منبع

Make ElementText direct child of Node [wip]

Michael Ragazzon 1 ماه پیش
والد
کامیت
2479218d0d

+ 3 - 0
Include/RmlUi/Config/Config.h

@@ -45,6 +45,7 @@
 	#include <list>
 	#include <map>
 	#include <memory>
+	#include <optional>
 	#include <queue>
 	#include <stack>
 	#include <string>
@@ -93,6 +94,8 @@ template <typename Key, typename Value>
 using StableUnorderedMap = std::unordered_map<Key, Value>;
 template <typename Key, typename Value>
 using UnorderedMultimap = std::unordered_multimap<Key, Value>;
+template <typename T>
+using Optional = std::optional<T>;
 
 	#ifdef RMLUI_NO_THIRDPARTY_CONTAINERS
 template <typename Key, typename Value>

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

@@ -623,11 +623,6 @@ protected:
 	/// @param[in] child The element that has been removed. This may be this element.
 	virtual void OnChildRemove(Element* child);
 
-	/// Forces a re-layout of this element, and any other elements required.
-	virtual void DirtyLayout();
-	/// Returns true if the element has been marked as needing a re-layout.
-	virtual bool IsLayoutDirty();
-
 	/// Returns the RML of this element and all children.
 	/// @param[out] content The content of this element and those under it, in XML form.
 	virtual void GetRML(String& content);

+ 11 - 8
Include/RmlUi/Core/ElementText.h

@@ -39,11 +39,11 @@ namespace Rml {
     @author Peter Curry
  */
 
-class RMLUICORE_API ElementText final : public Element {
+class RMLUICORE_API ElementText final : public Node {
 public:
-	RMLUI_RTTI_DefineWithParent(ElementText, Element)
+	RMLUI_RTTI_DefineWithParent(ElementText, Node)
 
-	ElementText(const String& tag);
+	ElementText();
 	virtual ~ElementText();
 
 	/// Sets the raw string this text element contains. The actual rendered text may be different due to whitespace formatting.
@@ -78,6 +78,8 @@ public:
 	/// Prevents the element from dirtying its document's layout when its text is changed.
 	void SuppressAutoLayout();
 
+	void OverrideColour(Optional<Colourb> override_color);
+
 	// Used to store the position and length of each line we have geometry for.
 	struct Line {
 		Line(String text, Vector2f position) : text(std::move(text)), position(position), width(0) {}
@@ -86,17 +88,17 @@ public:
 		int width;
 	};
 
+	const ComputedValues& GetComputedValues() const;
+
 	using LineList = Vector<Line>;
 
 	// Returns the current list of lines.
 	const LineList& GetLines() const { return lines; }
 
-protected:
-	void OnRender() override;
-
-	void OnPropertyChange(const PropertyIdSet& properties) override;
+	/// Called when properties on the parent element is changed.
+	void OnParentPropertyChange(Element* parent, const PropertyIdSet& changed_properties);
 
-	void GetRML(String& content) override;
+	void Render(Element* parent, Vector2f translation);
 
 private:
 	// Prepares the font effects this element uses for its font.
@@ -120,6 +122,7 @@ private:
 	// The decoration geometry we've generated for this string.
 	UniquePtr<Geometry> decoration;
 
+	Optional<Colourb> override_colour;
 	ColourbPremultiplied colour;
 	float opacity;
 

+ 3 - 0
Include/RmlUi/Core/Node.h

@@ -304,6 +304,9 @@ protected:
 	virtual void OnChildNodeRemove(Node* child, bool dom_node);
 	virtual void OnParentChange(Node* parent);
 
+	virtual void DirtyLayout();
+	virtual bool IsLayoutDirty();
+
 private:
 	void SetParent(Node* new_parent);
 

+ 2 - 1
Source/Core/DataViewDefault.cpp

@@ -396,7 +396,8 @@ bool DataViewText::Update(DataModel& model)
 			if (SystemInterface* system_interface = GetSystemInterface())
 				system_interface->TranslateString(text, new_text);
 
-			rmlui_static_cast<ElementText*>(element)->SetText(text);
+			// TODO(Michael)
+			// rmlui_static_cast<ElementText*>(element)->SetText(text);
 		}
 		else
 		{

+ 3 - 13
Source/Core/Element.cpp

@@ -1761,6 +1761,9 @@ void Element::OnPropertyChange(const PropertyIdSet& changed_properties)
 	{
 		dirty_transition = true;
 	}
+
+	for (ElementText* text : IterateChildren<ElementText>())
+		text->OnParentPropertyChange(this, changed_properties);
 }
 
 void Element::OnPseudoClassChange(const String& /*pseudo_class*/, bool /*activate*/) {}
@@ -1769,19 +1772,6 @@ void Element::OnChildAdd(Element* /*child*/) {}
 
 void Element::OnChildRemove(Element* /*child*/) {}
 
-void Element::DirtyLayout()
-{
-	if (Element* document = GetOwnerDocument())
-		document->DirtyLayout();
-}
-
-bool Element::IsLayoutDirty()
-{
-	if (Element* document = GetOwnerDocument())
-		return document->IsLayoutDirty();
-	return false;
-}
-
 Element* Element::GetClosestScrollableContainer()
 {
 	using namespace Style;

+ 41 - 28
Source/Core/ElementText.cpp

@@ -125,8 +125,8 @@ void LogMissingFontFace(Element* element)
 	}
 }
 
-ElementText::ElementText(const String& tag) :
-	Element(tag), colour(255, 255, 255), opacity(1), font_handle_version(0), geometry_dirty(true), dirty_layout_on_change(true),
+ElementText::ElementText() :
+	Node(), colour(255, 255, 255), opacity(1), font_handle_version(0), geometry_dirty(true), dirty_layout_on_change(true),
 	generated_decoration(Style::TextDecoration::None), decoration_property(Style::TextDecoration::None), font_effects_dirty(true),
 	font_effects_handle(0)
 {}
@@ -149,11 +149,12 @@ const String& ElementText::GetText() const
 	return text;
 }
 
-void ElementText::OnRender()
+void ElementText::Render(Element* parent, Vector2f translation)
 {
 	RMLUI_ZoneScoped;
+	RMLUI_ASSERT(parent);
 
-	FontFaceHandle font_face_handle = GetFontFaceHandle();
+	FontFaceHandle font_face_handle = parent->GetFontFaceHandle();
 	if (font_face_handle == 0)
 		return;
 
@@ -197,8 +198,6 @@ void ElementText::OnRender()
 		generated_decoration = decoration_property;
 	}
 
-	const Vector2f translation = GetAbsoluteOffset();
-
 	bool render = true;
 
 	// Do a visibility test against the scissor region to avoid unnecessary render calls. Instead of handling
@@ -207,9 +206,9 @@ void ElementText::OnRender()
 	if (!scissor_region.Valid())
 		scissor_region = Rectanglei::FromSize(render_manager.GetViewport());
 
-	if (!GetTransformState() || !GetTransformState()->GetTransform())
+	if (!parent->GetTransformState() || !parent->GetTransformState()->GetTransform())
 	{
-		const FontMetrics& font_metrics = GetFontEngineInterface()->GetFontMetrics(GetFontFaceHandle());
+		const FontMetrics& font_metrics = GetFontEngineInterface()->GetFontMetrics(parent->GetFontFaceHandle());
 		const int ascent = Math::RoundUpToInteger(font_metrics.ascent);
 		const int descent = Math::RoundUpToInteger(font_metrics.descent);
 
@@ -243,7 +242,10 @@ bool ElementText::GenerateLine(String& line, int& line_length, float& line_width
 	RMLUI_ZoneScoped;
 	RMLUI_ASSERT(maximum_line_width >= 0.f);
 
-	FontFaceHandle font_face_handle = GetFontFaceHandle();
+	Element* parent = GetParentElement();
+	RMLUI_ASSERT(parent);
+
+	FontFaceHandle font_face_handle = parent->GetFontFaceHandle();
 
 	// Initialise the output variables.
 	line.clear();
@@ -253,13 +255,13 @@ bool ElementText::GenerateLine(String& line, int& line_length, float& line_width
 	// Bail if we don't have a valid font face.
 	if (font_face_handle == 0)
 	{
-		LogMissingFontFace(GetParentElement() ? GetParentElement() : this);
+		LogMissingFontFace(parent);
 		return true;
 	}
 
 	// Determine how we are processing white-space while formatting the text.
 	using namespace Style;
-	const auto& computed = GetComputedValues();
+	const auto& computed = parent->GetComputedValues();
 	const WhiteSpace white_space_property = computed.white_space();
 	const bool collapse_white_space =
 		(white_space_property == WhiteSpace::Normal || white_space_property == WhiteSpace::Nowrap || white_space_property == WhiteSpace::Preline);
@@ -384,22 +386,35 @@ void ElementText::SuppressAutoLayout()
 	dirty_layout_on_change = false;
 }
 
-void ElementText::OnPropertyChange(const PropertyIdSet& changed_properties)
+void ElementText::OverrideColour(Optional<Colourb> new_override_colour)
 {
-	RMLUI_ZoneScoped;
+	override_colour = new_override_colour;
+	geometry_dirty = true;
+	generated_decoration = Style::TextDecoration::None;
+}
 
-	Element::OnPropertyChange(changed_properties);
+const ComputedValues& ElementText::GetComputedValues() const
+{
+	Element* parent_element = GetParentElement();
+	RMLUI_ASSERT(parent_element);
+	return parent_element->GetComputedValues();
+}
+
+void ElementText::OnParentPropertyChange(Element* parent, const PropertyIdSet& changed_properties)
+{
+	RMLUI_ZoneScoped;
+	RMLUI_ASSERT(parent);
 
 	bool colour_changed = false;
 	bool font_face_changed = false;
-	auto& computed = GetComputedValues();
+	auto& computed = parent->GetComputedValues();
 
 	if (changed_properties.Contains(PropertyId::Color) || changed_properties.Contains(PropertyId::Opacity))
 	{
 		const float new_opacity = computed.opacity();
 		const bool opacity_changed = opacity != new_opacity;
 
-		ColourbPremultiplied new_colour = computed.color().ToPremultiplied(new_opacity);
+		ColourbPremultiplied new_colour = override_colour.value_or(computed.color()).ToPremultiplied(new_opacity);
 		colour_changed = colour != new_colour;
 
 		if (colour_changed)
@@ -468,16 +483,12 @@ void ElementText::OnPropertyChange(const PropertyIdSet& changed_properties)
 	}
 }
 
-void ElementText::GetRML(String& content)
-{
-	content += StringUtilities::EncodeRml(text);
-}
-
 bool ElementText::UpdateFontEffects()
 {
 	RMLUI_ZoneScoped;
-
-	if (GetFontFaceHandle() == 0)
+	Element* parent = GetParentElement();
+	RMLUI_ASSERT(parent);
+	if (parent->GetFontFaceHandle() == 0)
 		return false;
 
 	font_effects_dirty = false;
@@ -486,16 +497,16 @@ bool ElementText::UpdateFontEffects()
 
 	// Fetch the font-effect for this text element
 	const FontEffectList* font_effects = &empty_font_effects;
-	if (GetComputedValues().has_font_effect())
+	if (parent->GetComputedValues().has_font_effect())
 	{
-		if (const Property* p = GetProperty(PropertyId::FontEffect))
+		if (const Property* p = parent->GetProperty(PropertyId::FontEffect))
 			if (FontEffectsPtr effects = p->Get<FontEffectsPtr>())
 				font_effects = &effects->list;
 	}
 
 	// Request a font layer configuration to match this set of effects. If this is different from
 	// our old configuration, then return true to indicate we'll need to regenerate geometry.
-	FontEffectsHandle new_font_effects_handle = GetFontEngineInterface()->PrepareFontEffects(GetFontFaceHandle(), *font_effects);
+	FontEffectsHandle new_font_effects_handle = GetFontEngineInterface()->PrepareFontEffects(parent->GetFontFaceHandle(), *font_effects);
 	if (new_font_effects_handle != font_effects_handle)
 	{
 		font_effects_handle = new_font_effects_handle;
@@ -508,10 +519,12 @@ bool ElementText::UpdateFontEffects()
 void ElementText::GenerateGeometry(RenderManager& render_manager, const FontFaceHandle font_face_handle)
 {
 	RMLUI_ZoneScopedC(0xD2691E);
+	Element* parent = GetParentElement();
+	RMLUI_ASSERT(parent);
 
-	const TextOverflowResolved text_overflow = ResolveTextOverflow(GetParentElement(), font_face_handle);
+	const TextOverflowResolved text_overflow = ResolveTextOverflow(parent, font_face_handle);
 
-	const auto& computed = GetComputedValues();
+	const auto& computed = parent->GetComputedValues();
 	const TextShapingContext text_shaping_context{computed.language(), computed.direction(), computed.font_kerning(), computed.letter_spacing()};
 
 	TexturedMeshList mesh_list;

+ 23 - 20
Source/Core/Elements/WidgetTextInput.cpp

@@ -422,8 +422,8 @@ void WidgetTextInput::UpdateSelectionColours()
 		colour.blue = 255 - colour.blue;
 	}
 
-	// Set the computed text colour on the element holding the selected text.
-	selected_text_element->SetProperty(PropertyId::Color, Property(colour, Unit::COLOUR));
+	// Override the color of the selected text.
+	selected_text_element->OverrideColour(colour);
 
 	// If the 'background-color' property has been set on the 'selection' element, use that as the
 	// background colour for the selected text. Otherwise, use the inverse of the selected text
@@ -466,16 +466,15 @@ void WidgetTextInput::OnResize()
 {
 	GenerateCursor();
 
-	Vector2f text_position = parent->GetBox().GetPosition(BoxArea::Content);
-	text_element->SetOffset(text_position, parent);
-	selected_text_element->SetOffset(text_position, parent);
+	text_position = parent->GetBox().GetPosition(BoxArea::Content);
 
 	ForceFormattingOnNextLayout();
 }
 
 void WidgetTextInput::OnRender()
 {
-	ElementUtilities::SetClippingRegion(text_element);
+	// TODO(Michael): Need to narrow clipping region to text_element.
+	ElementUtilities::SetClippingRegion(parent);
 
 	Vector2f text_translation = parent->GetAbsoluteOffset() - Vector2f(parent->GetScrollLeft(), parent->GetScrollTop());
 	selection_composition_geometry.Render(text_translation);
@@ -484,6 +483,10 @@ void WidgetTextInput::OnRender()
 	{
 		cursor_geometry.Render(text_translation + cursor_position);
 	}
+
+	// TODO(Michael): Text position should match whatever is done previously in text_element->SetOffset.
+	text_element->Render(parent, text_position);
+	selected_text_element->Render(parent, text_position);
 }
 
 void WidgetTextInput::OnLayout()
@@ -701,7 +704,8 @@ void WidgetTextInput::ProcessEvent(Event& event)
 		if (event.GetTargetElement() == parent)
 		{
 			Vector2f mouse_position = Vector2f(event.GetParameter<float>("mouse_x", 0), event.GetParameter<float>("mouse_y", 0));
-			mouse_position -= text_element->GetAbsoluteOffset();
+			// TODO(Michael): Not sure if this is correct.
+			mouse_position -= parent->GetAbsoluteOffset() + text_position;
 
 			const int cursor_line_index = CalculateLineIndex(mouse_position.y);
 			const int cursor_character_index = CalculateCharacterIndex(cursor_line_index, mouse_position.x);
@@ -1071,7 +1075,7 @@ float WidgetTextInput::GetAlignmentSpecificTextOffset(const Line& line) const
 {
 	// Callback to avoid expensive calculation in the cases where it is not needed.
 	auto RemainingWidth = [this](StringView editable_line_string) {
-		const float total_width = (float)ElementUtilities::GetStringWidth(text_element, editable_line_string);
+		const float total_width = (float)ElementUtilities::GetStringWidth(parent, editable_line_string);
 		return GetAvailableWidth() - total_width;
 	};
 
@@ -1116,7 +1120,7 @@ int WidgetTextInput::CalculateCharacterIndex(int line_index, float position)
 		++it;
 		const int offset = (int)it.offset();
 
-		const float line_width = (float)ElementUtilities::GetStringWidth(text_element, StringView(p_begin, p_begin + offset));
+		const float line_width = (float)ElementUtilities::GetStringWidth(parent, StringView(p_begin, p_begin + offset));
 		if (line_width > position)
 		{
 			if (position - prev_line_width < line_width - position)
@@ -1293,7 +1297,7 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 			if (count > 0)
 			{
 				line_content.append(count, ' ');
-				line_width += ElementUtilities::GetStringWidth(text_element, " ") * (int)count;
+				line_width += ElementUtilities::GetStringWidth(parent, " ") * (int)count;
 				line.editable_length += (int)count;
 				line.size += (int)count;
 				// Consume the hard wrap if we have one on this line, so that it doesn't make its own, empty line.
@@ -1315,7 +1319,7 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 		// the beginning of this line, then this will be empty).
 		if (!pre_selection.empty())
 		{
-			const int width = ElementUtilities::GetStringWidth(text_element, pre_selection);
+			const int width = ElementUtilities::GetStringWidth(parent, pre_selection);
 			text_element->AddLine(line_position + Vector2f{GetAlignmentSpecificTextOffset(line), 0}, String(pre_selection));
 			line_position.x += width;
 		}
@@ -1328,8 +1332,8 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 			// the two neighboring characters from each string and compare the string width with and without kerning, which should be much faster.
 			const Character left_back = StringUtilities::ToCharacter(StringUtilities::SeekBackwardUTF8(left.end() - 1, left.begin()), left.end());
 			const StringView right_front_u8 = StringView(right.begin(), StringUtilities::SeekForwardUTF8(right.begin() + 1, right.end()));
-			const int width_kerning = ElementUtilities::GetStringWidth(text_element, right_front_u8, left_back);
-			const int width_no_kerning = ElementUtilities::GetStringWidth(text_element, right_front_u8, Character::Null);
+			const int width_kerning = ElementUtilities::GetStringWidth(parent, right_front_u8, left_back);
+			const int width_no_kerning = ElementUtilities::GetStringWidth(parent, right_front_u8, Character::Null);
 			return float(width_kerning - width_no_kerning);
 		};
 
@@ -1339,7 +1343,7 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 		{
 			line_position.x += GetKerningBetween(pre_selection, selection);
 
-			const int selection_width = ElementUtilities::GetStringWidth(selected_text_element, selection);
+			const int selection_width = ElementUtilities::GetStringWidth(selected_text_element->GetParentElement(), selection);
 			const bool selection_contains_endline = (selection_begin_index + selection_length > line_begin + line.editable_length);
 			const Vector2f selection_size = {float(selection_width + (selection_contains_endline ? endline_font_width : 0)), line_height};
 			const Vector2f aligned_position = line_position + Vector2f{GetAlignmentSpecificTextOffset(line), 0};
@@ -1368,9 +1372,9 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 		if (!ime_composition.empty())
 		{
 			const bool composition_contains_endline = (ime_composition_end_index > line_begin + line.editable_length);
-			const int composition_width = ElementUtilities::GetStringWidth(text_element, ime_composition);
+			const int composition_width = ElementUtilities::GetStringWidth(parent, ime_composition);
 			const Vector2f composition_position = {
-				float(ElementUtilities::GetStringWidth(text_element, ime_pre_composition)) + GetAlignmentSpecificTextOffset(line),
+				float(ElementUtilities::GetStringWidth(parent, ime_pre_composition)) + GetAlignmentSpecificTextOffset(line),
 				line_position.y - top_to_baseline + line_height - COMPOSITION_UNDERLINE_WIDTH,
 			};
 			Vector2f line_size = {float(composition_width + (composition_contains_endline ? endline_font_width : 0)), COMPOSITION_UNDERLINE_WIDTH};
@@ -1417,7 +1421,7 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 
 void WidgetTextInput::GenerateCursor()
 {
-	cursor_size.x = Math::Round(ElementUtilities::GetDensityIndependentPixelRatio(text_element));
+	cursor_size.x = Math::Round(ElementUtilities::GetDensityIndependentPixelRatio(text_element->GetParentElement()));
 	cursor_size.y = GetLineHeight();
 
 	Colourb color = parent->GetComputedValues().color();
@@ -1440,15 +1444,14 @@ void WidgetTextInput::ForceFormattingOnNextLayout()
 
 void WidgetTextInput::UpdateCursorPosition(bool update_ideal_cursor_position)
 {
-	if (text_element->GetFontFaceHandle() == 0 || lines.empty())
+	if (parent->GetFontFaceHandle() == 0 || lines.empty())
 		return;
 
 	int cursor_line_index = 0, cursor_character_index = 0;
 	GetRelativeCursorIndices(cursor_line_index, cursor_character_index);
 
 	const auto& line = lines[cursor_line_index];
-	const int string_width_pre_cursor =
-		ElementUtilities::GetStringWidth(text_element, StringView(GetValue(), line.value_offset, cursor_character_index));
+	const int string_width_pre_cursor = ElementUtilities::GetStringWidth(parent, StringView(GetValue(), line.value_offset, cursor_character_index));
 	const float alignment_offset = GetAlignmentSpecificTextOffset(line);
 
 	cursor_position = {

+ 2 - 0
Source/Core/Elements/WidgetTextInput.h

@@ -313,6 +313,8 @@ private:
 	Vector2f cursor_position;
 	Vector2f cursor_size;
 	Geometry cursor_geometry;
+
+	Vector2f text_position;
 };
 
 } // namespace Rml

+ 3 - 2
Source/Core/Factory.cpp

@@ -430,8 +430,9 @@ bool Factory::InstanceElementText(Element* parent, const String& in_text)
 		}
 
 		// If we have curly brackets in the text, tag the element so that the appropriate data view (DataViewText) is constructed.
-		if (has_data_expression)
-			text_element->SetAttributes(ElementAttributes{{"data-text", Variant()}});
+		// TODO(Michael)
+		// if (has_data_expression)
+		//	text_element->SetAttributes(ElementAttributes{{"data-text", Variant()}});
 
 		// Unescape any escaped entities or unicode symbols
 		text = StringUtilities::DecodeRml(text);

+ 6 - 5
Source/Core/Layout/InlineLevelBox.cpp

@@ -50,12 +50,12 @@ InlineLevelBox::~InlineLevelBox() {}
 
 void InlineLevelBox::SubmitElementOnLayout()
 {
-	element->OnLayout();
+	GetElement()->OnLayout();
 }
 
 const FontMetrics& InlineLevelBox::GetFontMetrics() const
 {
-	if (FontFaceHandle handle = element->GetFontFaceHandle())
+	if (FontFaceHandle handle = GetElement()->GetFontFaceHandle())
 		return GetFontEngineInterface()->GetFontMetrics(handle);
 
 	// If there is no font face defined then we provide zero'd out font metrics. This situation can affect the layout,
@@ -78,7 +78,7 @@ void InlineLevelBox::SetHeightAndVerticalAlignment(float _height_above_baseline,
 
 	SetHeight(_height_above_baseline, _depth_below_baseline);
 
-	const Style::VerticalAlign vertical_align = element->GetComputedValues().vertical_align();
+	const Style::VerticalAlign vertical_align = GetElement()->GetComputedValues().vertical_align();
 	vertical_align_type = vertical_align.type;
 
 	// Determine the offset from the parent baseline.
@@ -209,7 +209,8 @@ void InlineLevelBox_Text::Submit(const PlacedFragment& placed_fragment)
 	if (principal_box)
 	{
 		element_offset = placed_fragment.position;
-		text_element->SetOffset(placed_fragment.position, placed_fragment.offset_parent);
+		// TODO(Michael): Where should we store the layout settings? Offset should really be part of that. Lines too? Or store fragments?
+		// text_element->SetOffset(placed_fragment.position, placed_fragment.offset_parent);
 		text_element->ClearLines();
 	}
 	else
@@ -227,6 +228,6 @@ String InlineLevelBox_Text::DebugDumpNameValue() const
 
 ElementText* InlineLevelBox_Text::GetTextElement()
 {
-	return rmlui_static_cast<ElementText*>(GetElement());
+	return rmlui_static_cast<ElementText*>(GetNode());
 }
 } // namespace Rml

+ 4 - 3
Source/Core/Layout/InlineLevelBox.h

@@ -68,9 +68,10 @@ public:
 	void operator delete(void* chunk, size_t size);
 
 protected:
-	InlineLevelBox(Element* element) : element(element) { RMLUI_ASSERT(element); }
+	InlineLevelBox(Node* node) : node(node) { RMLUI_ASSERT(node); }
 
-	Element* GetElement() const { return element; }
+	Node* GetNode() const { return node; }
+	Element* GetElement() const { return rmlui_static_cast<Element*>(node); }
 	const FontMetrics& GetFontMetrics() const;
 
 	// Set the height used for inline layout, and the vertical offset relative to our parent box.
@@ -95,7 +96,7 @@ private:
 	float spacing_left = 0.f;  // Left margin-border-padding for inline boxes.
 	float spacing_right = 0.f; // Right margin-border-padding for inline boxes.
 
-	Element* element;
+	Node* node;
 };
 
 /**

+ 13 - 0
Source/Core/Node.cpp

@@ -326,4 +326,17 @@ void Node::SetParent(Node* new_parent)
 	OnParentChange(parent);
 }
 
+void Node::DirtyLayout()
+{
+	if (Element* document = GetOwnerDocument())
+		document->DirtyLayout();
+}
+
+bool Node::IsLayoutDirty()
+{
+	if (Element* document = GetOwnerDocument())
+		return document->IsLayoutDirty();
+	return false;
+}
+
 } // namespace Rml

+ 2 - 2
Source/Core/NodeInstancer.cpp

@@ -69,9 +69,9 @@ NodeInstancerElement::~NodeInstancerElement()
 	}
 }
 
-NodePtr NodeInstancerText::InstanceNode(const String& tag)
+NodePtr NodeInstancerText::InstanceNode(const String& /*tag*/)
 {
-	ElementText* ptr = node_instancer_pools->pool_text_default.AllocateAndConstruct(tag);
+	ElementText* ptr = node_instancer_pools->pool_text_default.AllocateAndConstruct();
 	return NodePtr(ptr);
 }
 

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

@@ -139,7 +139,7 @@ TEST_CASE("XMLParser.escaping_tags")
 	CHECK(document->GetNumChildren() == 1);
 	CHECK(document->GetFirstElementChild()->GetTagName() == "#text");
 	// Text-access should yield decoded value, while RML-access should yield encoded value
-	CHECK(static_cast<ElementText*>(document->GetFirstChild())->GetText() == "<p>&lt;span/&gt;</p>");
+	CHECK(rmlui_static_cast<ElementText*>(document->GetFirstChild())->GetText() == "<p>&lt;span/&gt;</p>");
 	CHECK(document->GetInnerRML() == "&lt;p&gt;&amp;lt;span/&amp;gt;&lt;/p&gt;");
 
 	document->Close();