diff --git a/Source/Core/Layout/ContainerBox.cpp b/Source/Core/Layout/ContainerBox.cpp index 7db89adfb..40b5a488f 100644 --- a/Source/Core/Layout/ContainerBox.cpp +++ b/Source/Core/Layout/ContainerBox.cpp @@ -31,6 +31,7 @@ #include "../../../Include/RmlUi/Core/Element.h" #include "../../../Include/RmlUi/Core/ElementScroll.h" #include "../../../Include/RmlUi/Core/Profiling.h" +#include "FlexFormattingContext.h" #include "FormattingContext.h" #include "LayoutDetails.h" #include @@ -269,12 +270,12 @@ bool FlexContainer::Close(const Vector2f content_overflow_size, const Box& box, float FlexContainer::GetShrinkToFitWidth() const { - // We don't currently support shrink-to-fit layout of flex containers. However, for the trivial case of a fixed - // width, we simply return that. + // For the trivial case of a fixed width, we simply return that. if (element->GetComputedValues().width().type == Style::Width::Type::Length) return box.GetSize().x; - return 0.0f; + // Infer shrink-to-fit width from the intrinsic width of the element. + return FlexFormattingContext::GetMaxContentSize(element).x; } String FlexContainer::DebugDumpTree(int depth) const diff --git a/Source/Core/Layout/FlexFormattingContext.cpp b/Source/Core/Layout/FlexFormattingContext.cpp index 8187791eb..92fc53f5c 100644 --- a/Source/Core/Layout/FlexFormattingContext.cpp +++ b/Source/Core/Layout/FlexFormattingContext.cpp @@ -118,6 +118,28 @@ UniquePtr FlexFormattingContext::Format(ContainerBox* parent_containe return flex_container_box; } +Vector2f FlexFormattingContext::GetMaxContentSize(Element* element) +{ + // A large but finite number is used here, because the flexbox formatting algorithm + // needs to round numbers, and it doesn't support infinities. + const Vector2f infinity(10000.0f, 10000.0f); + RootBox root(infinity); + auto flex_container_box = MakeUnique(element, &root); + + FlexFormattingContext context; + context.flex_container_box = flex_container_box.get(); + context.element_flex = element; + context.flex_available_content_size = Vector2f(-1, -1); + context.flex_content_containing_block = infinity; + context.flex_max_size = Vector2f(FLT_MAX, FLT_MAX); + + // Format the flexbox and all its children. + Vector2f flex_resulting_content_size, content_overflow_size; + float flex_baseline = 0.f; + context.Format(flex_resulting_content_size, content_overflow_size, flex_baseline); + return flex_resulting_content_size; +} + struct FlexItem { // In the following, suffix '_a' means flex start edge while '_b' means flex end edge. struct Size { @@ -137,7 +159,7 @@ struct FlexItem { Size cross; float flex_shrink_factor; float flex_grow_factor; - Style::AlignSelf align_self; // 'Auto' is replaced by container's 'align-items' value + Style::AlignSelf align_self; // 'Auto' is replaced by container's 'align-items' value float inner_flex_base_size; // Inner size float flex_base_size; // Outer size @@ -315,10 +337,11 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector RMLUI_ASSERT(initial_box_size.y < 0.f); Box format_box = item.box; - if (initial_box_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)); - FormattingContext::FormatIndependent(flex_container_box, element, &format_box, FormattingContextType::Block); + 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; } diff --git a/Source/Core/Layout/FlexFormattingContext.h b/Source/Core/Layout/FlexFormattingContext.h index 30897846b..2c903ec23 100644 --- a/Source/Core/Layout/FlexFormattingContext.h +++ b/Source/Core/Layout/FlexFormattingContext.h @@ -43,8 +43,12 @@ class FlexContainer; */ class FlexFormattingContext final : public FormattingContext { public: + /// Formats a flex container element and its flex items according to flexbox layout rules. static UniquePtr Format(ContainerBox* parent_container, Element* element, const Box* override_initial_box); + /// Computes max-content size for a flex container. + static Vector2f GetMaxContentSize(Element* element); + private: FlexFormattingContext() = default; diff --git a/Source/Core/Layout/LayoutDetails.cpp b/Source/Core/Layout/LayoutDetails.cpp index ec3e33816..5bcd4ea4f 100644 --- a/Source/Core/Layout/LayoutDetails.cpp +++ b/Source/Core/Layout/LayoutDetails.cpp @@ -256,16 +256,17 @@ float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_b LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::UnalignedBlock); LayoutDetails::GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y); - // Currently we don't support shrink-to-fit width for flexboxes or tables. Just return a zero-sized width. + // Currently we don't support shrink-to-fit width for tables. Just return a zero-sized width. const Style::Display display = element->GetDisplay(); - if (display == Style::Display::Flex || display == Style::Display::InlineFlex || display == Style::Display::Table || - display == Style::Display::InlineTable) + if (display == Style::Display::Table || display == Style::Display::InlineTable) + { return 0.f; + } // Use a large size for the box content width, so that it is practically unconstrained. This makes the formatting // procedure act as if under a maximum content constraint. Children with percentage sizing values may be scaled // based on this width (such as 'width' or 'margin'), if so, the layout is considered undefined like in CSS 2. - const float max_content_constraint_width = containing_block.x + 1000.f; + const float max_content_constraint_width = containing_block.x + 10000.f; box.SetContent({max_content_constraint_width, box.GetSize().y}); // First, format the element under the above generated box. Then we ask the resulting box for its shrink-to-fit @@ -275,9 +276,14 @@ float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_b RootBox root(Math::Max(containing_block, Vector2f(0.f))); UniquePtr layout_box = FormattingContext::FormatIndependent(&root, element, &box, FormattingContextType::Block); - const float available_width = Math::Max(0.f, containing_block.x - box.GetSizeAcross(BoxDirection::Horizontal, BoxArea::Margin, BoxArea::Padding)); - - return Math::Min(available_width, layout_box->GetShrinkToFitWidth()); + float shrink_to_fit_width = layout_box->GetShrinkToFitWidth(); + if (containing_block.x >= 0) + { + const float available_width = + Math::Max(0.f, containing_block.x - box.GetSizeAcross(BoxDirection::Horizontal, BoxArea::Margin, BoxArea::Padding)); + shrink_to_fit_width = Math::Min(shrink_to_fit_width, available_width); + } + return shrink_to_fit_width; } ComputedAxisSize LayoutDetails::BuildComputedHorizontalSize(const ComputedValues& computed)