浏览代码

Add cache for fit-content height

Michael Ragazzon 5 月之前
父节点
当前提交
dcfbde799e

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

@@ -355,9 +355,9 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector
 			if (initial_box_size.x < 0.f && flex_available_content_size.x >= 0.f)
 			if (initial_box_size.x < 0.f && flex_available_content_size.x >= 0.f)
 				format_box.SetContent(Vector2f(flex_available_content_size.x - item.cross.sum_edges, initial_box_size.y));
 				format_box.SetContent(Vector2f(flex_available_content_size.x - item.cross.sum_edges, initial_box_size.y));
 
 
-			FormattingContext::FormatIndependent(flex_container_box, element, (format_box.GetSize().x >= 0 ? &format_box : nullptr),
-				FormattingContextType::Block);
-			item.inner_flex_base_size = element->GetBox().GetSize().y;
+			const float height = FormattingContext::FormatFitContentHeight(flex_container_box, element, format_box);
+
+			item.inner_flex_base_size = height;
 
 
 			// Apply the automatic block size as minimum size (§4.5). Strictly speaking, we should also apply this to
 			// Apply the automatic block size as minimum size (§4.5). Strictly speaking, we should also apply this to
 			// the other branches in column mode (and inline min-content size in row mode). However, the formatting step
 			// the other branches in column mode (and inline min-content size in row mode). However, the formatting step
