use crate::{algo::grid::track_sizing::TrackInfo, DefLength, LayoutTreeNode};
use alloc::vec::Vec;
use core::fmt::Debug;
use float_pigment_css::length_num::LengthNum;
use float_pigment_css::num_traits::Zero;
use super::track_sizing::IntrinsicTrackType;
pub(crate) struct GridTrack<T: LayoutTreeNode> {
pub min_sizing_function: TrackSizingFunction<T>,
pub max_sizing_function: TrackSizingFunction<T>,
pub base_size: T::Length,
pub growth_limit: Option<T::Length>,
}
impl<T: LayoutTreeNode> Debug for GridTrack<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"GridTrack {{ min: {:?}, max: {:?}, base_size: {:?}, growth_limit: {:?} }}",
self.min_sizing_function, self.max_sizing_function, self.base_size, self.growth_limit
)
}
}
impl<T: LayoutTreeNode> GridTrack<T> {
pub fn new(
min_sizing_function: TrackSizingFunction<T>,
max_sizing_function: TrackSizingFunction<T>,
) -> Self {
Self {
min_sizing_function,
max_sizing_function,
base_size: T::Length::zero(),
growth_limit: None,
}
}
pub fn from_single(sizing_function: TrackSizingFunction<T>) -> Self {
let max_fn = match &sizing_function {
TrackSizingFunction::Fixed(length) => TrackSizingFunction::Fixed(length.clone()),
TrackSizingFunction::Flex(fr) => TrackSizingFunction::Flex(*fr),
TrackSizingFunction::Auto => TrackSizingFunction::Auto,
TrackSizingFunction::Intrinsic => TrackSizingFunction::Intrinsic,
};
Self::new(sizing_function, max_fn)
}
#[inline(always)]
pub fn resolved_size(&self) -> T::Length {
self.base_size
}
#[inline(always)]
pub fn is_flexible(&self) -> bool {
matches!(self.max_sizing_function, TrackSizingFunction::Flex(_))
}
#[inline(always)]
pub fn is_auto(&self) -> bool {
matches!(self.max_sizing_function, TrackSizingFunction::Auto)
}
}
pub(crate) enum TrackSizingFunction<T: LayoutTreeNode> {
Fixed(DefLength<T::Length, T::LengthCustom>),
Flex(f32),
Auto,
Intrinsic,
}
impl<T: LayoutTreeNode> Debug for TrackSizingFunction<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Fixed(length) => write!(f, "Fixed({:?})", length),
Self::Flex(fr) => write!(f, "Flex({}fr)", fr),
Self::Auto => write!(f, "Auto"),
Self::Intrinsic => write!(f, "Intrinsic"),
}
}
}
pub(crate) struct GridTracks<T: LayoutTreeNode> {
tracks: Vec<GridTrack<T>>,
auto_count: usize,
}
impl<T: LayoutTreeNode> Debug for GridTracks<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"GridTracks {{ count: {}, auto_count: {} }}",
self.tracks.len(),
self.auto_count
)
}
}
impl<T: LayoutTreeNode> GridTracks<T> {
pub fn total_base_size(&self) -> T::Length {
self.tracks
.iter()
.fold(T::Length::zero(), |acc, track| acc + track.base_size)
}
pub fn resolved_sizes(&self) -> Vec<T::Length> {
self.tracks.iter().map(|t| t.resolved_size()).collect()
}
pub fn from_track_info(track_info: &[TrackInfo<T::Length>]) -> Self {
let mut tracks = Vec::with_capacity(track_info.len());
let mut auto_count = 0;
for item in track_info {
let sizing_fn = match item.track_type {
IntrinsicTrackType::Fr => TrackSizingFunction::Flex(item.fr_value),
IntrinsicTrackType::Auto => {
auto_count += 1;
TrackSizingFunction::Auto
}
IntrinsicTrackType::MinContent | IntrinsicTrackType::MaxContent => {
TrackSizingFunction::Intrinsic
}
IntrinsicTrackType::Fixed => TrackSizingFunction::Fixed(DefLength::Points(
item.base_size.unwrap_or(T::Length::zero()),
)),
};
let mut track = GridTrack::from_single(sizing_fn);
track.base_size = item.base_size.unwrap_or(T::Length::zero());
track.growth_limit = if item.track_type == IntrinsicTrackType::Fr {
None
} else {
item.growth_limit
};
tracks.push(track);
}
Self { tracks, auto_count }
}
pub fn maximize(&mut self, free_space: T::Length) {
if free_space <= T::Length::zero() {
return;
}
let mut unfrozen: Vec<usize> = Vec::with_capacity(self.tracks.len());
for (i, track) in self.tracks.iter().enumerate() {
if !track.is_flexible() {
unfrozen.push(i);
}
}
if unfrozen.is_empty() {
return;
}
let mut remaining = free_space;
loop {
if unfrozen.is_empty() || remaining <= T::Length::zero() {
break;
}
let share = remaining.div_f32(unfrozen.len() as f32);
let mut any_frozen = false;
let mut space_used = T::Length::zero();
unfrozen.retain(|&i| {
let track = &mut self.tracks[i];
let new_base = track.base_size + share;
if let Some(limit) = track.growth_limit {
if new_base >= limit {
let delta = limit - track.base_size;
if delta > T::Length::zero() {
space_used += delta;
track.base_size = limit;
}
any_frozen = true;
return false; }
}
space_used += share;
track.base_size = new_base;
true });
remaining -= space_used;
if !any_frozen {
break;
}
}
}
pub fn stretch_auto_tracks(&mut self, free_space: T::Length) {
if free_space <= T::Length::zero() {
return;
}
if self.auto_count == 0 {
return;
}
let space_per_track = free_space.div_f32(self.auto_count as f32);
for track in &mut self.tracks {
if track.is_auto() {
track.base_size += space_per_track;
}
}
}
}