Răsfoiți Sursa

Introduce formatting mode, allow disabling the layout cache

Michael Ragazzon 6 luni în urmă
părinte
comite
e855e74f47

+ 2 - 2
Source/Core/ElementScroll.cpp

@@ -66,7 +66,7 @@ void ElementScroll::EnableScrollbar(Orientation orientation, float element_width
 
 
 	// Determine the size of the scrollbar.
 	// Determine the size of the scrollbar.
 	Box box;
 	Box box;
-	LayoutDetails::BuildBox(box, Vector2f(element_width, element_width), scrollbars[orientation].element);
+	LayoutDetails::BuildBox(box, Vector2f(element_width, element_width), scrollbars[orientation].element, BuildBoxMode::UnalignedBlock);
 
 
 	if (orientation == VERTICAL)
 	if (orientation == VERTICAL)
 		scrollbars[orientation].size = box.GetSize(BoxArea::Margin).x;
 		scrollbars[orientation].size = box.GetSize(BoxArea::Margin).x;
@@ -191,7 +191,7 @@ void ElementScroll::FormatScrollbars()
 		CreateCorner();
 		CreateCorner();
 
 
 		Box corner_box;
 		Box corner_box;
-		LayoutDetails::BuildBox(corner_box, Vector2f(containing_block.x), corner);
+		LayoutDetails::BuildBox(corner_box, Vector2f(containing_block.x), corner, BuildBoxMode::UnalignedBlock);
 
 
 		corner_box.SetContent(Vector2f(scrollbars[VERTICAL].size, scrollbars[HORIZONTAL].size));
 		corner_box.SetContent(Vector2f(scrollbars[VERTICAL].size, scrollbars[HORIZONTAL].size));
 		corner->SetBox(corner_box);
 		corner->SetBox(corner_box);

+ 1 - 1
Source/Core/ElementUtilities.cpp

@@ -324,7 +324,7 @@ bool ElementUtilities::PositionElement(Element* element, Vector2f offset, Positi
 	containing_block.y -= parent->GetElementScroll()->GetScrollbarSize(ElementScroll::HORIZONTAL);
 	containing_block.y -= parent->GetElementScroll()->GetScrollbarSize(ElementScroll::HORIZONTAL);
 
 
 	Box box;
 	Box box;
-	LayoutDetails::BuildBox(box, containing_block, element);
+	LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::Block);
 	if (box.GetSize().y < 0.f)
 	if (box.GetSize().y < 0.f)
 		box.SetContent(Vector2f(box.GetSize().x, containing_block.y));
 		box.SetContent(Vector2f(box.GetSize().x, containing_block.y));
 	element->SetBox(box);
 	element->SetBox(box);

+ 2 - 1
Source/Core/Layout/BlockContainer.cpp

@@ -40,7 +40,8 @@ namespace Rml {
 
 
 BlockContainer::BlockContainer(ContainerBox* _parent_container, FloatedBoxSpace* _space, Element* _element, const Box& _box, float _min_height,
 BlockContainer::BlockContainer(ContainerBox* _parent_container, FloatedBoxSpace* _space, Element* _element, const Box& _box, float _min_height,
 	float _max_height) :
 	float _max_height) :
-	ContainerBox(Type::BlockContainer, _element, _parent_container), box(_box), min_height(_min_height), max_height(_max_height), space(_space)
+	ContainerBox(Type::BlockContainer, _element, _parent_container, _parent_container->GetFormattingMode()), box(_box), min_height(_min_height),
+	max_height(_max_height), space(_space)
 {
 {
 	RMLUI_ASSERT(element);
 	RMLUI_ASSERT(element);
 	RMLUI_ASSERT(box.GetSize().x >= 0.f);
 	RMLUI_ASSERT(box.GetSize().x >= 0.f);

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

@@ -128,7 +128,7 @@ UniquePtr<LayoutBox> BlockFormattingContext::Format(ContainerBox* parent_contain
 	if (override_initial_box)
 	if (override_initial_box)
 		box = *override_initial_box;
 		box = *override_initial_box;
 	else
 	else
-		LayoutDetails::BuildBox(box, containing_block, element);
+		LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::ShrinkableBlock, &parent_container->GetFormattingMode());
 
 
 	float min_height, max_height;
 	float min_height, max_height;
 	LayoutDetails::GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);
 	LayoutDetails::GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);
@@ -167,7 +167,7 @@ bool BlockFormattingContext::FormatBlockBox(BlockContainer* parent_container, El
 	const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
 	const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
 
 
 	Box box;
 	Box box;
-	LayoutDetails::BuildBox(box, containing_block, element);
+	LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::ShrinkableBlock, &parent_container->GetFormattingMode());
 	float min_height, max_height;
 	float min_height, max_height;
 	LayoutDetails::GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);
 	LayoutDetails::GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);
 
 

+ 7 - 15
Source/Core/Layout/ContainerBox.cpp

@@ -121,16 +121,6 @@ bool ContainerBox::IsScrollContainer() const
 	return LayoutDetails::IsScrollContainer(overflow_x, overflow_y);
 	return LayoutDetails::IsScrollContainer(overflow_x, overflow_y);
 }
 }
 
 
-bool ContainerBox::IsUnderMaxContentConstraint() const
-{
-	if (parent_container)
-		return parent_container->IsUnderMaxContentConstraint();
-	// TODO: Very hacky
-	if (const Box* box = GetIfBox())
-		return box->GetSize().x >= 10'000.f;
-	return false;
-}
-
 void ContainerBox::ClosePositionedElements()
 void ContainerBox::ClosePositionedElements()
 {
 {
 	// Any relatively positioned elements that we act as containing block for may need to have their positions
 	// Any relatively positioned elements that we act as containing block for may need to have their positions
@@ -184,8 +174,8 @@ void ContainerBox::SetElementBaseline(float element_baseline)
 	element->SetBaseline(element_baseline);
 	element->SetBaseline(element_baseline);
 }
 }
 
 
