diff --git a/Source/Core/Layout/ContainerBox.cpp b/Source/Core/Layout/ContainerBox.cpp index 7db89adfb..839deb370 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 @@ -274,7 +275,8 @@ float FlexContainer::GetShrinkToFitWidth() const 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..6c2487c4c 100644 --- a/Source/Core/Layout/FlexFormattingContext.cpp +++ b/Source/Core/Layout/FlexFormattingContext.cpp @@ -118,6 +118,27 @@ 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. + 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 = infinity; + context.flex_content_containing_block = infinity; + context.flex_max_size = infinity; + + // 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 { 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..1713b0ba7 100644 --- a/Source/Core/Layout/LayoutDetails.cpp +++ b/Source/Core/Layout/LayoutDetails.cpp @@ -256,16 +256,16 @@ 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 inline flexboxes or 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::InlineFlex || 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 +275,13 @@ 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)