Skip to content

Commit

Permalink
Compute shrink-to-fit width for flex blocks
Browse files Browse the repository at this point in the history
Refs #552
  • Loading branch information
alml committed Jan 2, 2024
1 parent bb25b88 commit c68bfdc
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 8 deletions.
4 changes: 3 additions & 1 deletion Source/Core/Layout/ContainerBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <algorithm>
Expand Down Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions Source/Core/Layout/FlexFormattingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,27 @@ UniquePtr<LayoutBox> 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<FlexContainer>(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 {
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/Layout/FlexFormattingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<LayoutBox> 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;

Expand Down
18 changes: 11 additions & 7 deletions Source/Core/Layout/LayoutDetails.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -275,9 +275,13 @@ float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_b
RootBox root(Math::Max(containing_block, Vector2f(0.f)));
UniquePtr<LayoutBox> 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)
Expand Down

0 comments on commit c68bfdc

Please sign in to comment.