// 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 grid_axis_used_size(track_sizes : Array[Double], gap : Double) -> Double {
let mut used = 0.0
for size in track_sizes {
used = used + size
}
let track_count = track_sizes.length()
if track_count > 1 {
used + gap * (track_count - 1).to_double()
} else {
used
}
}
///|
fn grid_definite_axis_border_content_size(
specified_size : Double?,
min_size : Dimension,
max_size : Dimension,
available_space : AvailableSpace,
inset : Double,
non_scroll_inset : Double,
) -> (Double, Double) {
match specified_size {
Some(size) => {
let border_box = @util.max_double(
@util.clamp_dimension(size, min_size, max_size, available_space),
non_scroll_inset,
)
(border_box, @util.max_double(border_box - inset, 0.0))
}
None => (0.0, 0.0)
}
}
///|
fn grid_auto_axis_border_content_size(
available_space : AvailableSpace,
inset : Double,
non_scroll_inset : Double,
min_size : Dimension,
max_size : Dimension,
min_basis : Double,
max_basis : Double,
used_size : Double,
) -> (Double, Double) {
let intrinsic_size = match available_space {
AvailMaxContent => max_basis
AvailMinContent => min_basis
AvailDefinite(_) => used_size
}
let border_box = @util.max_double(
@util.clamp_dimension(
@util.max_double(intrinsic_size + inset, inset),
min_size,
max_size,
available_space,
),
non_scroll_inset,
)
(border_box, @util.max_double(border_box - inset, 0.0))
}
///|
fn resolve_deferred_grid_percent_gaps(
content_width : Double,
col_gap : Double,
row_gap : Double,
gap_width_percent : Double?,
gap_height_percent : Double?,
) -> (Double, Double, Bool) {
let mut resolved_col_gap = col_gap
let mut resolved_row_gap = row_gap
let mut rerun_track_sizing = false
match gap_width_percent {
Some(percent) =>
if resolved_col_gap == 0.0 && content_width > 0.0 {
resolved_col_gap = content_width * percent
rerun_track_sizing = true
}
None => ()
}
match gap_height_percent {
Some(percent) =>
if resolved_row_gap == 0.0 && content_width > 0.0 {
resolved_row_gap = content_width * percent
rerun_track_sizing = true
}
None => ()
}
(resolved_col_gap, resolved_row_gap, rerun_track_sizing)
}
///|
fn[C] compute_initial_grid_track_sizing(
view : ChicleView[C],
grid_items : Array[GridItem],
placement_count : Int,
col_tracks : Array[Dimension],
row_tracks : Array[Dimension],
contributions : GridTrackContributions,
col_gap : Double,
row_gap : Double,
content_width : Double,
content_height : Double,
available_space : Size[AvailableSpace],
horiz_inset : Double,
vert_inset : Double,
specified_width : Double?,
specified_height : Double?,
justify_content_for_contrib : AlignContent,
default_align_items_for_contrib : AlignItems,
default_justify_items_for_contrib : AlignItems,
) -> (
Array[Double],
Array[Double],
Double,
Double,
Double,
Double,
Double,
Double,
) raise ChicleError {
compute_grid_column_track_contributions(
view, grid_items, placement_count, col_tracks, row_tracks, contributions, col_gap,
row_gap, content_height, specified_height, true,
)
let mut col_min_basis = axis_min_content_basis(
col_tracks,
contributions.column_minimum,
contributions.column_max_content,
col_gap,
)
let mut col_max_basis = axis_max_content_basis(
col_tracks,
contributions.column_minimum,
contributions.column_max_content,
col_gap,
)
let mut col_available = match specified_width {
Some(_) => content_width
None =>
available_inset_or_basis(
available_space.width,
horiz_inset,
col_min_basis,
col_max_basis,
)
}
let mut col_available_base = col_available
let col_alignment_inner_size = match specified_width {
Some(_) => Some(content_width)
None =>
match available_space.width {
AvailDefinite(_) => Some(col_available)
_ => None
}
}
let mut col_sizes = track_sizing_algorithm(
GridTrackSizingAlgorithmInput(
tracks=col_tracks,
available=col_available,
gap=col_gap,
min_contribution=contributions.column_minimum,
max_content_contribution=contributions.column_max_content,
expand_to_fill=specified_width is Some(_),
),
).sizes
if specified_width is None && available_space.width is AvailMaxContent {
match
compute_max_content_spanning_track_sizes_for_grid_items(
col_tracks, grid_items,
) {
Some(result) => {
col_sizes = result.0
col_available = result.1
col_available_base = col_available
let basis_with_gaps = result.1 +
col_gap * (col_tracks.length() - 1).to_double()
col_min_basis = basis_with_gaps
col_max_basis = basis_with_gaps
}
None => ()
}
}
let col_gutter_alignment_adjustment = compute_alignment_gutter_adjustment(
justify_content_for_contrib, col_alignment_inner_size, col_sizes, col_gap,
)
compute_grid_row_track_contributions(
view, grid_items, placement_count, col_sizes, contributions, col_gap, col_gutter_alignment_adjustment,
row_gap, default_align_items_for_contrib, default_justify_items_for_contrib,
)
let mut row_min_basis = axis_min_content_basis(
row_tracks,
contributions.row_minimum,
contributions.row_max_content,
row_gap,
)
let mut row_max_basis = axis_max_content_basis(
row_tracks,
contributions.row_minimum,
contributions.row_max_content,
row_gap,
)
let mut row_available_base = match specified_height {
Some(_) => content_height
None =>
available_inset_or_basis(
available_space.height,
vert_inset,
row_min_basis,
row_max_basis,
)
}
let mut row_sizes = track_sizing_algorithm(
GridTrackSizingAlgorithmInput(
tracks=row_tracks,
available=row_available_base,
gap=row_gap,
min_contribution=contributions.row_minimum,
max_content_contribution=contributions.row_max_content,
expand_to_fill=specified_height is Some(_),
),
).sizes
if specified_height is None && available_space.height is AvailMaxContent {
match
compute_max_content_spanning_row_track_sizes_for_grid_items(
row_tracks, grid_items,
) {
Some(result) => {
row_sizes = result.0
row_available_base = result.1
let basis_with_gaps = result.1 +
row_gap * (row_tracks.length() - 1).to_double()
row_min_basis = basis_with_gaps
row_max_basis = basis_with_gaps
}
None => ()
}
}
(
col_sizes, row_sizes, col_min_basis, col_max_basis, col_available_base, row_min_basis,
row_max_basis, row_available_base,
)
}
///|
fn[C] rerun_grid_track_sizing_after_percent_gap_resolution(
view : ChicleView[C],
grid_items : Array[GridItem],
placement_count : Int,
col_tracks : Array[Dimension],
row_tracks : Array[Dimension],
contributions : GridTrackContributions,
col_available_base : Double,
row_available_base : Double,
col_gap : Double,
row_gap : Double,
content_height : Double,
specified_width : Double?,
specified_height : Double?,
justify_content_for_contrib : AlignContent,
default_align_items_for_contrib : AlignItems,
default_justify_items_for_contrib : AlignItems,
) -> (Array[Double], Array[Double], Double, Double) raise ChicleError {
contributions.reset()
compute_grid_column_track_contributions(
view, grid_items, placement_count, col_tracks, row_tracks, contributions, col_gap,
row_gap, content_height, specified_height, false,
)
let col_sizes = track_sizing_algorithm(
GridTrackSizingAlgorithmInput(
tracks=col_tracks,
available=col_available_base,
gap=col_gap,
min_contribution=contributions.column_minimum,
max_content_contribution=contributions.column_max_content,
expand_to_fill=specified_width is Some(_),
),
).sizes
let col_gutter_alignment_adjustment = compute_alignment_gutter_adjustment(
justify_content_for_contrib,
match specified_width {
Some(_) => Some(col_available_base)
None => None
},
col_sizes,
col_gap,
)
compute_grid_row_track_contributions(
view, grid_items, placement_count, col_sizes, contributions, col_gap, col_gutter_alignment_adjustment,
row_gap, default_align_items_for_contrib, default_justify_items_for_contrib,
)
let row_sizes = track_sizing_algorithm(
GridTrackSizingAlgorithmInput(
tracks=row_tracks,
available=row_available_base,
gap=row_gap,
min_contribution=contributions.row_minimum,
max_content_contribution=contributions.row_max_content,
expand_to_fill=specified_height is Some(_),
),
).sizes
(
col_sizes,
row_sizes,
grid_axis_used_size(col_sizes, col_gap),
grid_axis_used_size(row_sizes, row_gap),
)
}