// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
fn[C] compute_flexbox_layout(
view : ChicleView[C],
node_id : NodeId,
known_dimensions : Size[Double?],
available_space : Size[AvailableSpace],
absolute_origin : Point[Double],
) -> Unit raise ChicleError {
let tree = view.tree
let node = match tree.nodes.get(node_id) {
Some(n) => n
None => raise InvalidNodeId(node_id)
}
let padding = @util.resolve_rect_width_basis(
node.style.padding,
available_space,
)
let border = @util.resolve_rect_width_basis(
node.style.border,
available_space,
)
let scrollbar_w = node.style.scrollbar_width
let scrollbar_x = match node.style.overflow.x {
OverflowScroll => scrollbar_w
_ => 0.0
}
let scrollbar_y = match node.style.overflow.y {
OverflowScroll => scrollbar_w
_ => 0.0
}
let horiz_non_scroll_inset = padding.left +
padding.right +
border.left +
border.right
let vert_non_scroll_inset = padding.top +
padding.bottom +
border.top +
border.bottom
let horiz_inset = horiz_non_scroll_inset + scrollbar_y
let vert_inset = vert_non_scroll_inset + scrollbar_x
let is_col = is_column(node.style.flex_direction)
let style_width = @util.resolve_optional_dimension(
node.style.size.width,
available_space.width,
)
let style_height = @util.resolve_optional_dimension(
node.style.size.height,
available_space.height,
)
let specified_width = match known_dimensions.width {
Some(w) => Some(w)
None => style_width
}
let specified_height = match known_dimensions.height {
Some(h) => Some(h)
None => style_height
}
let width_def_for_percent = known_dimensions.width is Some(_) ||
style_width is Some(_)
let height_def_for_percent = known_dimensions.height is Some(_) ||
style_height is Some(_)
let tentative_border_box_width = match specified_width {
Some(w) => @util.max_double(w, horiz_non_scroll_inset)
None => horiz_inset
}
let tentative_border_box_height = match specified_height {
Some(h) => @util.max_double(h, vert_non_scroll_inset)
None => vert_inset
}
let tentative_content_width = @util.max_double(
tentative_border_box_width - horiz_inset,
0.0,
)
let tentative_content_height = @util.max_double(
tentative_border_box_height - vert_inset,
0.0,
)
let parent_available_for_children = Size(
width=if width_def_for_percent {
@geometry.AvailDefinite(tentative_content_width)
} else {
available_space.width
},
height=if height_def_for_percent {
AvailDefinite(tentative_content_height)
} else {
available_space.height
},
)
let flow_children : Array[NodeId] = []
let abs_children : Array[NodeId] = []
let hidden_children : Array[NodeId] = []
for child_id in tree.children[node_id] {
let child = match tree.nodes.get(child_id) {
Some(c) => c
None => raise InvalidNodeId(child_id)
}
match child.style.display {
DisplayNone => hidden_children.push(child_id)
_ =>
match child.style.position {
PosAbsolute => abs_children.push(child_id)
PosRelative => flow_children.push(child_id)
}
}
}
let flow_count = flow_children.length()
let default_align_items = match node.style.align_items {
Some(v) => v
None => ItemsStretch
}
let base_sizes : Array[Size[Double]] = Array::make(flow_count, Size::zero())
let cross_is_auto : Array[Bool] = Array::make(flow_count, true)
let flex_grow : Array[Double] = Array::make(flow_count, 0.0)
let flex_shrink : Array[Double] = Array::make(flow_count, 1.0)
let margin_main_start : Array[Double] = Array::make(flow_count, 0.0)
let margin_main_end : Array[Double] = Array::make(flow_count, 0.0)
let margin_cross_start : Array[Double] = Array::make(flow_count, 0.0)
let margin_cross_end : Array[Double] = Array::make(flow_count, 0.0)
let margin_main_start_auto : Array[Bool] = Array::make(flow_count, false)
let margin_main_end_auto : Array[Bool] = Array::make(flow_count, false)
let margin_cross_start_auto : Array[Bool] = Array::make(flow_count, false)
let margin_cross_end_auto : Array[Bool] = Array::make(flow_count, false)
let align_for_flow : Array[AlignItems] = Array::make(
flow_count, default_align_items,
)
let baseline_offsets : Array[Double] = Array::make(flow_count, 0.0)
let base_main_sizes : Array[Double] = Array::make(flow_count, 0.0)
let preferred_main_sizes : Array[Double?] = Array::make(flow_count, None)
let main_size_is_known_constraint : Array[Bool] = Array::make(
flow_count, false,
)
let content_main_sizes : Array[Double] = Array::make(flow_count, 0.0)
let min_main_sizes : Array[Double] = Array::make(flow_count, 0.0)
let max_main_sizes : Array[Double?] = Array::make(flow_count, None)
let mut total_base_main = 0.0
let mut total_base_margin_main = 0.0
let mut max_cross = 0.0
let main_available_pre = if is_col {
parent_available_for_children.height
} else {
parent_available_for_children.width
}
let cross_available_pre = if is_col {
parent_available_for_children.width
} else {
parent_available_for_children.height
}
let gap_main_pre = resolve_gap_main(
node.style.gap,
is_col,
main_available_pre,
)
let gap_cross_pre = resolve_gap_cross(
node.style.gap,
is_col,
cross_available_pre,
)
let total_gap_main_pre = if flow_count > 1 {
gap_main_pre * (flow_count - 1).to_double()
} else {
0.0
}
let total_gap_cross_pre = if flow_count > 1 {
gap_cross_pre * (flow_count - 1).to_double()
} else {
0.0
}
let intrinsic_main_available = match main_available_pre {
AvailMinContent => @geometry.AvailMinContent
_ => AvailMaxContent
}
let intrinsic_available_for_children = if is_col {
Size(
width=parent_available_for_children.width,
height=intrinsic_main_available,
)
} else {
Size(
width=intrinsic_main_available,
height=parent_available_for_children.height,
)
}
for i in 0.. c
None => raise InvalidNodeId(child_id)
}
let align = match child.style.align_self {
Some(v) => v
None => default_align_items
}
align_for_flow[i] = align
let resolved_margin = @util.resolve_rect_width_basis(
child.style.margin,
parent_available_for_children,
)
let resolved_padding = @util.resolve_rect_width_basis(
child.style.padding,
parent_available_for_children,
)
let resolved_border = @util.resolve_rect_width_basis(
child.style.border,
parent_available_for_children,
)
let padding_border_main = if is_col {
resolved_padding.top +
resolved_padding.bottom +
resolved_border.top +
resolved_border.bottom
} else {
resolved_padding.left +
resolved_padding.right +
resolved_border.left +
resolved_border.right
}
let main_margin_start_auto = if is_col {
child.style.margin.top is DimAuto
} else {
child.style.margin.left is DimAuto
}
let main_margin_end_auto = if is_col {
child.style.margin.bottom is DimAuto
} else {
child.style.margin.right is DimAuto
}
let cross_margin_start_auto = if is_col {
child.style.margin.left is DimAuto
} else {
child.style.margin.top is DimAuto
}
let cross_margin_end_auto = if is_col {
child.style.margin.right is DimAuto
} else {
child.style.margin.bottom is DimAuto
}
let main_margin_start = if is_col {
resolved_margin.top
} else {
resolved_margin.left
}
let main_margin_end = if is_col {
resolved_margin.bottom
} else {
resolved_margin.right
}
let cross_margin_start = if is_col {
resolved_margin.left
} else {
resolved_margin.top
}
let cross_margin_end = if is_col {
resolved_margin.right
} else {
resolved_margin.bottom
}
let child_intrinsic_available = if is_col {
match (specified_width, child.style.size.width) {
(None, DimPercent(_)) =>
Size(
width=match parent_available_for_children.width {
AvailMinContent => @geometry.AvailMinContent
_ => AvailMaxContent
},
height=intrinsic_available_for_children.height,
)
_ => intrinsic_available_for_children
}
} else {
intrinsic_available_for_children
}
let intrinsic_known_dimensions = if tree.node_context_data[child_id]
is Some(_) &&
tree.children[child_id].length() == 0 &&
align is ItemsStretch {
let intrinsic_cross_available = if is_col {
child_intrinsic_available.width
} else {
child_intrinsic_available.height
}
match intrinsic_cross_available {
AvailDefinite(v) => {
let stretched = @util.max_double(
v - cross_margin_start - cross_margin_end,
0.0,
)
if is_col {
Size(width=Some(stretched), height=None)
} else {
Size(width=None, height=Some(stretched))
}
}
_ => Size(width=None, height=None)
}
} else {
Size(width=None, height=None)
}
view.perform_child_layout(
child_id,
intrinsic_known_dimensions,
child_intrinsic_available,
Point::zero(),
false,
)
let child_layout = tree.unrounded_layout(child_id)
content_main_sizes[i] = if is_col {
child_layout.size.height
} else {
child_layout.size.width
}
let main_axis_available = main_available_pre
let cross_axis_available = cross_available_pre
let basis = @util.resolve_optional_dimension(
child.style.flex_basis,
main_axis_available,
)
let main_from_size = if is_col {
@util.resolve_optional_dimension(
child.style.size.height,
main_axis_available,
)
} else {
@util.resolve_optional_dimension(
child.style.size.width,
main_axis_available,
)
}
let max_from_style = if is_col {
@util.resolve_optional_dimension(
child.style.max_size.height,
main_axis_available,
)
} else {
@util.resolve_optional_dimension(
child.style.max_size.width,
main_axis_available,
)
}
let mut main = match basis {
Some(v) => v
None =>
match main_from_size {
Some(v) => v
None =>
if is_col {
child_layout.size.height
} else {
child_layout.size.width
}
}
}
if basis is None &&
main_from_size is None &&
tree.children[child_id].length() == 0 &&
tree.node_context_data[child_id] is None &&
child.style.aspect_ratio is None {
main = padding_border_main
}
if main < padding_border_main {
main = padding_border_main
}
main_size_is_known_constraint[i] = basis is Some(_) ||
main_from_size is Some(_)
base_main_sizes[i] = main
preferred_main_sizes[i] = main_from_size
max_main_sizes[i] = max_from_style
let cross_from_size = if is_col {
match child.style.size.width {
DimPercent(_) =>
if specified_width is None {
None
} else {
@util.resolve_optional_dimension(
child.style.size.width,
cross_axis_available,
)
}
_ =>
@util.resolve_optional_dimension(
child.style.size.width,
cross_axis_available,
)
}
} else {
@util.resolve_optional_dimension(
child.style.size.height,
cross_axis_available,
)
}
let cross = match cross_from_size {
Some(v) => v
None =>
if is_col {
child_layout.size.width
} else {
child_layout.size.height
}
}
base_sizes[i] = make_size_from_main_cross(main, cross, is_col)
cross_is_auto[i] = match cross_from_size {
Some(_) => false
None => true
}
flex_grow[i] = child.style.flex_grow
flex_shrink[i] = child.style.flex_shrink
margin_main_start[i] = main_margin_start
margin_main_end[i] = main_margin_end
margin_cross_start[i] = cross_margin_start
margin_cross_end[i] = cross_margin_end
margin_main_start_auto[i] = main_margin_start_auto
margin_main_end_auto[i] = main_margin_end_auto
margin_cross_start_auto[i] = cross_margin_start_auto
margin_cross_end_auto[i] = cross_margin_end_auto
total_base_main = total_base_main + main
total_base_margin_main = total_base_margin_main +
main_margin_start +
main_margin_end
max_cross = @util.max_double(
max_cross,
cross + cross_margin_start + cross_margin_end,
)
if !is_col {
match align {
ItemsBaseline => baseline_offsets[i] = baseline_offset_y(tree, child_id)
_ => ()
}
}
let min_from_style = if is_col {
@util.resolve_optional_dimension(
child.style.min_size.height,
main_axis_available,
)
} else {
@util.resolve_optional_dimension(
child.style.min_size.width,
main_axis_available,
)
}
let overflow_main = if is_col {
child.style.overflow.y
} else {
child.style.overflow.x
}
min_main_sizes[i] = match min_from_style {
Some(v) => @util.max_double(v, 0.0)
None =>
match overflow_main {
OverflowVisible => -1.0
_ => 0.0
}
}
}
if !is_col {
let mut baseline_before_max = 0.0
let mut baseline_after_max = 0.0
for i in 0.. {
let cross = get_cross(base_sizes[i], is_col)
let baseline = baseline_offsets[i]
let after_inner = cross - baseline
let after_inner = if after_inner > 0.0 { after_inner } else { 0.0 }
let before = margin_cross_start[i] + baseline
let after = margin_cross_end[i] + after_inner
baseline_before_max = @util.max_double(baseline_before_max, before)
baseline_after_max = @util.max_double(baseline_after_max, after)
}
_ => ()
}
}
max_cross = @util.max_double(
max_cross,
baseline_before_max + baseline_after_max,
)
}
let total_base_outer_main = total_base_main + total_base_margin_main
// Container sizing (border-box)
let raw_width = match specified_width {
Some(w) => @util.max_double(w, horiz_non_scroll_inset)
None =>
if is_col {
@util.max_double(
max_cross + total_gap_cross_pre + horiz_inset,
horiz_inset,
)
} else {
@util.max_double(
total_base_outer_main + total_gap_main_pre + horiz_inset,
horiz_inset,
)
}
}
let raw_height = match specified_height {
Some(h) => @util.max_double(h, vert_non_scroll_inset)
None =>
if is_col {
@util.max_double(
total_base_outer_main + total_gap_main_pre + vert_inset,
vert_inset,
)
} else {
@util.max_double(
max_cross + total_gap_cross_pre + vert_inset,
vert_inset,
)
}
}
let mut border_box_width = @util.clamp_dimension(
raw_width,
node.style.min_size.width,
node.style.max_size.width,
available_space.width,
)
let mut border_box_height = @util.clamp_dimension(
raw_height,
node.style.min_size.height,
node.style.max_size.height,
available_space.height,
)
tree.set_unrounded_layout(node_id, {
..Layout::zero(),
location: absolute_origin,
size: Size(width=border_box_width, height=border_box_height),
})
let mut content_width = @util.max_double(border_box_width - horiz_inset, 0.0)
let mut content_height = @util.max_double(border_box_height - vert_inset, 0.0)
// Resolve gaps again now that the container's content size is known
let main_available_final = if flow_count > 1 {
if is_col {
@geometry.AvailDefinite(content_height)
} else {
AvailDefinite(content_width)
}
} else if is_col {
resolve_available_for_percent(height_def_for_percent, content_height)
} else {
resolve_available_for_percent(width_def_for_percent, content_width)
}
let cross_available_final = if is_col {
resolve_available_for_percent(width_def_for_percent, content_width)
} else {
resolve_available_for_percent(height_def_for_percent, content_height)
}
let gap_main = resolve_gap_main(node.style.gap, is_col, main_available_final)
let gap_cross = resolve_gap_cross(
node.style.gap,
is_col,
cross_available_final,
)
let justify = match node.style.justify_content {
Some(j) => j
None => AlignFlexStart
}
let is_reverse = match node.style.flex_direction {
FlexRowReverse | FlexColumnReverse => true
_ => false
}
let base_x = absolute_origin.x + border.left + padding.left
let base_y = absolute_origin.y + border.top + padding.top
let mut container_main = if is_col { content_height } else { content_width }
let mut container_cross = if is_col { content_width } else { content_height }
let is_wrap = match node.style.flex_wrap {
FlexNoWrap => false
_ => true
}
let is_wrap_reverse = node.style.flex_wrap is FlexWrapReverse
let main_is_definite = if is_col {
specified_height is Some(_)
} else {
specified_width is Some(_)
}
let cross_is_auto_container = if is_col {
specified_width is None
} else {
specified_height is None
}
if is_wrap && main_is_definite && flow_count > 0 {
// Build flex lines (single pass, simplified).
let lines : Array[Array[Int]] = []
let line_crosses : Array[Double] = []
let line_baseline_befores : Array[Double] = []
let mut current_line : Array[Int] = []
let mut current_outer_main = 0.0
let mut current_cross = 0.0
let mut current_baseline_before = 0.0
let mut current_baseline_after = 0.0
for i in 0.. if main > max_main { main = max_main }
None => ()
}
let cross = get_cross(base_sizes[i], is_col)
let outer_main = margin_main_start[i] + main + margin_main_end[i]
let outer_cross = margin_cross_start[i] + cross + margin_cross_end[i]
let mut item_before = 0.0
let mut item_after = 0.0
if !is_col {
match align_for_flow[i] {
ItemsBaseline => {
let baseline = baseline_offsets[i]
let after_inner = cross - baseline
let after_inner = if after_inner > 0.0 { after_inner } else { 0.0 }
item_before = margin_cross_start[i] + baseline
item_after = margin_cross_end[i] + after_inner
}
_ => ()
}
}
if current_line.length() == 0 {
current_line.push(i)
current_outer_main = outer_main
current_cross = outer_cross
current_baseline_before = item_before
current_baseline_after = item_after
current_cross = @util.max_double(
current_cross,
current_baseline_before + current_baseline_after,
)
} else {
let tentative = current_outer_main + gap_main + outer_main
if tentative > container_main {
lines.push(current_line)
line_crosses.push(current_cross)
line_baseline_befores.push(current_baseline_before)
current_line = []
current_line.push(i)
current_outer_main = outer_main
current_cross = outer_cross
current_baseline_before = item_before
current_baseline_after = item_after
current_cross = @util.max_double(
current_cross,
current_baseline_before + current_baseline_after,
)
} else {
current_line.push(i)
current_outer_main = tentative
current_cross = @util.max_double(current_cross, outer_cross)
current_baseline_before = @util.max_double(
current_baseline_before, item_before,
)
current_baseline_after = @util.max_double(
current_baseline_after, item_after,
)
current_cross = @util.max_double(
current_cross,
current_baseline_before + current_baseline_after,
)
}
}
}
if current_line.length() > 0 {
lines.push(current_line)
line_crosses.push(current_cross)
line_baseline_befores.push(current_baseline_before)
}
let line_count = lines.length()
let mut total_lines_cross = 0.0
for c in line_crosses {
total_lines_cross = total_lines_cross + c
}
let total_cross_gaps = if line_count > 1 {
gap_cross * (line_count - 1).to_double()
} else {
0.0
}
total_lines_cross = total_lines_cross + total_cross_gaps
// If the container's cross size is auto, compute it from wrapped line sizes.
if cross_is_auto_container {
if is_col {
border_box_width = @util.clamp_dimension(
@util.max_double(total_lines_cross + horiz_inset, horiz_inset),
node.style.min_size.width,
node.style.max_size.width,
available_space.width,
)
content_width = @util.max_double(border_box_width - horiz_inset, 0.0)
} else {
border_box_height = @util.clamp_dimension(
@util.max_double(total_lines_cross + vert_inset, vert_inset),
node.style.min_size.height,
node.style.max_size.height,
available_space.height,
)
content_height = @util.max_double(border_box_height - vert_inset, 0.0)
}
tree.set_unrounded_layout(node_id, {
..Layout::zero(),
location: absolute_origin,
size: Size(width=border_box_width, height=border_box_height),
})
container_main = if is_col { content_height } else { content_width }
container_cross = if is_col { content_width } else { content_height }
}
let align_content = match node.style.align_content {
Some(v) => v
None => AlignStretch
}
let leftover_cross = container_cross - total_lines_cross
let distributable_cross = if leftover_cross > 0.0 {
leftover_cross
} else {
0.0
}
let extra_per_line = match align_content {
AlignStretch =>
if line_count > 0 {
distributable_cross / line_count.to_double()
} else {
0.0
}
_ => 0.0
}
let mut start_cross = 0.0
let mut extra_gap_cross = 0.0
match align_content {
AlignStretch => ()
AlignCenter => start_cross = leftover_cross / 2.0
AlignFlexEnd | AlignEnd => start_cross = leftover_cross
AlignSpaceBetween =>
if line_count > 1 {
extra_gap_cross = distributable_cross / (line_count - 1).to_double()
}
AlignSpaceAround =>
if line_count > 0 {
extra_gap_cross = distributable_cross / line_count.to_double()
start_cross = extra_gap_cross / 2.0
}
AlignSpaceEvenly =>
if line_count > 0 {
extra_gap_cross = distributable_cross / (line_count + 1).to_double()
start_cross = extra_gap_cross
}
_ => ()
}
let line_cross_sizes : Array[Double] = Array::make(line_count, 0.0)
for li in 0.. 1 {
line_used = line_used + gap_main * (count - 1).to_double()
}
let line_leftover = container_main - line_used
let auto_main_start_values : Array[Double] = Array::make(flow_count, 0.0)
let auto_main_end_values : Array[Double] = Array::make(flow_count, 0.0)
let mut line_leftover_for_justify = line_leftover
let mut main_auto_count = 0
for idx in indices {
if margin_main_start_auto[idx] {
main_auto_count = main_auto_count + 1
}
if margin_main_end_auto[idx] {
main_auto_count = main_auto_count + 1
}
}
if main_auto_count > 0 && line_leftover > 0.0 {
let share = line_leftover / main_auto_count.to_double()
for idx in indices {
if margin_main_start_auto[idx] {
auto_main_start_values[idx] = share
}
if margin_main_end_auto[idx] {
auto_main_end_values[idx] = share
}
}
line_leftover_for_justify = 0.0
}
let distributable_line_leftover = if line_leftover_for_justify > 0.0 {
line_leftover_for_justify
} else {
0.0
}
let mut extra_between = 0.0
let start_main = match justify {
AlignSpaceBetween =>
if count > 1 {
extra_between = distributable_line_leftover /
(count - 1).to_double()
0.0
} else {
0.0
}
AlignSpaceAround =>
if count > 0 {
extra_between = distributable_line_leftover / count.to_double()
extra_between / 2.0
} else {
0.0
}
AlignSpaceEvenly =>
if count > 0 {
extra_between = distributable_line_leftover /
(count + 1).to_double()
extra_between
} else {
0.0
}
_ =>
resolve_justify_start_main(
justify, line_leftover_for_justify, is_reverse,
)
}
let actual_gap_main = gap_main + extra_between
let mut main_cursor = if is_reverse {
container_main - start_main
} else {
start_main
}
for idx in indices {
let child_id = flow_children[idx]
let child = match tree.nodes.get(child_id) {
Some(c) => c
None => raise InvalidNodeId(child_id)
}
let main_size = line_main_sizes[idx]
let align = match child.style.align_self {
Some(v) => v
None =>
match node.style.align_items {
Some(v) => v
None => ItemsStretch
}
}
let intrinsic_cross = get_cross(base_sizes[idx], is_col)
let available_cross_for_item = line_cross -
margin_cross_start[idx] -
margin_cross_end[idx]
let stretch_cross_available = @util.max_double(
available_cross_for_item, 0.0,
)
let cross_size_is_auto = if is_col {
child.style.size.width is DimAuto
} else {
child.style.size.height is DimAuto
}
let mut cross_size = match align {
ItemsStretch =>
if cross_size_is_auto {
stretch_cross_available
} else {
intrinsic_cross
}
_ =>
if cross_size_is_auto {
let child_wraps = match child.style.flex_wrap {
FlexNoWrap => false
_ => true
}
if tree.children[child_id].length() > 0 && child_wraps {
@util.min_double(intrinsic_cross, stretch_cross_available)
} else {
intrinsic_cross
}
} else {
intrinsic_cross
}
}
let cross_axis_available = if is_col {
child_available_for_final.width
} else {
child_available_for_final.height
}
let cross_min = if is_col {
@util.resolve_optional_dimension(
child.style.min_size.width,
cross_axis_available,
)
} else {
@util.resolve_optional_dimension(
child.style.min_size.height,
cross_axis_available,
)
}
let cross_max = if is_col {
@util.resolve_optional_dimension(
child.style.max_size.width,
cross_axis_available,
)
} else {
@util.resolve_optional_dimension(
child.style.max_size.height,
cross_axis_available,
)
}
match cross_min {
Some(v) => if cross_size < v { cross_size = v }
None => ()
}
match cross_max {
Some(v) => if cross_size > v { cross_size = v }
None => ()
}
if cross_size < 0.0 {
cross_size = 0.0
}
let mut auto_cross_start = 0.0
let mut auto_cross_count = 0
if margin_cross_start_auto[idx] {
auto_cross_count = auto_cross_count + 1
}
if margin_cross_end_auto[idx] {
auto_cross_count = auto_cross_count + 1
}
if auto_cross_count > 0 {
let remaining_cross = available_cross_for_item - cross_size
if auto_cross_count == 2 {
if remaining_cross > 0.0 {
auto_cross_start = remaining_cross / 2.0
} else {
auto_cross_start = 0.0
}
} else if margin_cross_start_auto[idx] {
auto_cross_start = if remaining_cross > 0.0 {
remaining_cross
} else {
0.0
}
} else {
()
}
}
let cross_pos_in_available = if auto_cross_count > 0 {
auto_cross_start
} else {
match align {
ItemsBaseline =>
if !is_col {
baseline_before_max -
margin_cross_start[idx] -
baseline_offsets[idx]
} else {
0.0
}
ItemsEnd | ItemsFlexEnd => available_cross_for_item - cross_size
ItemsCenter => (available_cross_for_item - cross_size) / 2.0
_ => 0.0
}
}
let cross_pos_in_available = if is_wrap_reverse {
available_cross_for_item - cross_size - cross_pos_in_available
} else {
cross_pos_in_available
}
let cross_offset = line_offset +
margin_cross_start[idx] +
cross_pos_in_available
let item_main_margin_start = margin_main_start[idx] +
auto_main_start_values[idx]
let item_main_margin_end = margin_main_end[idx] +
auto_main_end_values[idx]
let main_pos = if is_reverse {
main_cursor = main_cursor - item_main_margin_end - main_size
let pos = main_cursor + item_main_margin_start
main_cursor = main_cursor - item_main_margin_start - actual_gap_main
pos
} else {
let pos = main_cursor + item_main_margin_start
main_cursor = main_cursor +
item_main_margin_start +
main_size +
item_main_margin_end +
actual_gap_main
pos
}
let has_measure_context = tree.node_context_data[child_id] is Some(_)
let known_width = if has_measure_context {
let main_changed = @util.abs_double(main_size - base_main_sizes[idx]) >
0.0001
let main_known = if main_size_is_known_constraint[idx] || main_changed {
Some(main_size)
} else {
None
}
let cross_known = if !cross_is_auto[idx] {
Some(cross_size)
} else {
match align {
ItemsStretch =>
if cross_is_auto_container &&
main_is_definite &&
flow_count == 1 {
None
} else {
Some(cross_size)
}
_ => None
}
}
if is_col {
cross_known
} else {
main_known
}
} else {
let main_changed = @util.abs_double(main_size - base_main_sizes[idx]) >
0.0001
let child_wraps = match child.style.flex_wrap {
FlexNoWrap => false
_ => true
}
let allow_auto_main = tree.children[child_id].length() > 0 &&
child_wraps &&
!main_size_is_known_constraint[idx] &&
!main_changed
let main_known = if allow_auto_main { None } else { Some(main_size) }
if is_col {
Some(cross_size)
} else {
main_known
}
}
let known_height = if has_measure_context {
let main_changed = @util.abs_double(main_size - base_main_sizes[idx]) >
0.0001
let main_known = if main_size_is_known_constraint[idx] || main_changed {
Some(main_size)
} else {
None
}
let cross_known = if !cross_is_auto[idx] {
Some(cross_size)
} else {
match align {
ItemsStretch =>
if cross_is_auto_container &&
main_is_definite &&
flow_count == 1 {
None
} else {
Some(cross_size)
}
_ => None
}
}
if is_col {
main_known
} else {
cross_known
}
} else {
let main_changed = @util.abs_double(main_size - base_main_sizes[idx]) >
0.0001
let child_wraps = match child.style.flex_wrap {
FlexNoWrap => false
_ => true
}
let allow_auto_main = tree.children[child_id].length() > 0 &&
child_wraps &&
!main_size_is_known_constraint[idx] &&
!main_changed
let main_known = if allow_auto_main { None } else { Some(main_size) }
if is_col {
main_known
} else {
Some(cross_size)
}
}
let child_origin = if is_col {
Point(x=base_x + cross_offset, y=base_y + main_pos)
} else {
Point(x=base_x + main_pos, y=base_y + cross_offset)
}
view.perform_child_layout(
child_id,
Size(width=known_width, height=known_height),
child_available_for_final,
child_origin,
false,
)
}
}
} else {
// Single-line flex layout
let total_gap_main = if flow_count > 1 {
gap_main * (flow_count - 1).to_double()
} else {
0.0
}
if flow_count > 1 &&
!main_is_definite &&
!is_wrap &&
(match justify {
AlignFlexStart | AlignStart => true
_ => false
}) &&
!subtree_has_measure_context(tree, node_id) {
let intrinsic_main = compute_single_line_auto_intrinsic_main(
flow_count, gap_main_pre, base_main_sizes, preferred_main_sizes, min_main_sizes,
max_main_sizes, flex_grow, flex_shrink, margin_main_start, margin_main_end,
content_main_sizes,
)
if intrinsic_main > container_main {
if is_col {
border_box_height = @util.clamp_dimension(
@util.max_double(intrinsic_main + vert_inset, vert_inset),
node.style.min_size.height,
node.style.max_size.height,
available_space.height,
)
content_height = @util.max_double(border_box_height - vert_inset, 0.0)
container_main = content_height
} else {
border_box_width = @util.clamp_dimension(
@util.max_double(intrinsic_main + horiz_inset, horiz_inset),
node.style.min_size.width,
node.style.max_size.width,
available_space.width,
)
content_width = @util.max_double(border_box_width - horiz_inset, 0.0)
container_main = content_width
}
tree.set_unrounded_layout(node_id, {
..Layout::zero(),
location: absolute_origin,
size: Size(width=border_box_width, height=border_box_height),
})
}
}
// Flex grow/shrink (single-line, simplified)
let final_main_sizes : Array[Double] = Array::make(flow_count, 0.0)
let frozen : Array[Bool] = Array::make(flow_count, false)
let violations : Array[Double] = Array::make(flow_count, 0.0)
let mut initial_used = total_gap_main
for i in 0.. 0.0 && sum_grow > 0.0 && sum_grow < 1.0 {
let scaled = initial_free_space * sum_grow
if @util.abs_double(scaled) < @util.abs_double(free_space) {
scaled
} else {
free_space
}
} else if free_space < 0.0 && sum_shrink > 0.0 && sum_shrink < 1.0 {
let scaled = initial_free_space * sum_shrink
if @util.abs_double(scaled) < @util.abs_double(free_space) {
scaled
} else {
free_space
}
} else {
free_space
}
for i in 0.. 0.0 && sum_grow > 0.0 {
final_main_sizes[i] = base_main +
free_space * (flex_grow[i] / sum_grow)
} else if free_space < 0.0 &&
sum_shrink > 0.0 &&
sum_scaled_shrink > 0.0 {
let scaled = base_main * flex_shrink[i]
final_main_sizes[i] = base_main +
free_space * (scaled / sum_scaled_shrink)
} else {
final_main_sizes[i] = base_main
}
}
}
for i in 0.. c
None => raise InvalidNodeId(flow_children[i])
}
let has_definite_basis = @util.resolve_optional_dimension(
child_for_min.style.flex_basis,
main_available_pre,
)
is Some(_)
let should_resolve_auto_min = free_space < 0.0 ||
has_definite_basis ||
(
@util.double_approx_equal(free_space, 0.0) &&
tree.node_context_data[flow_children[i]] is Some(_) &&
main_available_pre is AvailMaxContent &&
cross_available_pre is AvailMaxContent
)
if !frozen[i] && min_main_sizes[i] < 0.0 && should_resolve_auto_min {
let auto_min = resolve_flex_auto_min_main(
view,
flow_children[i],
is_col,
parent_available_for_children,
main_available_pre,
)
min_main_sizes[i] = auto_min
}
}
let mut total_violation = 0.0
for i in 0.. if clamped > v { clamped = v }
None => ()
}
if clamped < 0.0 {
clamped = 0.0
}
violations[i] = clamped - unclamped
final_main_sizes[i] = clamped
total_violation = total_violation + violations[i]
}
}
for i in 0.. 0.0 {
frozen[i] = violations[i] > 0.0
} else if total_violation < 0.0 {
frozen[i] = violations[i] < 0.0
} else {
frozen[i] = true
}
}
}
}
// Justify content (single-line)
let mut used_main = 0.0
for i in 0.. 0 && leftover > 0.0 {
let share = leftover / main_auto_count.to_double()
for i in 0.. 0.0 {
leftover_for_justify
} else {
0.0
}
let mut extra_between = 0.0
let start_main = match justify {
AlignSpaceBetween =>
if flow_count > 1 {
extra_between = distributable_leftover / (flow_count - 1).to_double()
0.0
} else {
0.0
}
AlignSpaceAround =>
if flow_count > 0 {
extra_between = distributable_leftover / flow_count.to_double()
extra_between / 2.0
} else {
0.0
}
AlignSpaceEvenly =>
if flow_count > 0 {
extra_between = distributable_leftover / (flow_count + 1).to_double()
extra_between
} else {
0.0
}
_ => resolve_justify_start_main(justify, leftover_for_justify, is_reverse)
}
let actual_gap_main = gap_main + extra_between
let child_available_for_final = Size(
width=@geometry.AvailDefinite(content_width),
height=AvailDefinite(content_height),
)
let container_cross_for_placement = {
let s = tree.nodes[node_id].unrounded_layout.size
if is_col {
@util.max_double(s.width - horiz_inset, 0.0)
} else {
@util.max_double(s.height - vert_inset, 0.0)
}
}
let mut baseline_before_max = 0.0
if !is_col {
let mut baseline_after_max = 0.0
for i in 0.. {
let cross = get_cross(base_sizes[i], is_col)
let baseline = baseline_offsets[i]
let after_inner = cross - baseline
let after_inner = if after_inner > 0.0 { after_inner } else { 0.0 }
let before = margin_cross_start[i] + baseline
let after = margin_cross_end[i] + after_inner
baseline_before_max = @util.max_double(baseline_before_max, before)
baseline_after_max = @util.max_double(baseline_after_max, after)
}
_ => ()
}
}
}
let mut cursor = if is_reverse {
container_main - start_main
} else {
start_main
}
for i in 0.. c
None => raise InvalidNodeId(child_id)
}
let main_size = final_main_sizes[i]
let align = match child.style.align_self {
Some(v) => v
None =>
match node.style.align_items {
Some(v) => v
None => ItemsStretch
}
}
let intrinsic_cross = get_cross(base_sizes[i], is_col)
let available_cross_for_item = container_cross_for_placement -
margin_cross_start[i] -
margin_cross_end[i]
let stretch_cross_available = @util.max_double(
available_cross_for_item, 0.0,
)
let cross_size_is_auto = if is_col {
child.style.size.width is DimAuto
} else {
child.style.size.height is DimAuto
}
let mut cross_size = match align {
ItemsStretch =>
if cross_size_is_auto {
stretch_cross_available
} else {
intrinsic_cross
}
_ =>
if cross_size_is_auto {
let child_wraps = match child.style.flex_wrap {
FlexNoWrap => false
_ => true
}
if tree.children[child_id].length() > 0 && child_wraps {
@util.min_double(intrinsic_cross, stretch_cross_available)
} else {
intrinsic_cross
}
} else {
intrinsic_cross
}
}
let cross_axis_available = if is_col {
child_available_for_final.width
} else {
child_available_for_final.height
}
let cross_min = if is_col {
@util.resolve_optional_dimension(
child.style.min_size.width,
cross_axis_available,
)
} else {
@util.resolve_optional_dimension(
child.style.min_size.height,
cross_axis_available,
)
}
let cross_max = if is_col {
@util.resolve_optional_dimension(
child.style.max_size.width,
cross_axis_available,
)
} else {
@util.resolve_optional_dimension(
child.style.max_size.height,
cross_axis_available,
)
}
match cross_min {
Some(v) => if cross_size < v { cross_size = v }
None => ()
}
match cross_max {
Some(v) => if cross_size > v { cross_size = v }
None => ()
}
if cross_size < 0.0 {
cross_size = 0.0
}
let mut auto_cross_start = 0.0
let mut auto_cross_count = 0
if margin_cross_start_auto[i] {
auto_cross_count = auto_cross_count + 1
}
if margin_cross_end_auto[i] {
auto_cross_count = auto_cross_count + 1
}
if auto_cross_count > 0 {
let remaining_cross = available_cross_for_item - cross_size
if auto_cross_count == 2 {
if remaining_cross > 0.0 {
auto_cross_start = remaining_cross / 2.0
} else {
auto_cross_start = 0.0
}
} else if margin_cross_start_auto[i] {
auto_cross_start = if remaining_cross > 0.0 {
remaining_cross
} else {
0.0
}
} else {
()
}
}
let cross_pos_in_available = if auto_cross_count > 0 {
auto_cross_start
} else {
match align {
ItemsBaseline =>
if !is_col {
baseline_before_max - margin_cross_start[i] - baseline_offsets[i]
} else {
0.0
}
ItemsEnd | ItemsFlexEnd => available_cross_for_item - cross_size
ItemsCenter => (available_cross_for_item - cross_size) / 2.0
_ => 0.0
}
}
let cross_pos_in_available = if is_wrap_reverse {
available_cross_for_item - cross_size - cross_pos_in_available
} else {
cross_pos_in_available
}
let cross_offset = margin_cross_start[i] + cross_pos_in_available
let item_main_margin_start = margin_main_start[i] +
auto_main_start_values[i]
let item_main_margin_end = margin_main_end[i] + auto_main_end_values[i]
let main_pos = if is_reverse {
cursor = cursor - item_main_margin_end - main_size
let pos = cursor + item_main_margin_start
cursor = cursor - item_main_margin_start - actual_gap_main
pos
} else {
let pos = cursor + item_main_margin_start
cursor = cursor +
item_main_margin_start +
main_size +
item_main_margin_end +
actual_gap_main
pos
}
let has_measure_context = tree.node_context_data[child_id] is Some(_)
let known_width = if has_measure_context {
let main_changed = @util.abs_double(main_size - base_main_sizes[i]) >
0.0001
let main_known = if main_size_is_known_constraint[i] || main_changed {
Some(main_size)
} else {
None
}
let cross_known = if !cross_is_auto[i] {
Some(cross_size)
} else {
match align {
ItemsStretch =>
if cross_is_auto_container && main_is_definite && flow_count == 1 {
None
} else {
Some(cross_size)
}
_ => None
}
}
if is_col {
cross_known
} else {
main_known
}
} else {
let main_changed = @util.abs_double(main_size - base_main_sizes[i]) >
0.0001
let child_wraps = match child.style.flex_wrap {
FlexNoWrap => false
_ => true
}
let allow_auto_main = tree.children[child_id].length() > 0 &&
child_wraps &&
!main_size_is_known_constraint[i] &&
!main_changed
let main_known = if allow_auto_main { None } else { Some(main_size) }
let allow_auto_cross = tree.children[child_id].length() > 0 &&
child_wraps &&
cross_is_auto[i]
let cross_known = if !cross_is_auto[i] {
Some(cross_size)
} else {
match align {
ItemsStretch =>
if cross_is_auto_container && allow_auto_cross {
None
} else {
Some(cross_size)
}
_ => Some(cross_size)
}
}
if is_col {
cross_known
} else {
main_known
}
}
let known_height = if has_measure_context {
let main_changed = @util.abs_double(main_size - base_main_sizes[i]) >
0.0001
let main_known = if main_size_is_known_constraint[i] || main_changed {
Some(main_size)
} else {
None
}
let cross_known = if !cross_is_auto[i] {
Some(cross_size)
} else {
match align {
ItemsStretch =>
if cross_is_auto_container && main_is_definite && flow_count == 1 {
None
} else {
Some(cross_size)
}
_ => None
}
}
if is_col {
main_known
} else {
cross_known
}
} else {
let main_changed = @util.abs_double(main_size - base_main_sizes[i]) >
0.0001
let child_wraps = match child.style.flex_wrap {
FlexNoWrap => false
_ => true
}
let allow_auto_main = tree.children[child_id].length() > 0 &&
child_wraps &&
!main_size_is_known_constraint[i] &&
!main_changed
let main_known = if allow_auto_main { None } else { Some(main_size) }
let allow_auto_cross = tree.children[child_id].length() > 0 &&
child_wraps &&
cross_is_auto[i]
let cross_known = if !cross_is_auto[i] {
Some(cross_size)
} else {
match align {
ItemsStretch =>
if cross_is_auto_container && allow_auto_cross {
None
} else {
Some(cross_size)
}
_ => Some(cross_size)
}
}
if is_col {
main_known
} else {
cross_known
}
}
let child_origin = if is_col {
Point(x=base_x + cross_offset, y=base_y + main_pos)
} else {
Point(x=base_x + main_pos, y=base_y + cross_offset)
}
view.perform_child_layout(
child_id,
Size(width=known_width, height=known_height),
child_available_for_final,
child_origin,
false,
)
}
if cross_is_auto_container &&
flow_count > 1 &&
!subtree_has_measure_context(tree, node_id) {
let mut needed_cross = 0.0
for i in 0.. needed_cross {
needed_cross = end
}
}
container_cross = needed_cross
if is_col {
border_box_width = @util.clamp_dimension(
@util.max_double(container_cross + horiz_inset, horiz_inset),
node.style.min_size.width,
node.style.max_size.width,
available_space.width,
)
content_width = @util.max_double(border_box_width - horiz_inset, 0.0)
} else {
border_box_height = @util.clamp_dimension(
@util.max_double(container_cross + vert_inset, vert_inset),
node.style.min_size.height,
node.style.max_size.height,
available_space.height,
)
content_height = @util.max_double(border_box_height - vert_inset, 0.0)
}
tree.set_unrounded_layout(node_id, {
..Layout::zero(),
location: absolute_origin,
size: Size(width=border_box_width, height=border_box_height),
})
}
}
let justify_is_startish = match justify {
AlignFlexStart | AlignStart => true
_ => false
}
let align_items_for_resize = match node.style.align_items {
Some(v) => v
None => ItemsStretch
}
let cross_align_is_stretch = align_items_for_resize is ItemsStretch
let allow_auto_resize = !subtree_has_measure_context(tree, node_id)
if flow_count == 1 &&
(!main_is_definite || cross_is_auto_container) &&
justify_is_startish &&
cross_align_is_stretch {
let only_child = flow_children[0]
let child = match tree.nodes.get(only_child) {
Some(c) => c
None => raise InvalidNodeId(only_child)
}
let child_layout = tree.unrounded_layout(only_child)
let child_main_size = if is_col {
child_layout.size.height
} else {
child_layout.size.width
}
let child_cross_size = if is_col {
child_layout.size.width
} else {
child_layout.size.height
}
let mut child_outer_main = margin_main_start[0] +
child_main_size +
margin_main_end[0]
let child_outer_cross = margin_cross_start[0] +
child_cross_size +
margin_cross_end[0]
let cross_axis_available_for_min = if is_col {
@geometry.AvailDefinite(content_width)
} else {
AvailDefinite(content_height)
}
let cross_min_for_resize = if is_col {
@util.resolve_optional_dimension(
child.style.min_size.width,
cross_axis_available_for_min,
)
} else {
@util.resolve_optional_dimension(
child.style.min_size.height,
cross_axis_available_for_min,
)
}
let needs_main_resize_for_cross_min = match cross_min_for_resize {
Some(v) => v > container_cross + 0.0001
None => false
}
let needs_main_resize_for_measured_margin_reflow = tree.node_context_data[only_child]
is Some(_) &&
(
margin_main_start[0] > 0.0 ||
margin_main_end[0] > 0.0 ||
margin_cross_start[0] > 0.0 ||
margin_cross_end[0] > 0.0
) &&
@util.abs_double(child_main_size - base_main_sizes[0]) > 0.0001
let child_basis_is_definite = @util.resolve_optional_dimension(
child.style.flex_basis,
main_available_pre,
)
is Some(_)
let force_basisless_leaf_main = !is_col &&
tree.children[only_child].length() == 0 &&
child.style.size.width is DimAuto &&
tree.node_context_data[only_child] is None &&
child_basis_is_definite &&
main_available_pre is AvailMaxContent
if force_basisless_leaf_main {
child_outer_main = margin_main_start[0] +
content_main_sizes[0] +
margin_main_end[0]
}
let mut resized = false
if !main_is_definite &&
(
allow_auto_resize ||
needs_main_resize_for_cross_min ||
needs_main_resize_for_measured_margin_reflow
) {
container_main = child_outer_main
if is_col {
border_box_height = @util.clamp_dimension(
@util.max_double(container_main + vert_inset, vert_inset),
node.style.min_size.height,
node.style.max_size.height,
available_space.height,
)
content_height = @util.max_double(border_box_height - vert_inset, 0.0)
} else {
border_box_width = @util.clamp_dimension(
@util.max_double(container_main + horiz_inset, horiz_inset),
node.style.min_size.width,
node.style.max_size.width,
available_space.width,
)
content_width = @util.max_double(border_box_width - horiz_inset, 0.0)
}
resized = true
}
if cross_is_auto_container {
container_cross = child_outer_cross
if is_col {
border_box_width = @util.clamp_dimension(
@util.max_double(container_cross + horiz_inset, horiz_inset),
node.style.min_size.width,
node.style.max_size.width,
available_space.width,
)
content_width = @util.max_double(border_box_width - horiz_inset, 0.0)
} else {
border_box_height = @util.clamp_dimension(
@util.max_double(container_cross + vert_inset, vert_inset),
node.style.min_size.height,
node.style.max_size.height,
available_space.height,
)
content_height = @util.max_double(border_box_height - vert_inset, 0.0)
}
resized = true
}
if resized {
tree.set_unrounded_layout(node_id, {
..Layout::zero(),
location: absolute_origin,
size: Size(width=border_box_width, height=border_box_height),
})
if force_basisless_leaf_main {
let child_available_for_final = Size(
width=@geometry.AvailDefinite(content_width),
height=AvailDefinite(content_height),
)
let child_origin = Point(
x=base_x + margin_main_start[0],
y=base_y + margin_cross_start[0],
)
view.perform_child_layout(
only_child,
Size(width=Some(content_main_sizes[0]), height=Some(child_cross_size)),
child_available_for_final,
child_origin,
false,
)
}
}
}
if flow_count > 1 &&
!main_is_definite &&
!is_wrap &&
@util.abs_double(gap_main - gap_main_pre) < 0.0001 &&
justify_is_startish &&
allow_auto_resize {
let mut needed_main = 0.0
for i in 0.. needed_main {
needed_main = end
}
}
let mut intrinsic_needed_main = if flow_count > 1 {
gap_main_pre * (flow_count - 1).to_double()
} else {
0.0
}
for i in 0.. @util.max_double(flex_basis, pref)
None => flex_basis
}
let flex_basis_min = if flex_shrink[i] == 0.0 {
Some(clamping_basis)
} else {
None
}
let flex_basis_max = if flex_grow[i] == 0.0 {
Some(clamping_basis)
} else {
None
}
let min_main = @util.max_double(
match flex_basis_min {
Some(v) => v
None => resolved_min_main
},
resolved_min_main,
)
let max_main = match (max_main_sizes[i], flex_basis_max) {
(Some(a), Some(b)) => Some(@util.min_double(a, b))
(Some(a), None) => Some(a)
(None, Some(b)) => Some(b)
(None, None) => None
}
let content_contribution = match (preferred_main_sizes[i], max_main) {
(Some(pref), Some(max_v)) if max_v <= min_main || max_v <= pref => {
let mut v = pref
if v > max_v {
v = max_v
}
if v < min_main {
v = min_main
}
v
}
(_, Some(max_v)) if max_v <= min_main => min_main
_ => {
let mut v = content_main_sizes[i]
if v < min_main {
v = min_main
}
match max_main {
Some(max_v) => if v > max_v { v = max_v }
None => ()
}
v
}
}
intrinsic_needed_main = intrinsic_needed_main +
margin_main_start[i] +
content_contribution +
margin_main_end[i]
}
if intrinsic_needed_main > needed_main {
needed_main = intrinsic_needed_main
}
if is_col {
border_box_height = @util.clamp_dimension(
@util.max_double(needed_main + vert_inset, vert_inset),
node.style.min_size.height,
node.style.max_size.height,
available_space.height,
)
content_height = @util.max_double(border_box_height - vert_inset, 0.0)
container_main = content_height
} else {
border_box_width = @util.clamp_dimension(
@util.max_double(needed_main + horiz_inset, horiz_inset),
node.style.min_size.width,
node.style.max_size.width,
available_space.width,
)
content_width = @util.max_double(border_box_width - horiz_inset, 0.0)
container_main = content_width
}
tree.set_unrounded_layout(node_id, {
..Layout::zero(),
location: absolute_origin,
size: Size(width=border_box_width, height=border_box_height),
})
if cross_is_auto_container {
let mut needed_cross = 0.0
for i in 0.. needed_cross {
needed_cross = end
}
}
if is_col {
border_box_width = @util.clamp_dimension(
@util.max_double(needed_cross + horiz_inset, horiz_inset),
node.style.min_size.width,
node.style.max_size.width,
available_space.width,
)
content_width = @util.max_double(border_box_width - horiz_inset, 0.0)
container_cross = content_width
} else {
border_box_height = @util.clamp_dimension(
@util.max_double(needed_cross + vert_inset, vert_inset),
node.style.min_size.height,
node.style.max_size.height,
available_space.height,
)
content_height = @util.max_double(border_box_height - vert_inset, 0.0)
container_cross = content_height
}
tree.set_unrounded_layout(node_id, {
..Layout::zero(),
location: absolute_origin,
size: Size(width=border_box_width, height=border_box_height),
})
}
}
compute_flex_absolute_and_hidden_children(
view,
abs_children,
hidden_children,
absolute_origin,
border,
padding,
border_box_width,
border_box_height,
is_col,
container_main,
container_cross,
justify,
is_reverse,
is_wrap_reverse,
node.style.align_items,
)
let resolved_margin = @util.resolve_rect_width_basis(
node.style.margin,
available_space,
)
set_effective_margin_states(
tree,
node_id,
margin_collapse_state_from(resolved_margin.top),
margin_collapse_state_from(resolved_margin.bottom),
)
}