2021-01-18 19:33:46 +03:00
/*
2023-03-08 23:18:35 +03:00
* Copyright ( c ) 2021 - 2023 , Andreas Kling < kling @ serenityos . org >
2021-08-09 22:28:56 +03:00
* Copyright ( c ) 2021 , Tobias Christiansen < tobyase @ serenityos . org >
2021-01-18 19:33:46 +03:00
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-01-18 19:33:46 +03:00
*/
2021-09-23 15:26:53 +03:00
# include "InlineFormattingContext.h"
2021-10-01 14:43:36 +03:00
# include <AK/Function.h>
2022-03-31 23:11:38 +03:00
# include <AK/QuickSort.h>
2021-05-31 13:54:06 +03:00
# include <AK/StdLibExtras.h>
2021-10-06 21:02:41 +03:00
# include <LibWeb/Layout/BlockContainer.h>
2021-05-31 13:54:06 +03:00
# include <LibWeb/Layout/BlockFormattingContext.h>
2021-01-18 19:33:46 +03:00
# include <LibWeb/Layout/Box.h>
# include <LibWeb/Layout/FlexFormattingContext.h>
2022-06-21 21:40:26 +03:00
# include <LibWeb/Layout/ReplacedBox.h>
2021-05-31 13:54:06 +03:00
# include <LibWeb/Layout/TextNode.h>
2023-02-25 13:04:29 +03:00
# include <LibWeb/Layout/Viewport.h>
2021-01-18 19:33:46 +03:00
namespace Web : : Layout {
2022-04-09 15:47:41 +03:00
// NOTE: We use a custom clamping function here instead of AK::clamp(), since the AK version
// will VERIFY(max >= min) and CSS explicitly allows that (see css-values-4.)
template < typename T >
2022-07-12 15:31:41 +03:00
[[nodiscard]] constexpr T css_clamp ( T const & value , T const & min , T const & max )
2022-04-09 15:47:41 +03:00
{
return : : max ( min , : : min ( value , max ) ) ;
}
2023-07-17 22:19:33 +03:00
CSSPixels FlexFormattingContext : : get_pixel_width ( Box const & box , CSS : : Size const & size ) const
2023-07-17 08:54:15 +03:00
{
2023-07-17 22:19:33 +03:00
return calculate_inner_width ( box , containing_block_width_as_available_size ( box ) , size ) . to_px ( box ) ;
2022-07-19 15:27:53 +03:00
}
2023-07-17 22:19:33 +03:00
CSSPixels FlexFormattingContext : : get_pixel_height ( Box const & box , CSS : : Size const & size ) const
2022-07-19 15:27:53 +03:00
{
2023-07-17 22:19:33 +03:00
return calculate_inner_height ( box , containing_block_height_as_available_size ( box ) , size ) . to_px ( box ) ;
2021-10-13 22:24:00 +03:00
}
2022-07-17 00:30:32 +03:00
FlexFormattingContext : : FlexFormattingContext ( LayoutState & state , Box const & flex_container , FormattingContext * parent )
2022-02-19 22:13:47 +03:00
: FormattingContext ( Type : : Flex , state , flex_container , parent )
2022-02-21 19:42:09 +03:00
, m_flex_container_state ( m_state . get_mutable ( flex_container ) )
2021-10-13 20:59:15 +03:00
, m_flex_direction ( flex_container . computed_values ( ) . flex_direction ( ) )
2021-01-18 19:33:46 +03:00
{
}
2022-03-14 22:21:51 +03:00
FlexFormattingContext : : ~ FlexFormattingContext ( ) = default ;
2021-01-18 19:33:46 +03:00
2023-03-19 11:57:31 +03:00
CSSPixels FlexFormattingContext : : automatic_content_width ( ) const
{
return m_flex_container_state . content_width ( ) ;
}
2022-11-23 20:46:10 +03:00
CSSPixels FlexFormattingContext : : automatic_content_height ( ) const
2022-09-24 14:39:43 +03:00
{
2023-03-19 11:57:31 +03:00
return m_flex_container_state . content_height ( ) ;
2022-09-24 14:39:43 +03:00
}
2022-09-27 16:29:17 +03:00
void FlexFormattingContext : : run ( Box const & run_box , LayoutMode , AvailableSpace const & available_content_space )
2021-01-18 19:33:46 +03:00
{
2021-10-13 23:52:50 +03:00
VERIFY ( & run_box = = & flex_container ( ) ) ;
2022-09-27 16:29:17 +03:00
// NOTE: The available space provided by the parent context is basically our *content box*.
// FFC is currently written in a way that expects that to include padding and border as well,
// so we pad out the available space here to accommodate that.
// FIXME: Refactor the necessary parts of FFC so we don't need this hack!
auto available_width = available_content_space . width ;
if ( available_width . is_definite ( ) )
2023-08-12 19:27:21 +03:00
available_width = AvailableSize : : make_definite ( available_width . to_px_or_zero ( ) + m_flex_container_state . border_box_left ( ) + m_flex_container_state . border_box_right ( ) ) ;
2022-09-27 16:29:17 +03:00
auto available_height = available_content_space . height ;
if ( available_height . is_definite ( ) )
2023-08-12 19:27:21 +03:00
available_height = AvailableSize : : make_definite ( available_height . to_px_or_zero ( ) + m_flex_container_state . border_box_top ( ) + m_flex_container_state . border_box_bottom ( ) ) ;
2022-09-27 16:29:17 +03:00
m_available_space_for_flex_container = AxisAgnosticAvailableSpace {
. main = is_row_layout ( ) ? available_width : available_height ,
. cross = ! is_row_layout ( ) ? available_width : available_height ,
2022-10-04 00:37:38 +03:00
. space = { available_width , available_height } ,
2022-09-27 16:29:17 +03:00
} ;
2021-05-31 13:54:06 +03:00
// This implements https://www.w3.org/TR/css-flexbox-1/#layout-algorithm
2021-10-13 20:57:26 +03:00
// 1. Generate anonymous flex items
2021-10-13 23:52:50 +03:00
generate_anonymous_flex_items ( ) ;
2021-05-31 13:54:06 +03:00
2022-10-04 00:37:38 +03:00
// 2. Determine the available main and cross space for the flex items
determine_available_space_for_items ( AvailableSpace ( available_width , available_height ) ) ;
2022-07-20 20:06:47 +03:00
{
// https://drafts.csswg.org/css-flexbox-1/#definite-sizes
// 3. If a single-line flex container has a definite cross size,
// the automatic preferred outer cross size of any stretched flex items is the flex container’ s inner cross size
// (clamped to the flex item’ s min and max cross size) and is considered definite.
if ( is_single_line ( ) & & has_definite_cross_size ( flex_container ( ) ) ) {
2023-03-08 19:36:32 +03:00
auto flex_container_inner_cross_size = inner_cross_size ( flex_container ( ) ) ;
2022-07-20 20:06:47 +03:00
for ( auto & item : m_flex_items ) {
if ( ! flex_item_is_stretched ( item ) )
continue ;
2023-07-17 22:19:33 +03:00
auto item_min_cross_size = has_cross_min_size ( item . box ) ? specified_cross_min_size ( item . box ) : 0 ;
auto item_max_cross_size = has_cross_max_size ( item . box ) ? specified_cross_max_size ( item . box ) : INFINITY ;
2022-07-20 20:06:47 +03:00
auto item_preferred_outer_cross_size = css_clamp ( flex_container_inner_cross_size , item_min_cross_size , item_max_cross_size ) ;
auto item_inner_cross_size = item_preferred_outer_cross_size - item . margins . cross_before - item . margins . cross_after - item . padding . cross_before - item . padding . cross_after - item . borders . cross_before - item . borders . cross_after ;
set_cross_size ( item . box , item_inner_cross_size ) ;
}
}
}
2021-05-31 13:54:06 +03:00
// 3. Determine the flex base size and hypothetical main size of each item
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
2023-02-27 02:09:02 +03:00
if ( item . box - > is_replaced_box ( ) ) {
2022-06-21 21:40:26 +03:00
// FIXME: Get rid of prepare_for_replaced_layout() and make replaced elements figure out their intrinsic size lazily.
2023-02-27 02:09:02 +03:00
static_cast < ReplacedBox & > ( * item . box ) . prepare_for_replaced_layout ( ) ;
2022-06-21 21:40:26 +03:00
}
2023-03-08 19:24:35 +03:00
determine_flex_base_size_and_hypothetical_main_size ( item ) ;
2021-05-31 13:54:06 +03:00
}
2022-09-27 16:29:17 +03:00
if ( available_width . is_intrinsic_sizing_constraint ( ) | | available_height . is_intrinsic_sizing_constraint ( ) ) {
2023-01-07 19:43:34 +03:00
// We're computing intrinsic size for the flex container. This happens at the end of run().
} else {
2022-04-06 02:20:20 +03:00
2023-01-07 19:43:34 +03:00
// 4. Determine the main size of the flex container
determine_main_size_of_flex_container ( ) ;
2022-04-06 02:20:20 +03:00
}
2021-05-31 13:54:06 +03:00
// 5. Collect flex items into flex lines:
// After this step no additional items are to be added to flex_lines or any of its items!
2022-03-12 16:40:13 +03:00
collect_flex_items_into_flex_lines ( ) ;
2021-05-31 13:54:06 +03:00
2021-10-13 23:11:50 +03:00
// 6. Resolve the flexible lengths
2022-03-12 16:40:13 +03:00
resolve_flexible_lengths ( ) ;
2021-05-31 13:54:06 +03:00
// Cross Size Determination
// 7. Determine the hypothetical cross size of each item
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
determine_hypothetical_cross_size_of_item ( item , false ) ;
2021-05-31 13:54:06 +03:00
}
// 8. Calculate the cross size of each flex line.
2022-10-07 14:58:18 +03:00
calculate_cross_size_of_each_flex_line ( ) ;
2021-01-18 19:33:46 +03:00
2021-05-31 13:54:06 +03:00
// 9. Handle 'align-content: stretch'.
2022-10-14 14:50:40 +03:00
handle_align_content_stretch ( ) ;
2021-05-31 13:54:06 +03:00
// 10. Collapse visibility:collapse items.
// FIXME: This
// 11. Determine the used cross size of each flex item.
2021-10-13 23:52:50 +03:00
determine_used_cross_size_of_each_flex_item ( ) ;
2021-05-31 13:54:06 +03:00
// 12. Distribute any remaining free space.
2022-03-12 16:40:13 +03:00
distribute_any_remaining_free_space ( ) ;
2021-05-31 13:54:06 +03:00
// 13. Resolve cross-axis auto margins.
2022-09-28 18:23:22 +03:00
resolve_cross_axis_auto_margins ( ) ;
2021-05-31 13:54:06 +03:00
// 14. Align all flex items along the cross-axis
2021-10-13 23:52:50 +03:00
align_all_flex_items_along_the_cross_axis ( ) ;
2021-05-31 13:54:06 +03:00
// 15. Determine the flex container’ s used cross size:
2022-10-07 14:58:18 +03:00
determine_flex_container_used_cross_size ( ) ;
2021-05-31 13:54:06 +03:00
2022-07-20 20:06:47 +03:00
{
// https://drafts.csswg.org/css-flexbox-1/#definite-sizes
// 4. Once the cross size of a flex line has been determined,
// the cross sizes of items in auto-sized flex containers are also considered definite for the purpose of layout.
auto const & flex_container_computed_cross_size = is_row_layout ( ) ? flex_container ( ) . computed_values ( ) . height ( ) : flex_container ( ) . computed_values ( ) . width ( ) ;
if ( flex_container_computed_cross_size . is_auto ( ) ) {
for ( auto & item : m_flex_items ) {
2023-01-07 21:28:06 +03:00
set_cross_size ( item . box , item . cross_size . value ( ) ) ;
2022-07-20 20:06:47 +03:00
}
}
}
2022-07-22 23:02:02 +03:00
{
// NOTE: We re-resolve cross sizes here, now that we can resolve percentages.
// 7. Determine the hypothetical cross size of each item
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
determine_hypothetical_cross_size_of_item ( item , true ) ;
2022-07-22 23:02:02 +03:00
}
// 11. Determine the used cross size of each flex item.
determine_used_cross_size_of_each_flex_item ( ) ;
}
2021-10-13 23:25:39 +03:00
// 16. Align all flex lines (per align-content)
2021-10-13 23:44:54 +03:00
align_all_flex_lines ( ) ;
2022-02-27 12:01:38 +03:00
2023-01-07 19:43:34 +03:00
if ( available_width . is_intrinsic_sizing_constraint ( ) | | available_height . is_intrinsic_sizing_constraint ( ) ) {
// We're computing intrinsic size for the flex container.
determine_intrinsic_size_of_flex_container ( ) ;
2023-03-14 14:57:10 +03:00
} else {
// This is a normal layout (not intrinsic sizing).
// AD-HOC: Finally, layout the inside of all flex items.
copy_dimensions_from_flex_items_to_boxes ( ) ;
for ( auto & item : m_flex_items ) {
auto & box_state = m_state . get ( item . box ) ;
if ( auto independent_formatting_context = layout_inside ( item . box , LayoutMode : : Normal , box_state . available_inner_space_or_constraints_from ( m_available_space_for_flex_container - > space ) ) )
independent_formatting_context - > parent_context_did_dimension_child_root_box ( ) ;
2023-07-03 21:32:57 +03:00
compute_inset ( item . box ) ;
2023-03-14 14:57:10 +03:00
}
2023-01-07 19:43:34 +03:00
}
2022-07-22 17:36:17 +03:00
}
2022-06-21 21:17:28 +03:00
2022-07-22 17:36:17 +03:00
void FlexFormattingContext : : parent_context_did_dimension_child_root_box ( )
{
2022-06-21 21:17:28 +03:00
flex_container ( ) . for_each_child_of_type < Box > ( [ & ] ( Layout : : Box & box ) {
2022-09-27 16:29:17 +03:00
if ( box . is_absolutely_positioned ( ) ) {
auto & cb_state = m_state . get ( * box . containing_block ( ) ) ;
auto available_width = AvailableSize : : make_definite ( cb_state . content_width ( ) + cb_state . padding_left + cb_state . padding_right ) ;
auto available_height = AvailableSize : : make_definite ( cb_state . content_height ( ) + cb_state . padding_top + cb_state . padding_bottom ) ;
layout_absolutely_positioned_element ( box , AvailableSpace ( available_width , available_height ) ) ;
}
2022-06-21 21:17:28 +03:00
} ) ;
2021-05-31 13:54:06 +03:00
}
2021-10-13 20:57:26 +03:00
2021-10-13 23:44:54 +03:00
void FlexFormattingContext : : populate_specified_margins ( FlexItem & item , CSS : : FlexDirection flex_direction ) const
2021-10-13 20:57:26 +03:00
{
2023-02-27 02:09:02 +03:00
auto width_of_containing_block = m_state . get ( * item . box - > containing_block ( ) ) . content_width ( ) ;
2021-10-13 20:57:26 +03:00
// FIXME: This should also take reverse-ness into account
if ( flex_direction = = CSS : : FlexDirection : : Row | | flex_direction = = CSS : : FlexDirection : : RowReverse ) {
2023-02-27 02:09:02 +03:00
item . borders . main_before = item . box - > computed_values ( ) . border_left ( ) . width ;
item . borders . main_after = item . box - > computed_values ( ) . border_right ( ) . width ;
item . borders . cross_before = item . box - > computed_values ( ) . border_top ( ) . width ;
item . borders . cross_after = item . box - > computed_values ( ) . border_bottom ( ) . width ;
2023-05-06 17:34:55 +03:00
item . padding . main_before = item . box - > computed_values ( ) . padding ( ) . left ( ) . to_px ( item . box , width_of_containing_block ) ;
item . padding . main_after = item . box - > computed_values ( ) . padding ( ) . right ( ) . to_px ( item . box , width_of_containing_block ) ;
item . padding . cross_before = item . box - > computed_values ( ) . padding ( ) . top ( ) . to_px ( item . box , width_of_containing_block ) ;
item . padding . cross_after = item . box - > computed_values ( ) . padding ( ) . bottom ( ) . to_px ( item . box , width_of_containing_block ) ;
2023-02-27 02:09:02 +03:00
2023-05-06 17:34:55 +03:00
item . margins . main_before = item . box - > computed_values ( ) . margin ( ) . left ( ) . to_px ( item . box , width_of_containing_block ) ;
item . margins . main_after = item . box - > computed_values ( ) . margin ( ) . right ( ) . to_px ( item . box , width_of_containing_block ) ;
item . margins . cross_before = item . box - > computed_values ( ) . margin ( ) . top ( ) . to_px ( item . box , width_of_containing_block ) ;
item . margins . cross_after = item . box - > computed_values ( ) . margin ( ) . bottom ( ) . to_px ( item . box , width_of_containing_block ) ;
2023-02-27 02:09:02 +03:00
item . margins . main_before_is_auto = item . box - > computed_values ( ) . margin ( ) . left ( ) . is_auto ( ) ;
item . margins . main_after_is_auto = item . box - > computed_values ( ) . margin ( ) . right ( ) . is_auto ( ) ;
item . margins . cross_before_is_auto = item . box - > computed_values ( ) . margin ( ) . top ( ) . is_auto ( ) ;
item . margins . cross_after_is_auto = item . box - > computed_values ( ) . margin ( ) . bottom ( ) . is_auto ( ) ;
2021-10-13 20:57:26 +03:00
} else {
2023-02-27 02:09:02 +03:00
item . borders . main_before = item . box - > computed_values ( ) . border_top ( ) . width ;
item . borders . main_after = item . box - > computed_values ( ) . border_bottom ( ) . width ;
item . borders . cross_before = item . box - > computed_values ( ) . border_left ( ) . width ;
item . borders . cross_after = item . box - > computed_values ( ) . border_right ( ) . width ;
2023-05-06 17:34:55 +03:00
item . padding . main_before = item . box - > computed_values ( ) . padding ( ) . top ( ) . to_px ( item . box , width_of_containing_block ) ;
item . padding . main_after = item . box - > computed_values ( ) . padding ( ) . bottom ( ) . to_px ( item . box , width_of_containing_block ) ;
item . padding . cross_before = item . box - > computed_values ( ) . padding ( ) . left ( ) . to_px ( item . box , width_of_containing_block ) ;
item . padding . cross_after = item . box - > computed_values ( ) . padding ( ) . right ( ) . to_px ( item . box , width_of_containing_block ) ;
2023-02-27 02:09:02 +03:00
2023-05-06 17:34:55 +03:00
item . margins . main_before = item . box - > computed_values ( ) . margin ( ) . top ( ) . to_px ( item . box , width_of_containing_block ) ;
item . margins . main_after = item . box - > computed_values ( ) . margin ( ) . bottom ( ) . to_px ( item . box , width_of_containing_block ) ;
item . margins . cross_before = item . box - > computed_values ( ) . margin ( ) . left ( ) . to_px ( item . box , width_of_containing_block ) ;
item . margins . cross_after = item . box - > computed_values ( ) . margin ( ) . right ( ) . to_px ( item . box , width_of_containing_block ) ;
2023-02-27 02:09:02 +03:00
item . margins . main_before_is_auto = item . box - > computed_values ( ) . margin ( ) . top ( ) . is_auto ( ) ;
item . margins . main_after_is_auto = item . box - > computed_values ( ) . margin ( ) . bottom ( ) . is_auto ( ) ;
item . margins . cross_before_is_auto = item . box - > computed_values ( ) . margin ( ) . left ( ) . is_auto ( ) ;
item . margins . cross_after_is_auto = item . box - > computed_values ( ) . margin ( ) . right ( ) . is_auto ( ) ;
2021-10-13 20:57:26 +03:00
}
2023-07-08 05:48:11 +03:00
}
2021-10-13 20:57:26 +03:00
// https://www.w3.org/TR/css-flexbox-1/#flex-items
2021-10-13 23:52:50 +03:00
void FlexFormattingContext : : generate_anonymous_flex_items ( )
2021-10-13 20:57:26 +03:00
{
// More like, sift through the already generated items.
// After this step no items are to be added or removed from flex_items!
// It holds every item we need to consider and there should be nothing in the following
// calculations that could change that.
// This is particularly important since we take references to the items stored in flex_items
// later, whose addresses won't be stable if we added or removed any items.
2022-03-31 23:11:38 +03:00
HashMap < int , Vector < FlexItem > > order_item_bucket ;
2021-10-13 23:52:50 +03:00
flex_container ( ) . for_each_child_of_type < Box > ( [ & ] ( Box & child_box ) {
2022-12-28 12:11:54 +03:00
if ( can_skip_is_anonymous_text_run ( child_box ) )
return IterationDecision : : Continue ;
2021-10-13 20:57:26 +03:00
// Skip any "out-of-flow" children
if ( child_box . is_out_of_flow ( * this ) )
return IterationDecision : : Continue ;
child_box . set_flex_item ( true ) ;
2023-03-08 19:24:35 +03:00
FlexItem item = { child_box } ;
populate_specified_margins ( item , m_flex_direction ) ;
2022-03-31 23:11:38 +03:00
auto & order_bucket = order_item_bucket . ensure ( child_box . computed_values ( ) . order ( ) ) ;
2023-03-08 19:24:35 +03:00
order_bucket . append ( move ( item ) ) ;
2022-03-31 23:11:38 +03:00
2021-10-13 20:57:26 +03:00
return IterationDecision : : Continue ;
} ) ;
2022-03-31 23:11:38 +03:00
auto keys = order_item_bucket . keys ( ) ;
2022-04-02 12:29:55 +03:00
if ( is_direction_reverse ( ) ) {
quick_sort ( keys , [ ] ( auto & a , auto & b ) { return a > b ; } ) ;
} else {
quick_sort ( keys , [ ] ( auto & a , auto & b ) { return a < b ; } ) ;
}
2022-03-31 23:11:38 +03:00
for ( auto key : keys ) {
auto order_bucket = order_item_bucket . get ( key ) ;
if ( order_bucket . has_value ( ) ) {
2023-03-09 01:13:05 +03:00
auto & items = order_bucket . value ( ) ;
2022-04-02 12:29:55 +03:00
if ( is_direction_reverse ( ) ) {
2023-03-08 19:24:35 +03:00
for ( auto item : items . in_reverse ( ) ) {
m_flex_items . append ( move ( item ) ) ;
2022-04-02 12:29:55 +03:00
}
} else {
2023-03-08 19:24:35 +03:00
for ( auto item : items ) {
m_flex_items . append ( move ( item ) ) ;
2022-04-02 12:29:55 +03:00
}
2022-03-31 23:11:38 +03:00
}
}
}
2021-10-13 20:57:26 +03:00
}
2021-10-13 22:24:00 +03:00
bool FlexFormattingContext : : has_definite_main_size ( Box const & box ) const
{
2022-07-17 19:46:38 +03:00
auto const & used_values = m_state . get ( box ) ;
return is_row_layout ( ) ? used_values . has_definite_width ( ) : used_values . has_definite_height ( ) ;
2021-10-13 22:24:00 +03:00
}
2023-03-08 19:36:32 +03:00
CSSPixels FlexFormattingContext : : inner_main_size ( Box const & box ) const
2021-10-13 22:24:00 +03:00
{
2022-02-20 17:51:24 +03:00
auto const & box_state = m_state . get ( box ) ;
2022-07-17 18:59:02 +03:00
return is_row_layout ( ) ? box_state . content_width ( ) : box_state . content_height ( ) ;
2021-10-13 22:24:00 +03:00
}
2023-03-08 19:36:32 +03:00
CSSPixels FlexFormattingContext : : inner_cross_size ( Box const & box ) const
2021-10-13 22:24:00 +03:00
{
2022-02-20 17:51:24 +03:00
auto const & box_state = m_state . get ( box ) ;
2022-07-17 18:59:02 +03:00
return is_row_layout ( ) ? box_state . content_height ( ) : box_state . content_width ( ) ;
2021-10-13 22:24:00 +03:00
}
bool FlexFormattingContext : : has_main_min_size ( Box const & box ) const
{
2022-07-14 13:52:01 +03:00
auto const & value = is_row_layout ( ) ? box . computed_values ( ) . min_width ( ) : box . computed_values ( ) . min_height ( ) ;
return ! value . is_auto ( ) ;
2021-10-13 22:24:00 +03:00
}
bool FlexFormattingContext : : has_cross_min_size ( Box const & box ) const
{
2022-07-14 13:52:01 +03:00
auto const & value = is_row_layout ( ) ? box . computed_values ( ) . min_height ( ) : box . computed_values ( ) . min_width ( ) ;
return ! value . is_auto ( ) ;
2021-10-13 22:24:00 +03:00
}
bool FlexFormattingContext : : has_definite_cross_size ( Box const & box ) const
{
2022-07-17 19:46:38 +03:00
auto const & used_values = m_state . get ( box ) ;
return is_row_layout ( ) ? used_values . has_definite_height ( ) : used_values . has_definite_width ( ) ;
2021-10-13 22:24:00 +03:00
}
2023-07-17 22:19:33 +03:00
CSSPixels FlexFormattingContext : : specified_main_min_size ( Box const & box ) const
2021-10-13 22:24:00 +03:00
{
return is_row_layout ( )
2023-07-17 22:19:33 +03:00
? get_pixel_width ( box , box . computed_values ( ) . min_width ( ) )
: get_pixel_height ( box , box . computed_values ( ) . min_height ( ) ) ;
2021-10-13 22:24:00 +03:00
}
2023-07-17 22:19:33 +03:00
CSSPixels FlexFormattingContext : : specified_cross_min_size ( Box const & box ) const
2021-10-13 22:24:00 +03:00
{
return is_row_layout ( )
2023-07-17 22:19:33 +03:00
? get_pixel_height ( box , box . computed_values ( ) . min_height ( ) )
: get_pixel_width ( box , box . computed_values ( ) . min_width ( ) ) ;
2021-10-13 22:24:00 +03:00
}
bool FlexFormattingContext : : has_main_max_size ( Box const & box ) const
{
2022-07-14 13:52:01 +03:00
auto const & value = is_row_layout ( ) ? box . computed_values ( ) . max_width ( ) : box . computed_values ( ) . max_height ( ) ;
2022-09-25 16:48:23 +03:00
return ! value . is_none ( ) ;
2021-10-13 22:24:00 +03:00
}
bool FlexFormattingContext : : has_cross_max_size ( Box const & box ) const
{
2022-07-14 13:52:01 +03:00
auto const & value = ! is_row_layout ( ) ? box . computed_values ( ) . max_width ( ) : box . computed_values ( ) . max_height ( ) ;
2022-09-25 16:48:23 +03:00
return ! value . is_none ( ) ;
2021-10-13 22:24:00 +03:00
}
2023-07-17 22:19:33 +03:00
CSSPixels FlexFormattingContext : : specified_main_max_size ( Box const & box ) const
2021-10-13 22:24:00 +03:00
{
return is_row_layout ( )
2023-07-17 22:19:33 +03:00
? get_pixel_width ( box , box . computed_values ( ) . max_width ( ) )
: get_pixel_height ( box , box . computed_values ( ) . max_height ( ) ) ;
2021-10-13 22:24:00 +03:00
}
2023-07-17 22:19:33 +03:00
CSSPixels FlexFormattingContext : : specified_cross_max_size ( Box const & box ) const
2021-10-13 22:24:00 +03:00
{
return is_row_layout ( )
2023-07-17 22:19:33 +03:00
? get_pixel_height ( box , box . computed_values ( ) . max_height ( ) )
: get_pixel_width ( box , box . computed_values ( ) . max_width ( ) ) ;
2021-10-13 22:24:00 +03:00
}
bool FlexFormattingContext : : is_cross_auto ( Box const & box ) const
{
2022-02-18 18:10:11 +03:00
auto & cross_length = is_row_layout ( ) ? box . computed_values ( ) . height ( ) : box . computed_values ( ) . width ( ) ;
2022-07-07 13:36:02 +03:00
return cross_length . is_auto ( ) ;
2021-10-13 22:24:00 +03:00
}
2022-11-03 20:10:17 +03:00
void FlexFormattingContext : : set_main_size ( Box const & box , CSSPixels size )
2021-10-13 22:24:00 +03:00
{
if ( is_row_layout ( ) )
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( box ) . set_content_width ( size ) ;
2021-10-13 22:24:00 +03:00
else
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( box ) . set_content_height ( size ) ;
2021-10-13 22:24:00 +03:00
}
2022-11-03 20:10:17 +03:00
void FlexFormattingContext : : set_cross_size ( Box const & box , CSSPixels size )
2021-10-13 22:24:00 +03:00
{
if ( is_row_layout ( ) )
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( box ) . set_content_height ( size ) ;
2021-10-13 22:24:00 +03:00
else
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( box ) . set_content_width ( size ) ;
2021-10-13 22:24:00 +03:00
}
2022-11-03 20:10:17 +03:00
void FlexFormattingContext : : set_offset ( Box const & box , CSSPixels main_offset , CSSPixels cross_offset )
2021-10-13 22:24:00 +03:00
{
if ( is_row_layout ( ) )
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( box ) . offset = CSSPixelPoint { main_offset , cross_offset } ;
2021-10-13 22:24:00 +03:00
else
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( box ) . offset = CSSPixelPoint { cross_offset , main_offset } ;
2021-10-13 22:24:00 +03:00
}
2022-11-03 20:10:17 +03:00
void FlexFormattingContext : : set_main_axis_first_margin ( FlexItem & item , CSSPixels margin )
2021-10-13 22:24:00 +03:00
{
2022-06-21 22:18:00 +03:00
item . margins . main_before = margin ;
2021-10-13 22:24:00 +03:00
if ( is_row_layout ( ) )
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( item . box ) . margin_left = margin ;
2021-10-13 22:24:00 +03:00
else
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( item . box ) . margin_top = margin ;
2021-10-13 22:24:00 +03:00
}
2022-11-03 20:10:17 +03:00
void FlexFormattingContext : : set_main_axis_second_margin ( FlexItem & item , CSSPixels margin )
2021-10-13 22:24:00 +03:00
{
2022-06-21 22:18:00 +03:00
item . margins . main_after = margin ;
2021-10-13 22:24:00 +03:00
if ( is_row_layout ( ) )
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( item . box ) . margin_right = margin ;
2021-10-13 22:24:00 +03:00
else
2022-11-04 23:32:50 +03:00
m_state . get_mutable ( item . box ) . margin_bottom = margin ;
2021-10-13 22:24:00 +03:00
}
2022-09-27 16:29:17 +03:00
// https://drafts.csswg.org/css-flexbox-1/#algo-available
void FlexFormattingContext : : determine_available_space_for_items ( AvailableSpace const & available_space )
2021-10-13 22:24:00 +03:00
{
2022-09-27 16:29:17 +03:00
// For each dimension, if that dimension of the flex container’ s content box is a definite size, use that;
// if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint;
// otherwise, subtract the flex container’ s margin, border, and padding from the space available to the flex container in that dimension and use that value.
// This might result in an infinite value.
2021-10-13 22:24:00 +03:00
2022-09-27 16:29:17 +03:00
Optional < AvailableSize > available_width_for_items ;
if ( m_flex_container_state . has_definite_width ( ) ) {
2023-05-01 18:49:31 +03:00
available_width_for_items = AvailableSize : : make_definite ( m_flex_container_state . content_width ( ) ) ;
2021-10-13 22:24:00 +03:00
} else {
2022-09-27 16:29:17 +03:00
if ( available_space . width . is_intrinsic_sizing_constraint ( ) ) {
available_width_for_items = available_space . width ;
} else {
if ( available_space . width . is_definite ( ) ) {
2023-08-12 19:27:21 +03:00
auto remaining = available_space . width . to_px_or_zero ( )
2022-09-27 16:29:17 +03:00
- m_flex_container_state . margin_left
- m_flex_container_state . margin_right
- m_flex_container_state . border_left
- m_flex_container_state . padding_right
- m_flex_container_state . padding_left
- m_flex_container_state . padding_right ;
available_width_for_items = AvailableSize : : make_definite ( remaining ) ;
} else {
available_width_for_items = AvailableSize : : make_indefinite ( ) ;
2022-08-27 12:44:10 +03:00
}
2021-10-13 22:41:35 +03:00
}
}
2022-09-27 16:29:17 +03:00
Optional < AvailableSize > available_height_for_items ;
if ( m_flex_container_state . has_definite_height ( ) ) {
2023-05-01 18:49:31 +03:00
available_height_for_items = AvailableSize : : make_definite ( m_flex_container_state . content_height ( ) ) ;
2021-10-13 22:41:35 +03:00
} else {
2022-09-27 16:29:17 +03:00
if ( available_space . height . is_intrinsic_sizing_constraint ( ) ) {
available_height_for_items = available_space . height ;
} else {
if ( available_space . height . is_definite ( ) ) {
2023-08-12 19:27:21 +03:00
auto remaining = available_space . height . to_px_or_zero ( )
2022-09-27 16:29:17 +03:00
- m_flex_container_state . margin_top
- m_flex_container_state . margin_bottom
- m_flex_container_state . border_top
- m_flex_container_state . padding_bottom
- m_flex_container_state . padding_top
- m_flex_container_state . padding_bottom ;
available_height_for_items = AvailableSize : : make_definite ( remaining ) ;
} else {
available_height_for_items = AvailableSize : : make_indefinite ( ) ;
2022-08-27 12:44:10 +03:00
}
2021-10-13 22:41:35 +03:00
}
}
2022-09-27 16:29:17 +03:00
if ( is_row_layout ( ) ) {
m_available_space_for_items = AxisAgnosticAvailableSpace {
. main = * available_width_for_items ,
. cross = * available_height_for_items ,
2022-10-04 00:37:38 +03:00
. space = { * available_width_for_items , * available_height_for_items } ,
2022-09-27 16:29:17 +03:00
} ;
} else {
m_available_space_for_items = AxisAgnosticAvailableSpace {
. main = * available_height_for_items ,
. cross = * available_width_for_items ,
2022-10-04 00:37:38 +03:00
. space = { * available_width_for_items , * available_height_for_items } ,
2022-09-27 16:29:17 +03:00
} ;
}
2021-10-13 22:41:35 +03:00
}
2022-07-07 13:39:24 +03:00
// https://drafts.csswg.org/css-flexbox-1/#propdef-flex-basis
2023-06-21 20:39:07 +03:00
CSS : : FlexBasis FlexFormattingContext : : used_flex_basis_for_item ( FlexItem const & item ) const
2022-07-07 13:39:24 +03:00
{
2023-02-27 02:09:02 +03:00
auto flex_basis = item . box - > computed_values ( ) . flex_basis ( ) ;
2022-07-07 13:39:24 +03:00
2023-06-21 20:39:07 +03:00
if ( flex_basis . has < CSS : : Size > ( ) & & flex_basis . get < CSS : : Size > ( ) . is_auto ( ) ) {
2022-07-07 13:39:24 +03:00
// https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto
// When specified on a flex item, the auto keyword retrieves the value of the main size property as the used flex-basis.
// If that value is itself auto, then the used value is content.
2023-02-27 02:09:02 +03:00
auto const & main_size = is_row_layout ( ) ? item . box - > computed_values ( ) . width ( ) : item . box - > computed_values ( ) . height ( ) ;
2022-07-07 13:39:24 +03:00
if ( main_size . is_auto ( ) ) {
2023-06-21 20:39:07 +03:00
flex_basis = CSS : : FlexBasisContent { } ;
2022-07-07 13:39:24 +03:00
} else {
2023-06-21 20:39:07 +03:00
flex_basis = main_size ;
2022-07-07 13:39:24 +03:00
}
}
2023-04-18 10:10:28 +03:00
// For example, percentage values of flex-basis are resolved against the flex item’ s containing block
// (i.e. its flex container); and if that containing block’ s size is indefinite,
// the used value for flex-basis is content.
2023-06-21 20:39:07 +03:00
if ( flex_basis . has < CSS : : Size > ( )
& & flex_basis . get < CSS : : Size > ( ) . is_percentage ( )
2023-04-18 10:10:28 +03:00
& & ! has_definite_main_size ( flex_container ( ) ) ) {
2023-06-21 20:39:07 +03:00
flex_basis = CSS : : FlexBasisContent { } ;
2023-04-18 10:10:28 +03:00
}
2022-07-07 13:39:24 +03:00
return flex_basis ;
}
2023-05-24 11:50:57 +03:00
CSSPixels FlexFormattingContext : : calculate_main_size_from_cross_size_and_aspect_ratio ( CSSPixels cross_size , double aspect_ratio ) const
2023-05-10 11:09:29 +03:00
{
if ( is_row_layout ( ) )
return cross_size * aspect_ratio ;
return cross_size / aspect_ratio ;
}
2023-07-05 18:30:48 +03:00
CSSPixels FlexFormattingContext : : calculate_cross_size_from_main_size_and_aspect_ratio ( CSSPixels main_size , double aspect_ratio ) const
{
if ( is_row_layout ( ) )
return main_size / aspect_ratio ;
return main_size * aspect_ratio ;
}
2023-05-10 11:09:29 +03:00
// This function takes a size in the main axis and adjusts it according to the aspect ratio of the box
// if the min/max constraints in the cross axis forces us to come up with a new main axis size.
CSSPixels FlexFormattingContext : : adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints ( Box const & box , CSSPixels main_size , CSS : : Size const & min_cross_size , CSS : : Size const & max_cross_size ) const
{
if ( ! max_cross_size . is_none ( ) ) {
2023-05-10 18:15:19 +03:00
auto max_cross_size_px = max_cross_size . to_px ( box , ! is_row_layout ( ) ? m_flex_container_state . content_width ( ) : m_flex_container_state . content_height ( ) ) ;
2023-06-08 18:47:50 +03:00
main_size = min ( main_size , calculate_main_size_from_cross_size_and_aspect_ratio ( max_cross_size_px , box . preferred_aspect_ratio ( ) . value ( ) ) ) ;
2023-05-10 11:09:29 +03:00
}
if ( ! min_cross_size . is_auto ( ) ) {
2023-05-10 18:15:19 +03:00
auto min_cross_size_px = min_cross_size . to_px ( box , ! is_row_layout ( ) ? m_flex_container_state . content_width ( ) : m_flex_container_state . content_height ( ) ) ;
2023-06-08 18:47:50 +03:00
main_size = max ( main_size , calculate_main_size_from_cross_size_and_aspect_ratio ( min_cross_size_px , box . preferred_aspect_ratio ( ) . value ( ) ) ) ;
2023-05-10 11:09:29 +03:00
}
return main_size ;
}
2021-10-13 22:49:28 +03:00
// https://www.w3.org/TR/css-flexbox-1/#algo-main-item
2023-03-08 19:24:35 +03:00
void FlexFormattingContext : : determine_flex_base_size_and_hypothetical_main_size ( FlexItem & item )
2021-10-13 22:49:28 +03:00
{
2023-03-08 19:24:35 +03:00
auto & child_box = item . box ;
2021-10-14 00:26:31 +03:00
2023-03-08 19:24:35 +03:00
item . flex_base_size = [ & ] {
item . used_flex_basis = used_flex_basis_for_item ( item ) ;
2021-10-14 00:26:31 +03:00
2023-06-21 20:39:07 +03:00
item . used_flex_basis_is_definite = [ & ] ( CSS : : FlexBasis const & flex_basis ) - > bool {
if ( ! flex_basis . has < CSS : : Size > ( ) )
2022-07-19 14:34:21 +03:00
return false ;
2023-06-21 20:39:07 +03:00
auto const & size = flex_basis . get < CSS : : Size > ( ) ;
if ( size . is_auto ( ) | | size . is_min_content ( ) | | size . is_max_content ( ) | | size . is_fit_content ( ) )
2022-07-19 14:34:21 +03:00
return false ;
2023-06-21 20:39:07 +03:00
if ( size . is_length ( ) )
2022-07-19 14:34:21 +03:00
return true ;
2023-05-16 17:59:47 +03:00
bool can_resolve_percentages = is_row_layout ( )
? m_flex_container_state . has_definite_width ( )
: m_flex_container_state . has_definite_height ( ) ;
2023-06-21 20:39:07 +03:00
if ( size . is_calculated ( ) ) {
auto const & calc_value = size . calculated ( ) ;
2023-06-02 16:34:26 +03:00
if ( calc_value . resolves_to_percentage ( ) )
2023-05-16 17:59:47 +03:00
return can_resolve_percentages ;
2023-06-02 16:34:26 +03:00
if ( calc_value . resolves_to_length ( ) ) {
if ( calc_value . contains_percentage ( ) )
return can_resolve_percentages ;
return true ;
}
2022-07-23 01:25:05 +03:00
return false ;
}
2023-06-21 20:39:07 +03:00
VERIFY ( size . is_percentage ( ) ) ;
2023-05-16 17:59:47 +03:00
return can_resolve_percentages ;
2023-06-21 20:39:07 +03:00
} ( * item . used_flex_basis ) ;
2022-07-19 14:34:21 +03:00
2021-10-14 00:26:31 +03:00
// A. If the item has a definite used flex basis, that’ s the flex base size.
2023-03-08 19:24:35 +03:00
if ( item . used_flex_basis_is_definite ) {
2023-06-21 20:39:07 +03:00
auto const & size = item . used_flex_basis - > get < CSS : : Size > ( ) ;
2022-07-19 15:27:53 +03:00
if ( is_row_layout ( ) )
2023-07-17 22:19:33 +03:00
return get_pixel_width ( child_box , size ) ;
return get_pixel_height ( child_box , size ) ;
2021-10-13 22:49:28 +03:00
}
2021-10-14 00:26:31 +03:00
// B. If the flex item has ...
// - an intrinsic aspect ratio,
// - a used flex basis of content, and
// - a definite cross size,
2023-06-08 18:47:50 +03:00
if ( item . box - > has_preferred_aspect_ratio ( )
2023-06-21 20:39:07 +03:00
& & item . used_flex_basis - > has < CSS : : FlexBasisContent > ( )
2023-03-08 19:24:35 +03:00
& & has_definite_cross_size ( item . box ) ) {
2021-10-14 00:26:31 +03:00
// flex_base_size is calculated from definite cross size and intrinsic aspect ratio
2023-05-10 11:09:29 +03:00
return adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints (
item . box ,
2023-06-08 18:47:50 +03:00
calculate_main_size_from_cross_size_and_aspect_ratio ( inner_cross_size ( item . box ) , item . box - > preferred_aspect_ratio ( ) . value ( ) ) ,
2023-05-10 11:09:29 +03:00
computed_cross_min_size ( item . box ) ,
computed_cross_max_size ( item . box ) ) ;
2021-10-14 00:26:31 +03:00
}
// C. If the used flex basis is content or depends on its available space,
// and the flex container is being sized under a min-content or max-content constraint
// (e.g. when performing automatic table layout [CSS21]), size the item under that constraint.
// The flex base size is the item’ s resulting main size.
2023-06-21 20:39:07 +03:00
if ( item . used_flex_basis - > has < CSS : : FlexBasisContent > ( ) & & m_available_space_for_items - > main . is_intrinsic_sizing_constraint ( ) ) {
2022-09-27 16:29:17 +03:00
if ( m_available_space_for_items - > main . is_min_content ( ) )
2023-03-08 19:24:35 +03:00
return calculate_min_content_main_size ( item ) ;
return calculate_max_content_main_size ( item ) ;
2021-10-14 00:26:31 +03:00
}
// D. Otherwise, if the used flex basis is content or depends on its available space,
// the available main size is infinite, and the flex item’ s inline axis is parallel to the main axis,
// lay the item out using the rules for a box in an orthogonal flow [CSS3-WRITING-MODES].
// The flex base size is the item’ s max-content main size.
2023-06-21 20:39:07 +03:00
if ( item . used_flex_basis - > has < CSS : : FlexBasisContent > ( )
2021-10-14 00:26:31 +03:00
// FIXME: && main_size is infinite && inline axis is parallel to the main axis
& & false & & false ) {
TODO ( ) ;
// Use rules for a flex_container in orthogonal flow
}
// E. Otherwise, size the item into the available space using its used flex basis in place of its main size,
// treating a value of content as max-content. If a cross size is needed to determine the main size
// (e.g. when the flex item’ s main size is in its block axis) and the flex item’ s cross size is auto and not definite,
// in this calculation use fit-content as the flex item’ s cross size.
// The flex base size is the item’ s resulting main size.
2023-03-27 21:56:20 +03:00
// NOTE: If the flex item has a definite main size, just use that as the flex base size.
2021-10-14 00:26:31 +03:00
if ( has_definite_main_size ( child_box ) )
2023-03-27 21:56:20 +03:00
return inner_main_size ( child_box ) ;
// NOTE: There's a fundamental problem with many CSS specifications in that they neglect to mention
// which width to provide when calculating the intrinsic height of a box in various situations.
// Spec bug: https://github.com/w3c/csswg-drafts/issues/2890
// NOTE: This is one of many situations where that causes trouble: if this is a flex column layout,
// we may need to calculate the intrinsic height of a flex item. This requires a width, but a
// width won't be determined until later on in the flex layout algorithm.
2023-08-05 10:11:57 +03:00
// In the specific case above (E), the spec mentions using `fit-content` in place of `auto`
// if "a cross size is needed to determine the main size", so that's exactly what we do.
2023-03-27 21:56:20 +03:00
2023-08-05 10:11:57 +03:00
// NOTE: Finding a suitable width for intrinsic height determination actually happens elsewhere,
// in the various helpers that calculate the intrinsic sizes of a flex item,
// e.g. calculate_min_content_main_size().
2023-03-27 21:56:20 +03:00
2023-06-21 20:39:07 +03:00
if ( item . used_flex_basis - > has < CSS : : FlexBasisContent > ( ) ) {
2023-03-27 21:56:20 +03:00
return calculate_max_content_main_size ( item ) ;
}
2022-03-12 16:40:13 +03:00
2023-03-27 21:56:20 +03:00
return calculate_fit_content_main_size ( item ) ;
2021-10-14 00:26:31 +03:00
} ( ) ;
2023-05-10 11:09:29 +03:00
// AD-HOC: This is not mentioned in the spec, but if the item has an aspect ratio,
// we may need to adjust the main size in response to cross size min/max constraints.
2023-06-08 18:47:50 +03:00
if ( item . box - > has_preferred_aspect_ratio ( ) ) {
2023-05-10 11:09:29 +03:00
item . flex_base_size = adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints ( child_box , item . flex_base_size , computed_cross_min_size ( child_box ) , computed_cross_max_size ( child_box ) ) ;
}
2021-10-14 00:26:31 +03:00
// The hypothetical main size is the item’ s flex base size clamped according to its used min and max main sizes (and flooring the content box size at zero).
2023-07-17 22:19:33 +03:00
auto clamp_min = has_main_min_size ( child_box ) ? specified_main_min_size ( child_box ) : automatic_minimum_size ( item ) ;
auto clamp_max = has_main_max_size ( child_box ) ? specified_main_max_size ( child_box ) : NumericLimits < float > : : max ( ) ;
2023-05-24 11:50:57 +03:00
item . hypothetical_main_size = max ( CSSPixels ( 0 ) , css_clamp ( item . flex_base_size , clamp_min , clamp_max ) ) ;
2022-10-15 10:26:30 +03:00
// NOTE: At this point, we set the hypothetical main size as the flex item's *temporary* main size.
// The size may change again when we resolve flexible lengths, but this is necessary in order for
// descendants of this flex item to resolve percentage sizes against something.
//
// The spec just barely hand-waves about this, but it seems to *roughly* match what other engines do.
// See "Note" section here: https://drafts.csswg.org/css-flexbox-1/#definite-sizes
if ( is_row_layout ( ) )
2023-03-08 19:24:35 +03:00
m_state . get_mutable ( item . box ) . set_temporary_content_width ( item . hypothetical_main_size ) ;
2022-10-15 10:26:30 +03:00
else
2023-03-08 19:24:35 +03:00
m_state . get_mutable ( item . box ) . set_temporary_content_height ( item . hypothetical_main_size ) ;
2021-10-13 22:49:28 +03:00
}
2022-06-21 22:06:19 +03:00
// https://drafts.csswg.org/css-flexbox-1/#min-size-auto
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : automatic_minimum_size ( FlexItem const & item ) const
2022-03-30 20:50:57 +03:00
{
2023-03-26 12:57:12 +03:00
// To provide a more reasonable default minimum size for flex items,
// the used value of a main axis automatic minimum size on a flex item that is not a scroll container is its content-based minimum size;
// for scroll containers the automatic minimum size is zero, as usual.
if ( ! item . box - > is_scroll_container ( ) )
return content_based_minimum_size ( item ) ;
return 0 ;
2022-06-21 22:06:19 +03:00
}
// https://drafts.csswg.org/css-flexbox-1/#specified-size-suggestion
2022-11-03 20:10:17 +03:00
Optional < CSSPixels > FlexFormattingContext : : specified_size_suggestion ( FlexItem const & item ) const
2022-06-21 22:06:19 +03:00
{
// If the item’ s preferred main size is definite and not automatic,
// then the specified size suggestion is that size. It is otherwise undefined.
2023-06-08 12:09:35 +03:00
if ( has_definite_main_size ( item . box ) & & ! should_treat_main_size_as_auto ( item . box ) ) {
2023-04-17 18:31:18 +03:00
// NOTE: We use get_pixel_{width,height} to ensure that CSS box-sizing is respected.
2023-07-17 22:19:33 +03:00
return is_row_layout ( ) ? get_pixel_width ( item . box , computed_main_size ( item . box ) ) : get_pixel_height ( item . box , computed_main_size ( item . box ) ) ;
2023-04-17 18:31:18 +03:00
}
2022-06-21 22:06:19 +03:00
return { } ;
}
// https://drafts.csswg.org/css-flexbox-1/#content-size-suggestion
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : content_size_suggestion ( FlexItem const & item ) const
2022-06-21 22:06:19 +03:00
{
2023-05-10 11:09:29 +03:00
auto suggestion = calculate_min_content_main_size ( item ) ;
2023-06-08 18:47:50 +03:00
if ( item . box - > has_preferred_aspect_ratio ( ) ) {
2023-05-10 11:09:29 +03:00
suggestion = adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints ( item . box , suggestion , computed_cross_min_size ( item . box ) , computed_cross_max_size ( item . box ) ) ;
}
return suggestion ;
2022-06-21 22:06:19 +03:00
}
2022-06-22 19:05:28 +03:00
// https://drafts.csswg.org/css-flexbox-1/#transferred-size-suggestion
2022-11-03 20:10:17 +03:00
Optional < CSSPixels > FlexFormattingContext : : transferred_size_suggestion ( FlexItem const & item ) const
2022-06-22 19:05:28 +03:00
{
// If the item has a preferred aspect ratio and its preferred cross size is definite,
// then the transferred size suggestion is that size
// (clamped by its minimum and maximum cross sizes if they are definite), converted through the aspect ratio.
2023-06-08 18:47:50 +03:00
if ( item . box - > has_preferred_aspect_ratio ( ) & & has_definite_cross_size ( item . box ) ) {
auto aspect_ratio = item . box - > preferred_aspect_ratio ( ) . value ( ) ;
2023-05-10 11:09:29 +03:00
return adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints (
item . box ,
calculate_main_size_from_cross_size_and_aspect_ratio ( inner_cross_size ( item . box ) , aspect_ratio ) ,
computed_cross_min_size ( item . box ) ,
computed_cross_max_size ( item . box ) ) ;
2022-06-22 19:05:28 +03:00
}
// It is otherwise undefined.
return { } ;
}
2022-06-21 22:06:19 +03:00
// https://drafts.csswg.org/css-flexbox-1/#content-based-minimum-size
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : content_based_minimum_size ( FlexItem const & item ) const
2022-06-21 22:06:19 +03:00
{
auto unclamped_size = [ & ] {
// The content-based minimum size of a flex item is the smaller of its specified size suggestion
// and its content size suggestion if its specified size suggestion exists;
if ( auto specified_size_suggestion = this - > specified_size_suggestion ( item ) ; specified_size_suggestion . has_value ( ) ) {
return min ( specified_size_suggestion . value ( ) , content_size_suggestion ( item ) ) ;
}
2022-06-22 19:05:28 +03:00
// otherwise, the smaller of its transferred size suggestion and its content size suggestion
// if the element is replaced and its transferred size suggestion exists;
2023-02-27 02:09:02 +03:00
if ( item . box - > is_replaced_box ( ) ) {
2022-06-22 19:05:28 +03:00
if ( auto transferred_size_suggestion = this - > transferred_size_suggestion ( item ) ; transferred_size_suggestion . has_value ( ) ) {
return min ( transferred_size_suggestion . value ( ) , content_size_suggestion ( item ) ) ;
}
}
2022-06-21 22:06:19 +03:00
// otherwise its content size suggestion.
return content_size_suggestion ( item ) ;
} ( ) ;
// In all cases, the size is clamped by the maximum main size if it’ s definite.
if ( has_main_max_size ( item . box ) ) {
2023-07-17 22:19:33 +03:00
return min ( unclamped_size , specified_main_max_size ( item . box ) ) ;
2022-06-21 22:06:19 +03:00
}
return unclamped_size ;
2022-03-30 20:50:57 +03:00
}
2022-10-04 19:57:09 +03:00
bool FlexFormattingContext : : can_determine_size_of_child ( ) const
{
return true ;
}
void FlexFormattingContext : : determine_width_of_child ( Box const & , AvailableSpace const & )
{
// NOTE: For now, we simply do nothing here. If a child context is calling up to us
// and asking us to determine its width, we've already done so as part of the
// flex layout algorithm.
}
void FlexFormattingContext : : determine_height_of_child ( Box const & , AvailableSpace const & )
{
// NOTE: For now, we simply do nothing here. If a child context is calling up to us
// and asking us to determine its height, we've already done so as part of the
// flex layout algorithm.
}
2022-09-27 16:29:17 +03:00
// https://drafts.csswg.org/css-flexbox-1/#algo-main-container
void FlexFormattingContext : : determine_main_size_of_flex_container ( )
2021-10-13 23:03:39 +03:00
{
2022-09-27 16:29:17 +03:00
// Determine the main size of the flex container using the rules of the formatting context in which it participates.
// NOTE: The automatic block size of a block-level flex container is its max-content size.
2022-10-04 19:57:09 +03:00
// FIXME: The code below doesn't know how to size absolutely positioned flex containers at all.
// We just leave it alone for now and let the parent context deal with it.
if ( flex_container ( ) . is_absolutely_positioned ( ) )
return ;
2022-09-27 16:29:17 +03:00
// FIXME: Once all parent contexts now how to size a given child, we can remove
// `can_determine_size_of_child()`.
if ( parent ( ) - > can_determine_size_of_child ( ) ) {
if ( is_row_layout ( ) ) {
2022-10-04 00:37:38 +03:00
parent ( ) - > determine_width_of_child ( flex_container ( ) , m_available_space_for_flex_container - > space ) ;
2022-09-27 16:29:17 +03:00
} else {
2022-10-04 00:37:38 +03:00
parent ( ) - > determine_height_of_child ( flex_container ( ) , m_available_space_for_flex_container - > space ) ;
2022-09-27 16:29:17 +03:00
}
return ;
}
if ( is_row_layout ( ) ) {
if ( ! flex_container ( ) . is_out_of_flow ( * parent ( ) ) & & m_state . get ( * flex_container ( ) . containing_block ( ) ) . has_definite_width ( ) ) {
2022-11-04 23:32:50 +03:00
set_main_size ( flex_container ( ) , calculate_stretch_fit_width ( flex_container ( ) , m_available_space_for_flex_container - > space . width ) ) ;
2022-09-27 16:29:17 +03:00
} else {
2022-11-04 23:32:50 +03:00
set_main_size ( flex_container ( ) , calculate_max_content_width ( flex_container ( ) ) ) ;
2022-09-27 16:29:17 +03:00
}
} else {
if ( ! has_definite_main_size ( flex_container ( ) ) )
2023-08-12 19:27:21 +03:00
set_main_size ( flex_container ( ) , calculate_max_content_height ( flex_container ( ) , m_available_space_for_flex_container - > space . width . to_px_or_zero ( ) ) ) ;
2021-10-13 23:03:39 +03:00
}
}
2021-10-13 23:07:55 +03:00
// https://www.w3.org/TR/css-flexbox-1/#algo-line-break
2022-03-12 16:40:13 +03:00
void FlexFormattingContext : : collect_flex_items_into_flex_lines ( )
2021-10-13 23:07:55 +03:00
{
// FIXME: Also support wrap-reverse
2021-10-14 00:38:04 +03:00
// If the flex container is single-line, collect all the flex items into a single flex line.
if ( is_single_line ( ) ) {
2021-10-13 23:07:55 +03:00
FlexLine line ;
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
line . items . append ( item ) ;
2021-10-13 23:07:55 +03:00
}
2021-10-14 00:38:04 +03:00
m_flex_lines . append ( move ( line ) ) ;
return ;
}
// Otherwise, starting from the first uncollected item, collect consecutive items one by one
// until the first time that the next collected item would not fit into the flex container’ s inner main size
// (or until a forced break is encountered, see §10 Fragmenting Flex Layout).
// If the very first uncollected item wouldn't fit, collect just it into the line.
// For this step, the size of a flex item is its outer hypothetical main size. (Note: This can be negative.)
// Repeat until all flex items have been collected into flex lines.
FlexLine line ;
2022-11-03 20:10:17 +03:00
CSSPixels line_main_size = 0 ;
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
2023-03-11 13:44:33 +03:00
auto const outer_hypothetical_main_size = item . outer_hypothetical_main_size ( ) ;
2023-08-12 19:27:21 +03:00
if ( ! line . items . is_empty ( ) & & ( line_main_size + outer_hypothetical_main_size ) > m_available_space_for_items - > main ) {
2021-10-14 00:38:04 +03:00
m_flex_lines . append ( move ( line ) ) ;
line = { } ;
line_main_size = 0 ;
2021-10-13 23:07:55 +03:00
}
2023-03-08 19:24:35 +03:00
line . items . append ( item ) ;
2022-03-23 13:40:42 +03:00
line_main_size + = outer_hypothetical_main_size ;
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between flex items.
line_main_size + = main_gap ( ) ;
2021-10-13 23:07:55 +03:00
}
2021-10-14 00:38:04 +03:00
m_flex_lines . append ( move ( line ) ) ;
2021-10-13 23:07:55 +03:00
}
2023-03-08 23:18:35 +03:00
// https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
void FlexFormattingContext : : resolve_flexible_lengths_for_line ( FlexLine & line )
2021-10-13 23:11:50 +03:00
{
2023-06-01 15:20:11 +03:00
// AD-HOC: The spec tells us to use the "flex container’ s inner main size" in this algorithm,
// but that doesn't work when we're sizing under a max-content constraint.
// In that case, there is effectively infinite size available in the main axis,
// but the inner main size has not been assigned yet.
// We solve this by calculating our own "available main size" here, which is essentially
// infinity under max-content, 0 under min-content, and the inner main size otherwise.
2023-08-12 19:27:21 +03:00
AvailableSize available_main_size { AvailableSize : : make_indefinite ( ) } ;
2023-06-01 15:20:11 +03:00
if ( m_available_space_for_items - > main . is_intrinsic_sizing_constraint ( ) )
2023-08-12 19:27:21 +03:00
available_main_size = m_available_space_for_items - > main ;
2023-06-01 15:20:11 +03:00
else
2023-08-12 19:27:21 +03:00
available_main_size = AvailableSize : : make_definite ( inner_main_size ( flex_container ( ) ) ) ;
2023-06-01 15:20:11 +03:00
2023-03-08 23:18:35 +03:00
// 1. Determine the used flex factor.
// Sum the outer hypothetical main sizes of all items on the line.
// If the sum is less than the flex container’ s inner main size,
// use the flex grow factor for the rest of this algorithm; otherwise, use the flex shrink factor
2021-10-13 23:11:50 +03:00
enum FlexFactor {
FlexGrowFactor ,
FlexShrinkFactor
} ;
2023-03-08 23:18:35 +03:00
auto used_flex_factor = [ & ] ( ) - > FlexFactor {
CSSPixels sum = 0 ;
for ( auto const & item : line . items ) {
sum + = item . outer_hypothetical_main_size ( ) ;
}
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between flex items.
sum + = main_gap ( ) * ( line . items . size ( ) - 1 ) ;
2023-06-01 15:20:11 +03:00
// AD-HOC: Note that we're using our own "available main size" explained above
// instead of the flex container’ s inner main size.
if ( sum < available_main_size )
2023-03-08 23:18:35 +03:00
return FlexFactor : : FlexGrowFactor ;
return FlexFactor : : FlexShrinkFactor ;
} ( ) ;
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
// 2. Each item in the flex line has a target main size, initially set to its flex base size.
// Each item is initially unfrozen and may become frozen.
for ( auto & item : line . items ) {
item . target_main_size = item . flex_base_size ;
item . frozen = false ;
}
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
// 3. Size inflexible items.
for ( FlexItem & item : line . items ) {
if ( used_flex_factor = = FlexFactor : : FlexGrowFactor ) {
2023-02-27 02:09:02 +03:00
item . flex_factor = item . box - > computed_values ( ) . flex_grow ( ) ;
2023-03-08 23:18:35 +03:00
} else if ( used_flex_factor = = FlexFactor : : FlexShrinkFactor ) {
2023-02-27 02:09:02 +03:00
item . flex_factor = item . box - > computed_values ( ) . flex_shrink ( ) ;
2023-03-08 23:18:35 +03:00
}
// Freeze, setting its target main size to its hypothetical main size…
// - any item that has a flex factor of zero
// - if using the flex grow factor: any item that has a flex base size greater than its hypothetical main size
// - if using the flex shrink factor: any item that has a flex base size smaller than its hypothetical main size
if ( item . flex_factor . value ( ) = = 0
| | ( used_flex_factor = = FlexFactor : : FlexGrowFactor & & item . flex_base_size > item . hypothetical_main_size )
| | ( used_flex_factor = = FlexFactor : : FlexShrinkFactor & & item . flex_base_size < item . hypothetical_main_size ) ) {
item . frozen = true ;
item . target_main_size = item . hypothetical_main_size ;
2021-10-13 23:11:50 +03:00
}
2023-03-08 23:18:35 +03:00
}
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
// 4. Calculate initial free space
// Sum the outer sizes of all items on the line, and subtract this from the flex container’ s inner main size.
// For frozen items, use their outer target main size; for other items, use their outer flex base size.
2023-07-25 23:18:08 +03:00
auto calculate_remaining_free_space = [ & ] ( ) - > Optional < CSSPixels > {
// AD-HOC: If the container is sized under max-content constraints, then remaining_free_space won't have
// a value to avoid leaking an infinite value into layout calculations.
2023-08-12 19:27:21 +03:00
if ( available_main_size . is_intrinsic_sizing_constraint ( ) )
2023-07-25 23:18:08 +03:00
return { } ;
2023-03-08 23:18:35 +03:00
CSSPixels sum = 0 ;
for ( auto const & item : line . items ) {
if ( item . frozen )
sum + = item . outer_target_main_size ( ) ;
else
sum + = item . outer_flex_base_size ( ) ;
2021-10-13 23:11:50 +03:00
}
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between flex items.
sum + = main_gap ( ) * ( line . items . size ( ) - 1 ) ;
2023-06-01 15:20:11 +03:00
// AD-HOC: Note that we're using our own "available main size" explained above
// instead of the flex container’ s inner main size.
2023-08-12 19:27:21 +03:00
return available_main_size . to_px_or_zero ( ) - sum ;
2023-03-08 23:18:35 +03:00
} ;
auto const initial_free_space = calculate_remaining_free_space ( ) ;
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
// 5. Loop
while ( true ) {
// a. Check for flexible items.
// If all the flex items on the line are frozen, free space has been distributed; exit this loop.
if ( all_of ( line . items , [ ] ( auto const & item ) { return item . frozen ; } ) ) {
break ;
}
// b. Calculate the remaining free space as for initial free space, above.
line . remaining_free_space = calculate_remaining_free_space ( ) ;
// If the sum of the unfrozen flex items’ flex factors is less than one, multiply the initial free space by this sum.
2023-07-25 23:18:08 +03:00
if ( auto sum_of_flex_factor_of_unfrozen_items = line . sum_of_flex_factor_of_unfrozen_items ( ) ; sum_of_flex_factor_of_unfrozen_items < 1 & & initial_free_space . has_value ( ) ) {
auto value = initial_free_space . value ( ) * sum_of_flex_factor_of_unfrozen_items ;
2023-03-08 23:18:35 +03:00
// If the magnitude of this value is less than the magnitude of the remaining free space, use this as the remaining free space.
2023-07-25 23:18:08 +03:00
if ( abs ( value ) < abs ( line . remaining_free_space . value ( ) ) )
2023-03-08 23:18:35 +03:00
line . remaining_free_space = value ;
}
2023-07-06 09:47:29 +03:00
// AD-HOC: We allow the remaining free space to be infinite, but we can't let infinity
// leak into the layout geometry, so we treat infinity as zero when used in arithmetic.
2023-07-25 23:18:08 +03:00
auto remaining_free_space_or_zero_if_infinite = line . remaining_free_space . has_value ( ) ? line . remaining_free_space . value ( ) : 0 ;
2023-07-06 09:47:29 +03:00
2023-03-08 23:18:35 +03:00
// c. If the remaining free space is non-zero, distribute it proportional to the flex factors:
if ( line . remaining_free_space ! = 0 ) {
// If using the flex grow factor
if ( used_flex_factor = = FlexFactor : : FlexGrowFactor ) {
// For every unfrozen item on the line,
// find the ratio of the item’ s flex grow factor to the sum of the flex grow factors of all unfrozen items on the line.
auto sum_of_flex_factor_of_unfrozen_items = line . sum_of_flex_factor_of_unfrozen_items ( ) ;
for ( auto & item : line . items ) {
if ( item . frozen )
continue ;
2023-05-24 11:50:57 +03:00
double ratio = item . flex_factor . value ( ) / sum_of_flex_factor_of_unfrozen_items ;
2023-03-08 23:18:35 +03:00
// Set the item’ s target main size to its flex base size plus a fraction of the remaining free space proportional to the ratio.
2023-07-06 09:47:29 +03:00
item . target_main_size = item . flex_base_size + ( remaining_free_space_or_zero_if_infinite * ratio ) ;
2023-03-08 23:18:35 +03:00
}
}
// If using the flex shrink factor
else if ( used_flex_factor = = FlexFactor : : FlexShrinkFactor ) {
// For every unfrozen item on the line, multiply its flex shrink factor by its inner flex base size, and note this as its scaled flex shrink factor.
for ( auto & item : line . items ) {
2023-03-11 13:50:42 +03:00
if ( item . frozen )
continue ;
2023-06-12 21:37:35 +03:00
item . scaled_flex_shrink_factor = item . flex_factor . value ( ) * item . flex_base_size . to_double ( ) ;
2021-10-13 23:11:50 +03:00
}
2023-03-08 23:18:35 +03:00
auto sum_of_scaled_flex_shrink_factors_of_all_unfrozen_items_on_line = line . sum_of_scaled_flex_shrink_factor_of_unfrozen_items ( ) ;
for ( auto & item : line . items ) {
2023-03-11 13:50:42 +03:00
if ( item . frozen )
continue ;
2023-03-08 23:18:35 +03:00
// Find the ratio of the item’ s scaled flex shrink factor to the sum of the scaled flex shrink factors of all unfrozen items on the line.
2023-05-24 11:50:57 +03:00
double ratio = 1.0 ;
2023-03-08 23:18:35 +03:00
if ( sum_of_scaled_flex_shrink_factors_of_all_unfrozen_items_on_line ! = 0 )
ratio = item . scaled_flex_shrink_factor / sum_of_scaled_flex_shrink_factors_of_all_unfrozen_items_on_line ;
// Set the item’ s target main size to its flex base size minus a fraction of the absolute value of the remaining free space proportional to the ratio.
// (Note this may result in a negative inner main size; it will be corrected in the next step.)
2023-07-06 09:47:29 +03:00
item . target_main_size = item . flex_base_size - ( abs ( remaining_free_space_or_zero_if_infinite ) * ratio ) ;
2021-10-13 23:11:50 +03:00
}
}
}
2023-03-08 23:18:35 +03:00
// d. Fix min/max violations.
CSSPixels total_violation = 0 ;
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
// Clamp each non-frozen item’ s target main size by its used min and max main sizes and floor its content-box size at zero.
for ( auto & item : line . items ) {
if ( item . frozen )
continue ;
auto used_min_main_size = has_main_min_size ( item . box )
2023-07-17 22:19:33 +03:00
? specified_main_min_size ( item . box )
2023-03-08 23:18:35 +03:00
: automatic_minimum_size ( item ) ;
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
auto used_max_main_size = has_main_max_size ( item . box )
2023-07-17 22:19:33 +03:00
? specified_main_max_size ( item . box )
2023-03-08 23:18:35 +03:00
: NumericLimits < float > : : max ( ) ;
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
auto original_target_main_size = item . target_main_size ;
item . target_main_size = css_clamp ( item . target_main_size , used_min_main_size , used_max_main_size ) ;
item . target_main_size = max ( item . target_main_size , CSSPixels ( 0 ) ) ;
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
// If the item’ s target main size was made smaller by this, it’ s a max violation.
if ( item . target_main_size < original_target_main_size )
item . is_max_violation = true ;
2021-10-13 23:11:50 +03:00
2023-03-08 23:18:35 +03:00
// If the item’ s target main size was made larger by this, it’ s a min violation.
if ( item . target_main_size > original_target_main_size )
item . is_min_violation = true ;
total_violation + = item . target_main_size - original_target_main_size ;
}
// e. Freeze over-flexed items.
// The total violation is the sum of the adjustments from the previous step ∑(clamped size - unclamped size).
// If the total violation is:
// Zero
// Freeze all items.
if ( total_violation = = 0 ) {
for ( auto & item : line . items ) {
if ( ! item . frozen )
2023-03-08 19:24:35 +03:00
item . frozen = true ;
2021-10-13 23:11:50 +03:00
}
}
2023-03-08 23:18:35 +03:00
// Positive
// Freeze all the items with min violations.
else if ( total_violation > 0 ) {
for ( auto & item : line . items ) {
if ( ! item . frozen & & item . is_min_violation )
item . frozen = true ;
}
}
// Negative
// Freeze all the items with max violations.
else {
for ( auto & item : line . items ) {
if ( ! item . frozen & & item . is_max_violation )
item . frozen = true ;
}
2021-10-13 23:11:50 +03:00
}
2023-03-08 23:18:35 +03:00
// NOTE: This freezes at least one item, ensuring that the loop makes progress and eventually terminates.
// f. Return to the start of this loop.
}
2022-07-14 00:23:56 +03:00
2023-03-08 23:18:35 +03:00
// NOTE: Calculate the remaining free space once again here, since it's needed later when aligning items.
line . remaining_free_space = calculate_remaining_free_space ( ) ;
// 6. Set each item’ s used main size to its target main size.
for ( auto & item : line . items ) {
item . main_size = item . target_main_size ;
set_main_size ( item . box , item . target_main_size ) ;
}
}
// https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
void FlexFormattingContext : : resolve_flexible_lengths ( )
{
for ( auto & line : m_flex_lines ) {
resolve_flexible_lengths_for_line ( line ) ;
2021-10-13 23:11:50 +03:00
}
}
2022-02-27 15:39:38 +03:00
// https://drafts.csswg.org/css-flexbox-1/#algo-cross-item
2022-07-22 23:02:02 +03:00
void FlexFormattingContext : : determine_hypothetical_cross_size_of_item ( FlexItem & item , bool resolve_percentage_min_max_sizes )
2021-10-13 23:15:16 +03:00
{
2022-02-27 15:39:38 +03:00
// Determine the hypothetical cross size of each item by performing layout
// as if it were an in-flow block-level box with the used main size
// and the given available space, treating auto as fit-content.
2022-07-22 23:02:02 +03:00
auto const & computed_min_size = this - > computed_cross_min_size ( item . box ) ;
auto const & computed_max_size = this - > computed_cross_max_size ( item . box ) ;
2023-07-17 22:19:33 +03:00
auto clamp_min = ( ! computed_min_size . is_auto ( ) & & ( resolve_percentage_min_max_sizes | | ! computed_min_size . contains_percentage ( ) ) ) ? specified_cross_min_size ( item . box ) : 0 ;
auto clamp_max = ( ! computed_max_size . is_none ( ) & & ( resolve_percentage_min_max_sizes | | ! computed_max_size . contains_percentage ( ) ) ) ? specified_cross_max_size ( item . box ) : NumericLimits < float > : : max ( ) ;
2022-07-22 23:02:02 +03:00
2022-02-27 15:39:38 +03:00
// If we have a definite cross size, this is easy! No need to perform layout, we can just use it as-is.
if ( has_definite_cross_size ( item . box ) ) {
2022-11-27 10:08:05 +03:00
// To avoid subtracting padding and border twice for `box-sizing: border-box` only min and max clamp should happen on a second pass
if ( resolve_percentage_min_max_sizes ) {
item . hypothetical_cross_size = css_clamp ( item . hypothetical_cross_size , clamp_min , clamp_max ) ;
return ;
}
2023-05-02 07:51:32 +03:00
item . hypothetical_cross_size = css_clamp ( inner_cross_size ( item . box ) , clamp_min , clamp_max ) ;
2022-02-27 15:39:38 +03:00
return ;
2021-10-13 23:15:16 +03:00
}
2023-08-05 10:11:57 +03:00
if ( item . box - > has_preferred_aspect_ratio ( ) & & item . main_size . has_value ( ) ) {
item . hypothetical_cross_size = calculate_cross_size_from_main_size_and_aspect_ratio ( item . main_size . value ( ) , item . box - > preferred_aspect_ratio ( ) . value ( ) ) ;
return ;
}
2023-01-07 20:08:31 +03:00
2023-08-05 10:11:57 +03:00
auto computed_cross_size = [ & ] ( ) - > CSS : : Size {
// "... treating auto as fit-content"
if ( should_treat_cross_size_as_auto ( item . box ) )
return CSS : : Size : : make_fit_content ( ) ;
return this - > computed_cross_size ( item . box ) ;
} ( ) ;
if ( computed_cross_size . is_min_content ( ) ) {
item . hypothetical_cross_size = css_clamp ( calculate_min_content_cross_size ( item ) , clamp_min , clamp_max ) ;
return ;
}
2023-07-05 18:30:48 +03:00
2023-08-05 10:11:57 +03:00
if ( computed_cross_size . is_max_content ( ) ) {
item . hypothetical_cross_size = css_clamp ( calculate_max_content_cross_size ( item ) , clamp_min , clamp_max ) ;
return ;
}
if ( computed_cross_size . is_fit_content ( ) ) {
2023-01-07 20:08:31 +03:00
CSSPixels fit_content_cross_size = 0 ;
if ( is_row_layout ( ) ) {
2023-01-07 21:28:06 +03:00
auto available_width = item . main_size . has_value ( ) ? AvailableSize : : make_definite ( item . main_size . value ( ) ) : AvailableSize : : make_indefinite ( ) ;
2023-01-07 20:08:31 +03:00
auto available_height = AvailableSize : : make_indefinite ( ) ;
fit_content_cross_size = calculate_fit_content_height ( item . box , AvailableSpace ( available_width , available_height ) ) ;
} else {
fit_content_cross_size = calculate_fit_content_width ( item . box , m_available_space_for_items - > space ) ;
}
item . hypothetical_cross_size = css_clamp ( fit_content_cross_size , clamp_min , clamp_max ) ;
2022-07-22 23:02:02 +03:00
return ;
}
2021-10-13 23:15:16 +03:00
2022-07-22 23:02:02 +03:00
// For indefinite cross sizes, we perform a throwaway layout and then measure it.
LayoutState throwaway_state ( & m_state ) ;
2022-03-12 16:40:13 +03:00
2023-01-07 20:08:31 +03:00
auto & box_state = throwaway_state . get_mutable ( item . box ) ;
2022-07-22 23:02:02 +03:00
if ( is_row_layout ( ) ) {
2023-01-07 21:28:06 +03:00
box_state . set_content_width ( item . main_size . value ( ) ) ;
2022-02-27 15:39:38 +03:00
} else {
2023-01-07 21:28:06 +03:00
box_state . set_content_height ( item . main_size . value ( ) ) ;
2022-02-27 15:39:38 +03:00
}
2022-07-22 23:02:02 +03:00
// Item has definite main size, layout with that as the used main size.
auto independent_formatting_context = create_independent_formatting_context_if_needed ( throwaway_state , item . box ) ;
// NOTE: Flex items should always create an independent formatting context!
VERIFY ( independent_formatting_context ) ;
2023-01-07 21:28:06 +03:00
auto available_width = is_row_layout ( ) ? AvailableSize : : make_definite ( item . main_size . value ( ) ) : AvailableSize : : make_indefinite ( ) ;
auto available_height = is_row_layout ( ) ? AvailableSize : : make_indefinite ( ) : AvailableSize : : make_definite ( item . main_size . value ( ) ) ;
2023-01-07 20:08:31 +03:00
independent_formatting_context - > run ( item . box , LayoutMode : : Normal , AvailableSpace ( available_width , available_height ) ) ;
2022-07-22 23:02:02 +03:00
2022-09-24 14:47:58 +03:00
auto automatic_cross_size = is_row_layout ( ) ? independent_formatting_context - > automatic_content_height ( )
2023-01-07 20:08:31 +03:00
: independent_formatting_context - > automatic_content_width ( ) ;
2022-07-22 23:02:02 +03:00
2022-11-03 20:10:17 +03:00
item . hypothetical_cross_size = css_clamp ( automatic_cross_size , clamp_min , clamp_max ) ;
2021-10-13 23:17:33 +03:00
}
// https://www.w3.org/TR/css-flexbox-1/#algo-cross-line
2022-10-07 14:58:18 +03:00
void FlexFormattingContext : : calculate_cross_size_of_each_flex_line ( )
2021-10-13 23:17:33 +03:00
{
2021-10-18 19:37:43 +03:00
// If the flex container is single-line and has a definite cross size, the cross size of the flex line is the flex container’ s inner cross size.
if ( is_single_line ( ) & & has_definite_cross_size ( flex_container ( ) ) ) {
2023-03-08 19:36:32 +03:00
m_flex_lines [ 0 ] . cross_size = inner_cross_size ( flex_container ( ) ) ;
2021-10-18 19:37:43 +03:00
return ;
}
2021-10-13 23:17:33 +03:00
2021-10-18 19:37:43 +03:00
// Otherwise, for each flex line:
for ( auto & flex_line : m_flex_lines ) {
// FIXME: 1. Collect all the flex items whose inline-axis is parallel to the main-axis, whose align-self is baseline,
// and whose cross-axis margins are both non-auto. Find the largest of the distances between each item’ s baseline
// and its hypothetical outer cross-start edge, and the largest of the distances between each item’ s baseline
// and its hypothetical outer cross-end edge, and sum these two values.
// 2. Among all the items not collected by the previous step, find the largest outer hypothetical cross size.
2022-11-03 20:10:17 +03:00
CSSPixels largest_hypothetical_cross_size = 0 ;
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
if ( largest_hypothetical_cross_size < item . hypothetical_cross_size_with_margins ( ) )
largest_hypothetical_cross_size = item . hypothetical_cross_size_with_margins ( ) ;
2021-10-13 23:17:33 +03:00
}
2021-10-18 19:37:43 +03:00
// 3. The used cross-size of the flex line is the largest of the numbers found in the previous two steps and zero.
2023-05-24 11:50:57 +03:00
flex_line . cross_size = max ( CSSPixels ( 0 ) , largest_hypothetical_cross_size ) ;
2021-10-13 23:17:33 +03:00
}
2021-10-18 19:37:43 +03:00
// If the flex container is single-line, then clamp the line’ s cross-size to be within the container’ s computed min and max cross sizes.
// Note that if CSS 2.1’ s definition of min/max-width/height applied more generally, this behavior would fall out automatically.
2023-08-01 19:32:08 +03:00
// AD-HOC: We don't do this when the flex container is being sized under a min-content or max-content constraint.
if ( is_single_line ( ) & & ! m_available_space_for_flex_container - > cross . is_intrinsic_sizing_constraint ( ) ) {
2022-10-07 14:58:18 +03:00
auto const & computed_min_size = this - > computed_cross_min_size ( flex_container ( ) ) ;
auto const & computed_max_size = this - > computed_cross_max_size ( flex_container ( ) ) ;
2023-07-17 22:19:33 +03:00
auto cross_min_size = ( ! computed_min_size . is_auto ( ) & & ! computed_min_size . contains_percentage ( ) ) ? specified_cross_min_size ( flex_container ( ) ) : 0 ;
auto cross_max_size = ( ! computed_max_size . is_none ( ) & & ! computed_max_size . contains_percentage ( ) ) ? specified_cross_max_size ( flex_container ( ) ) : INFINITY ;
2022-07-12 15:31:41 +03:00
m_flex_lines [ 0 ] . cross_size = css_clamp ( m_flex_lines [ 0 ] . cross_size , cross_min_size , cross_max_size ) ;
2022-10-07 14:58:18 +03:00
}
2021-10-13 23:17:33 +03:00
}
2021-10-13 23:15:16 +03:00
2021-10-13 23:19:17 +03:00
// https://www.w3.org/TR/css-flexbox-1/#algo-stretch
2021-10-13 23:52:50 +03:00
void FlexFormattingContext : : determine_used_cross_size_of_each_flex_item ( )
2021-10-13 23:19:17 +03:00
{
2021-10-13 23:44:54 +03:00
for ( auto & flex_line : m_flex_lines ) {
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
2022-07-07 12:44:35 +03:00
// If a flex item has align-self: stretch, its computed cross size property is auto,
// and neither of its cross-axis margins are auto, the used outer cross size is the used cross size of its flex line,
// clamped according to the item’ s used min and max cross sizes.
2023-03-08 19:24:35 +03:00
if ( alignment_for_item ( item . box ) = = CSS : : AlignItems : : Stretch
& & is_cross_auto ( item . box )
& & ! item . margins . cross_before_is_auto
& & ! item . margins . cross_after_is_auto ) {
2022-10-13 14:48:41 +03:00
auto unclamped_cross_size = flex_line . cross_size
2023-03-08 19:24:35 +03:00
- item . margins . cross_before - item . margins . cross_after
- item . padding . cross_before - item . padding . cross_after
- item . borders . cross_before - item . borders . cross_after ;
2022-10-13 14:48:41 +03:00
2023-03-08 19:24:35 +03:00
auto const & computed_min_size = computed_cross_min_size ( item . box ) ;
auto const & computed_max_size = computed_cross_max_size ( item . box ) ;
2023-07-17 22:19:33 +03:00
auto cross_min_size = ( ! computed_min_size . is_auto ( ) & & ! computed_min_size . contains_percentage ( ) ) ? specified_cross_min_size ( item . box ) : 0 ;
auto cross_max_size = ( ! computed_max_size . is_none ( ) & & ! computed_max_size . contains_percentage ( ) ) ? specified_cross_max_size ( item . box ) : INFINITY ;
2022-10-13 14:48:41 +03:00
2023-03-08 19:24:35 +03:00
item . cross_size = css_clamp ( unclamped_cross_size , cross_min_size , cross_max_size ) ;
2021-10-13 23:19:17 +03:00
} else {
2022-07-07 12:44:35 +03:00
// Otherwise, the used cross size is the item’ s hypothetical cross size.
2023-03-08 19:24:35 +03:00
item . cross_size = item . hypothetical_cross_size ;
2021-10-13 23:19:17 +03:00
}
}
}
}
2021-10-13 23:20:55 +03:00
// https://www.w3.org/TR/css-flexbox-1/#algo-main-align
2022-03-12 16:40:13 +03:00
void FlexFormattingContext : : distribute_any_remaining_free_space ( )
2021-10-13 23:20:55 +03:00
{
2021-10-13 23:44:54 +03:00
for ( auto & flex_line : m_flex_lines ) {
2021-10-13 23:20:55 +03:00
// 12.1.
2022-11-03 20:10:17 +03:00
CSSPixels used_main_space = 0 ;
2021-10-13 23:20:55 +03:00
size_t auto_margins = 0 ;
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
used_main_space + = item . main_size . value ( ) ;
if ( item . margins . main_before_is_auto )
2021-10-13 23:20:55 +03:00
+ + auto_margins ;
2022-03-23 13:40:42 +03:00
2023-03-08 19:24:35 +03:00
if ( item . margins . main_after_is_auto )
2021-10-13 23:20:55 +03:00
+ + auto_margins ;
2022-07-12 01:17:13 +03:00
2023-03-08 19:24:35 +03:00
used_main_space + = item . margins . main_before + item . margins . main_after
+ item . borders . main_before + item . borders . main_after
+ item . padding . main_before + item . padding . main_after ;
2021-10-13 23:20:55 +03:00
}
2022-07-06 20:15:31 +03:00
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between flex items.
used_main_space + = main_gap ( ) * ( flex_line . items . size ( ) - 1 ) ;
2023-07-25 23:18:08 +03:00
if ( flex_line . remaining_free_space . has_value ( ) & & flex_line . remaining_free_space . value ( ) > 0 & & auto_margins > 0 ) {
CSSPixels size_per_auto_margin = flex_line . remaining_free_space . value ( ) / auto_margins ;
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
if ( item . margins . main_before_is_auto )
set_main_axis_first_margin ( item , size_per_auto_margin ) ;
if ( item . margins . main_after_is_auto )
set_main_axis_second_margin ( item , size_per_auto_margin ) ;
2021-10-13 23:20:55 +03:00
}
} else {
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
if ( item . margins . main_before_is_auto )
set_main_axis_first_margin ( item , 0 ) ;
if ( item . margins . main_after_is_auto )
set_main_axis_second_margin ( item , 0 ) ;
2021-10-13 23:20:55 +03:00
}
}
// 12.2.
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between items.
CSSPixels space_between_items = main_gap ( ) ;
2022-11-03 20:10:17 +03:00
CSSPixels initial_offset = 0 ;
2021-10-13 23:20:55 +03:00
auto number_of_items = flex_line . items . size ( ) ;
2023-07-24 14:16:25 +03:00
if ( auto_margins = = 0 & & number_of_items > 0 ) {
2023-03-10 23:46:18 +03:00
switch ( flex_container ( ) . computed_values ( ) . justify_content ( ) ) {
case CSS : : JustifyContent : : Start :
2023-07-26 00:51:29 +03:00
initial_offset = 0 ;
break ;
2023-03-10 23:46:18 +03:00
case CSS : : JustifyContent : : FlexStart :
if ( is_direction_reverse ( ) ) {
initial_offset = inner_main_size ( flex_container ( ) ) ;
} else {
initial_offset = 0 ;
}
break ;
case CSS : : JustifyContent : : End :
2023-07-26 00:51:29 +03:00
initial_offset = inner_main_size ( flex_container ( ) ) ;
break ;
2023-03-10 23:46:18 +03:00
case CSS : : JustifyContent : : FlexEnd :
if ( is_direction_reverse ( ) ) {
initial_offset = 0 ;
} else {
initial_offset = inner_main_size ( flex_container ( ) ) ;
}
break ;
case CSS : : JustifyContent : : Center :
2023-05-24 11:50:57 +03:00
initial_offset = ( inner_main_size ( flex_container ( ) ) - used_main_space ) / 2.0 ;
2023-05-08 18:46:17 +03:00
if ( is_direction_reverse ( ) ) {
initial_offset = inner_main_size ( flex_container ( ) ) - initial_offset ;
}
2023-03-10 23:46:18 +03:00
break ;
case CSS : : JustifyContent : : SpaceBetween :
2023-05-28 16:39:21 +03:00
if ( is_direction_reverse ( ) ) {
initial_offset = inner_main_size ( flex_container ( ) ) ;
} else {
initial_offset = 0 ;
}
2023-07-25 23:18:08 +03:00
if ( flex_line . remaining_free_space . has_value ( ) & & number_of_items > 1 )
space_between_items = flex_line . remaining_free_space . value ( ) / ( number_of_items - 1 ) ;
2023-03-10 23:46:18 +03:00
break ;
case CSS : : JustifyContent : : SpaceAround :
2023-07-25 23:18:08 +03:00
if ( flex_line . remaining_free_space . has_value ( ) )
space_between_items = flex_line . remaining_free_space . value ( ) / number_of_items ;
2023-05-28 16:39:21 +03:00
if ( is_direction_reverse ( ) ) {
initial_offset = inner_main_size ( flex_container ( ) ) - space_between_items / 2.0 ;
} else {
initial_offset = space_between_items / 2.0 ;
}
2023-03-10 23:46:18 +03:00
break ;
2023-07-05 18:43:48 +03:00
case CSS : : JustifyContent : : SpaceEvenly :
2023-07-25 23:18:08 +03:00
if ( flex_line . remaining_free_space . has_value ( ) )
space_between_items = flex_line . remaining_free_space . value ( ) / ( number_of_items + 1 ) ;
2023-07-05 18:43:48 +03:00
if ( is_direction_reverse ( ) ) {
initial_offset = inner_main_size ( flex_container ( ) ) - space_between_items ;
} else {
initial_offset = space_between_items ;
}
break ;
2023-03-10 23:46:18 +03:00
}
}
// For reverse, we use FlexRegionRenderCursor::Right
// to indicate the cursor offset is the end and render backwards
// Otherwise the cursor offset is the 'start' of the region or initial offset
2022-07-31 21:18:31 +03:00
enum class FlexRegionRenderCursor {
Left ,
Right
} ;
auto flex_region_render_cursor = FlexRegionRenderCursor : : Left ;
2023-07-26 01:50:08 +03:00
if ( auto_margins = = 0 ) {
switch ( flex_container ( ) . computed_values ( ) . justify_content ( ) ) {
case CSS : : JustifyContent : : FlexStart :
case CSS : : JustifyContent : : Center :
case CSS : : JustifyContent : : SpaceAround :
case CSS : : JustifyContent : : SpaceBetween :
case CSS : : JustifyContent : : SpaceEvenly :
if ( is_direction_reverse ( ) ) {
flex_region_render_cursor = FlexRegionRenderCursor : : Right ;
}
break ;
case CSS : : JustifyContent : : End :
2022-10-04 02:02:29 +03:00
flex_region_render_cursor = FlexRegionRenderCursor : : Right ;
2023-07-26 01:50:08 +03:00
break ;
case CSS : : JustifyContent : : FlexEnd :
if ( ! is_direction_reverse ( ) ) {
flex_region_render_cursor = FlexRegionRenderCursor : : Right ;
}
break ;
default :
break ;
2022-10-04 02:02:29 +03:00
}
2021-10-13 23:20:55 +03:00
}
2022-11-03 20:10:17 +03:00
CSSPixels cursor_offset = initial_offset ;
2022-07-06 23:11:48 +03:00
2023-03-10 23:46:18 +03:00
auto place_item = [ & ] ( FlexItem & item ) {
2023-01-07 21:28:06 +03:00
auto amount_of_main_size_used = item . main_size . value ( )
2023-03-10 23:46:18 +03:00
+ item . margins . main_before
2022-07-06 23:11:48 +03:00
+ item . borders . main_before
+ item . padding . main_before
2023-03-10 23:46:18 +03:00
+ item . margins . main_after
2022-07-06 23:11:48 +03:00
+ item . borders . main_after
+ item . padding . main_after
+ space_between_items ;
2023-07-26 00:51:29 +03:00
if ( is_direction_reverse ( ) & & flex_region_render_cursor = = FlexRegionRenderCursor : : Right ) {
2023-03-10 23:46:18 +03:00
item . main_offset = cursor_offset - item . main_size . value ( ) - item . margins . main_after - item . borders . main_after - item . padding . main_after ;
2022-07-31 21:18:31 +03:00
cursor_offset - = amount_of_main_size_used ;
} else if ( flex_region_render_cursor = = FlexRegionRenderCursor : : Right ) {
cursor_offset - = amount_of_main_size_used ;
2023-03-10 23:46:18 +03:00
item . main_offset = cursor_offset + item . margins . main_before + item . borders . main_before + item . padding . main_before ;
2022-07-06 23:11:48 +03:00
} else {
2023-03-10 23:46:18 +03:00
item . main_offset = cursor_offset + item . margins . main_before + item . borders . main_before + item . padding . main_before ;
2022-07-31 21:18:31 +03:00
cursor_offset + = amount_of_main_size_used ;
2022-07-06 23:11:48 +03:00
}
} ;
2023-07-26 00:51:29 +03:00
if ( flex_region_render_cursor = = FlexRegionRenderCursor : : Right ) {
2022-11-02 15:32:49 +03:00
for ( ssize_t i = flex_line . items . size ( ) - 1 ; i > = 0 ; - - i ) {
auto & item = flex_line . items [ i ] ;
2023-03-10 23:46:18 +03:00
place_item ( item ) ;
2022-07-06 23:11:48 +03:00
}
} else {
2022-11-02 15:32:49 +03:00
for ( size_t i = 0 ; i < flex_line . items . size ( ) ; + + i ) {
auto & item = flex_line . items [ i ] ;
2023-03-10 23:46:18 +03:00
place_item ( item ) ;
2022-07-06 23:11:48 +03:00
}
2021-10-13 23:20:55 +03:00
}
}
}
2022-03-13 02:03:59 +03:00
void FlexFormattingContext : : dump_items ( ) const
{
2022-07-17 18:59:02 +03:00
dbgln ( " \033 [34;1mflex-container \033 [0m {}, direction: {}, current-size: {}x{} " , flex_container ( ) . debug_description ( ) , is_row_layout ( ) ? " row " : " column " , m_flex_container_state . content_width ( ) , m_flex_container_state . content_height ( ) ) ;
2022-03-13 02:03:59 +03:00
for ( size_t i = 0 ; i < m_flex_lines . size ( ) ; + + i ) {
dbgln ( " {} flex-line #{}: " , flex_container ( ) . debug_description ( ) , i ) ;
for ( size_t j = 0 ; j < m_flex_lines [ i ] . items . size ( ) ; + + j ) {
2023-03-08 19:24:35 +03:00
auto & item = m_flex_lines [ i ] . items [ j ] ;
2023-02-27 02:09:02 +03:00
dbgln ( " {} flex-item #{}: {} (main:{}, cross:{}) " , flex_container ( ) . debug_description ( ) , j , item . box - > debug_description ( ) , item . main_size . value_or ( - 1 ) , item . cross_size . value_or ( - 1 ) ) ;
2022-03-13 02:03:59 +03:00
}
}
}
2022-10-25 00:17:36 +03:00
CSS : : AlignItems FlexFormattingContext : : alignment_for_item ( Box const & box ) const
2022-07-12 00:52:36 +03:00
{
2022-10-25 00:17:36 +03:00
switch ( box . computed_values ( ) . align_self ( ) ) {
2022-07-12 00:52:36 +03:00
case CSS : : AlignSelf : : Auto :
return flex_container ( ) . computed_values ( ) . align_items ( ) ;
2023-06-19 17:50:04 +03:00
case CSS : : AlignSelf : : End :
return CSS : : AlignItems : : End ;
2022-07-12 00:52:36 +03:00
case CSS : : AlignSelf : : Normal :
return CSS : : AlignItems : : Normal ;
case CSS : : AlignSelf : : SelfStart :
return CSS : : AlignItems : : SelfStart ;
case CSS : : AlignSelf : : SelfEnd :
return CSS : : AlignItems : : SelfEnd ;
case CSS : : AlignSelf : : FlexStart :
return CSS : : AlignItems : : FlexStart ;
case CSS : : AlignSelf : : FlexEnd :
return CSS : : AlignItems : : FlexEnd ;
case CSS : : AlignSelf : : Center :
return CSS : : AlignItems : : Center ;
case CSS : : AlignSelf : : Baseline :
return CSS : : AlignItems : : Baseline ;
2023-06-06 17:54:46 +03:00
case CSS : : AlignSelf : : Start :
return CSS : : AlignItems : : Start ;
2022-07-12 00:52:36 +03:00
case CSS : : AlignSelf : : Stretch :
return CSS : : AlignItems : : Stretch ;
case CSS : : AlignSelf : : Safe :
return CSS : : AlignItems : : Safe ;
case CSS : : AlignSelf : : Unsafe :
return CSS : : AlignItems : : Unsafe ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2021-10-13 23:52:50 +03:00
void FlexFormattingContext : : align_all_flex_items_along_the_cross_axis ( )
2021-10-13 23:22:30 +03:00
{
// FIXME: Take better care of margins
2021-10-13 23:44:54 +03:00
for ( auto & flex_line : m_flex_lines ) {
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
2023-05-24 11:50:57 +03:00
CSSPixels half_line_size = flex_line . cross_size / 2.0 ;
2023-03-08 19:24:35 +03:00
switch ( alignment_for_item ( item . box ) ) {
2021-10-13 23:22:30 +03:00
case CSS : : AlignItems : : Baseline :
// FIXME: Implement this
// Fallthrough
2023-06-06 17:54:46 +03:00
case CSS : : AlignItems : : Start :
2021-10-13 23:22:30 +03:00
case CSS : : AlignItems : : FlexStart :
case CSS : : AlignItems : : Stretch :
2023-03-08 19:24:35 +03:00
item . cross_offset = - half_line_size + item . margins . cross_before + item . borders . cross_before + item . padding . cross_before ;
2021-10-13 23:22:30 +03:00
break ;
2023-06-06 17:54:46 +03:00
case CSS : : AlignItems : : End :
2021-10-13 23:22:30 +03:00
case CSS : : AlignItems : : FlexEnd :
2023-03-08 19:24:35 +03:00
item . cross_offset = half_line_size - item . cross_size . value ( ) - item . margins . cross_after - item . borders . cross_after - item . padding . cross_after ;
2021-10-13 23:22:30 +03:00
break ;
case CSS : : AlignItems : : Center :
2023-05-24 11:50:57 +03:00
item . cross_offset = - ( item . cross_size . value ( ) / 2.0 ) ;
2022-07-12 18:11:21 +03:00
break ;
2021-10-13 23:22:30 +03:00
default :
break ;
}
}
}
}
2021-10-13 23:24:09 +03:00
// https://www.w3.org/TR/css-flexbox-1/#algo-cross-container
2022-10-07 14:58:18 +03:00
void FlexFormattingContext : : determine_flex_container_used_cross_size ( )
2021-10-13 23:24:09 +03:00
{
2022-11-03 20:10:17 +03:00
CSSPixels cross_size = 0 ;
2021-10-13 23:52:50 +03:00
if ( has_definite_cross_size ( flex_container ( ) ) ) {
2022-02-28 02:02:00 +03:00
// Flex container has definite cross size: easy-peasy.
2023-03-08 19:36:32 +03:00
cross_size = inner_cross_size ( flex_container ( ) ) ;
2021-10-13 23:24:09 +03:00
} else {
2022-02-28 02:02:00 +03:00
// Flex container has indefinite cross size.
auto cross_size_value = is_row_layout ( ) ? flex_container ( ) . computed_values ( ) . height ( ) : flex_container ( ) . computed_values ( ) . width ( ) ;
2022-09-14 12:34:36 +03:00
if ( cross_size_value . is_auto ( ) | | cross_size_value . contains_percentage ( ) ) {
2022-02-28 02:02:00 +03:00
// If a content-based cross size is needed, use the sum of the flex lines' cross sizes.
2022-11-03 20:10:17 +03:00
CSSPixels sum_of_flex_lines_cross_sizes = 0 ;
2022-02-28 02:02:00 +03:00
for ( auto & flex_line : m_flex_lines ) {
sum_of_flex_lines_cross_sizes + = flex_line . cross_size ;
}
cross_size = sum_of_flex_lines_cross_sizes ;
2022-03-13 02:02:03 +03:00
2022-09-14 12:34:36 +03:00
if ( cross_size_value . contains_percentage ( ) ) {
2022-03-13 02:02:03 +03:00
// FIXME: Handle percentage values here! Right now we're just treating them as "auto"
}
2022-02-28 02:02:00 +03:00
} else {
// Otherwise, resolve the indefinite size at this point.
2023-05-06 17:34:55 +03:00
cross_size = cross_size_value . to_px ( flex_container ( ) , inner_cross_size ( * flex_container ( ) . containing_block ( ) ) ) ;
2021-10-13 23:24:09 +03:00
}
}
2023-08-01 19:32:08 +03:00
// AD-HOC: We don't apply min/max cross size constraints when sizing the flex container under an intrinsic sizing constraint.
if ( ! m_available_space_for_flex_container - > cross . is_intrinsic_sizing_constraint ( ) ) {
auto const & computed_min_size = this - > computed_cross_min_size ( flex_container ( ) ) ;
auto const & computed_max_size = this - > computed_cross_max_size ( flex_container ( ) ) ;
auto cross_min_size = ( ! computed_min_size . is_auto ( ) & & ! computed_min_size . contains_percentage ( ) ) ? specified_cross_min_size ( flex_container ( ) ) : 0 ;
auto cross_max_size = ( ! computed_max_size . is_none ( ) & & ! computed_max_size . contains_percentage ( ) ) ? specified_cross_max_size ( flex_container ( ) ) : INFINITY ;
set_cross_size ( flex_container ( ) , css_clamp ( cross_size , cross_min_size , cross_max_size ) ) ;
} else {
set_cross_size ( flex_container ( ) , cross_size ) ;
}
2021-10-13 23:24:09 +03:00
}
2021-10-13 23:25:39 +03:00
// https://www.w3.org/TR/css-flexbox-1/#algo-line-align
2021-10-13 23:44:54 +03:00
void FlexFormattingContext : : align_all_flex_lines ( )
2021-10-13 23:25:39 +03:00
{
2022-10-14 14:50:40 +03:00
if ( m_flex_lines . is_empty ( ) )
return ;
2021-10-13 23:25:39 +03:00
// FIXME: Support reverse
2022-07-14 01:01:12 +03:00
2023-03-08 19:36:32 +03:00
CSSPixels cross_size_of_flex_container = inner_cross_size ( flex_container ( ) ) ;
2022-07-14 01:01:12 +03:00
2022-02-28 01:58:54 +03:00
if ( is_single_line ( ) ) {
// For single-line flex containers, we only need to center the line along the cross axis.
auto & flex_line = m_flex_lines [ 0 ] ;
2023-05-24 11:50:57 +03:00
CSSPixels center_of_line = cross_size_of_flex_container / 2.0 ;
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
item . cross_offset + = center_of_line ;
2022-07-12 16:54:11 +03:00
}
2022-02-28 01:58:54 +03:00
} else {
2022-07-14 01:01:12 +03:00
2022-11-03 20:10:17 +03:00
CSSPixels sum_of_flex_line_cross_sizes = 0 ;
2022-10-14 14:50:40 +03:00
for ( auto & line : m_flex_lines )
sum_of_flex_line_cross_sizes + = line . cross_size ;
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between flex lines.
sum_of_flex_line_cross_sizes + = cross_gap ( ) * ( m_flex_lines . size ( ) - 1 ) ;
2022-11-03 20:10:17 +03:00
CSSPixels start_of_current_line = 0 ;
CSSPixels gap_size = 0 ;
2022-10-14 14:50:40 +03:00
switch ( flex_container ( ) . computed_values ( ) . align_content ( ) ) {
case CSS : : AlignContent : : FlexStart :
start_of_current_line = 0 ;
break ;
case CSS : : AlignContent : : FlexEnd :
start_of_current_line = cross_size_of_flex_container - sum_of_flex_line_cross_sizes ;
break ;
case CSS : : AlignContent : : Center :
start_of_current_line = ( cross_size_of_flex_container / 2 ) - ( sum_of_flex_line_cross_sizes / 2 ) ;
break ;
2022-12-14 23:30:59 +03:00
case CSS : : AlignContent : : SpaceBetween : {
start_of_current_line = 0 ;
2022-11-03 20:10:17 +03:00
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes ;
2022-12-14 23:30:59 +03:00
if ( leftover_free_space > = 0 ) {
int gap_count = m_flex_lines . size ( ) - 1 ;
gap_size = leftover_free_space / gap_count ;
}
2022-10-14 14:50:40 +03:00
break ;
2022-12-14 23:30:59 +03:00
}
case CSS : : AlignContent : : SpaceAround : {
2022-11-03 20:10:17 +03:00
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes ;
2022-12-14 23:30:59 +03:00
if ( leftover_free_space < 0 ) {
// If the leftover free-space is negative this value is identical to center.
start_of_current_line = ( cross_size_of_flex_container / 2 ) - ( sum_of_flex_line_cross_sizes / 2 ) ;
break ;
}
gap_size = leftover_free_space / m_flex_lines . size ( ) ;
// The spacing between the first/last lines and the flex container edges is half the size of the spacing between flex lines.
start_of_current_line = gap_size / 2 ;
2022-10-14 14:50:40 +03:00
break ;
2022-12-14 23:30:59 +03:00
}
2023-07-05 18:43:48 +03:00
case CSS : : AlignContent : : SpaceEvenly : {
auto leftover_free_space = cross_size_of_flex_container - sum_of_flex_line_cross_sizes ;
if ( leftover_free_space < 0 ) {
// If the leftover free-space is negative this value is identical to center.
start_of_current_line = ( cross_size_of_flex_container / 2 ) - ( sum_of_flex_line_cross_sizes / 2 ) ;
break ;
}
gap_size = leftover_free_space / ( m_flex_lines . size ( ) + 1 ) ;
// The spacing between the first/last lines and the flex container edges is the size of the spacing between flex lines.
start_of_current_line = gap_size ;
break ;
}
2022-10-14 14:50:40 +03:00
case CSS : : AlignContent : : Stretch :
start_of_current_line = 0 ;
break ;
}
2022-07-14 01:01:12 +03:00
for ( auto & flex_line : m_flex_lines ) {
2022-11-03 20:10:17 +03:00
CSSPixels center_of_current_line = start_of_current_line + ( flex_line . cross_size / 2 ) ;
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
item . cross_offset + = center_of_current_line ;
2022-07-14 01:01:12 +03:00
}
2022-12-14 23:30:59 +03:00
start_of_current_line + = flex_line . cross_size + gap_size ;
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between flex lines.
start_of_current_line + = cross_gap ( ) ;
2022-07-14 01:01:12 +03:00
}
2021-10-13 23:25:39 +03:00
}
}
2022-02-28 01:58:54 +03:00
void FlexFormattingContext : : copy_dimensions_from_flex_items_to_boxes ( )
{
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
auto const & box = item . box ;
2022-03-26 03:30:08 +03:00
auto & box_state = m_state . get_mutable ( box ) ;
2023-05-06 17:34:55 +03:00
box_state . padding_left = box - > computed_values ( ) . padding ( ) . left ( ) . to_px ( box , m_flex_container_state . content_width ( ) ) ;
box_state . padding_right = box - > computed_values ( ) . padding ( ) . right ( ) . to_px ( box , m_flex_container_state . content_width ( ) ) ;
box_state . padding_top = box - > computed_values ( ) . padding ( ) . top ( ) . to_px ( box , m_flex_container_state . content_width ( ) ) ;
box_state . padding_bottom = box - > computed_values ( ) . padding ( ) . bottom ( ) . to_px ( box , m_flex_container_state . content_width ( ) ) ;
2022-03-26 03:30:08 +03:00
2023-05-06 17:34:55 +03:00
box_state . margin_left = box - > computed_values ( ) . margin ( ) . left ( ) . to_px ( box , m_flex_container_state . content_width ( ) ) ;
box_state . margin_right = box - > computed_values ( ) . margin ( ) . right ( ) . to_px ( box , m_flex_container_state . content_width ( ) ) ;
box_state . margin_top = box - > computed_values ( ) . margin ( ) . top ( ) . to_px ( box , m_flex_container_state . content_width ( ) ) ;
box_state . margin_bottom = box - > computed_values ( ) . margin ( ) . bottom ( ) . to_px ( box , m_flex_container_state . content_width ( ) ) ;
2022-03-26 03:30:08 +03:00
2023-02-27 02:09:02 +03:00
box_state . border_left = box - > computed_values ( ) . border_left ( ) . width ;
box_state . border_right = box - > computed_values ( ) . border_right ( ) . width ;
box_state . border_top = box - > computed_values ( ) . border_top ( ) . width ;
box_state . border_bottom = box - > computed_values ( ) . border_bottom ( ) . width ;
2022-03-26 03:30:08 +03:00
2023-03-08 19:24:35 +03:00
set_main_size ( box , item . main_size . value ( ) ) ;
set_cross_size ( box , item . cross_size . value ( ) ) ;
set_offset ( box , item . main_offset , item . cross_offset ) ;
2022-02-28 01:58:54 +03:00
}
}
2022-04-06 02:20:20 +03:00
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-sizes
2022-09-27 16:29:17 +03:00
void FlexFormattingContext : : determine_intrinsic_size_of_flex_container ( )
2022-04-06 02:20:20 +03:00
{
2022-10-04 12:56:59 +03:00
if ( m_available_space_for_flex_container - > main . is_intrinsic_sizing_constraint ( ) ) {
2022-11-03 20:10:17 +03:00
CSSPixels main_size = calculate_intrinsic_main_size_of_flex_container ( ) ;
2022-10-04 12:56:59 +03:00
set_main_size ( flex_container ( ) , main_size ) ;
}
if ( m_available_space_for_items - > cross . is_intrinsic_sizing_constraint ( ) ) {
2022-11-03 20:10:17 +03:00
CSSPixels cross_size = calculate_intrinsic_cross_size_of_flex_container ( ) ;
2022-10-04 12:56:59 +03:00
set_cross_size ( flex_container ( ) , cross_size ) ;
2022-04-06 02:20:20 +03:00
}
}
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-main-sizes
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_intrinsic_main_size_of_flex_container ( )
2022-04-06 02:20:20 +03:00
{
// The min-content main size of a single-line flex container is calculated identically to the max-content main size,
// except that the flex items’ min-content contributions are used instead of their max-content contributions.
// However, for a multi-line container, it is simply the largest min-content contribution of all the non-collapsed flex items in the flex container.
2022-09-27 16:29:17 +03:00
if ( ! is_single_line ( ) & & m_available_space_for_items - > main . is_min_content ( ) ) {
2022-11-03 20:10:17 +03:00
CSSPixels largest_contribution = 0 ;
2023-03-08 19:24:35 +03:00
for ( auto const & item : m_flex_items ) {
2022-04-06 02:20:20 +03:00
// FIXME: Skip collapsed flex items.
2023-03-08 19:24:35 +03:00
largest_contribution = max ( largest_contribution , calculate_main_min_content_contribution ( item ) ) ;
2022-04-06 02:20:20 +03:00
}
return largest_contribution ;
}
// The max-content main size of a flex container is, fundamentally, the smallest size the flex container
// can take such that when flex layout is run with that container size, each flex item ends up at least
// as large as its max-content contribution, to the extent allowed by the items’ flexibility.
// It is calculated, considering only non-collapsed flex items, by:
// 1. For each flex item, subtract its outer flex base size from its max-content contribution size.
2022-07-13 14:26:02 +03:00
// If that result is positive, divide it by the item’ s flex grow factor if the flex grow factor is ≥ 1,
// or multiply it by the flex grow factor if the flex grow factor is < 1; if the result is negative,
// divide it by the item’ s scaled flex shrink factor (if dividing by zero, treat the result as negative infinity).
// This is the item’ s desired flex fraction.
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
2022-11-03 20:10:17 +03:00
CSSPixels contribution = 0 ;
2022-09-27 16:29:17 +03:00
if ( m_available_space_for_items - > main . is_min_content ( ) )
2023-03-08 19:24:35 +03:00
contribution = calculate_main_min_content_contribution ( item ) ;
2022-09-27 16:29:17 +03:00
else if ( m_available_space_for_items - > main . is_max_content ( ) )
2023-03-08 19:24:35 +03:00
contribution = calculate_main_max_content_contribution ( item ) ;
2022-04-06 02:20:20 +03:00
2023-03-08 19:24:35 +03:00
CSSPixels outer_flex_base_size = item . flex_base_size + item . margins . main_before + item . margins . main_after + item . borders . main_before + item . borders . main_after + item . padding . main_before + item . padding . main_after ;
2022-07-13 14:26:02 +03:00
2022-11-03 20:10:17 +03:00
CSSPixels result = contribution - outer_flex_base_size ;
2022-07-13 14:26:02 +03:00
if ( result > 0 ) {
2023-02-27 02:09:02 +03:00
if ( item . box - > computed_values ( ) . flex_grow ( ) > = 1 ) {
result / = item . box - > computed_values ( ) . flex_grow ( ) ;
2022-07-13 14:26:02 +03:00
} else {
2023-02-27 02:09:02 +03:00
result * = item . box - > computed_values ( ) . flex_grow ( ) ;
2022-07-13 14:26:02 +03:00
}
} else if ( result < 0 ) {
2023-03-08 19:24:35 +03:00
if ( item . scaled_flex_shrink_factor = = 0 )
2022-07-13 14:26:02 +03:00
result = - INFINITY ;
else
2023-03-08 19:24:35 +03:00
result / = item . scaled_flex_shrink_factor ;
2022-07-13 14:26:02 +03:00
}
2022-04-06 02:20:20 +03:00
2023-06-12 21:37:35 +03:00
item . desired_flex_fraction = result . to_double ( ) ;
2022-04-06 02:20:20 +03:00
}
// 2. Place all flex items into lines of infinite length.
m_flex_lines . clear ( ) ;
if ( ! m_flex_items . is_empty ( ) )
m_flex_lines . append ( FlexLine { } ) ;
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
2022-04-06 02:20:20 +03:00
// FIXME: Honor breaking requests.
2023-03-08 19:24:35 +03:00
m_flex_lines . last ( ) . items . append ( item ) ;
2022-04-06 02:20:20 +03:00
}
2022-07-13 14:26:02 +03:00
// Within each line, find the greatest (most positive) desired flex fraction among all the flex items.
// This is the line’ s chosen flex fraction.
2022-04-06 02:20:20 +03:00
for ( auto & flex_line : m_flex_lines ) {
2022-07-13 14:26:02 +03:00
float greatest_desired_flex_fraction = 0 ;
float sum_of_flex_grow_factors = 0 ;
float sum_of_flex_shrink_factors = 0 ;
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
greatest_desired_flex_fraction = max ( greatest_desired_flex_fraction , item . desired_flex_fraction ) ;
2023-02-27 02:09:02 +03:00
sum_of_flex_grow_factors + = item . box - > computed_values ( ) . flex_grow ( ) ;
sum_of_flex_shrink_factors + = item . box - > computed_values ( ) . flex_shrink ( ) ;
2022-04-06 02:20:20 +03:00
}
2022-07-13 14:26:02 +03:00
float chosen_flex_fraction = greatest_desired_flex_fraction ;
// 3. If the chosen flex fraction is positive, and the sum of the line’ s flex grow factors is less than 1,
// divide the chosen flex fraction by that sum.
if ( chosen_flex_fraction > 0 & & sum_of_flex_grow_factors < 1 )
chosen_flex_fraction / = sum_of_flex_grow_factors ;
// If the chosen flex fraction is negative, and the sum of the line’ s flex shrink factors is less than 1,
// multiply the chosen flex fraction by that sum.
if ( chosen_flex_fraction < 0 & & sum_of_flex_shrink_factors < 1 )
chosen_flex_fraction * = sum_of_flex_shrink_factors ;
2022-04-06 02:20:20 +03:00
2022-07-22 23:02:02 +03:00
flex_line . chosen_flex_fraction = chosen_flex_fraction ;
}
2022-11-03 20:10:17 +03:00
auto determine_main_size = [ & ] ( ) - > CSSPixels {
CSSPixels largest_sum = 0 ;
2022-07-22 23:02:02 +03:00
for ( auto & flex_line : m_flex_lines ) {
// 4. Add each item’ s flex base size to the product of its flex grow factor (scaled flex shrink factor, if shrinking)
// and the chosen flex fraction, then clamp that result by the max main size floored by the min main size.
2022-11-03 20:10:17 +03:00
CSSPixels sum = 0 ;
2023-03-08 19:24:35 +03:00
for ( auto & item : flex_line . items ) {
2023-05-24 11:50:57 +03:00
double product = 0 ;
2023-03-08 19:24:35 +03:00
if ( item . desired_flex_fraction > 0 )
2023-05-24 11:50:57 +03:00
product = flex_line . chosen_flex_fraction * static_cast < double > ( item . box - > computed_values ( ) . flex_grow ( ) ) ;
2023-03-08 19:24:35 +03:00
else if ( item . desired_flex_fraction < 0 )
product = flex_line . chosen_flex_fraction * item . scaled_flex_shrink_factor ;
auto result = item . flex_base_size + product ;
2022-04-06 02:20:20 +03:00
2023-03-08 19:24:35 +03:00
auto const & computed_min_size = this - > computed_main_min_size ( item . box ) ;
auto const & computed_max_size = this - > computed_main_max_size ( item . box ) ;
2022-07-22 23:02:02 +03:00
2023-07-17 22:19:33 +03:00
auto clamp_min = ( ! computed_min_size . is_auto ( ) & & ! computed_min_size . contains_percentage ( ) ) ? specified_main_min_size ( item . box ) : automatic_minimum_size ( item ) ;
auto clamp_max = ( ! computed_max_size . is_none ( ) & & ! computed_max_size . contains_percentage ( ) ) ? specified_main_max_size ( item . box ) : NumericLimits < float > : : max ( ) ;
2022-07-22 23:02:02 +03:00
result = css_clamp ( result , clamp_min , clamp_max ) ;
// NOTE: The spec doesn't mention anything about the *outer* size here, but if we don't add the margin box,
// flex items with non-zero padding/border/margin in the main axis end up overflowing the container.
2023-03-08 19:24:35 +03:00
result = item . add_main_margin_box_sizes ( result ) ;
2022-07-22 23:02:02 +03:00
sum + = result ;
}
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between flex items.
sum + = main_gap ( ) * ( flex_line . items . size ( ) - 1 ) ;
2022-07-22 23:02:02 +03:00
largest_sum = max ( largest_sum , sum ) ;
}
2022-07-21 02:19:10 +03:00
// 5. The flex container’ s max-content size is the largest sum (among all the lines) of the afore-calculated sizes of all items within a single line.
2022-07-22 23:02:02 +03:00
return largest_sum ;
} ;
2022-04-06 02:20:20 +03:00
2022-09-27 16:29:17 +03:00
auto main_size = determine_main_size ( ) ;
set_main_size ( flex_container ( ) , main_size ) ;
return main_size ;
2022-04-06 02:20:20 +03:00
}
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-cross-sizes
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_intrinsic_cross_size_of_flex_container ( )
2022-04-06 02:20:20 +03:00
{
// The min-content/max-content cross size of a single-line flex container
// is the largest min-content contribution/max-content contribution (respectively) of its flex items.
if ( is_single_line ( ) ) {
2022-07-22 23:02:02 +03:00
auto calculate_largest_contribution = [ & ] ( bool resolve_percentage_min_max_sizes ) {
2022-11-03 20:10:17 +03:00
CSSPixels largest_contribution = 0 ;
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
2022-11-03 20:10:17 +03:00
CSSPixels contribution = 0 ;
2022-09-27 16:29:17 +03:00
if ( m_available_space_for_items - > cross . is_min_content ( ) )
2023-03-08 19:24:35 +03:00
contribution = calculate_cross_min_content_contribution ( item , resolve_percentage_min_max_sizes ) ;
2022-09-27 16:29:17 +03:00
else if ( m_available_space_for_items - > cross . is_max_content ( ) )
2023-03-08 19:24:35 +03:00
contribution = calculate_cross_max_content_contribution ( item , resolve_percentage_min_max_sizes ) ;
2022-07-22 23:02:02 +03:00
largest_contribution = max ( largest_contribution , contribution ) ;
}
return largest_contribution ;
} ;
auto first_pass_largest_contribution = calculate_largest_contribution ( false ) ;
set_cross_size ( flex_container ( ) , first_pass_largest_contribution ) ;
auto second_pass_largest_contribution = calculate_largest_contribution ( true ) ;
return second_pass_largest_contribution ;
2022-04-06 02:20:20 +03:00
}
2022-10-08 14:37:44 +03:00
if ( is_row_layout ( ) ) {
// row multi-line flex container cross-size
// The min-content/max-content cross size is the sum of the flex line cross sizes resulting from
// sizing the flex container under a cross-axis min-content constraint/max-content constraint (respectively).
// NOTE: We fall through to the ad-hoc section below.
} else {
// column multi-line flex container cross-size
// The min-content cross size is the largest min-content contribution among all of its flex items.
if ( m_available_space_for_items - > cross . is_min_content ( ) ) {
auto calculate_largest_contribution = [ & ] ( bool resolve_percentage_min_max_sizes ) {
2022-11-03 20:10:17 +03:00
CSSPixels largest_contribution = 0 ;
2023-03-08 19:24:35 +03:00
for ( auto & item : m_flex_items ) {
CSSPixels contribution = calculate_cross_min_content_contribution ( item , resolve_percentage_min_max_sizes ) ;
2022-10-08 14:37:44 +03:00
largest_contribution = max ( largest_contribution , contribution ) ;
}
return largest_contribution ;
} ;
auto first_pass_largest_contribution = calculate_largest_contribution ( false ) ;
set_cross_size ( flex_container ( ) , first_pass_largest_contribution ) ;
auto second_pass_largest_contribution = calculate_largest_contribution ( true ) ;
return second_pass_largest_contribution ;
}
// The max-content cross size is the sum of the flex line cross sizes resulting from
// sizing the flex container under a cross-axis max-content constraint,
// using the largest max-content cross-size contribution among the flex items
// as the available space in the cross axis for each of the flex items during layout.
// NOTE: We fall through to the ad-hoc section below.
}
2022-09-27 16:29:17 +03:00
2022-11-03 20:10:17 +03:00
CSSPixels sum_of_flex_line_cross_sizes = 0 ;
2022-04-06 02:20:20 +03:00
for ( auto & flex_line : m_flex_lines ) {
sum_of_flex_line_cross_sizes + = flex_line . cross_size ;
}
2023-04-14 11:35:26 +03:00
// CSS-FLEXBOX-2: Account for gap between flex lines.
sum_of_flex_line_cross_sizes + = cross_gap ( ) * ( m_flex_lines . size ( ) - 1 ) ;
2022-04-06 02:20:20 +03:00
return sum_of_flex_line_cross_sizes ;
}
2022-07-07 16:31:53 +03:00
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-item-contributions
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_main_min_content_contribution ( FlexItem const & item ) const
2022-04-06 02:20:20 +03:00
{
2022-07-07 16:31:53 +03:00
// The main-size min-content contribution of a flex item is
// the larger of its outer min-content size and outer preferred size if that is not auto,
// clamped by its min/max main size.
2022-07-21 03:06:17 +03:00
auto larger_size = [ & ] {
auto inner_min_content_size = calculate_min_content_main_size ( item ) ;
if ( computed_main_size ( item . box ) . is_auto ( ) )
return inner_min_content_size ;
2023-07-17 22:19:33 +03:00
auto inner_preferred_size = is_row_layout ( ) ? get_pixel_width ( item . box , computed_main_size ( item . box ) ) : get_pixel_height ( item . box , computed_main_size ( item . box ) ) ;
2022-07-21 03:06:17 +03:00
return max ( inner_min_content_size , inner_preferred_size ) ;
2022-07-07 16:31:53 +03:00
} ( ) ;
2023-07-17 22:19:33 +03:00
auto clamp_min = has_main_min_size ( item . box ) ? specified_main_min_size ( item . box ) : automatic_minimum_size ( item ) ;
auto clamp_max = has_main_max_size ( item . box ) ? specified_main_max_size ( item . box ) : NumericLimits < float > : : max ( ) ;
2022-07-21 03:06:17 +03:00
auto clamped_inner_size = css_clamp ( larger_size , clamp_min , clamp_max ) ;
return item . add_main_margin_box_sizes ( clamped_inner_size ) ;
2022-04-06 02:20:20 +03:00
}
2022-07-07 16:31:53 +03:00
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-item-contributions
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_main_max_content_contribution ( FlexItem const & item ) const
2022-04-06 02:20:20 +03:00
{
2022-07-21 03:06:17 +03:00
// The main-size max-content contribution of a flex item is
// the larger of its outer max-content size and outer preferred size if that is not auto,
// clamped by its min/max main size.
auto larger_size = [ & ] {
auto inner_max_content_size = calculate_max_content_main_size ( item ) ;
if ( computed_main_size ( item . box ) . is_auto ( ) )
return inner_max_content_size ;
2023-07-17 22:19:33 +03:00
auto inner_preferred_size = is_row_layout ( ) ? get_pixel_width ( item . box , computed_main_size ( item . box ) ) : get_pixel_height ( item . box , computed_main_size ( item . box ) ) ;
2022-07-21 03:06:17 +03:00
return max ( inner_max_content_size , inner_preferred_size ) ;
2022-07-07 16:31:53 +03:00
} ( ) ;
2023-07-17 22:19:33 +03:00
auto clamp_min = has_main_min_size ( item . box ) ? specified_main_min_size ( item . box ) : automatic_minimum_size ( item ) ;
auto clamp_max = has_main_max_size ( item . box ) ? specified_main_max_size ( item . box ) : NumericLimits < float > : : max ( ) ;
2022-07-21 03:06:17 +03:00
auto clamped_inner_size = css_clamp ( larger_size , clamp_min , clamp_max ) ;
return item . add_main_margin_box_sizes ( clamped_inner_size ) ;
2022-04-06 02:20:20 +03:00
}
2022-11-05 13:38:28 +03:00
bool FlexFormattingContext : : should_treat_main_size_as_auto ( Box const & box ) const
{
if ( is_row_layout ( ) )
return should_treat_width_as_auto ( box , m_available_space_for_items - > space ) ;
return should_treat_height_as_auto ( box , m_available_space_for_items - > space ) ;
}
bool FlexFormattingContext : : should_treat_cross_size_as_auto ( Box const & box ) const
{
if ( is_row_layout ( ) )
return should_treat_height_as_auto ( box , m_available_space_for_items - > space ) ;
return should_treat_width_as_auto ( box , m_available_space_for_items - > space ) ;
}
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_cross_min_content_contribution ( FlexItem const & item , bool resolve_percentage_min_max_sizes ) const
2022-04-06 02:20:20 +03:00
{
2022-11-05 13:38:28 +03:00
auto size = [ & ] {
if ( should_treat_cross_size_as_auto ( item . box ) )
return calculate_min_content_cross_size ( item ) ;
2023-07-17 22:19:33 +03:00
return ! is_row_layout ( ) ? get_pixel_width ( item . box , computed_cross_size ( item . box ) ) : get_pixel_height ( item . box , computed_cross_size ( item . box ) ) ;
2022-07-11 19:50:05 +03:00
} ( ) ;
2022-07-22 23:02:02 +03:00
auto const & computed_min_size = this - > computed_cross_min_size ( item . box ) ;
auto const & computed_max_size = this - > computed_cross_max_size ( item . box ) ;
2023-07-17 22:19:33 +03:00
auto clamp_min = ( ! computed_min_size . is_auto ( ) & & ( resolve_percentage_min_max_sizes | | ! computed_min_size . contains_percentage ( ) ) ) ? specified_cross_min_size ( item . box ) : 0 ;
auto clamp_max = ( ! computed_max_size . is_none ( ) & & ( resolve_percentage_min_max_sizes | | ! computed_max_size . contains_percentage ( ) ) ) ? specified_cross_max_size ( item . box ) : NumericLimits < float > : : max ( ) ;
2022-07-22 23:02:02 +03:00
2022-11-05 13:38:28 +03:00
auto clamped_inner_size = css_clamp ( size , clamp_min , clamp_max ) ;
2022-07-11 19:50:05 +03:00
2022-07-21 03:06:17 +03:00
return item . add_cross_margin_box_sizes ( clamped_inner_size ) ;
2022-04-06 02:20:20 +03:00
}
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_cross_max_content_contribution ( FlexItem const & item , bool resolve_percentage_min_max_sizes ) const
2022-04-06 02:20:20 +03:00
{
2022-11-05 13:38:28 +03:00
auto size = [ & ] {
if ( should_treat_cross_size_as_auto ( item . box ) )
return calculate_max_content_cross_size ( item ) ;
2023-07-17 22:19:33 +03:00
return ! is_row_layout ( ) ? get_pixel_width ( item . box , computed_cross_size ( item . box ) ) : get_pixel_height ( item . box , computed_cross_size ( item . box ) ) ;
2022-07-11 19:50:05 +03:00
} ( ) ;
2022-07-22 23:02:02 +03:00
auto const & computed_min_size = this - > computed_cross_min_size ( item . box ) ;
auto const & computed_max_size = this - > computed_cross_max_size ( item . box ) ;
2023-07-17 22:19:33 +03:00
auto clamp_min = ( ! computed_min_size . is_auto ( ) & & ( resolve_percentage_min_max_sizes | | ! computed_min_size . contains_percentage ( ) ) ) ? specified_cross_min_size ( item . box ) : 0 ;
auto clamp_max = ( ! computed_max_size . is_none ( ) & & ( resolve_percentage_min_max_sizes | | ! computed_max_size . contains_percentage ( ) ) ) ? specified_cross_max_size ( item . box ) : NumericLimits < float > : : max ( ) ;
2022-07-22 23:02:02 +03:00
2022-11-05 13:38:28 +03:00
auto clamped_inner_size = css_clamp ( size , clamp_min , clamp_max ) ;
2022-07-11 19:50:05 +03:00
2022-07-21 03:06:17 +03:00
return item . add_cross_margin_box_sizes ( clamped_inner_size ) ;
2022-04-06 02:20:20 +03:00
}
2023-08-05 10:11:57 +03:00
CSSPixels FlexFormattingContext : : calculate_width_to_use_when_determining_intrinsic_height_of_item ( FlexItem const & item ) const
2023-03-28 19:29:12 +03:00
{
2023-08-05 10:11:57 +03:00
auto const & box = * item . box ;
auto computed_width = box . computed_values ( ) . width ( ) ;
auto const & computed_min_width = box . computed_values ( ) . min_width ( ) ;
auto const & computed_max_width = box . computed_values ( ) . max_width ( ) ;
auto clamp_min = ( ! computed_min_width . is_auto ( ) & & ( ! computed_min_width . contains_percentage ( ) ) ) ? specified_cross_min_size ( box ) : 0 ;
auto clamp_max = ( ! computed_max_width . is_none ( ) & & ( ! computed_max_width . contains_percentage ( ) ) ) ? specified_cross_max_size ( box ) : NumericLimits < float > : : max ( ) ;
CSSPixels width ;
if ( should_treat_width_as_auto ( box , m_available_space_for_items - > space ) | | computed_width . is_fit_content ( ) )
width = calculate_fit_content_width ( box , m_available_space_for_items - > space ) ;
else if ( computed_width . is_min_content ( ) )
width = calculate_min_content_width ( box ) ;
else if ( computed_width . is_max_content ( ) )
width = calculate_max_content_width ( box ) ;
return css_clamp ( width , clamp_min , clamp_max ) ;
2023-03-28 19:29:12 +03:00
}
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_min_content_main_size ( FlexItem const & item ) const
2022-07-08 13:50:04 +03:00
{
2023-01-15 19:51:30 +03:00
if ( is_row_layout ( ) ) {
return calculate_min_content_width ( item . box ) ;
}
2023-03-27 21:56:20 +03:00
auto available_space = m_state . get ( item . box ) . available_inner_space_or_constraints_from ( m_available_space_for_items - > space ) ;
if ( available_space . width . is_indefinite ( ) ) {
2023-08-05 10:11:57 +03:00
available_space . width = AvailableSize : : make_definite ( calculate_width_to_use_when_determining_intrinsic_height_of_item ( item ) ) ;
2023-03-27 21:56:20 +03:00
}
2023-08-12 16:17:17 +03:00
return calculate_min_content_height ( item . box , available_space . width . to_px_or_zero ( ) ) ;
2023-01-15 19:51:30 +03:00
}
CSSPixels FlexFormattingContext : : calculate_max_content_main_size ( FlexItem const & item ) const
{
if ( is_row_layout ( ) ) {
return calculate_max_content_width ( item . box ) ;
}
2023-03-27 21:56:20 +03:00
auto available_space = m_state . get ( item . box ) . available_inner_space_or_constraints_from ( m_available_space_for_items - > space ) ;
if ( available_space . width . is_indefinite ( ) ) {
2023-08-05 10:11:57 +03:00
available_space . width = AvailableSize : : make_definite ( calculate_width_to_use_when_determining_intrinsic_height_of_item ( item ) ) ;
2023-03-27 21:56:20 +03:00
}
2023-08-12 16:17:17 +03:00
return calculate_max_content_height ( item . box , available_space . width . to_px_or_zero ( ) ) ;
2022-07-08 13:50:04 +03:00
}
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_fit_content_main_size ( FlexItem const & item ) const
2022-07-09 16:17:47 +03:00
{
2023-01-25 16:16:25 +03:00
if ( is_row_layout ( ) )
2023-03-27 21:56:20 +03:00
return calculate_fit_content_width ( item . box , m_available_space_for_items - > space ) ;
return calculate_fit_content_height ( item . box , m_available_space_for_items - > space ) ;
2022-07-09 16:17:47 +03:00
}
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_fit_content_cross_size ( FlexItem const & item ) const
2022-07-09 16:17:47 +03:00
{
2023-01-25 16:16:25 +03:00
if ( ! is_row_layout ( ) )
2023-03-27 21:56:20 +03:00
return calculate_fit_content_width ( item . box , m_available_space_for_items - > space ) ;
return calculate_fit_content_height ( item . box , m_available_space_for_items - > space ) ;
2022-07-09 16:17:47 +03:00
}
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_min_content_cross_size ( FlexItem const & item ) const
2022-07-08 13:50:04 +03:00
{
2023-01-07 21:32:12 +03:00
if ( is_row_layout ( ) ) {
2023-01-25 17:01:42 +03:00
auto available_space = m_state . get ( item . box ) . available_inner_space_or_constraints_from ( m_available_space_for_flex_container - > space ) ;
2023-03-27 21:56:20 +03:00
if ( available_space . width . is_indefinite ( ) ) {
2023-08-05 10:11:57 +03:00
available_space . width = AvailableSize : : make_definite ( calculate_width_to_use_when_determining_intrinsic_height_of_item ( item ) ) ;
2023-03-27 21:56:20 +03:00
}
2023-08-12 16:17:17 +03:00
return calculate_min_content_height ( item . box , available_space . width . to_px_or_zero ( ) ) ;
2023-01-07 21:32:12 +03:00
}
return calculate_min_content_width ( item . box ) ;
2022-07-08 13:50:04 +03:00
}
2022-11-03 20:10:17 +03:00
CSSPixels FlexFormattingContext : : calculate_max_content_cross_size ( FlexItem const & item ) const
2022-07-08 13:50:04 +03:00
{
2023-01-07 21:32:12 +03:00
if ( is_row_layout ( ) ) {
2023-01-25 17:01:42 +03:00
auto available_space = m_state . get ( item . box ) . available_inner_space_or_constraints_from ( m_available_space_for_flex_container - > space ) ;
2023-03-27 21:56:20 +03:00
if ( available_space . width . is_indefinite ( ) ) {
2023-08-05 10:11:57 +03:00
available_space . width = AvailableSize : : make_definite ( calculate_width_to_use_when_determining_intrinsic_height_of_item ( item ) ) ;
2023-03-27 21:56:20 +03:00
}
2023-08-12 16:17:17 +03:00
return calculate_max_content_height ( item . box , available_space . width . to_px_or_zero ( ) ) ;
2023-01-07 21:32:12 +03:00
}
return calculate_max_content_width ( item . box ) ;
2022-07-08 13:50:04 +03:00
}
2022-07-20 20:06:47 +03:00
// https://drafts.csswg.org/css-flexbox-1/#stretched
bool FlexFormattingContext : : flex_item_is_stretched ( FlexItem const & item ) const
{
2022-10-25 00:17:36 +03:00
auto alignment = alignment_for_item ( item . box ) ;
2022-07-20 20:06:47 +03:00
if ( alignment ! = CSS : : AlignItems : : Stretch )
return false ;
// If the cross size property of the flex item computes to auto, and neither of the cross-axis margins are auto, the flex item is stretched.
2023-02-27 02:09:02 +03:00
auto const & computed_cross_size = is_row_layout ( ) ? item . box - > computed_values ( ) . height ( ) : item . box - > computed_values ( ) . width ( ) ;
2022-07-20 20:06:47 +03:00
return computed_cross_size . is_auto ( ) & & ! item . margins . cross_before_is_auto & & ! item . margins . cross_after_is_auto ;
}
2022-09-25 16:48:23 +03:00
CSS : : Size const & FlexFormattingContext : : computed_main_size ( Box const & box ) const
2022-07-21 01:27:26 +03:00
{
return is_row_layout ( ) ? box . computed_values ( ) . width ( ) : box . computed_values ( ) . height ( ) ;
}
2022-07-22 23:02:02 +03:00
2022-09-25 16:48:23 +03:00
CSS : : Size const & FlexFormattingContext : : computed_main_min_size ( Box const & box ) const
2022-07-22 23:02:02 +03:00
{
return is_row_layout ( ) ? box . computed_values ( ) . min_width ( ) : box . computed_values ( ) . min_height ( ) ;
}
2022-09-25 16:48:23 +03:00
CSS : : Size const & FlexFormattingContext : : computed_main_max_size ( Box const & box ) const
2022-07-22 23:02:02 +03:00
{
return is_row_layout ( ) ? box . computed_values ( ) . max_width ( ) : box . computed_values ( ) . max_height ( ) ;
}
2022-09-25 16:48:23 +03:00
CSS : : Size const & FlexFormattingContext : : computed_cross_size ( Box const & box ) const
2022-07-21 01:27:26 +03:00
{
return ! is_row_layout ( ) ? box . computed_values ( ) . width ( ) : box . computed_values ( ) . height ( ) ;
}
2022-09-25 16:48:23 +03:00
CSS : : Size const & FlexFormattingContext : : computed_cross_min_size ( Box const & box ) const
2022-07-22 23:02:02 +03:00
{
return ! is_row_layout ( ) ? box . computed_values ( ) . min_width ( ) : box . computed_values ( ) . min_height ( ) ;
}
2022-09-25 16:48:23 +03:00
CSS : : Size const & FlexFormattingContext : : computed_cross_max_size ( Box const & box ) const
2022-07-22 23:02:02 +03:00
{
return ! is_row_layout ( ) ? box . computed_values ( ) . max_width ( ) : box . computed_values ( ) . max_height ( ) ;
}
2022-09-28 18:23:22 +03:00
// https://drafts.csswg.org/css-flexbox-1/#algo-cross-margins
void FlexFormattingContext : : resolve_cross_axis_auto_margins ( )
{
for ( auto & line : m_flex_lines ) {
for ( auto & item : line . items ) {
// If a flex item has auto cross-axis margins:
2023-03-08 19:24:35 +03:00
if ( ! item . margins . cross_before_is_auto & & ! item . margins . cross_after_is_auto )
2022-09-28 18:23:22 +03:00
continue ;
// If its outer cross size (treating those auto margins as zero) is less than the cross size of its flex line,
// distribute the difference in those sizes equally to the auto margins.
2023-03-08 19:24:35 +03:00
auto outer_cross_size = item . cross_size . value ( ) + item . padding . cross_before + item . padding . cross_after + item . borders . cross_before + item . borders . cross_after ;
2022-09-28 18:23:22 +03:00
if ( outer_cross_size < line . cross_size ) {
2022-11-03 20:10:17 +03:00
CSSPixels remainder = line . cross_size - outer_cross_size ;
2023-03-08 19:24:35 +03:00
if ( item . margins . cross_before_is_auto & & item . margins . cross_after_is_auto ) {
2023-05-24 11:50:57 +03:00
item . margins . cross_before = remainder / 2.0 ;
item . margins . cross_after = remainder / 2.0 ;
2023-03-08 19:24:35 +03:00
} else if ( item . margins . cross_before_is_auto ) {
item . margins . cross_before = remainder ;
2022-09-28 18:23:22 +03:00
} else {
2023-03-08 19:24:35 +03:00
item . margins . cross_after = remainder ;
2022-09-28 18:23:22 +03:00
}
} else {
// FIXME: Otherwise, if the block-start or inline-start margin (whichever is in the cross axis) is auto, set it to zero.
// Set the opposite margin so that the outer cross size of the item equals the cross size of its flex line.
}
}
}
}
2022-10-14 14:50:40 +03:00
// https://drafts.csswg.org/css-flexbox-1/#algo-line-stretch
void FlexFormattingContext : : handle_align_content_stretch ( )
{
// If the flex container has a definite cross size,
if ( ! has_definite_cross_size ( flex_container ( ) ) )
return ;
// align-content is stretch,
if ( flex_container ( ) . computed_values ( ) . align_content ( ) ! = CSS : : AlignContent : : Stretch )
return ;
// and the sum of the flex lines' cross sizes is less than the flex container’ s inner cross size,
2022-11-03 20:10:17 +03:00
CSSPixels sum_of_flex_line_cross_sizes = 0 ;
2022-10-14 14:50:40 +03:00
for ( auto & line : m_flex_lines )
sum_of_flex_line_cross_sizes + = line . cross_size ;
2023-04-16 20:31:55 +03:00
// CSS-FLEXBOX-2: Account for gap between flex lines.
sum_of_flex_line_cross_sizes + = cross_gap ( ) * ( m_flex_lines . size ( ) - 1 ) ;
2023-03-08 19:36:32 +03:00
if ( sum_of_flex_line_cross_sizes > = inner_cross_size ( flex_container ( ) ) )
2022-10-14 14:50:40 +03:00
return ;
// increase the cross size of each flex line by equal amounts
// such that the sum of their cross sizes exactly equals the flex container’ s inner cross size.
2023-03-08 19:36:32 +03:00
CSSPixels remainder = inner_cross_size ( flex_container ( ) ) - sum_of_flex_line_cross_sizes ;
2022-11-03 20:10:17 +03:00
CSSPixels extra_per_line = remainder / m_flex_lines . size ( ) ;
2022-10-14 14:50:40 +03:00
for ( auto & line : m_flex_lines )
line . cross_size + = extra_per_line ;
}
2022-10-25 00:17:36 +03:00
// https://drafts.csswg.org/css-flexbox-1/#abspos-items
2022-11-23 20:46:10 +03:00
CSSPixelPoint FlexFormattingContext : : calculate_static_position ( Box const & box ) const
2022-10-25 00:17:36 +03:00
{
// The cross-axis edges of the static-position rectangle of an absolutely-positioned child
// of a flex container are the content edges of the flex container.
2022-11-03 20:10:17 +03:00
CSSPixels cross_offset = 0 ;
2023-03-08 19:36:32 +03:00
CSSPixels half_line_size = inner_cross_size ( flex_container ( ) ) / 2 ;
2022-10-25 00:17:36 +03:00
auto const & box_state = m_state . get ( box ) ;
2022-11-03 20:10:17 +03:00
CSSPixels cross_margin_before = is_row_layout ( ) ? box_state . margin_top : box_state . margin_left ;
CSSPixels cross_margin_after = is_row_layout ( ) ? box_state . margin_bottom : box_state . margin_right ;
CSSPixels cross_border_before = is_row_layout ( ) ? box_state . border_top : box_state . border_left ;
CSSPixels cross_border_after = is_row_layout ( ) ? box_state . border_bottom : box_state . border_right ;
CSSPixels cross_padding_after = is_row_layout ( ) ? box_state . padding_bottom : box_state . padding_right ;
2023-07-07 16:24:57 +03:00
CSSPixels main_border_before = is_row_layout ( ) ? box_state . border_left : box_state . border_top ;
CSSPixels main_border_after = is_row_layout ( ) ? box_state . border_right : box_state . border_bottom ;
2022-10-25 00:17:36 +03:00
switch ( alignment_for_item ( box ) ) {
case CSS : : AlignItems : : Baseline :
// FIXME: Implement this
// Fallthrough
2023-06-06 17:54:46 +03:00
case CSS : : AlignItems : : Start :
2022-10-25 00:17:36 +03:00
case CSS : : AlignItems : : FlexStart :
2023-07-04 15:57:41 +03:00
case CSS : : AlignItems : : SelfStart :
2022-10-25 00:17:36 +03:00
case CSS : : AlignItems : : Stretch :
2023-07-04 15:57:41 +03:00
case CSS : : AlignItems : : Normal :
2023-07-07 16:24:57 +03:00
cross_offset = - half_line_size + cross_margin_before ;
2022-10-25 00:17:36 +03:00
break ;
2023-06-06 17:54:46 +03:00
case CSS : : AlignItems : : End :
2023-07-04 15:57:41 +03:00
case CSS : : AlignItems : : SelfEnd :
2022-10-25 00:17:36 +03:00
case CSS : : AlignItems : : FlexEnd :
2023-03-08 19:36:32 +03:00
cross_offset = half_line_size - inner_cross_size ( box ) - cross_margin_after - cross_border_after - cross_padding_after ;
2022-10-25 00:17:36 +03:00
break ;
case CSS : : AlignItems : : Center :
2023-07-07 16:24:57 +03:00
cross_offset = - ( ( inner_cross_size ( box ) + cross_border_before + cross_border_after ) / 2.0 ) ;
2022-10-25 00:17:36 +03:00
break ;
default :
break ;
}
2023-05-24 11:50:57 +03:00
cross_offset + = inner_cross_size ( flex_container ( ) ) / 2.0 ;
2022-10-25 00:17:36 +03:00
// The main-axis edges of the static-position rectangle are where the margin edges of the child
// would be positioned if it were the sole flex item in the flex container,
// assuming both the child and the flex container were fixed-size boxes of their used size.
// (For this purpose, auto margins are treated as zero.
bool pack_from_end = true ;
2022-11-03 20:10:17 +03:00
CSSPixels main_offset = 0 ;
2022-10-25 00:17:36 +03:00
switch ( flex_container ( ) . computed_values ( ) . justify_content ( ) ) {
2022-12-20 22:16:29 +03:00
case CSS : : JustifyContent : : Start :
2023-07-10 18:52:43 +03:00
pack_from_end = false ;
break ;
2022-10-25 00:17:36 +03:00
case CSS : : JustifyContent : : FlexStart :
2023-07-10 18:52:43 +03:00
case CSS : : JustifyContent : : SpaceBetween :
2023-07-04 14:28:47 +03:00
pack_from_end = is_direction_reverse ( ) ;
2022-10-25 00:17:36 +03:00
break ;
2023-07-04 14:28:47 +03:00
case CSS : : JustifyContent : : End :
2023-07-10 18:52:43 +03:00
pack_from_end = true ;
break ;
2022-10-25 00:17:36 +03:00
case CSS : : JustifyContent : : FlexEnd :
2023-07-04 14:28:47 +03:00
pack_from_end = ! is_direction_reverse ( ) ;
2022-10-25 00:17:36 +03:00
break ;
case CSS : : JustifyContent : : Center :
case CSS : : JustifyContent : : SpaceAround :
2023-07-05 18:43:48 +03:00
case CSS : : JustifyContent : : SpaceEvenly :
2023-07-04 14:28:47 +03:00
pack_from_end = false ;
2023-07-07 16:24:57 +03:00
main_offset = ( inner_main_size ( flex_container ( ) ) - inner_main_size ( box ) - main_border_before - main_border_after ) / 2.0 ;
2022-10-25 00:17:36 +03:00
break ;
}
// NOTE: Next, we add the flex container's padding since abspos boxes are placed relative to the padding edge
// of their abspos containing block.
if ( pack_from_end ) {
main_offset + = is_row_layout ( ) ? m_flex_container_state . padding_right : m_flex_container_state . padding_bottom ;
2023-07-04 14:28:47 +03:00
} else {
main_offset + = is_row_layout ( ) ? m_flex_container_state . padding_left : m_flex_container_state . padding_top ;
2022-10-25 00:17:36 +03:00
}
2023-07-04 14:28:47 +03:00
if ( pack_from_end )
2023-07-10 18:52:43 +03:00
main_offset + = inner_main_size ( flex_container ( ) ) - inner_main_size ( box ) - main_border_before - main_border_after ;
2022-10-25 00:17:36 +03:00
2022-11-04 23:32:50 +03:00
auto static_position_offset = is_row_layout ( ) ? CSSPixelPoint { main_offset , cross_offset } : CSSPixelPoint { cross_offset , main_offset } ;
2022-10-25 00:17:36 +03:00
2023-05-31 11:18:34 +03:00
auto absolute_position_of_flex_container = absolute_content_rect ( flex_container ( ) ) . location ( ) ;
auto absolute_position_of_abspos_containing_block = absolute_content_rect ( * box . containing_block ( ) ) . location ( ) ;
2022-10-25 00:17:36 +03:00
auto diff = absolute_position_of_flex_container - absolute_position_of_abspos_containing_block ;
2022-11-04 23:32:50 +03:00
return static_position_offset + diff ;
2022-10-25 00:17:36 +03:00
}
2023-05-24 11:50:57 +03:00
double FlexFormattingContext : : FlexLine : : sum_of_flex_factor_of_unfrozen_items ( ) const
2023-03-08 23:18:35 +03:00
{
2023-05-24 11:50:57 +03:00
double sum = 0 ;
2023-03-08 23:18:35 +03:00
for ( auto const & item : items ) {
if ( ! item . frozen )
sum + = item . flex_factor . value ( ) ;
}
return sum ;
}
2023-05-24 11:50:57 +03:00
double FlexFormattingContext : : FlexLine : : sum_of_scaled_flex_shrink_factor_of_unfrozen_items ( ) const
2023-03-08 23:18:35 +03:00
{
2023-05-24 11:50:57 +03:00
double sum = 0 ;
2023-03-08 23:18:35 +03:00
for ( auto const & item : items ) {
if ( ! item . frozen )
sum + = item . scaled_flex_shrink_factor ;
}
return sum ;
}
2023-04-14 11:35:26 +03:00
CSSPixels FlexFormattingContext : : main_gap ( ) const
{
auto const & computed_values = flex_container ( ) . computed_values ( ) ;
auto gap = is_row_layout ( ) ? computed_values . column_gap ( ) : computed_values . row_gap ( ) ;
2023-05-06 17:34:55 +03:00
return gap . to_px ( flex_container ( ) , inner_main_size ( flex_container ( ) ) ) ;
2023-04-14 11:35:26 +03:00
}
CSSPixels FlexFormattingContext : : cross_gap ( ) const
{
auto const & computed_values = flex_container ( ) . computed_values ( ) ;
auto gap = is_row_layout ( ) ? computed_values . row_gap ( ) : computed_values . column_gap ( ) ;
2023-05-06 17:34:55 +03:00
return gap . to_px ( flex_container ( ) , inner_cross_size ( flex_container ( ) ) ) ;
2023-04-14 11:35:26 +03:00
}
2021-01-18 19:33:46 +03:00
}