-ContainerBox::ContainerBox(Type type, Element* element, ContainerBox* parent_container) :
-	LayoutBox(type), element(element), parent_container(parent_container)
+ContainerBox::ContainerBox(Type type, Element* element, ContainerBox* parent_container, const FormattingMode& formatting_mode) :
+	LayoutBox(type), element(element), formatting_mode(formatting_mode), parent_container(parent_container)
 {
 {
 	bool is_absolute_positioning_containing_block = false;
 	bool is_absolute_positioning_containing_block = false;
 	if (element)
 	if (element)
@@ -318,7 +308,8 @@ String RootBox::DebugDumpTree(int depth) const
 	return String(depth * 2, ' ') + "RootBox";
 	return String(depth * 2, ' ') + "RootBox";
 }
 }
 
 
-FlexContainer::FlexContainer(Element* element, ContainerBox* parent_container) : ContainerBox(Type::FlexContainer, element, parent_container)
+FlexContainer::FlexContainer(Element* element, ContainerBox* parent_container) :
+	ContainerBox(Type::FlexContainer, element, parent_container, parent_container->GetFormattingMode())
 {
 {
 	RMLUI_ASSERT(element);
 	RMLUI_ASSERT(element);
 }
 }
@@ -341,7 +332,7 @@ float FlexContainer::GetShrinkToFitWidth() const
 		return box.GetSize().x;
 		return box.GetSize().x;
 
 
 	// Infer shrink-to-fit width from the intrinsic width of the element.
 	// Infer shrink-to-fit width from the intrinsic width of the element.
-	return FlexFormattingContext::GetMaxContentSize(element).x;
+	return FlexFormattingContext::GetMaxContentSize(element, GetFormattingMode()).x;
 }
 }
 
 
 String FlexContainer::DebugDumpTree(int depth) const
 String FlexContainer::DebugDumpTree(int depth) const
@@ -349,7 +340,8 @@ String FlexContainer::DebugDumpTree(int depth) const
 	return String(depth * 2, ' ') + "FlexContainer" + " | " + LayoutDetails::GetDebugElementName(element);
 	return String(depth * 2, ' ') + "FlexContainer" + " | " + LayoutDetails::GetDebugElementName(element);
 }
 }
 
 
-TableWrapper::TableWrapper(Element* element, ContainerBox* parent_container) : ContainerBox(Type::TableWrapper, element, parent_container)
+TableWrapper::TableWrapper(Element* element, ContainerBox* parent_container) :
+	ContainerBox(Type::TableWrapper, element, parent_container, parent_container->GetFormattingMode())
 {
 {
 	RMLUI_ASSERT(element);
 	RMLUI_ASSERT(element);
 }
 }

+ 20 - 7
Source/Core/Layout/ContainerBox.h

