Jelajahi Sumber

Layout refactor: Get containing block size from container box

Should be functionally equivalent.
Michael Ragazzon 6 bulan lalu
induk
melakukan
015057095a

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

@@ -84,7 +84,7 @@ bool BlockContainer::Close(BlockContainer* parent_block_container)
 	// If we are the root of our block formatting context, this will be null. Otherwise increment our parent's cursor to account for this box.
 	if (parent_block_container)
 	{
-		RMLUI_ASSERTMSG(GetParent() == parent_block_container, "Mismatched parent box.");
+		AssertMatchesParentContainer(parent_block_container);
 
 		// If this close fails, it means this block box has caused our parent box to generate an automatic vertical scrollbar.
 		if (!parent_block_container->EncloseChildBox(this, position, box.GetSizeAcross(BoxDirection::Vertical, BoxArea::Border),

+ 4 - 5
Source/Core/Layout/BlockFormattingContext.cpp

@@ -122,7 +122,7 @@ UniquePtr<LayoutBox> BlockFormattingContext::Format(ContainerBox* parent_contain
 	RMLUI_ZoneName(name.c_str(), name.size());
 #endif
 
-	const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent_container, element->GetPosition()).size;
+	const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
 
 	Box box;
 	if (override_initial_box)
@@ -164,7 +164,7 @@ UniquePtr<LayoutBox> BlockFormattingContext::Format(ContainerBox* parent_contain
 bool BlockFormattingContext::FormatBlockBox(BlockContainer* parent_container, Element* element)
 {
 	RMLUI_ZoneScopedC(0x2F4F4F);
-	const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent_container, element->GetPosition()).size;
+	const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
 
 	Box box;
 	LayoutDetails::BuildBox(box, containing_block, element);
@@ -192,7 +192,7 @@ bool BlockFormattingContext::FormatBlockBox(BlockContainer* parent_container, El
 bool BlockFormattingContext::FormatInlineBox(BlockContainer* parent_container, Element* element)
 {
 	RMLUI_ZoneScopedC(0x3F6F6F);
-	const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent_container, element->GetPosition()).size;
+	const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
 
 	Box box;
 	LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::Inline);
@@ -238,8 +238,7 @@ bool BlockFormattingContext::FormatBlockContainerChild(BlockContainer* parent_co
 	if (position_property == Style::Position::Absolute || position_property == Style::Position::Fixed)
 	{
 		const Vector2f static_position = parent_container->GetOpenStaticPosition(display) - parent_container->GetPosition();
-		ContainingBlock containing_block = LayoutDetails::GetContainingBlock(parent_container, position_property);
-		containing_block.container->AddAbsoluteElement(element, static_position, parent_container->GetElement());
+		parent_container->AddAbsoluteElement(element, static_position, parent_container->GetElement());
 		return true;
 	}
 

+ 59 - 3
Source/Core/Layout/ContainerBox.cpp

@@ -56,7 +56,7 @@ void ContainerBox::ResetScrollbars(const Box& box)
 void ContainerBox::AddAbsoluteElement(Element* element, Vector2f static_position, Element* static_relative_offset_parent)
 {
 	// We may possibly be adding the same element from a previous layout iteration. If so, this ensures it is updated with the latest static position.
-	absolute_elements[element] = AbsoluteElement{static_position, static_relative_offset_parent};
+	absolute_positioning_containing_block->absolute_elements[element] = AbsoluteElement{static_position, static_relative_offset_parent};
 }
 
 void ContainerBox::AddRelativeElement(Element* element)
@@ -66,6 +66,56 @@ void ContainerBox::AddRelativeElement(Element* element)
 		relative_elements.push_back(element);
 }
 
+Vector2f ContainerBox::GetContainingBlockSize(Style::Position position) const
+{
+	RMLUI_ASSERT(absolute_positioning_containing_block);
+	using Style::Position;
+
+	Vector2f result;
+
+	switch (position)
+	{
+	case Position::Static:
+	case Position::Relative:
+	{
+		const Box* box = GetIfBox();
+		if (!box)
+		{
+			RMLUI_ERROR;
+			return {};
+		}
+		result = box->GetSize(BoxArea::Content);
+		if (element)
+		{
+			// For static elements we subtract the scrollbar size so that elements normally don't overlap their parent's
+			// scrollbars. In CSS, this would also be done for absolutely positioned elements. We might want to copy
+			// that behavior in the future. Then, we would also need to change the element offset behavior and ideally
+			// also make positioned boxes contribute to the scrollable area.
+			ElementScroll* element_scroll = element->GetElementScroll();
+			if (result.x >= 0.f)
+				result.x = Math::Max(result.x - element_scroll->GetScrollbarSize(ElementScroll::VERTICAL), 0.f);
+			if (result.y >= 0.f)
+				result.y = Math::Max(result.y - element_scroll->GetScrollbarSize(ElementScroll::HORIZONTAL), 0.f);
+		}
+	}
+	break;
+	case Position::Absolute:
+	case Position::Fixed:
+	{
+		const Box* box = absolute_positioning_containing_block->GetIfBox();
+		if (!box)
+		{
+			RMLUI_ERROR;
+			return {};
+		}
+		result = box->GetSize(BoxArea::Padding);
+	}
+	break;
+	}
+
+	return result;
+}
+
 bool ContainerBox::IsScrollContainer() const
 {
 	return LayoutDetails::IsScrollContainer(overflow_x, overflow_y);
@@ -73,7 +123,7 @@ bool ContainerBox::IsScrollContainer() const
 
 void ContainerBox::ClosePositionedElements()
 {
-	// Any relatively positioned elements that we act as containing block for may need to be have their positions
+	// Any relatively positioned elements that we act as containing block for may need to have their positions
 	// updated to reflect changes to the size of this block box. Update relative offsets before handling absolute
 	// elements, as this may affect the resolved static position of the absolute elements.
 	for (Element* child : relative_elements)
@@ -127,6 +177,7 @@ void ContainerBox::SetElementBaseline(float element_baseline)
 ContainerBox::ContainerBox(Type type, Element* element, ContainerBox* parent_container) :
 	LayoutBox(type), element(element), parent_container(parent_container)
 {
+	bool is_absolute_positioning_containing_block = false;
 	if (element)
 	{
 		const auto& computed = element->GetComputedValues();
@@ -135,6 +186,11 @@ ContainerBox::ContainerBox(Type type, Element* element, ContainerBox* parent_con
 		is_absolute_positioning_containing_block = (computed.position() != Style::Position::Static || computed.has_local_transform() ||
 			computed.has_local_perspective() || computed.has_filter() || computed.has_backdrop_filter() || computed.has_mask_image());
 	}
+
+	if (is_absolute_positioning_containing_block || !parent_container)
+		absolute_positioning_containing_block = this;
+	else
+		absolute_positioning_containing_block = parent_container->absolute_positioning_containing_block;
 }
 
 bool ContainerBox::CatchOverflow(const Vector2f content_overflow_size, const Box& box, const float max_height) const
@@ -227,7 +283,7 @@ bool ContainerBox::SubmitBox(const Vector2f content_overflow_size, const Box& bo
 
 		// Set the visible overflow size so that ancestors can catch any overflow produced by us. That is, hiding it or
 		// providing a scrolling mechanism. If this box is a scroll container we catch our own overflow here. Thus, in
-		// this case, only our border box is visible from our ancestor's perpective.
+		// this case, only our border box is visible from our ancestor's perspective.
 		if (is_scroll_container)
 		{
 			visible_overflow_size = border_size;

+ 25 - 9
Source/Core/Layout/ContainerBox.h

@@ -43,21 +43,32 @@ namespace Rml {
 */
 class ContainerBox : public LayoutBox {
 public:
-	// Enable or disable scrollbars for the element we represent, preparing it for the first round of layouting, according to our properties.
+	/// Enable or disable scrollbars for the element we represent, preparing it for the first round of layouting, according to our properties.
 	void ResetScrollbars(const Box& box);
 
-	// Adds an absolutely positioned element, to be formatted and positioned when closing this container, see 'ClosePositionedElements'.
+	/// Adds an absolutely positioned element located within this container. The element is added to the absolute
+	/// positioning containing block box, and will be formatted and positioned when closing that box, see
+	/// 'ClosePositionedElements'.
 	void AddAbsoluteElement(Element* element, Vector2f static_position, Element* static_relative_offset_parent);
-	// Adds a relatively positioned element which we act as a containing block for.
+	/// Adds a relatively positioned element which this container acts as a containing block for.
 	void AddRelativeElement(Element* element);
 
-	ContainerBox* GetParent() { return parent_container; }
 	Element* GetElement() { return element; }
 
+	/// Returns the size of the containing block for a box taking part in this container.
+	/// @param[in] position The position property of the current box.
+	/// @return The containing size, possibly indefinite (represented by negative size) along one or both axes.
+	Vector2f GetContainingBlockSize(Style::Position position) const;
+
+	/// Returns true if this container can have scrollbars enabled, as determined by its overflow properties.
 	bool IsScrollContainer() const;
 
-	// Returns true if this box acts as a containing block for absolutely positioned descendants.
-	bool IsAbsolutePositioningContainingBlock() const { return is_absolute_positioning_containing_block; }
+	void AssertMatchesParentContainer(ContainerBox* container_box) const
+	{
+		RMLUI_ASSERTMSG(container_box == parent_container, "Mismatched parent box.");
+		(void)container_box;
+	}
+	Element* GetAbsolutePositioningContainingBlockElementForDebug() const { return absolute_positioning_containing_block->element; }
 
 protected:
 	ContainerBox(Type type, Element* element, ContainerBox* parent_container);
@@ -67,14 +78,14 @@ protected:
 	/// @param[in] content_overflow_size The size of the visible content, relative to our content area.
 	/// @param[in] box The box built for the element, possibly with a non-determinate height.
 	/// @param[in] max_height Maximum height of the content area, if any.
-	/// @returns True if no overflow occured, false if it did.
+	/// @returns True if no overflow occurred, false if it did.
 	bool CatchOverflow(const Vector2f content_overflow_size, const Box& box, const float max_height) const;
 
 	/// Set the box and scrollable area on our element, possibly catching any overflow.
 	/// @param[in] content_overflow_size The size of the visible content, relative to our content area.
 	/// @param[in] box The box to be set on the element.
 	/// @param[in] max_height Maximum height of the content area, if any.
-	/// @returns True if no overflow occured, false if it did.
+	/// @returns True if no overflow occurred, false if it did.
 	bool SubmitBox(const Vector2f content_overflow_size, const Box& box, const float max_height);
 
 	/// Formats, sizes, and positions all absolute elements whose containing block is this, and offsets relative elements.
@@ -98,9 +109,14 @@ private:
 
 	Style::Overflow overflow_x = Style::Overflow::Visible;
 	Style::Overflow overflow_y = Style::Overflow::Visible;
-	bool is_absolute_positioning_containing_block = false;
 
 	ContainerBox* parent_container = nullptr;
+
+	// For absolutely positioned boxes we use the first positioned ancestor. We deviate from the CSS specs where they
+	// use a separate containing block for fixed boxes. In RCSS, we use the same rules on fixed boxes, as this is
+	// particularly helpful on handles and other widgets that should not scroll with the window. This is a common design
+	// pattern in target applications for this library.
+	ContainerBox* absolute_positioning_containing_block = nullptr; // [not null]
 };
 
 /**

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

@@ -48,7 +48,7 @@ UniquePtr<LayoutBox> FlexFormattingContext::Format(ContainerBox* parent_containe
 	ElementScroll* element_scroll = element->GetElementScroll();
 	const ComputedValues& computed = element->GetComputedValues();
 
-	const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent_container, element->GetPosition()).size;
+	const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
 	RMLUI_ASSERT(containing_block.x >= 0.f);
 
 	// Build the initial box as specified by the flex's style, as if it was a normal block element.
@@ -285,8 +285,7 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector
 		}
 		else if (computed.position() == Style::Position::Absolute || computed.position() == Style::Position::Fixed)
 		{
-			ContainerBox* absolute_containing_block = LayoutDetails::GetContainingBlock(flex_container_box, computed.position()).container;
-			absolute_containing_block->AddAbsoluteElement(element, {}, element_flex);
+			flex_container_box->AddAbsoluteElement(element, {}, element_flex);
 			continue;
 		}
 		else if (computed.position() == Style::Position::Relative)

+ 0 - 48
Source/Core/Layout/LayoutDetails.cpp

@@ -170,54 +170,6 @@ void LayoutDetails::GetDefiniteMinMaxHeight(float& min_height, float& max_height
 	}
 }
 
-ContainingBlock LayoutDetails::GetContainingBlock(ContainerBox* parent_container, const Style::Position position)
-{
-	RMLUI_ASSERT(parent_container);
-	using Style::Position;
-
-	ContainerBox* container = parent_container;
-	BoxArea area = BoxArea::Content;
-
-	// For absolutely positioned boxes we look for the first positioned ancestor. We deviate from the CSS specs by using
-	// the same rules for fixed boxes, as that is particularly helpful on handles and other widgets that should not
-	// scroll with the window. This is a common design pattern in target applications for this library, although this
-	// behavior may be reconsidered in the future.
-	if (position == Position::Absolute || position == Position::Fixed)
-	{
-		area = BoxArea::Padding;
-
-		while (container->GetParent() && !container->IsAbsolutePositioningContainingBlock())
-			container = container->GetParent();
-	}
-
-	const Box* box = container->GetIfBox();
-	if (!box)
-	{
-		RMLUI_ERROR;
-		return {container, {}};
-	}
-
-	Vector2f containing_block = box->GetSize(area);
-
-	if (position == Position::Static || position == Position::Relative)
-	{
-		// For static elements we subtract the scrollbar size so that elements normally don't overlap their parent's
-		// scrollbars. In CSS, this would also be done for absolutely positioned elements, we might want to copy that
-		// behavior in the future. If so, we would also need to change the element offset behavior, and ideally also
-		// make positioned boxes contribute to the scrollable area.
-		if (Element* element = container->GetElement())
-		{
-			ElementScroll* element_scroll = element->GetElementScroll();
-			if (containing_block.x >= 0.f)
-				containing_block.x = Math::Max(containing_block.x - element_scroll->GetScrollbarSize(ElementScroll::VERTICAL), 0.f);
-			if (containing_block.y >= 0.f)
-				containing_block.y = Math::Max(containing_block.y - element_scroll->GetScrollbarSize(ElementScroll::HORIZONTAL), 0.f);
-		}
-	}
-
-	return {container, containing_block};
-}
-
 void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element,
 	BuildBoxMode box_context, bool replaced_element)
 {

+ 0 - 11
Source/Core/Layout/LayoutDetails.h

@@ -51,11 +51,6 @@ struct ComputedAxisSize {
 	Style::BoxSizing box_sizing;
 };
 
-struct ContainingBlock {
-	ContainerBox* container;
-	Vector2f size;
-};
-
 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.
@@ -87,12 +82,6 @@ public:
 	static void GetDefiniteMinMaxHeight(float& min_height, float& max_height, const ComputedValues& computed, const Box& box,
 		float containing_block_height);
 
-	/// Returns the containing block for a box.
-	/// @param[in] parent_container The parent container of the current box.
-	/// @param[in] position The position property of the current box.
-	/// @return The containing block box and size, possibly indefinite (represented by negative size) along one or both axes.
-	static ContainingBlock GetContainingBlock(ContainerBox* parent_container, Style::Position position);
-
 	/// 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.

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

@@ -46,7 +46,7 @@ UniquePtr<LayoutBox> ReplacedFormattingContext::Format(ContainerBox* parent_cont
 		box = *override_initial_box;
 	else
 	{
-		const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent_container, element->GetPosition()).size;
+		const Vector2f containing_block = parent_container->GetContainingBlockSize(element->GetPosition());
 		LayoutDetails::BuildBox(box, containing_block, element);
 	}
 

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

@@ -49,7 +49,7 @@ UniquePtr<LayoutBox> TableFormattingContext::Format(ContainerBox* parent_contain
 		return table_wrapper_box;
 	}
 
-	const Vector2f containing_block = LayoutDetails::GetContainingBlock(parent_container, element_table->GetPosition()).size;
+	const Vector2f containing_block = parent_container->GetContainingBlockSize(element_table->GetPosition());
 	RMLUI_ASSERT(containing_block.x >= 0.f);
 	const ComputedValues& computed_table = element_table->GetComputedValues();
 

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

@@ -289,8 +289,7 @@ void TableGrid::PushRow(Element* element_row, ElementList cell_elements, TableWr
 		const Style::Position cell_position = element_cell->GetPosition();
 		if (cell_position == Style::Position::Absolute || cell_position == Style::Position::Fixed)
 		{
-			ContainerBox* containing_box = LayoutDetails::GetContainingBlock(&table_wrapper, cell_position).container;
-			containing_box->AddAbsoluteElement(element_cell, {}, table_wrapper.GetElement());
+			table_wrapper.AddAbsoluteElement(element_cell, {}, table_wrapper.GetElement());
 		}
 		else
 		{