use cvkg_core::{Alignment, Distribution, LayoutCache, LayoutView, Rect};
#[derive(Debug, Clone)]
pub struct ProgressiveChild {
pub hash: u64,
pub laid_out: bool,
pub rect: Rect,
}
pub struct ProgressiveLayoutContext<'a> {
pub children: &'a [&'a dyn LayoutView],
pub entries: Vec<ProgressiveChild>,
pub spacing: f32,
pub alignment: Alignment,
pub distribution: Distribution,
pub bounds: Rect,
pub completed: usize,
pub fallback_applied: bool,
}
impl<'a> ProgressiveLayoutContext<'a> {
pub fn new(
bounds: Rect,
subviews: &'a [&'a dyn LayoutView],
spacing: f32,
alignment: Alignment,
distribution: Distribution,
) -> Self {
let entries = subviews
.iter()
.map(|v| ProgressiveChild {
hash: v.view_hash(),
laid_out: false,
rect: Rect::zero(),
})
.collect();
Self {
children: subviews,
entries,
spacing,
alignment,
distribution,
bounds,
completed: 0,
fallback_applied: false,
}
}
pub fn layout_next_batch(&mut self, batch_size: usize) -> bool {
self.layout_next_batch_inner(batch_size, None);
self.is_complete()
}
pub fn layout_next_batch_with_cache(
&mut self,
batch_size: usize,
cache: &mut LayoutCache,
) -> (bool, Vec<Rect>) {
self.layout_next_batch_inner(batch_size, Some(cache));
let new_rects: Vec<Rect> = self
.entries
.iter()
.filter(|e| e.laid_out && e.rect != Rect::zero())
.map(|e| e.rect)
.collect();
(self.is_complete(), new_rects)
}
fn layout_next_batch_inner(
&mut self,
batch_size: usize,
mut cache: Option<&mut LayoutCache>,
) {
let mut processed = 0;
let mut batch_indices = Vec::new();
for (i, entry) in self.entries.iter().enumerate() {
if entry.laid_out {
continue;
}
if processed >= batch_size {
break;
}
batch_indices.push(i);
processed += 1;
}
if batch_indices.is_empty() {
return;
}
let batch_subviews: Vec<&dyn LayoutView> = batch_indices
.iter()
.map(|&i| self.children[i])
.collect();
let rects = match cache {
Some(ref mut c) => crate::taffy_engine::HStack::compute_layout_incremental(
self.spacing,
self.alignment,
self.distribution,
self.bounds,
0,
&batch_subviews,
*c,
),
None => {
let mut tmp = LayoutCache::new();
crate::taffy_engine::HStack::compute_layout_incremental(
self.spacing,
self.alignment,
self.distribution,
self.bounds,
0,
&batch_subviews,
&mut tmp,
)
}
};
for (local_idx, &global_idx) in batch_indices.iter().enumerate() {
if local_idx < rects.len() {
self.entries[global_idx].rect = rects[local_idx];
self.entries[global_idx].laid_out = true;
self.completed += 1;
}
}
if let Some(c) = cache.as_mut() {
for (local_idx, &global_idx) in batch_indices.iter().enumerate() {
if local_idx < rects.len() {
let hash = self.entries[global_idx].hash;
if hash != 0 {
c.previous_rects.insert(hash, rects[local_idx]);
}
}
}
}
}
pub fn is_complete(&self) -> bool {
self.fallback_applied || self.completed >= self.entries.len()
}
pub fn progress(&self) -> (usize, usize) {
(self.completed, self.entries.len())
}
pub fn apply_remaining_fallback(&mut self, cache: &mut LayoutCache) -> Vec<Rect> {
let mut fallback_rects = Vec::new();
let remaining: Vec<usize> = self
.entries
.iter()
.enumerate()
.filter(|(_, e)| !e.laid_out)
.map(|(i, _)| i)
.collect();
if remaining.is_empty() {
self.fallback_applied = true;
return fallback_rects;
}
let cols = (remaining.len() as f32).sqrt().ceil() as usize;
let rows = (remaining.len() + cols - 1) / cols;
let cell_w = self.bounds.width / cols as f32;
let cell_h = self.bounds.height / rows as f32;
for (offset, &idx) in remaining.iter().enumerate() {
let hash = self.entries[idx].hash;
let rect = if hash != 0 {
cache
.previous_rects
.get(&hash)
.copied()
.unwrap_or_else(|| {
let col = offset % cols;
let row = offset / cols;
Rect {
x: self.bounds.x + col as f32 * cell_w,
y: self.bounds.y + row as f32 * cell_h,
width: cell_w,
height: cell_h,
}
})
} else {
let col = offset % cols;
let row = offset / cols;
Rect {
x: self.bounds.x + col as f32 * cell_w,
y: self.bounds.y + row as f32 * cell_h,
width: cell_w,
height: cell_h,
}
};
self.entries[idx].rect = rect;
self.entries[idx].laid_out = true;
self.completed += 1;
if hash != 0 {
cache.previous_rects.insert(hash, rect);
}
fallback_rects.push(rect);
}
self.fallback_applied = true;
fallback_rects
}
pub fn take_rects(self) -> Vec<Rect> {
self.entries.into_iter().map(|e| e.rect).collect()
}
}