@@ -699,8 +699,8 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector
 				{
 				{
 					RMLUI_ZoneScopedNC("FlexItemFormat Cross Height", 0x6060A5);
 					RMLUI_ZoneScopedNC("FlexItemFormat Cross Height", 0x6060A5);
 					item.box.SetContent(Vector2f(GetInnerUsedMainSize(item), content_size.y));
 					item.box.SetContent(Vector2f(GetInnerUsedMainSize(item), content_size.y));
-					FormattingContext::FormatIndependent(flex_container_box, item.element, &item.box, FormattingContextType::Block);
-					item.hypothetical_cross_size = item.element->GetBox().GetSize().y + item.cross.sum_edges;
+					const float fit_content_height = FormattingContext::FormatFitContentHeight(flex_container_box, item.element, item.box);
+					item.hypothetical_cross_size = fit_content_height + item.cross.sum_edges;
 				}
 				}
 				else
 				else
 				{
 				{

+ 44 - 8
Source/Core/Layout/FormattingContext.cpp

@@ -119,7 +119,7 @@ UniquePtr<LayoutBox> FormattingContext::FormatIndependent(ContainerBox* parent_c
 	UniquePtr<LayoutBox> layout_box;
 	UniquePtr<LayoutBox> layout_box;
 
 
 	const FormattingMode& formatting_mode = parent_container->GetFormattingMode();
 	const FormattingMode& formatting_mode = parent_container->GetFormattingMode();
-	if (type != FormattingContextType::None && formatting_mode.constraint == FormattingMode::Constraint::None)
+	if (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),
@@ -221,6 +221,47 @@ float FormattingContext::FormatFitContentWidth(ContainerBox* parent_container, E
 	return box.GetSize().x;
 	return box.GetSize().x;
 }
 }
 
 
+float FormattingContext::FormatFitContentHeight(ContainerBox* parent_container, Element* element, const Box& box)
+{
+	if (box.GetSize().y >= 0.f)
+		return box.GetSize().y;
+
+	LayoutNode* layout_node = element->GetLayoutNode();
+
+	float max_content_height = box.GetSize().y;
+	if (Optional<float> cached_height = layout_node->GetMaxContentHeightIfCached())
+	{
+		max_content_height = *cached_height;
+	}
+	else
+	{
+		FormattingContextType type = GetFormattingContextType(element);
+		if (type == FormattingContextType::None)
+			type = FormattingContextType::Block;
+
+		FormattingMode formatting_mode = parent_container->GetFormattingMode();
+		formatting_mode.constraint = FormattingMode::Constraint::MaxContent;
+		const Vector2f root_containing_block(-1.f);
+		RootBox root(Box(root_containing_block), formatting_mode);
+
+		auto layout_box = FormattingContext::FormatIndependent(&root, element, &box, FormattingContextType::Block);
+
+		const Box* formatted_box = layout_box->GetIfBox();
+		if (!formatted_box)
+		{
+			Log::Message(Log::LT_WARNING, "Could not retrieve fit-content height from formatted result in element %s", element->GetAddress().c_str());
+			return -1.f;
+		}
+
+		max_content_height = formatted_box->GetSize().y;
+		layout_node->CommitMaxContentHeight(max_content_height);
+	}
+
+	// In the block axis, min-content, max-content, and fit-content are all equivalent for container boxes. Thus, we
+	// should not clamp to the available height, like we do for the fit-content width.
+	return max_content_height;
+}
+
 void FormattingContext::FormatFitContentWidth(Box& box, Element* element, FormattingContextType type, const FormattingMode& parent_formatting_mode,
 void FormattingContext::FormatFitContentWidth(Box& box, Element* element, FormattingContextType type, const FormattingMode& parent_formatting_mode,
 	const Vector2f containing_block)
 	const Vector2f containing_block)
 {
 {
@@ -232,15 +273,10 @@ void FormattingContext::FormatFitContentWidth(Box& box, Element* element, Format
 	RMLUI_ZoneText(zone_text.c_str(), zone_text.size());
 	RMLUI_ZoneText(zone_text.c_str(), zone_text.size());
 #endif
 #endif
 
 
-	LayoutNode* layout_node = element->GetLayoutNode();
-
 	const float box_height = box.GetSize().y;
 	const float box_height = box.GetSize().y;
-
+	LayoutNode* layout_node = element->GetLayoutNode();
 	float max_content_width = -1.f;
 	float max_content_width = -1.f;
-	// TODO: The shrink-to-fit width is only cached for every other nested flexbox during the initial
-	// GetShrinkToFitWidth. I.e. the first .outer flexbox below #nested is formatted outside of this function. Even
-	// though in principle I believe we should be able to store its formatted width. Maybe move this caching into
-	// FormatIndependent somehow?
+
 	if (Optional<float> cached_width = layout_node->GetMaxContentWidthIfCached())
 	if (Optional<float> cached_width = layout_node->GetMaxContentWidthIfCached())
 	{
 	{
 		max_content_width = *cached_width;
 		max_content_width = *cached_width;

+ 9 - 1
Source/Core/Layout/FormattingContext.h

@@ -63,7 +63,7 @@ public:
 	static UniquePtr<LayoutBox> FormatIndependent(ContainerBox* parent_container, Element* element, const Box* override_initial_box,
 	static UniquePtr<LayoutBox> FormatIndependent(ContainerBox* parent_container, Element* element, const Box* override_initial_box,
 		FormattingContextType default_context);
 		FormattingContextType default_context);
 
 
-	/// Format the element under a max-content width constraint, and returning its fit-content width.
+	/// Format the element under a max-content width constraint and retrieve its fit-content width.
 	/// @param[in] parent_container The container box which should act as the new box's parent.
 	/// @param[in] parent_container The container box which should act as the new box's parent.
 	/// @param[in] element The element to be formatted.
 	/// @param[in] element The element to be formatted.
 	/// @param[in] containing_block The element's containing block.
 	/// @param[in] containing_block The element's containing block.
@@ -71,6 +71,14 @@ public:
 	/// @note The width is not clamped according to the element's min-/max-width properties.
 	/// @note The width is not clamped according to the element's min-/max-width properties.
 	static float FormatFitContentWidth(ContainerBox* parent_container, Element* element, Vector2f containing_block);
 	static float FormatFitContentWidth(ContainerBox* parent_container, Element* element, Vector2f containing_block);
 
 
+	/// Format the element under a max-content height constraint and retrieve its fit-content height.
+	/// @param[in] parent_container The container box which should act as the new box's parent.
+	/// @param[in] element The element to be formatted.
+	/// @param[in] box The initial box to format with, assumed to be built in the same way on every invocation for the current element.
+	/// @return The fit-content height of the element.
+	/// @note The height is not clamped according to the element's min-/max-height properties.
+	static float FormatFitContentHeight(ContainerBox* parent_container, Element* element, const Box& box);
+
 protected:
 protected:
 	FormattingContext() = default;
 	FormattingContext() = default;
 	~FormattingContext() = default;
 	~FormattingContext() = default;

+ 1 - 0
Source/Core/Layout/LayoutNode.cpp

@@ -80,6 +80,7 @@ void LayoutNode::SetDirty(DirtyLayoutType dirty_type)
 	//	(dirty_type & DirtyLayoutType::Child) != DirtyLayoutType::None, element->GetAddress().c_str());
 	//	(dirty_type & DirtyLayoutType::Child) != DirtyLayoutType::None, element->GetAddress().c_str());
 	dirty_flag = dirty_flag | dirty_type;
 	dirty_flag = dirty_flag | dirty_type;
 	committed_max_content_width.reset();
 	committed_max_content_width.reset();
+	committed_max_content_height.reset();
 }
 }
 
 
 void LayoutNode::CommitLayout(Vector2f containing_block_size, Vector2f absolutely_positioning_containing_block_size, const Box* override_box,
 void LayoutNode::CommitLayout(Vector2f containing_block_size, Vector2f absolutely_positioning_containing_block_size, const Box* override_box,

+ 4 - 0
Source/Core/Layout/LayoutNode.h

@@ -129,6 +129,9 @@ public:
 	Optional<float> GetMaxContentWidthIfCached() const { return committed_max_content_width; }
 	Optional<float> GetMaxContentWidthIfCached() const { return committed_max_content_width; }
 	void CommitMaxContentWidth(float width) { committed_max_content_width = width; }
 	void CommitMaxContentWidth(float width) { committed_max_content_width = width; }
 
 
+	Optional<float> GetMaxContentHeightIfCached() const { return committed_max_content_height; }
+	void CommitMaxContentHeight(float height) { committed_max_content_height = height; }
+
 	// TODO: Remove and replace with a better interface.
 	// TODO: Remove and replace with a better interface.
 	const Optional<CommittedLayout>& GetCommittedLayout() const { return committed_layout; }
 	const Optional<CommittedLayout>& GetCommittedLayout() const { return committed_layout; }
 
 
@@ -149,6 +152,7 @@ private:
 
 
 	Optional<CommittedLayout> committed_layout;
 	Optional<CommittedLayout> committed_layout;
 	Optional<float> committed_max_content_width;
 	Optional<float> committed_max_content_width;
+	Optional<float> committed_max_content_height;
 };
 };
 
 
 } // namespace Rml
 } // namespace Rml