Browse Source

LayoutEngine: Move the box generation out of the LayoutBlockBox and pass it in.

Michael Ragazzon 5 years ago
parent
commit
59b7f050d5

+ 17 - 19
Source/Core/LayoutBlockBox.cpp

@@ -40,7 +40,8 @@
 namespace Rml {
 
 // Creates a new block box for rendering a block element.
-LayoutBlockBox::LayoutBlockBox(LayoutBlockBox* _parent, Element* _element, float override_shrink_to_fit_width) : position(0), visible_outer_width(0)
+LayoutBlockBox::LayoutBlockBox(LayoutBlockBox* _parent, Element* _element, const Box& _box, float _min_height, float _max_height) 
+	: position(0), visible_outer_width(0), box(_box), min_height(_min_height), max_height(_max_height)
 {
 	RMLUI_ZoneScoped;
 
@@ -57,38 +58,33 @@ LayoutBlockBox::LayoutBlockBox(LayoutBlockBox* _parent, Element* _element, float
 	vertical_overflow = false;
 
 	// Get our offset root from our parent, if it has one; otherwise, our element is the offset parent.
-	if (parent != nullptr &&
-		parent->offset_root->GetElement() != nullptr)
+	if (parent && parent->offset_root->GetElement())
 		offset_root = parent->offset_root;
 	else
 		offset_root = this;
 
 	// Determine the offset parent for this element.
 	const LayoutBlockBox* self_offset_parent;
-	if (parent != nullptr &&
-		parent->offset_parent->GetElement() != nullptr)
+	if (parent && parent->offset_parent->GetElement())
 		self_offset_parent = parent->offset_parent;
 	else
 		self_offset_parent = this;
 
 	// Determine the offset parent for our children.
-	if (parent != nullptr &&
-		parent->offset_parent->GetElement() != nullptr &&
-		(element == nullptr || element->GetPosition() == Style::Position::Static))
+	if (parent &&
+		parent->offset_parent->GetElement() &&
+		(!element || element->GetPosition() == Style::Position::Static))
 		offset_parent = parent->offset_parent;
 	else
 		offset_parent = this;
 
 	// Build the box for our element, and position it if we can.
-	if (parent != nullptr)
+	if (parent)
 	{
 		space->ImportSpace(*parent->space);
 
-		// Build our box if possible; if not, it will have to be set up manually.
-		LayoutDetails::BuildBox(box, min_height, max_height, parent, element, false, override_shrink_to_fit_width);
-
 		// Position ourselves within our containing block (if we have a valid offset parent).
-		if (parent->GetElement() != nullptr)
+		if (parent->GetElement())
 		{
 			if (self_offset_parent != this)
 			{
@@ -101,7 +97,7 @@ LayoutBlockBox::LayoutBlockBox(LayoutBlockBox* _parent, Element* _element, float
 		}
 	}
 
-	if (element != nullptr)
+	if (element)
 	{
 		const auto& computed = element->GetComputedValues();
 		wrap_content = computed.white_space != Style::WhiteSpace::Nowrap;
@@ -131,6 +127,8 @@ LayoutBlockBox::LayoutBlockBox(LayoutBlockBox* _parent, Element* _element, float
 // Creates a new block box in an inline context.
 LayoutBlockBox::LayoutBlockBox(LayoutBlockBox* _parent) : position(-1, -1)
 {
+	RMLUI_ASSERT(_parent);
+
 	parent = _parent;
 	offset_parent = parent->offset_parent;
 	offset_root = parent->offset_root;
@@ -147,9 +145,9 @@ LayoutBlockBox::LayoutBlockBox(LayoutBlockBox* _parent) : position(-1, -1)
 	box_cursor = 0;
 	vertical_overflow = false;
 
-	LayoutDetails::BuildBox(box, min_height, max_height, parent, nullptr, false);
+	const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent);
+	box.SetContent(Vector2f(containing_block.x, -1));
 	parent->PositionBlockBox(position, box, Style::Clear::None);
-	box.SetContent(Vector2f(box.GetSize(Box::CONTENT).x, -1));
 
 	// Reset the min and max heights; they're not valid for inline block boxes.
 	min_height = 0;
@@ -282,7 +280,7 @@ LayoutBlockBox::CloseResult LayoutBlockBox::Close()
 		}
 
 		// Set the baseline for inline-block elements to the baseline of the last line of the element.
-		// This is a special rule for inline-blocks (see CSS 2.1 §10.8.1).
+		// This is a special rule for inline-blocks (see CSS 2.1 Sec. 10.8.1).
 		if (element->GetDisplay() == Style::Display::InlineBlock)
 		{
 			bool found_baseline = false;
@@ -359,7 +357,7 @@ LayoutInlineBox* LayoutBlockBox::CloseLineBox(LayoutLineBox* child, UniquePtr<La
 }
 
 // Adds a new block element to this block box.
-LayoutBlockBox* LayoutBlockBox::AddBlockElement(Element* element, float override_shrink_to_fit_width)
+LayoutBlockBox* LayoutBlockBox::AddBlockElement(Element* element, const Box& box, float min_height, float max_height)
 {
 	RMLUI_ZoneScoped;
 
@@ -392,7 +390,7 @@ LayoutBlockBox* LayoutBlockBox::AddBlockElement(Element* element, float override
 		}
 	}
 
-	block_boxes.push_back(MakeUnique<LayoutBlockBox>(this, element, override_shrink_to_fit_width));
+	block_boxes.push_back(MakeUnique<LayoutBlockBox>(this, element, box, min_height, max_height));
 	return block_boxes.back().get();
 }
 

+ 8 - 7
Source/Core/LayoutBlockBox.h

@@ -61,8 +61,10 @@ public:
 	/// Creates a new block box for rendering a block element.
 	/// @param parent[in] The parent of this block box. This will be nullptr for the root element.
 	/// @param element[in] The element this block box is laying out.
-	/// @param override_shrink_to_fit_width[in] Provide a fixed shrink-to-fit width instead of formatting the element when its properties allow shrinking.
-	LayoutBlockBox(LayoutBlockBox* parent, Element* element, float override_shrink_to_fit_width = -1);
+	/// @param box[in] The box used for this block box.
+	/// @param min_height[in] The minimum height of the content box.
+	/// @param max_height[in] The maximum height of the content box.
+	LayoutBlockBox(LayoutBlockBox* parent, Element* element, const Box& box, float min_height, float max_height);
 	/// Creates a new block box in an inline context.
 	/// @param parent[in] The parent of this block box.
 	LayoutBlockBox(LayoutBlockBox* parent);
@@ -87,10 +89,11 @@ public:
 
 	/// Adds a new block element to this block-context box.
 	/// @param element[in] The new block element.
-	/// @param placed[in] True if the element is to be placed, false otherwise.
-	/// @param override_shrink_to_fit_width[in] Provide a fixed shrink-to-fit width instead of formatting the element when its properties allow shrinking.
+	/// @param box[in] The box used for the new block box.
+	/// @param min_height[in] The minimum height of the content box.
+	/// @param max_height[in] The maximum height of the content box.
 	/// @return The block box representing the element. Once the element's children have been positioned, Close() must be called on it.
-	LayoutBlockBox* AddBlockElement(Element* element, float override_shrink_to_fit_width = -1);
+	LayoutBlockBox* AddBlockElement(Element* element, const Box& box, float min_height, float max_height);
 	/// Adds a new inline element to this inline-context box.
 	/// @param element[in] The new inline element.
 	/// @param box[in] The box defining the element's bounds.
@@ -153,10 +156,8 @@ public:
 
 
 	/// Returns the block box's dimension box.
-	/// @return The block box's dimension box.
 	Box& GetBox();
 	/// Returns the block box's dimension box.
-	/// @return The block box's dimension box.
 	const Box& GetBox() const;
 
 	void* operator new(size_t size);

+ 36 - 23
Source/Core/LayoutDetails.cpp

@@ -150,27 +150,11 @@ void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* eleme
 // Generates the box for an element placed in a block box.
 void LayoutDetails::BuildBox(Box& box, float& min_height, float& max_height, LayoutBlockBox* containing_box, Element* element, bool inline_element, float override_shrink_to_fit_width)
 {
-	Vector2f containing_block = GetContainingBlock(containing_box);
-	BuildBox(box, containing_block, element, inline_element, override_shrink_to_fit_width);
+	Vector2f containing_block = LayoutDetails::GetContainingBlock(containing_box);
 
-	float box_height = box.GetSize().y;
-	if (box_height < 0 && element)
-	{
-		auto& computed = element->GetComputedValues();
-		min_height = ResolveValue(computed.min_height, containing_block.y);
-		max_height = (computed.max_height.value < 0.f ? FLT_MAX : ResolveValue(computed.max_height, containing_block.y));
+	BuildBox(box, containing_block, element, inline_element, override_shrink_to_fit_width);
 
-		if (computed.box_sizing == Style::BoxSizing::BorderBox)
-		{
-			min_height = BorderHeightToContentHeight(min_height, box);
-			max_height = BorderHeightToContentHeight(max_height, box);
-		}
-	}
-	else
-	{
-		min_height = box_height;
-		max_height = box_height;
-	}
+	GetMinMaxHeight(min_height, max_height, (element ? &element->GetComputedValues() : nullptr), box, containing_block.y);
 }
 
 // Clamps the width of an element based from its min-width and max-width properties.
@@ -203,9 +187,33 @@ float LayoutDetails::ClampHeight(float height, const ComputedValues& computed, c
 	return Math::Clamp(height, min_height, max_height);
 }
 
+// Generates the box for an element placed in a block box.
+void LayoutDetails::GetMinMaxHeight(float& min_height, float& max_height, const ComputedValues* computed, const Box& box, float containing_block_height)
+{
+	const float box_height = box.GetSize().y;
+	if (box_height < 0 && computed)
+	{
+		min_height = ResolveValue(computed->min_height, containing_block_height);
+		max_height = (computed->max_height.value < 0.f ? FLT_MAX : ResolveValue(computed->max_height, containing_block_height));
+
+		if (computed->box_sizing == Style::BoxSizing::BorderBox)
+		{
+			min_height = BorderHeightToContentHeight(min_height, box);
+			max_height = BorderHeightToContentHeight(max_height, box);
+		}
+	}
+	else
+	{
+		min_height = box_height;
+		max_height = box_height;
+	}
+}
+
 // Returns the fully-resolved, fixed-width and -height containing block from a block box.
 Vector2f LayoutDetails::GetContainingBlock(const LayoutBlockBox* containing_box)
 {
+	RMLUI_ASSERT(containing_box);
+
 	Vector2f containing_block;
 
 	containing_block.x = containing_box->GetBox().GetSize(Box::CONTENT).x;
@@ -234,15 +242,20 @@ Vector2f LayoutDetails::GetContainingBlock(const LayoutBlockBox* containing_box)
 
 float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_block)
 {
-	// First we need to format the element, then we get the shrink-to-fit width based on the largest line or box.
+	RMLUI_ASSERT(element);
 
-	LayoutBlockBox containing_block_box(nullptr, nullptr);
-	containing_block_box.GetBox().SetContent(containing_block);
+	Box box;
+	float min_height, max_height;
+	LayoutDetails::BuildBox(box, containing_block, element, false, containing_block.x);
+	LayoutDetails::GetMinMaxHeight(min_height, max_height, &element->GetComputedValues(), box, containing_block.y);
+
+	// First we need to format the element, then we get the shrink-to-fit width based on the largest line or box.
+	LayoutBlockBox containing_block_box(nullptr, nullptr, Box(containing_block), 0.0f, FLT_MAX);
 
 	// Here we fix the element's width to its containing block so that any content is wrapped at this width.
 	// We can consider to instead set this to infinity and clamp it to the available width later after formatting,
 	// but right now the formatting procedure doesn't work well with such numbers.
-	LayoutBlockBox* block_context_box = containing_block_box.AddBlockElement(element, containing_block.x);
+	LayoutBlockBox* block_context_box = containing_block_box.AddBlockElement(element, box, min_height, max_height);
 
 	// @performance. Some formatting can be simplified, eg. absolute elements do not contribute to the shrink-to-fit width.
 	// Also, children of elements with a fixed width and height don't need to be formatted further.

+ 10 - 0
Source/Core/LayoutDetails.h

@@ -64,16 +64,26 @@ public:
 	/// Clamps the width of an element based from its min-width and max-width properties.
 	/// @param[in] width The width to clamp.
 	/// @param[in] element The element to read the properties from.
+	/// @param[in] box The box to base the calculations on.
 	/// @param[in] containing_block_width The width of the element's containing block.
 	/// @return The clamped width.
 	static float ClampWidth(float width, const ComputedValues& computed, const Box& box, float containing_block_width);
 	/// Clamps the height of an element based from its min-height and max-height properties.
 	/// @param[in] height The height to clamp.
 	/// @param[in] element The element to read the properties from.
+	/// @param[in] box The box to base the calculations on.
 	/// @param[in] containing_block_height The height of the element's containing block.
 	/// @return The clamped height.
 	static float ClampHeight(float height, const ComputedValues& computed, const Box& box, float containing_block_height);
 
+	/// Retrieves the minimum and maximum height of an element's box.
+	/// @param[out] min_height The minimum height of the element's box.
+	/// @param[out] max_height The maximum height of the element's box.
+	/// @param[in] computed The computed values to get the min/max-height values, or nullptr.
+	/// @param[in] box The box to base the calculations on.
+	/// @param[in] containing_block_height The height of the element's containing block.
+	static void GetMinMaxHeight(float& min_height, float& max_height, const ComputedValues* computed, const Box& box, float containing_block_height);
+
 	/// Returns the fully-resolved, fixed-width and -height containing block from a block box.
 	/// @param[in] containing_box The leaf box.
 	/// @return The dimensions of the content area, using the latest fixed dimensions for width and height in the hierarchy.

+ 25 - 11
Source/Core/LayoutEngine.cpp

@@ -49,18 +49,27 @@ struct LayoutChunk
 static Pool< LayoutChunk > layout_chunk_pool(200, true);
 
 // Formats the contents for a root-level element (usually a document or floating element).
-void LayoutEngine::FormatElement(Element* element, Vector2f containing_block)
+void LayoutEngine::FormatElement(Element* element, Vector2f containing_block, const Box* override_initial_box)
 {
+	RMLUI_ASSERT(element);
 #ifdef RMLUI_ENABLE_PROFILING
 	RMLUI_ZoneScopedC(0xB22222);
 	auto name = CreateString(80, "%s %x", element->GetAddress(false, false).c_str(), element);
 	RMLUI_ZoneName(name.c_str(), name.size());
 #endif
 
-	LayoutBlockBox containing_block_box(nullptr, nullptr);
-	containing_block_box.GetBox().SetContent(containing_block);
+	LayoutBlockBox containing_block_box(nullptr, nullptr, Box(containing_block), 0.0f, FLT_MAX);
 
-	LayoutBlockBox* block_context_box = containing_block_box.AddBlockElement(element);
+	Box box;
+	if (override_initial_box)
+		box = *override_initial_box;
+	else
+		LayoutDetails::BuildBox(box, containing_block, element, false);
+
+	float min_height, max_height;
+	LayoutDetails::GetMinMaxHeight(min_height, max_height, &element->GetComputedValues(), box, containing_block.y);
+
+	LayoutBlockBox* block_context_box = containing_block_box.AddBlockElement(element, box, min_height, max_height);
 
 	for (int layout_iteration = 0; layout_iteration < 2; layout_iteration++)
 	{
@@ -129,10 +138,10 @@ bool LayoutEngine::FormatElement(LayoutBlockBox* block_context_box, Element* ele
 	// The element is nothing exceptional, so we treat it as a normal block, inline or replaced element.
 	switch (computed.display)
 	{
-		case Style::Display::Block:       return FormatElementBlock(block_context_box, element); break;
-		case Style::Display::Inline:      return FormatElementInline(block_context_box, element); break;
-		case Style::Display::InlineBlock: return FormatElementInlineBlock(block_context_box, element); break;
-		default: RMLUI_ERROR;
+		case Style::Display::Block:       return FormatElementBlock(block_context_box, element);
+		case Style::Display::Inline:      return FormatElementInline(block_context_box, element);
+		case Style::Display::InlineBlock: return FormatElementInlineBlock(block_context_box, element);
+		case Style::Display::None:        RMLUI_ERROR; /* handled above */ break;
 	}
 
 	return true;
@@ -143,7 +152,11 @@ bool LayoutEngine::FormatElementBlock(LayoutBlockBox* block_context_box, Element
 {
 	RMLUI_ZoneScopedC(0x2F4F4F);
 
-	LayoutBlockBox* new_block_context_box = block_context_box->AddBlockElement(element);
+	Box box;
+	float min_height, max_height;
+	LayoutDetails::BuildBox(box, min_height, max_height, block_context_box, element, false);
+
+	LayoutBlockBox* new_block_context_box = block_context_box->AddBlockElement(element, box, min_height, max_height);
 	if (new_block_context_box == nullptr)
 		return false;
 
@@ -190,9 +203,10 @@ bool LayoutEngine::FormatElementInline(LayoutBlockBox* block_context_box, Elemen
 {
 	RMLUI_ZoneScopedC(0x3F6F6F);
 
+	const Vector2f containing_block = LayoutDetails::GetContainingBlock(block_context_box);
+
 	Box box;
-	float min_height, max_height;
-	LayoutDetails::BuildBox(box, min_height, max_height, block_context_box, element, true);
+	LayoutDetails::BuildBox(box, containing_block, element, true);
 	LayoutInlineBox* inline_box = block_context_box->AddInlineElement(element, box);
 
 	// Format the element's children.

+ 5 - 4
Source/Core/LayoutEngine.h

@@ -30,6 +30,7 @@
 #define RMLUI_CORE_LAYOUTENGINE_H
 
 #include "LayoutBlockBox.h"
+#include "../../Include/RmlUi/Core/Types.h"
 
 namespace Rml {
 
@@ -43,10 +44,10 @@ class LayoutEngine
 {
 public:
 	/// Formats the contents for a root-level element (usually a document, floating or replaced element). Establishes a new block formatting context.
-	/// @param element[in] The element to lay out.
-	/// @param containing_block[in] The size of the containing block.
-	/// @param shrink_to_fit[in] True to shrink the element to the width of its contents.
-	static void FormatElement(Element* element, Vector2f containing_block);
+	/// @param[in] element The element to lay out.
+	/// @param[in] containing_block The size of the containing block.
+	/// @param[in] override_initial_box Optional pointer to a box to override the generated box for the element.
+	static void FormatElement(Element* element, Vector2f containing_block, const Box* override_initial_box = nullptr);
 
 	/// Positions a single element and its children within a block formatting context.
 	/// @param[in] block_context_box The open block box to layout the element in.