// 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_grid_relative_children_final_placement(
view : ChicleView[C],
node_id : NodeId,
grid_items : Array[GridItem],
placement_count : Int,
col_tracks : Array[Dimension],
row_tracks : Array[Dimension],
col_sizes : Array[Double],
row_sizes : Array[Double],
col_gap_effective : Double,
row_gap_effective : Double,
start_x : Double,
start_y : Double,
absolute_origin : Point[Double],
border : Rect[Double],
padding : Rect[Double],
content_width : Double,
default_align_items : AlignItems,
default_justify_items : AlignItems,
row_count : Int,
col_count : Int,
) -> Unit raise ChicleError {
let tree = view.tree
let base_x = absolute_origin.x + border.left + padding.left
let base_y = absolute_origin.y + border.top + padding.top
let row_baseline_before_max : Array[Double] = Array::make(row_count, 0.0)
let row_baseline_after_max : Array[Double] = Array::make(row_count, 0.0)
for i in 0.. v
None => default_align_items
}
if item.row_span != 1 {
continue
}
match align {
ItemsBaseline => ()
_ => continue
}
let grid_area_size = item.grid_area_size(
col_sizes, row_sizes, col_gap_effective, row_gap_effective,
)
let cell_w = grid_area_size.width
let cell_h = grid_area_size.height
let child_available = Size(
width=@geometry.AvailDefinite(cell_w),
height=AvailDefinite(cell_h),
)
let resolved_margin = @util.resolve_rect_width_basis(
child_style.margin,
child_available,
)
let margin_left_auto = child_style.margin.left is DimAuto
let margin_right_auto = child_style.margin.right is DimAuto
let margin_top_auto = child_style.margin.top is DimAuto
let margin_bottom_auto = child_style.margin.bottom is DimAuto
let has_auto_margin_x = margin_left_auto || margin_right_auto
let has_auto_margin_y = margin_top_auto || margin_bottom_auto
if has_auto_margin_y {
continue
}
let available_w_for_item = @util.max_double(
cell_w - resolved_margin.left - resolved_margin.right,
0.0,
)
let available_h_for_item = @util.max_double(
cell_h - resolved_margin.top - resolved_margin.bottom,
0.0,
)
let item_available = Size(
width=@geometry.AvailDefinite(available_w_for_item),
height=AvailDefinite(available_h_for_item),
)
let justify = match child_style.justify_self {
Some(v) => v
None => default_justify_items
}
let stretch_fit_limit_w = if item.column_span == 1 {
match child_style.overflow.x {
OverflowVisible => None
_ =>
match col_tracks[item.column] {
DimFitContent(limit) =>
@util.resolve_optional_dimension(
limit,
AvailDefinite(content_width),
)
_ => None
}
}
} else {
None
}
let known_w = match justify {
ItemsStretch =>
if child_style.size.width is DimAuto && !has_auto_margin_x {
match stretch_fit_limit_w {
Some(limit_w) =>
Some(
if limit_w < available_w_for_item {
limit_w
} else {
available_w_for_item
},
)
None => Some(available_w_for_item)
}
} else {
None
}
_ => None
}
let known_w = match (known_w, child_style.size.width) {
(None, DimPercent(p)) => Some(cell_w * p)
_ => known_w
}
view.perform_child_layout(
child_id,
Size(width=known_w, height=None),
item_available,
Point::zero(),
false,
)
let baseline = grid_item_baseline_offset_y(tree, child_id)
let intrinsic = tree.nodes[child_id].unrounded_layout.size
item.set_baseline(baseline~)
let before = resolved_margin.top + baseline
let after = resolved_margin.bottom +
@util.max_double(intrinsic.height - baseline, 0.0)
row_baseline_before_max[item.row] = @util.max_double(
row_baseline_before_max[item.row],
before,
)
row_baseline_after_max[item.row] = @util.max_double(
row_baseline_after_max[item.row],
after,
)
}
for row in 0..
if baseline_total > row_sizes[row] {
row_sizes[row] = baseline_total
}
_ => ()
}
}
for i in 0.. compute_hidden_layout(tree, child_id, Point::zero())
_ => {
if !item.is_in_bounds(row_count, col_count) {
continue
} else {
()
}
match child_style.position {
PosAbsolute => continue
PosRelative => ()
}
let grid_area_origin = item.grid_area_origin(
col_sizes, row_sizes, col_gap_effective, row_gap_effective, start_x, start_y,
)
let grid_area_size = item.grid_area_size(
col_sizes, row_sizes, col_gap_effective, row_gap_effective,
)
let cell_w = grid_area_size.width
let cell_h = grid_area_size.height
let child_available = Size(
width=@geometry.AvailDefinite(cell_w),
height=AvailDefinite(cell_h),
)
let resolved_margin = @util.resolve_rect_width_basis(
child_style.margin,
child_available,
)
let baseline_shim = item.baseline_shim
let margin_left_auto = child_style.margin.left is DimAuto
let margin_right_auto = child_style.margin.right is DimAuto
let margin_top_auto = child_style.margin.top is DimAuto
let margin_bottom_auto = child_style.margin.bottom is DimAuto
let has_auto_margin_x = margin_left_auto || margin_right_auto
let has_auto_margin_y = margin_top_auto || margin_bottom_auto
let available_w_for_item = @util.max_double(
cell_w - resolved_margin.left - resolved_margin.right,
0.0,
)
let available_h_for_item = @util.max_double(
cell_h - resolved_margin.top - resolved_margin.bottom - baseline_shim,
0.0,
)
let item_available = Size(
width=@geometry.AvailDefinite(available_w_for_item),
height=AvailDefinite(available_h_for_item),
)
let justify = match child_style.justify_self {
Some(v) => v
None => default_justify_items
}
let align = match child_style.align_self {
Some(v) => v
None => default_align_items
}
let stretch_fit_limit_w = if item.column_span == 1 {
match child_style.overflow.x {
OverflowVisible => None
_ =>
match col_tracks[item.column] {
DimFitContent(limit) =>
@util.resolve_optional_dimension(
limit,
AvailDefinite(content_width),
)
_ => None
}
}
} else {
None
}
let mut known_w = match justify {
ItemsStretch =>
if child_style.size.width is DimAuto && !has_auto_margin_x {
match stretch_fit_limit_w {
Some(limit_w) =>
Some(
if limit_w < available_w_for_item {
limit_w
} else {
available_w_for_item
},
)
None => Some(available_w_for_item)
}
} else {
None
}
_ => None
}
let mut known_h = match align {
ItemsStretch =>
if child_style.size.height is DimAuto &&
!has_auto_margin_y &&
child_style.aspect_ratio is None {
Some(available_h_for_item)
} else {
None
}
_ => None
}
match (known_w, child_style.size.width) {
(None, DimPercent(p)) => known_w = Some(cell_w * p)
_ => ()
}
match (known_h, child_style.size.height) {
(None, DimPercent(p)) => known_h = Some(cell_h * p)
_ => ()
}
view.perform_child_layout(
child_id,
Size(width=known_w, height=known_h),
child_available,
Point::zero(),
false,
)
match (align, child_style.aspect_ratio, child_style.size.height) {
(ItemsStretch, Some(ratio), DimAuto) =>
if ratio > 0.0 &&
known_w is Some(_) &&
known_h is None &&
!has_auto_margin_y {
let intrinsic_after_stretch = tree.nodes[child_id].unrounded_layout.size
let max_height = @util.resolve_optional_dimension(
child_style.max_size.height,
item_available.height,
)
match max_height {
Some(max_h) if intrinsic_after_stretch.height > max_h => {
known_h = Some(max_h)
known_w = Some((max_h * ratio).round())
}
_ => {
let ratio_height = (intrinsic_after_stretch.width / ratio).round()
if intrinsic_after_stretch.height > ratio_height {
known_h = Some(ratio_height)
} else if intrinsic_after_stretch.height < ratio_height {
known_w = Some(
(intrinsic_after_stretch.height * ratio).round(),
)
} else {
()
}
}
}
view.perform_child_layout(
child_id,
Size(width=known_w, height=known_h),
child_available,
Point::zero(),
false,
)
} else {
()
}
_ => ()
}
let intrinsic = tree.nodes[child_id].unrounded_layout.size
let free_x = @util.max_double(
available_w_for_item - intrinsic.width,
0.0,
)
let free_y = @util.max_double(
available_h_for_item - intrinsic.height,
0.0,
)
let margin_left = if margin_left_auto {
if margin_right_auto {
free_x / 2.0
} else {
free_x
}
} else {
resolved_margin.left
}
let margin_top = if margin_top_auto {
if margin_bottom_auto {
free_y / 2.0 + baseline_shim
} else {
free_y + baseline_shim
}
} else {
resolved_margin.top + baseline_shim
}
let offset_x = if has_auto_margin_x {
0.0
} else {
match justify {
ItemsEnd | ItemsFlexEnd => available_w_for_item - intrinsic.width
ItemsCenter => (available_w_for_item - intrinsic.width) / 2.0
_ => 0.0
}
}
let offset_y = if has_auto_margin_y {
0.0
} else {
match align {
ItemsEnd | ItemsFlexEnd => available_h_for_item - intrinsic.height
ItemsCenter => (available_h_for_item - intrinsic.height) / 2.0
ItemsBaseline => 0.0
_ => 0.0
}
}
let final_location = Point(
x=base_x + grid_area_origin.x + margin_left + offset_x,
y=base_y + grid_area_origin.y + margin_top + offset_y,
)
view.perform_child_layout(
child_id,
Size(width=known_w, height=known_h),
child_available,
final_location,
false,
)
let final_size = tree.nodes[child_id].unrounded_layout.size
item.set_final_area(
y_position=final_location.y,
height=final_size.height,
)
}
}
}
tree.set_first_baselines(
node_id,
Point(x=None, y=grid_items_first_row_baseline(grid_items)),
)
}