@@ -36,6 +36,16 @@
 
 
 namespace Rml {
 namespace Rml {
 
 
+struct FormattingMode {
+	enum class Constraint : uint8_t {
+		None,
+		MaxContent,
+	};
+
+	Constraint constraint = Constraint::None;
+	bool allow_format_independent_cache = true;
+};
+
 /**
 /**
     Abstraction for layout boxes that can act as a containing block.
     Abstraction for layout boxes that can act as a containing block.
 
 
@@ -62,8 +72,8 @@ public:
 
 
 	/// Returns true if this container can have scrollbars enabled, as determined by its overflow properties.
 	/// Returns true if this container can have scrollbars enabled, as determined by its overflow properties.
 	bool IsScrollContainer() const;
 	bool IsScrollContainer() const;
-	/// Returns true if this container is being layed-out under max-content constraint.
-	bool IsUnderMaxContentConstraint() const;
+	/// Returns the currently active formatting mode for this container tree.
+	const FormattingMode& GetFormattingMode() const { return formatting_mode; }
 
 
 	void AssertMatchesParentContainer(ContainerBox* container_box) const
 	void AssertMatchesParentContainer(ContainerBox* container_box) const
 	{
 	{
@@ -73,7 +83,7 @@ public:
 	Element* GetAbsolutePositioningContainingBlockElementForDebug() const { return absolute_positioning_containing_block->element; }
 	Element* GetAbsolutePositioningContainingBlockElementForDebug() const { return absolute_positioning_containing_block->element; }
 
 
 protected:
 protected:
-	ContainerBox(Type type, Element* element, ContainerBox* parent_container);
+	ContainerBox(Type type, Element* element, ContainerBox* parent_container, const FormattingMode& formatting_mode);
 
 
 	/// Checks if we have a new overflow on an auto-scrolling element. If so, our vertical scrollbar will be enabled and
 	/// Checks if we have a new overflow on an auto-scrolling element. If so, our vertical scrollbar will be enabled and
 	/// our block boxes will be destroyed. All content will need to be re-formatted.
 	/// our block boxes will be destroyed. All content will need to be re-formatted.
@@ -114,6 +124,8 @@ private:
 	Style::Overflow overflow_x = Style::Overflow::Visible;
 	Style::Overflow overflow_x = Style::Overflow::Visible;
 	Style::Overflow overflow_y = Style::Overflow::Visible;
 	Style::Overflow overflow_y = Style::Overflow::Visible;
 
 
+	FormattingMode formatting_mode;
+
 	ContainerBox* parent_container = nullptr;
 	ContainerBox* parent_container = nullptr;
 
 
 	// For absolutely positioned boxes we use the first positioned ancestor. We deviate from the CSS specs where they
 	// For absolutely positioned boxes we use the first positioned ancestor. We deviate from the CSS specs where they
@@ -128,9 +140,9 @@ private:
 */
 */
 class RootBox final : public ContainerBox {
 class RootBox final : public ContainerBox {
 public:
 public:
-	RootBox(Vector2f containing_block) : ContainerBox(Type::Root, nullptr, nullptr), box(containing_block) {}
-	RootBox(const Box& box) : ContainerBox(Type::Root, nullptr, nullptr), box(box) {}
-	RootBox(Vector2f containing_block, RootBox* absolute_root) : ContainerBox(Type::Root, nullptr, absolute_root), box(containing_block) {}
+	RootBox(const Box& box, FormattingMode formatting_mode) : ContainerBox(Type::Root, nullptr, nullptr, formatting_mode), box(box) {}
+	RootBox(const Box& box, RootBox* absolute_root) : ContainerBox(Type::Root, nullptr, absolute_root, absolute_root->GetFormattingMode()), box(box)
+	{}
 
 
 	const Box* GetIfBox() const override { return &box; }
 	const Box* GetIfBox() const override { return &box; }
 	String DebugDumpTree(int depth) const override;
 	String DebugDumpTree(int depth) const override;
@@ -193,7 +205,8 @@ class CachedContainer final : public ContainerBox {
 public:
 public:
 	CachedContainer(Element* element, ContainerBox* parent_container, const Box& box, Vector2f visible_overflow_size,
 	CachedContainer(Element* element, ContainerBox* parent_container, const Box& box, Vector2f visible_overflow_size,
 		Optional<float> baseline_of_last_line) :
 		Optional<float> baseline_of_last_line) :
-		ContainerBox(Type::CachedContainer, element, parent_container), box(box), baseline_of_last_line(baseline_of_last_line)
+		ContainerBox(Type::CachedContainer, element, parent_container, parent_container->GetFormattingMode()), box(box),
+		baseline_of_last_line(baseline_of_last_line)
 	{
 	{
 		SetVisibleOverflowSize(visible_overflow_size);
 		SetVisibleOverflowSize(visible_overflow_size);
 	}
 	}

+ 9 - 5
Source/Core/Layout/FlexFormattingContext.cpp

@@ -56,7 +56,7 @@ UniquePtr<LayoutBox> FlexFormattingContext::Format(ContainerBox* parent_containe
 	if (override_initial_box)
 	if (override_initial_box)
 		box = *override_initial_box;
 		box = *override_initial_box;
 	else
 	else
-		LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::Block);
+		LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::ShrinkableBlock, &parent_container->GetFormattingMode());
 
 
 	// Start with any auto-scrollbars off.
 	// Start with any auto-scrollbars off.
 	flex_container_box->ResetScrollbars(box);
 	flex_container_box->ResetScrollbars(box);
@@ -115,11 +115,13 @@ UniquePtr<LayoutBox> FlexFormattingContext::Format(ContainerBox* parent_containe
 	return flex_container_box;
 	return flex_container_box;
 }
 }
 
 
-Vector2f FlexFormattingContext::GetMaxContentSize(Element* element)
+Vector2f FlexFormattingContext::GetMaxContentSize(Element* element, const FormattingMode& formatting_mode)
 {
 {
+	RMLUI_ASSERT(formatting_mode.constraint == FormattingMode::Constraint::MaxContent);
+
 	// A large but finite number is used here, since layouting doesn't always work well with infinities.
 	// A large but finite number is used here, since layouting doesn't always work well with infinities.
 	const Vector2f infinity(10000.0f, 10000.0f);
 	const Vector2f infinity(10000.0f, 10000.0f);
-	RootBox root(infinity);
+	RootBox root(Box(infinity), formatting_mode);
 	auto flex_container_box = MakeUnique<FlexContainer>(element, &root);
 	auto flex_container_box = MakeUnique<FlexContainer>(element, &root);
 
 
 	FlexFormattingContext context;
 	FlexFormattingContext context;
@@ -347,7 +349,8 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector
 		}
 		}
 		else if (main_axis_horizontal)
 		else if (main_axis_horizontal)
 		{
 		{
-			item.inner_flex_base_size = LayoutDetails::GetShrinkToFitWidth(element, flex_content_containing_block);
+			item.inner_flex_base_size =
+				LayoutDetails::GetShrinkToFitWidth(element, flex_content_containing_block, flex_container_box->GetFormattingMode());
 		}
 		}
 		else
 		else
 		{
 		{
@@ -715,7 +718,8 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector
 				{
 				{
 					item.box.SetContent(Vector2f(content_size.x, GetInnerUsedMainSize(item)));
 					item.box.SetContent(Vector2f(content_size.x, GetInnerUsedMainSize(item)));
 					item.hypothetical_cross_size =
 					item.hypothetical_cross_size =
-						LayoutDetails::GetShrinkToFitWidth(item.element, flex_content_containing_block) + item.cross.sum_edges;
+						LayoutDetails::GetShrinkToFitWidth(item.element, flex_content_containing_block, flex_container_box->GetFormattingMode()) +
+						item.cross.sum_edges;
 				}
 				}
 				else
 				else
 				{
 				{

+ 2 - 1
Source/Core/Layout/FlexFormattingContext.h

@@ -37,6 +37,7 @@ namespace Rml {
 class LayoutBox;
 class LayoutBox;
 class ContainerBox;
 class ContainerBox;
 class FlexContainer;
 class FlexContainer;
+struct FormattingMode;
 
 
 /*
 /*
     Formats a flex container element and its flex items according to flexible box (flexbox) layout rules.
     Formats a flex container element and its flex items according to flexible box (flexbox) layout rules.
@@ -47,7 +48,7 @@ public:
 	static UniquePtr<LayoutBox> Format(ContainerBox* parent_container, Element* element, const Box* override_initial_box);
 	static UniquePtr<LayoutBox> Format(ContainerBox* parent_container, Element* element, const Box* override_initial_box);
 
 
 	/// Computes max-content size for a flex container.
 	/// Computes max-content size for a flex container.
-	static Vector2f GetMaxContentSize(Element* element);
+	static Vector2f GetMaxContentSize(Element* element, const FormattingMode& formatting_mode);
 
 
 private:
 private:
 	FlexFormattingContext() = default;
 	FlexFormattingContext() = default;

+ 10 - 6
Source/Core/Layout/FormattingContext.cpp

@@ -100,20 +100,24 @@ UniquePtr<LayoutBox> FormattingContext::FormatIndependent(ContainerBox* parent_c
 
 
 	UniquePtr<LayoutBox> layout_box;
 	UniquePtr<LayoutBox> layout_box;
 
 
-	const bool is_under_max_content_constraint = parent_container->IsUnderMaxContentConstraint();
-	if (type != FormattingContextType::None && !is_under_max_content_constraint)
+	const FormattingMode& formatting_mode = parent_container->GetFormattingMode();
+	if (type != FormattingContextType::None && formatting_mode.constraint == FormattingMode::Constraint::None)
 	{
 	{
 		LayoutNode* layout_node = element->GetLayoutNode();
 		LayoutNode* layout_node = element->GetLayoutNode();
 		if (layout_node->CommittedLayoutMatches(parent_container->GetContainingBlockSize(Style::Position::Static),
 		if (layout_node->CommittedLayoutMatches(parent_container->GetContainingBlockSize(Style::Position::Static),
 				parent_container->GetContainingBlockSize(Style::Position::Static), override_initial_box))
 				parent_container->GetContainingBlockSize(Style::Position::Static), override_initial_box))
 		{
 		{
-			Log::Message(Log::LT_INFO, "Layout cache match on element: %s", element->GetAddress().c_str());
+			Log::Message(Log::LT_INFO, "Layout cache match on element%s: %s",
+				(formatting_mode.allow_format_independent_cache ? "" : " (skipping cache due to formatting mode)"), element->GetAddress().c_str());
 			// TODO: How to deal with ShrinkToFitWidth, in particular for the returned box? Store it in the LayoutNode?
 			// TODO: How to deal with ShrinkToFitWidth, in particular for the returned box? Store it in the LayoutNode?
 			//   Maybe best not to use this committed layout at all during max-content layouting. Instead, skip this here,
 			//   Maybe best not to use this committed layout at all during max-content layouting. Instead, skip this here,
 			//   return zero in the CacheContainerBox, and make a separate LayoutNode cache for shrink-to-fit width that
 			//   return zero in the CacheContainerBox, and make a separate LayoutNode cache for shrink-to-fit width that
 			//   is fetched in LayoutDetails::ShrinkToFitWidth().
 			//   is fetched in LayoutDetails::ShrinkToFitWidth().
-			layout_box = MakeUnique<CachedContainer>(element, parent_container, element->GetBox(),
-				layout_node->GetCommittedLayout()->visible_overflow_size, layout_node->GetCommittedLayout()->baseline_of_last_line);
+			if (formatting_mode.allow_format_independent_cache)
+			{
+				layout_box = MakeUnique<CachedContainer>(element, parent_container, element->GetBox(),
+					layout_node->GetCommittedLayout()->visible_overflow_size, layout_node->GetCommittedLayout()->baseline_of_last_line);
+			}
 		}
 		}
 	}
 	}
 
 
@@ -129,7 +133,7 @@ UniquePtr<LayoutBox> FormattingContext::FormatIndependent(ContainerBox* parent_c
 		}
 		}
 	}
 	}
 
 
-	if (layout_box && !is_under_max_content_constraint)
+	if (layout_box && formatting_mode.constraint == FormattingMode::Constraint::None)
 	{
 	{
 		Optional<float> baseline_of_last_line;
 		Optional<float> baseline_of_last_line;
 		float baseline_of_last_line_value = 0.f;
 		float baseline_of_last_line_value = 0.f;

+ 32 - 14
Source/Core/Layout/LayoutDetails.cpp

@@ -49,8 +49,10 @@ static inline float BorderSizeToContentSize(float border_size, float border_padd
 	return Math::Max(0.0f, border_size - border_padding_edges_size);
 	return Math::Max(0.0f, border_size - border_padding_edges_size);
 }
 }
 
 
-void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* element, BuildBoxMode box_context)
+void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* element, BuildBoxMode box_mode, const FormattingMode* formatting_mode)
 {
 {
+	// A shrinkable block may start formatting, thus the current formatting mode must be provided.
+	RMLUI_ASSERT(box_mode != BuildBoxMode::ShrinkableBlock || formatting_mode != nullptr);
 	if (!element)
 	if (!element)
 	{
 	{
 		box.SetContent(containing_block);
 		box.SetContent(containing_block);
@@ -84,7 +86,7 @@ void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* eleme
 
 
 	// Calculate the content area and constraints. 'auto' width and height are handled later.
 	// Calculate the content area and constraints. 'auto' width and height are handled later.
 	// For inline non-replaced elements, width and height are ignored, so we can skip the calculations.
 	// For inline non-replaced elements, width and height are ignored, so we can skip the calculations.
-	if (box_context == BuildBoxMode::Block || box_context == BuildBoxMode::UnalignedBlock || replaced_element)
+	if (box_mode == BuildBoxMode::Block || box_mode == BuildBoxMode::ShrinkableBlock || box_mode == BuildBoxMode::UnalignedBlock || replaced_element)
 	{
 	{
 		content_area.x = ResolveValueOr(computed.width(), containing_block.x, -1.f);
 		content_area.x = ResolveValueOr(computed.width(), containing_block.x, -1.f);
 		content_area.y = ResolveValueOr(computed.height(), containing_block.y, -1.f);
 		content_area.y = ResolveValueOr(computed.height(), containing_block.y, -1.f);
@@ -125,7 +127,8 @@ void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* eleme
 	box.SetContent(content_area);
 	box.SetContent(content_area);
 
 
 	// Evaluate the margins, and width and height if they are auto.
 	// Evaluate the margins, and width and height if they are auto.
-	BuildBoxSizeAndMargins(box, min_size, max_size, containing_block, element, box_context, replaced_element);
+	BuildBoxSizeAndMargins(box, min_size, max_size, containing_block, element, box_mode, replaced_element,
+		(box_mode == BuildBoxMode::ShrinkableBlock ? formatting_mode : nullptr));
 }
 }
 
 
 void LayoutDetails::GetMinMaxWidth(float& min_width, float& max_width, const ComputedValues& computed, const Box& box, float containing_block_width)
 void LayoutDetails::GetMinMaxWidth(float& min_width, float& max_width, const ComputedValues& computed, const Box& box, float containing_block_width)
@@ -170,12 +173,24 @@ void LayoutDetails::GetDefiniteMinMaxHeight(float& min_height, float& max_height
 	}
 	}
 }
 }
 
 
+void LayoutDetails::BuildAutoMarginsForBlockBox(Box& box, Vector2f containing_block, Element* element)
+{
+	RMLUI_ASSERT(box.GetSize().x >= 0.f && box.GetSize().y >= 0.f);
+	const Vector2f initial_content_size = box.GetSize();
+
+	const Vector2f min_size = {0, 0};
+	const Vector2f max_size = {FLT_MAX, FLT_MAX};
+
+	BuildBoxSizeAndMargins(box, min_size, max_size, containing_block, element, BuildBoxMode::Block, true, nullptr);
+	RMLUI_ASSERT(box.GetSize() == initial_content_size);
+}
+
 void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element,
 void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element,
-	BuildBoxMode box_context, bool replaced_element)
+	BuildBoxMode box_mode, bool replaced_element, const FormattingMode* formatting_mode)
 {
 {
 	const ComputedValues& computed = element->GetComputedValues();
 	const ComputedValues& computed = element->GetComputedValues();
 
 
-	if (box_context == BuildBoxMode::Inline || box_context == BuildBoxMode::UnalignedBlock)
+	if (box_mode == BuildBoxMode::Inline || box_mode == BuildBoxMode::UnalignedBlock)
 	{
 	{
 		// For inline elements, their calculations are straightforward. No worrying about auto margins and dimensions, etc.
 		// For inline elements, their calculations are straightforward. No worrying about auto margins and dimensions, etc.
 		// Evaluate the margins. Any declared as 'auto' will resolve to 0.
 		// Evaluate the margins. Any declared as 'auto' will resolve to 0.
@@ -187,12 +202,12 @@ void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f
 	else
 	else
 	{
 	{
 		// The element is block, so we need to run the box through the ringer to potentially evaluate auto margins and dimensions.
 		// The element is block, so we need to run the box through the ringer to potentially evaluate auto margins and dimensions.
-		BuildBoxWidth(box, computed, min_size.x, max_size.x, containing_block, element, replaced_element);
+		BuildBoxWidth(box, computed, min_size.x, max_size.x, containing_block, element, replaced_element, formatting_mode);
 		BuildBoxHeight(box, computed, min_size.y, max_size.y, containing_block.y);
 		BuildBoxHeight(box, computed, min_size.y, max_size.y, containing_block.y);
 	}
 	}
 }
 }
 
 
-float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_block)
+float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_block, const FormattingMode& current_formatting_mode)
 {
 {
 	RMLUI_ASSERT(element);
 	RMLUI_ASSERT(element);
 
 
@@ -222,11 +237,14 @@ float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_b
 	const float max_content_constraint_width = containing_block.x + 10'000.f;
 	const float max_content_constraint_width = containing_block.x + 10'000.f;
 	box.SetContent({max_content_constraint_width, box.GetSize().y});
 	box.SetContent({max_content_constraint_width, box.GetSize().y});
 
 
+	FormattingMode formatting_mode = current_formatting_mode;
+	formatting_mode.constraint = FormattingMode::Constraint::MaxContent;
+
 	// First, format the element under the above-generated box. Then we ask the resulting box for its shrink-to-fit
 	// First, format the element under the above-generated box. Then we ask the resulting box for its shrink-to-fit
 	// width. For block containers, this is essentially its largest line or child box.
 	// width. For block containers, this is essentially its largest line or child box.
 	// @performance. Some formatting can be simplified, e.g. absolute elements do not contribute to the shrink-to-fit
 	// @performance. Some formatting can be simplified, e.g. 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.
 	// width. Also, children of elements with a fixed width and height don't need to be formatted further.
-	RootBox root(Math::Max(containing_block, Vector2f(0.f)));
+	RootBox root(Box(Math::Max(containing_block, Vector2f(0.f))), formatting_mode);
 	UniquePtr<LayoutBox> layout_box = FormattingContext::FormatIndependent(&root, element, &box, FormattingContextType::Block);
 	UniquePtr<LayoutBox> layout_box = FormattingContext::FormatIndependent(&root, element, &box, FormattingContextType::Block);
 
 
 	float shrink_to_fit_width = layout_box->GetShrinkToFitWidth();
 	float shrink_to_fit_width = layout_box->GetShrinkToFitWidth();
@@ -361,7 +379,7 @@ Vector2f LayoutDetails::CalculateSizeForReplacedElement(const Vector2f specified
 }
 }
 
 
 void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, float min_width, float max_width, Vector2f containing_block,
 void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, float min_width, float max_width, Vector2f containing_block,
-	Element* element, bool replaced_element, float override_shrink_to_fit_width)
+	Element* element, bool replaced_element, const FormattingMode* formatting_mode, float override_shrink_to_fit_width)
 {
 {
 	RMLUI_ZoneScoped;
 	RMLUI_ZoneScoped;
 
 
@@ -403,15 +421,15 @@ void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, floa
 	{
 	{
 		// Apply the shrink-to-fit algorithm here to find the width of the element.
 		// Apply the shrink-to-fit algorithm here to find the width of the element.
 		// See CSS 2.1 section 10.3.7 for when this should be applied.
 		// See CSS 2.1 section 10.3.7 for when this should be applied.
-		const bool shrink_to_fit = !replaced_element &&
+		const bool shrink_to_fit = formatting_mode != nullptr && !replaced_element &&
 			((computed.float_() != Style::Float::None) || (absolutely_positioned && inset_auto) ||
 			((computed.float_() != Style::Float::None) || (absolutely_positioned && inset_auto) ||
 				(computed.display() == Style::Display::InlineBlock || computed.display() == Style::Display::InlineFlex));
 				(computed.display() == Style::Display::InlineBlock || computed.display() == Style::Display::InlineFlex));
 
 
 		if (!shrink_to_fit)
 		if (!shrink_to_fit)
 		{
 		{
 			// The width is set to whatever remains of the containing block.
 			// The width is set to whatever remains of the containing block.
-			content_area.x = containing_block.x - (GetInsetWidth() + box.GetSizeAcross(BoxDirection::Horizontal, BoxArea::Margin, BoxArea::Padding));
-			content_area.x = Math::Max(0.0f, content_area.x);
+			const float accumulated_edges = GetInsetWidth() + box.GetSizeAcross(BoxDirection::Horizontal, BoxArea::Margin, BoxArea::Padding);
+			content_area.x = Math::Max(containing_block.x - accumulated_edges, 0.f);
 		}
 		}
 		else if (override_shrink_to_fit_width >= 0)
 		else if (override_shrink_to_fit_width >= 0)
 		{
 		{
@@ -419,7 +437,7 @@ void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, floa
 		}
 		}
 		else
 		else
 		{
 		{
-			content_area.x = GetShrinkToFitWidth(element, containing_block);
+			content_area.x = GetShrinkToFitWidth(element, containing_block, *formatting_mode);
 			override_shrink_to_fit_width = content_area.x;
 			override_shrink_to_fit_width = content_area.x;
 		}
 		}
 	}
 	}
@@ -444,7 +462,7 @@ void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, floa
 		box.SetContent(content_area);
 		box.SetContent(content_area);
 
 
 		if (num_auto_margins > 0)
 		if (num_auto_margins > 0)
-			BuildBoxWidth(box, computed, min_width, max_width, containing_block, element, replaced_element, clamped_width);
+			BuildBoxWidth(box, computed, min_width, max_width, containing_block, element, replaced_element, formatting_mode, clamped_width);
 	}
 	}
 	else
 	else
 		box.SetContent(content_area);
 		box.SetContent(content_area);

+ 29 - 17
Source/Core/Layout/LayoutDetails.h

@@ -37,6 +37,7 @@ namespace Rml {
 class Box;
 class Box;
 class BlockContainer;
 class BlockContainer;
 class ContainerBox;
 class ContainerBox;
+struct FormattingMode;
 
 
 /**
 /**
     ComputedAxisSize is an abstraction of an element's computed size properties along a single axis, either horizontally or vertically,
     ComputedAxisSize is an abstraction of an element's computed size properties along a single axis, either horizontally or vertically,
@@ -52,9 +53,10 @@ struct ComputedAxisSize {
 };
 };
 
 
 enum class BuildBoxMode {
 enum class BuildBoxMode {
-	Block,          // Sets edges and size if available, auto width can result in shrink-to-fit width, auto margins are used for alignment.
-	Inline,         // Sets edges, ignores width, height, and auto margins.
-	UnalignedBlock, // Like block, but auto width returns -1, and auto margins are resolved to zero.
+	Block, // Sets edges and size if available, auto width uses stretch-fit width (never shrink-to-fit), auto margins are used for alignment.
+	ShrinkableBlock, // Like block, but uses shrink-to-fit width when appropriate, determined by formatting the element.
+	UnalignedBlock,  // Like block, but auto width returns -1, and auto margins are resolved to zero.
+	Inline,          // Sets edges, ignores width, height, and auto margins.
 };
 };
 
 
 /**
 /**
@@ -68,8 +70,10 @@ public:
 	/// @param[out] box The box to be built.
 	/// @param[out] box The box to be built.
 	/// @param[in] containing_block The dimensions of the content area of the block containing the element.
 	/// @param[in] containing_block The dimensions of the content area of the block containing the element.
 	/// @param[in] element The element to build the box for.
 	/// @param[in] element The element to build the box for.
-	/// @param[in] box_context The formatting context in which the box is generated.
-	static void BuildBox(Box& box, Vector2f containing_block, Element* element, BuildBoxMode box_context = BuildBoxMode::Block);
+	/// @param[in] box_mode The mode which determines how the box is built.
+	/// @param[in] formatting_mode Active formatting mode, must be set when using box mode ShrinkableBlock.
+	static void BuildBox(Box& box, Vector2f containing_block, Element* element, BuildBoxMode box_mode,
+		const FormattingMode* formatting_mode = nullptr);
 
 
 	// Retrieves the minimum and maximum width from an element's computed values.
 	// Retrieves the minimum and maximum width from an element's computed values.
 	static void GetMinMaxWidth(float& min_width, float& max_width, const ComputedValues& computed, const Box& box, float containing_block_width);
 	static void GetMinMaxWidth(float& min_width, float& max_width, const ComputedValues& computed, const Box& box, float containing_block_width);
@@ -82,21 +86,14 @@ public:
 	static void GetDefiniteMinMaxHeight(float& min_height, float& max_height, const ComputedValues& computed, const Box& box,
 	static void GetDefiniteMinMaxHeight(float& min_height, float& max_height, const ComputedValues& computed, const Box& box,
 		float containing_block_height);
 		float containing_block_height);
 
 
-	/// Builds margins of a Box, and resolves any auto width or height for non-inline elements. The height may be left unresolved if it depends on the
-	/// element's children.
-	/// @param[in,out] box The box to generate. The padding and borders, in addition to any definite content area, must be set on the box already.
-	/// Auto width and height are specified by negative content size.
-	/// @param[in] min_size The element's minimum width and height.
-	/// @param[in] max_size The element's maximum width and height.
+	/// Builds the box margins for a block box, assumes the size and other edges are already set.
+	/// @param[in,out] box The box to generate.
 	/// @param[in] containing_block The size of the containing block.
 	/// @param[in] containing_block The size of the containing block.
 	/// @param[in] element The element the box is being generated for.
 	/// @param[in] element The element the box is being generated for.
-	/// @param[in] box_context The formatting context in which the box is generated.
-	/// @param[in] replaced_element True when the element is a replaced element.
-	static void BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element,
-		BuildBoxMode box_context, bool replaced_element);
+	static void BuildAutoMarginsForBlockBox(Box& box, Vector2f containing_block, Element* element);
 
 
 	/// Formats the element and returns the width of its contents.
 	/// Formats the element and returns the width of its contents.
-	static float GetShrinkToFitWidth(Element* element, Vector2f containing_block);
+	static float GetShrinkToFitWidth(Element* element, Vector2f containing_block, const FormattingMode& current_formatting_mode);
 
 
 	/// Build computed axis size along the horizontal direction (width and friends).
 	/// Build computed axis size along the horizontal direction (width and friends).
 	static ComputedAxisSize BuildComputedHorizontalSize(const ComputedValues& computed);
 	static ComputedAxisSize BuildComputedHorizontalSize(const ComputedValues& computed);
@@ -114,6 +111,20 @@ public:
 	}
 	}
 
 
 private:
 private:
+	/// Builds margins of a Box, and resolves any auto width or height for non-inline elements. The height may be left unresolved if it depends on the
+	/// element's children.
+	/// @param[in,out] box The box to generate. The padding and borders, in addition to any definite content area, must be set on the box already.
+	/// Auto width and height are specified by negative content size.
+	/// @param[in] min_size The element's minimum width and height.
+	/// @param[in] max_size The element's maximum width and height.
+	/// @param[in] containing_block The size of the containing block.
+	/// @param[in] element The element the box is being generated for.
+	/// @param[in] box_mode The mode which determines how the box is built.
+	/// @param[in] replaced_element True when the element is a replaced element.
+	/// @param[in] formatting_mode Active formatting mode, must be set when using ShrinkableBlock box context.
+	static void BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element,
+		BuildBoxMode box_mode, bool replaced_element, const FormattingMode* formatting_mode);
+
 	/// Calculates and returns the content size for replaced elements.
 	/// Calculates and returns the content size for replaced elements.
 	static Vector2f CalculateSizeForReplacedElement(Vector2f specified_content_size, Vector2f min_size, Vector2f max_size, Vector2f intrinsic_size,
 	static Vector2f CalculateSizeForReplacedElement(Vector2f specified_content_size, Vector2f min_size, Vector2f max_size, Vector2f intrinsic_size,
 		float intrinsic_ratio);
 		float intrinsic_ratio);
@@ -127,10 +138,11 @@ private:
 	/// @param[in] containing_block The size of the containing block.
 	/// @param[in] containing_block The size of the containing block.
 	/// @param[in] element The element the box is being generated for.
 	/// @param[in] element The element the box is being generated for.
 	/// @param[in] replaced_element True when the element is a replaced element.
 	/// @param[in] replaced_element True when the element is a replaced element.
+	/// @param[in] formatting_mode Active formatting mode, must be set when using ShrinkableBlock box context.
 	/// @param[in] override_shrink_to_fit_width Provide a fixed shrink-to-fit width instead of formatting the element when its properties allow
 	/// @param[in] override_shrink_to_fit_width Provide a fixed shrink-to-fit width instead of formatting the element when its properties allow
 	/// shrinking.
 	/// shrinking.
 	static void BuildBoxWidth(Box& box, const ComputedValues& computed, float min_width, float max_width, Vector2f containing_block, Element* element,
 	static void BuildBoxWidth(Box& box, const ComputedValues& computed, float min_width, float max_width, Vector2f containing_block, Element* element,
-		bool replaced_element, float override_shrink_to_fit_width = -1);
+		bool replaced_element, const FormattingMode* formatting_mode, float override_shrink_to_fit_width = -1);
 	/// Builds the block-specific height and vertical margins of a Box.
 	/// Builds the block-specific height and vertical margins of a Box.
 	/// @param[in,out] box The box to generate. The padding and borders must be set on the box already. The content area is used instead of the height
 	/// @param[in,out] box The box to generate. The padding and borders must be set on the box already. The content area is used instead of the height
 	/// property, and -1 means auto height.
 	/// property, and -1 means auto height.

+ 3 - 3
Source/Core/Layout/LayoutEngine.cpp

@@ -35,12 +35,12 @@
 
 
 namespace Rml {
 namespace Rml {
 
 
-void LayoutEngine::FormatElement(Element* element, Vector2f containing_block, Vector2f absolutely_positioning_containing_block)
+void LayoutEngine::FormatElement(Element* element, Vector2f containing_block, Vector2f absolutely_positioning_containing_block, bool allow_cache)
 {
 {
 	RMLUI_ASSERT(element && containing_block.x >= 0 && containing_block.y >= 0);
 	RMLUI_ASSERT(element && containing_block.x >= 0 && containing_block.y >= 0);
 
 
-	RootBox absolute_root(absolutely_positioning_containing_block);
-	RootBox root(containing_block, &absolute_root);
+	RootBox absolute_root(Box(absolutely_positioning_containing_block), FormattingMode{FormattingMode::Constraint::None, allow_cache});
+	RootBox root(Box(containing_block), &absolute_root);
 
 
 	auto layout_box = FormattingContext::FormatIndependent(&root, element, nullptr, FormattingContextType::Block);
 	auto layout_box = FormattingContext::FormatIndependent(&root, element, nullptr, FormattingContextType::Block);
 	if (!layout_box)
 	if (!layout_box)

+ 2 - 1
Source/Core/Layout/LayoutEngine.h

@@ -47,7 +47,8 @@ public:
 	/// @param[in] element The element to lay out.
 	/// @param[in] element The element to lay out.
 	/// @param[in] containing_block The size of the containing block.
 	/// @param[in] containing_block The size of the containing block.
 	/// @param[in] absolutely_positioning_containing_block The size of the absolutely positioning containing block.
 	/// @param[in] absolutely_positioning_containing_block The size of the absolutely positioning containing block.
-	static void FormatElement(Element* element, Vector2f containing_block, Vector2f absolutely_positioning_containing_block);
+	/// @param[in] allow_cache True to allow skipping layout of elements that match the current layout.
+	static void FormatElement(Element* element, Vector2f containing_block, Vector2f absolutely_positioning_containing_block, bool allow_cache = true);
 };
 };
 
 
 } // namespace Rml
 } // namespace Rml

+ 2 - 2
Source/Core/Layout/ReplacedFormattingContext.cpp

@@ -47,7 +47,7 @@ UniquePtr<LayoutBox> ReplacedFormattingContext::Format(ContainerBox* parent_cont
 	else
 	else
 	{
 	{
 		const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
 		const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
-		LayoutDetails::BuildBox(box, containing_block, element);
+		LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::ShrinkableBlock, &parent_container->GetFormattingMode());
 	}
 	}
 
 
 	// Submit the box and notify the element.
 	// Submit the box and notify the element.
@@ -58,7 +58,7 @@ UniquePtr<LayoutBox> ReplacedFormattingContext::Format(ContainerBox* parent_cont
 	// own rendering, this could cause conflicting or strange layout results, and is done at the user's own risk.
 	// own rendering, this could cause conflicting or strange layout results, and is done at the user's own risk.
 	if (element->HasChildNodes())
 	if (element->HasChildNodes())
 	{
 	{
-		RootBox root(box);
+		RootBox root(box, parent_container->GetFormattingMode());
 		BlockFormattingContext::Format(&root, element, &box);
 		BlockFormattingContext::Format(&root, element, &box);
 	}
 	}
 
 

+ 2 - 3
Source/Core/Layout/TableFormattingContext.cpp

@@ -58,7 +58,7 @@ UniquePtr<LayoutBox> TableFormattingContext::Format(ContainerBox* parent_contain
 	if (override_initial_box)
 	if (override_initial_box)
 		box = *override_initial_box;
 		box = *override_initial_box;
 	else
 	else
-		LayoutDetails::BuildBox(box, containing_block, element_table, BuildBoxMode::Block);
+		LayoutDetails::BuildBox(box, containing_block, element_table, BuildBoxMode::ShrinkableBlock, &parent_container->GetFormattingMode());
 
 
 	TableFormattingContext context;
 	TableFormattingContext context;
 	context.element_table = element_table;
 	context.element_table = element_table;
@@ -99,8 +99,7 @@ UniquePtr<LayoutBox> TableFormattingContext::Format(ContainerBox* parent_contain
 	if (table_content_size != initial_content_size)
 	if (table_content_size != initial_content_size)
 	{
 	{
 		// Perform this step to re-evaluate any auto margins.
 		// Perform this step to re-evaluate any auto margins.
-		LayoutDetails::BuildBoxSizeAndMargins(box, context.table_min_size, context.table_max_size, containing_block, element_table,
-			BuildBoxMode::Block, true);
+		LayoutDetails::BuildAutoMarginsForBlockBox(box, containing_block, element_table);
 	}
 	}
 
 
 	// Change the table baseline coordinates to the element baseline, which is defined as the distance from the element's bottom margin edge.
 	// Change the table baseline coordinates to the element baseline, which is defined as the distance from the element's bottom margin edge.

+ 4 - 4
Source/Core/WidgetScroll.cpp

@@ -210,7 +210,7 @@ void WidgetScroll::FormatElements(const Vector2f containing_block, float slider_
 
 
 	// Build the box for the containing slider element.
 	// Build the box for the containing slider element.
 	Box parent_box;
 	Box parent_box;
-	LayoutDetails::BuildBox(parent_box, containing_block, parent);
+	LayoutDetails::BuildBox(parent_box, containing_block, parent, BuildBoxMode::UnalignedBlock);
 	slider_length -= orientation == VERTICAL
 	slider_length -= orientation == VERTICAL
 		? (parent_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Top) + parent_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Bottom))
 		? (parent_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Top) + parent_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Bottom))
 		: (parent_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Left) + parent_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Right));
 		: (parent_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Left) + parent_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Right));
@@ -224,7 +224,7 @@ void WidgetScroll::FormatElements(const Vector2f containing_block, float slider_
 
 
 	// Generate the initial dimensions for the track. It'll need to be cut down to fit the arrows.
 	// Generate the initial dimensions for the track. It'll need to be cut down to fit the arrows.
 	Box track_box;
 	Box track_box;
-	LayoutDetails::BuildBox(track_box, parent_box.GetSize(), track);
+	LayoutDetails::BuildBox(track_box, parent_box.GetSize(), track, BuildBoxMode::Block);
 	content = track_box.GetSize();
 	content = track_box.GetSize();
 	content[length_axis] = slider_length -= orientation == VERTICAL
 	content[length_axis] = slider_length -= orientation == VERTICAL
 		? (track_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Top) + track_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Bottom))
 		? (track_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Top) + track_box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Bottom))
@@ -238,7 +238,7 @@ void WidgetScroll::FormatElements(const Vector2f containing_block, float slider_
 	for (int i = 0; i < 2; i++)
 	for (int i = 0; i < 2; i++)
 	{
 	{
 		Box arrow_box;
 		Box arrow_box;
-		LayoutDetails::BuildBox(arrow_box, parent_box.GetSize(), arrows[i]);
+		LayoutDetails::BuildBox(arrow_box, parent_box.GetSize(), arrows[i], BuildBoxMode::Block);
 
 
 		// Clamp the size to (0, 0).
 		// Clamp the size to (0, 0).
 		Vector2f arrow_size = arrow_box.GetSize();
 		Vector2f arrow_size = arrow_box.GetSize();
@@ -292,7 +292,7 @@ void WidgetScroll::FormatElements(const Vector2f containing_block, float slider_
 void WidgetScroll::FormatBar()
 void WidgetScroll::FormatBar()
 {
 {
 	Box bar_box;
 	Box bar_box;
-	LayoutDetails::BuildBox(bar_box, parent->GetBox().GetSize(), bar);
+	LayoutDetails::BuildBox(bar_box, parent->GetBox().GetSize(), bar, BuildBoxMode::Block);
 
 
 	const auto& computed = bar->GetComputedValues();
 	const auto& computed = bar->GetComputedValues();