Skip to content

Commit

Permalink
Merge pull request #257 from mikke89/flexbox
Browse files Browse the repository at this point in the history
Flexbox layout
  • Loading branch information
mikke89 authored Dec 5, 2021
2 parents 2de889a + 555b6f0 commit 352a52e
Show file tree
Hide file tree
Showing 38 changed files with 2,741 additions and 171 deletions.
2 changes: 2 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
AccessModifierOffset: -4
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
Expand Down
2 changes: 2 additions & 0 deletions CMake/FileList.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ set(Core_HDR_FILES
${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.h
${PROJECT_SOURCE_DIR}/Source/Core/LayoutDetails.h
${PROJECT_SOURCE_DIR}/Source/Core/LayoutEngine.h
${PROJECT_SOURCE_DIR}/Source/Core/LayoutFlex.h
${PROJECT_SOURCE_DIR}/Source/Core/LayoutInlineBox.h
${PROJECT_SOURCE_DIR}/Source/Core/LayoutInlineBoxText.h
${PROJECT_SOURCE_DIR}/Source/Core/LayoutLineBox.h
Expand Down Expand Up @@ -339,6 +340,7 @@ set(Core_SRC_FILES
${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.cpp
${PROJECT_SOURCE_DIR}/Source/Core/LayoutDetails.cpp
${PROJECT_SOURCE_DIR}/Source/Core/LayoutEngine.cpp
${PROJECT_SOURCE_DIR}/Source/Core/LayoutFlex.cpp
${PROJECT_SOURCE_DIR}/Source/Core/LayoutInlineBox.cpp
${PROJECT_SOURCE_DIR}/Source/Core/LayoutInlineBoxText.cpp
${PROJECT_SOURCE_DIR}/Source/Core/LayoutLineBox.cpp
Expand Down
27 changes: 22 additions & 5 deletions Include/RmlUi/Core/ComputedValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@
#include "Animation.h"

namespace Rml {

namespace Style
{
namespace Style {

struct LengthPercentageAuto {
enum Type { Auto, Length, Percentage } type = Length;
Expand All @@ -60,7 +58,7 @@ struct NumberAuto {
using Margin = LengthPercentageAuto;
using Padding = LengthPercentage;

enum class Display : uint8_t { None, Block, Inline, InlineBlock, Table, TableRow, TableRowGroup, TableColumn, TableColumnGroup, TableCell };
enum class Display : uint8_t { None, Block, Inline, InlineBlock, Flex, Table, TableRow, TableRowGroup, TableColumn, TableColumnGroup, TableCell };
enum class Position : uint8_t { Static, Relative, Absolute, Fixed };

using Top = LengthPercentageAuto;
Expand Down Expand Up @@ -135,6 +133,14 @@ using TransformOrigin = LengthPercentage;
enum class OriginX : uint8_t { Left, Center, Right };
enum class OriginY : uint8_t { Top, Center, Bottom };

enum class AlignContent : uint8_t { FlexStart, FlexEnd, Center, SpaceBetween, SpaceAround, Stretch };
enum class AlignItems : uint8_t { FlexStart, FlexEnd, Center, Baseline, Stretch };
enum class AlignSelf : uint8_t { Auto, FlexStart, FlexEnd, Center, Baseline, Stretch };
using FlexBasis = LengthPercentageAuto;
enum class FlexDirection : uint8_t { Row, RowReverse, Column, ColumnReverse };
enum class FlexWrap : uint8_t { Nowrap, Wrap, WrapReverse };
enum class JustifyContent : uint8_t { FlexStart, FlexEnd, Center, SpaceBetween, SpaceAround };


/*
A computed value is a value resolved as far as possible :before: introducing layouting. See CSS specs for details of each property.
Expand Down Expand Up @@ -223,8 +229,19 @@ struct ComputedValues

bool has_decorator = false;
bool has_font_effect = false;

AlignContent align_content = AlignContent::Stretch;
AlignItems align_items = AlignItems::Stretch;
AlignSelf align_self = AlignSelf::Auto;
FlexDirection flex_direction = FlexDirection::Row;
FlexWrap flex_wrap = FlexWrap::Nowrap;
JustifyContent justify_content = JustifyContent::FlexStart;
FlexBasis flex_basis = { FlexBasis::Auto };
float flex_grow = 0.f;
float flex_shrink = 1.f;
};
}

} // namespace Style


// Resolves a computed LengthPercentage value to the base unit 'px'.
Expand Down
2 changes: 2 additions & 0 deletions Include/RmlUi/Core/ElementScroll.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class RMLUICORE_API ElementScroll
bool CreateScrollbar(Orientation orientation);
// Creates the scrollbar corner.
bool CreateCorner();
// Update properties of scroll elements immediately after construction.
void UpdateScrollElementProperties(Element* scroll_element);

Element* element;

Expand Down
12 changes: 12 additions & 0 deletions Include/RmlUi/Core/ID.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ enum class ShorthandId : uint8_t
Gap,
PerspectiveOrigin,
TransformOrigin,
Flex,
FlexFlow,

NumDefinedIds,
FirstCustomId = NumDefinedIds,
Expand Down Expand Up @@ -159,6 +161,16 @@ enum class PropertyId : uint8_t

FillImage,

AlignContent,
AlignItems,
AlignSelf,
FlexBasis,
FlexDirection,
FlexGrow,
FlexShrink,
FlexWrap,
JustifyContent,

NumDefinedIds,
FirstCustomId = NumDefinedIds,

Expand Down
8 changes: 8 additions & 0 deletions Include/RmlUi/Core/Math.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ Type Lerp(float t, Type v0, Type v1)
return v0 * (1.0f - t) + v1 * t;
}

/// Element-wise maximum.
template <>
RMLUICORE_API Vector2f Max<Vector2f>(Vector2f a, Vector2f b);
/// Element-wise minimum.
template <>
RMLUICORE_API Vector2f Min<Vector2f>(Vector2f a, Vector2f b);

/// Color interpolation.
RMLUICORE_API Colourb RoundedLerp(float t, Colourb c0, Colourb c1);

/// Evaluates if a number is, or close to, zero.
Expand Down
4 changes: 3 additions & 1 deletion Include/RmlUi/Core/PropertySpecification.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ enum class ShorthandType
// Repeatedly resolves the full value string on each property, whether it is a normal property or another shorthand.
RecursiveRepeat,
// Comma-separated list of properties or shorthands, the number of declared values must match the specified.
RecursiveCommaSeparated
RecursiveCommaSeparated,
// The 'flex' shorthand has some special behavior but otherwise acts like 'FallThrough'.
Flex
};


Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2260,7 +2260,7 @@ void Element::BuildStackingContext(ElementList* new_stacking_context)
ordered_child.order = RenderOrder::Positioned;
else if (child->GetFloat() != Style::Float::None)
ordered_child.order = RenderOrder::Floating;
else if (child_display == Style::Display::Block || child_display == Style::Display::Table)
else if (child_display == Style::Display::Block || child_display == Style::Display::Table || child_display == Style::Display::Flex)
ordered_child.order = RenderOrder::Block;
else
ordered_child.order = RenderOrder::Inline;
Expand Down
25 changes: 17 additions & 8 deletions Source/Core/ElementScroll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ void ElementScroll::FormatScrollbars()
}
}


// Creates one of the scroll component's scrollbar.
bool ElementScroll::CreateScrollbar(Orientation orientation)
{
Expand All @@ -226,12 +225,7 @@ bool ElementScroll::CreateScrollbar(Orientation orientation)

Element* child = element->AppendChild(std::move(scrollbar_element), false);

// The construction of scrollbars can occur during layouting, then we need some properties and computed values straight away.
Context* context = element->GetContext();

const float dp_ratio = (context ? context->GetDensityIndependentPixelRatio() : 1.0f);
const Vector2f vp_dimensions = (context ? Vector2f(context->GetDimensions()) : Vector2f(1.0f));
child->Update(dp_ratio, vp_dimensions);
UpdateScrollElementProperties(child);

return true;
}
Expand All @@ -244,11 +238,26 @@ bool ElementScroll::CreateCorner()

ElementPtr corner_element = Factory::InstanceElement(element, "*", "scrollbarcorner", XMLAttributes());
corner = corner_element.get();
element->AppendChild(std::move(corner_element), false);
Element* child = element->AppendChild(std::move(corner_element), false);

UpdateScrollElementProperties(child);

return true;
}

void ElementScroll::UpdateScrollElementProperties(Element* scroll_element)
{
// The construction of scrollbars can occur during layouting, then we need some properties and computed values straight away.
// In particular their size. Furthermore, updating these properties straight away avoids dirtying the document after layouting,
// which may result in one less additional and unnecessary layouting procedure.

Context* context = element->GetContext();

const float dp_ratio = (context ? context->GetDensityIndependentPixelRatio() : 1.0f);
const Vector2f vp_dimensions = (context ? Vector2f(context->GetDimensions()) : Vector2f(1.0f));
scroll_element->Update(dp_ratio, vp_dimensions);
}

ElementScroll::Scrollbar::Scrollbar()
{}

Expand Down
28 changes: 28 additions & 0 deletions Source/Core/ElementStyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,34 @@ PropertyIdSet ElementStyle::ComputeValues(Style::ComputedValues& values, const S
values.has_font_effect = (p->unit == Property::FONTEFFECT);
break;

case PropertyId::AlignContent:
values.align_content = (AlignContent)p->Get<int>();
break;
case PropertyId::AlignItems:
values.align_items = (AlignItems)p->Get<int>();
break;
case PropertyId::AlignSelf:
values.align_self = (AlignSelf)p->Get<int>();
break;
case PropertyId::FlexBasis:
values.flex_basis = ComputeLengthPercentageAuto(p, font_size, document_font_size, dp_ratio, vp_dimensions);
break;
case PropertyId::FlexDirection:
values.flex_direction = (FlexDirection)p->Get<int>();
break;
case PropertyId::FlexGrow:
values.flex_grow = p->Get<float>();
break;
case PropertyId::FlexShrink:
values.flex_shrink = p->Get<float>();
break;
case PropertyId::FlexWrap:
values.flex_wrap = (FlexWrap)p->Get<int>();
break;
case PropertyId::JustifyContent:
values.justify_content = (JustifyContent)p->Get<int>();
break;

// Unhandled properties. Must be manually retrieved with 'GetProperty()'.
case PropertyId::FillImage:
case PropertyId::CaretColor:
Expand Down
10 changes: 7 additions & 3 deletions Source/Core/ElementUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,12 @@ bool ElementUtilities::GetClippingRegion(Vector2i& clip_origin, Vector2i& clip_d
clipping_element->GetClientHeight() < clipping_element->GetScrollHeight() - 0.5f)
{
const Box::Area client_area = clipping_element->GetClientArea();
const Vector2i element_origin(clipping_element->GetAbsoluteOffset(client_area));
const Vector2i element_dimensions(clipping_element->GetBox().GetSize(client_area));
Vector2f element_origin_f = clipping_element->GetAbsoluteOffset(client_area);
Vector2f element_dimensions_f = clipping_element->GetBox().GetSize(client_area);
Math::SnapToPixelGrid(element_origin_f, element_dimensions_f);

const Vector2i element_origin(element_origin_f);
const Vector2i element_dimensions(element_dimensions_f);

if (clip_origin == Vector2i(-1, -1) && clip_dimensions == Vector2i(-1, -1))
{
Expand Down Expand Up @@ -299,7 +303,7 @@ void ElementUtilities::FormatElement(Element* element, Vector2f containing_block
// Generates the box for an element.
void ElementUtilities::BuildBox(Box& box, Vector2f containing_block, Element* element, bool inline_element)
{
LayoutDetails::BuildBox(box, containing_block, element, inline_element);
LayoutDetails::BuildBox(box, containing_block, element, inline_element ? BoxContext::Inline : BoxContext::Block);
}

// Sizes an element, and positions it within its parent offset from the borders of its content area.
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/LayoutBlockBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,8 @@ bool LayoutBlockBox::CatchVerticalOverflow(float cursor)
box_cursor = 0;
interrupted_chain = nullptr;

inner_content_size = Vector2f(0);

return false;
}
}
Expand Down
40 changes: 32 additions & 8 deletions Source/Core/LayoutDetails.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static inline float BorderSizeToContentSize(float border_size, float border_padd
}

// Generates the box for an element.
void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* element, bool inline_element, float override_shrink_to_fit_width)
void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* element, BoxContext box_context, float override_shrink_to_fit_width)
{
if (!element)
{
Expand Down Expand Up @@ -81,7 +81,7 @@ void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* eleme

// Calculate the content area and constraints. 'auto' width and height are handled later.
// For inline non-replaced elements, width and height are ignored, so we can skip the calculations.
if (!inline_element || replaced_element)
if (box_context == BoxContext::Block || box_context == BoxContext::FlexOrTable || replaced_element)
{
if (content_area.x < 0 && computed.width.type != Style::Width::Auto)
content_area.x = ResolveValue(computed.width, containing_block.x);
Expand Down Expand Up @@ -125,15 +125,16 @@ void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* eleme
box.SetContent(content_area);

// Evaluate the margins, and width and height if they are auto.
BuildBoxSizeAndMargins(box, min_size, max_size, containing_block, element, inline_element, replaced_element, override_shrink_to_fit_width);
BuildBoxSizeAndMargins(box, min_size, max_size, containing_block, element, box_context, replaced_element, override_shrink_to_fit_width);
}

// Generates the box for an element placed in a block box.
void LayoutDetails::BuildBox(Box& box, float& min_height, float& max_height, LayoutBlockBox* containing_box, Element* element, bool inline_element, float override_shrink_to_fit_width)
void LayoutDetails::BuildBox(Box& box, float& min_height, float& max_height, LayoutBlockBox* containing_box, Element* element, BoxContext box_context,
float override_shrink_to_fit_width)
{
Vector2f containing_block = LayoutDetails::GetContainingBlock(containing_box);

BuildBox(box, containing_block, element, inline_element, override_shrink_to_fit_width);
BuildBox(box, containing_block, element, box_context, override_shrink_to_fit_width);

if (element)
GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);
Expand Down Expand Up @@ -213,11 +214,12 @@ Vector2f LayoutDetails::GetContainingBlock(const LayoutBlockBox* containing_box)
}


void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element, bool inline_element, bool replaced_element, float override_shrink_to_fit_width)
void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element,
BoxContext box_context, bool replaced_element, float override_shrink_to_fit_width)
{
const ComputedValues& computed = element->GetComputedValues();

if (inline_element)
if (box_context == BoxContext::Inline || box_context == BoxContext::FlexOrTable)
{
// For inline elements, their calculations are straightforward. No worrying about auto margins and dimensions, etc.
// Evaluate the margins. Any declared as 'auto' will resolve to 0.
Expand All @@ -240,7 +242,7 @@ float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_b

Box box;
float min_height, max_height;
LayoutDetails::BuildBox(box, containing_block, element, false, containing_block.x);
LayoutDetails::BuildBox(box, containing_block, element, BoxContext::Block, containing_block.x);
LayoutDetails::GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);

// First we need to format the element, then we get the shrink-to-fit width based on the largest line or box.
Expand All @@ -266,6 +268,28 @@ float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_b
return Math::Min(containing_block.x, block_context_box->GetShrinkToFitWidth());
}

ComputedAxisSize LayoutDetails::BuildComputedHorizontalSize(const ComputedValues& computed)
{
return ComputedAxisSize{computed.width, computed.min_width, computed.max_width, computed.padding_left, computed.padding_right,
computed.margin_left, computed.margin_right, computed.border_left_width, computed.border_right_width, computed.box_sizing};
}

ComputedAxisSize LayoutDetails::BuildComputedVerticalSize(const ComputedValues& computed)
{
return ComputedAxisSize{computed.height, computed.min_height, computed.max_height, computed.padding_top, computed.padding_bottom,
computed.margin_top, computed.margin_bottom, computed.border_top_width, computed.border_bottom_width, computed.box_sizing};
}

void LayoutDetails::GetEdgeSizes(float& margin_a, float& margin_b, float& padding_border_a, float& padding_border_b,
const ComputedAxisSize& computed_size, const float base_value)
{
margin_a = ResolveValue(computed_size.margin_a, base_value);
margin_b = ResolveValue(computed_size.margin_b, base_value);

padding_border_a = Math::Max(0.0f, ResolveValue(computed_size.padding_a, base_value)) + Math::Max(0.0f, computed_size.border_a);
padding_border_b = Math::Max(0.0f, ResolveValue(computed_size.padding_b, base_value)) + Math::Max(0.0f, computed_size.border_b);
}

Vector2f LayoutDetails::CalculateSizeForReplacedElement(const Vector2f specified_content_size, const Vector2f min_size, const Vector2f max_size, const Vector2f intrinsic_size, const float intrinsic_ratio)
{
// Start with the element's specified width and height. If any of them are auto, use the element's intrinsic
Expand Down
Loading

0 comments on commit 352a52e

Please sign in to comment.