use super::flexbox::inner_area;
use super::*;
use std::sync::Arc;
#[derive(Default)]
pub(crate) struct FrameData {
pub scroll_infos: Vec<(u32, u32)>,
pub scroll_rects: Vec<Rect>,
pub hit_areas: Vec<Rect>,
pub group_rects: Vec<(Arc<str>, Rect)>,
pub content_areas: Vec<(Rect, Rect)>,
pub focus_rects: Vec<(usize, Rect)>,
pub focus_groups: Vec<Option<Arc<str>>>,
pub raw_draw_rects: Vec<RawDrawRect>,
}
pub(crate) struct RawDrawRect {
pub draw_id: usize,
pub rect: Rect,
pub top_clip_rows: u32,
pub original_height: u32,
}
pub(crate) fn collect_all(node: &LayoutNode) -> FrameData {
let mut data = FrameData::default();
if node.is_scrollable {
let viewport_h = node.size.1.saturating_sub(node.frame_vertical());
data.scroll_infos.push((node.content_height, viewport_h));
data.scroll_rects
.push(Rect::new(node.pos.0, node.pos.1, node.size.0, node.size.1));
}
if let Some(id) = node.focus_id {
if node.pos.1 + node.size.1 > 0 {
data.focus_rects.push((
id,
Rect::new(node.pos.0, node.pos.1, node.size.0, node.size.1),
));
}
}
if let Some(id) = node.interaction_id {
let rect = if node.pos.1 + node.size.1 > 0 {
Rect::new(node.pos.0, node.pos.1, node.size.0, node.size.1)
} else {
Rect::new(0, 0, 0, 0)
};
if id >= data.hit_areas.len() {
data.hit_areas.resize(id + 1, Rect::new(0, 0, 0, 0));
}
data.hit_areas[id] = rect;
}
let child_offset = if node.is_scrollable {
node.scroll_offset
} else {
0
};
for child in &node.children {
collect_all_inner(child, &mut data, child_offset, None, None, 1);
}
for overlay in &node.overlays {
collect_all_inner(&overlay.node, &mut data, 0, None, None, 1);
}
data
}
fn collect_all_inner(
node: &LayoutNode,
data: &mut FrameData,
y_offset: u32,
active_group: Option<&Arc<str>>,
viewport: Option<Rect>,
depth: usize,
) {
if depth > super::tree::MAX_LAYOUT_DEPTH {
panic!(
"layout tree depth exceeds {}: check for recursive container nesting",
super::tree::MAX_LAYOUT_DEPTH
);
}
if node.is_scrollable {
let viewport_h = node.size.1.saturating_sub(node.frame_vertical());
data.scroll_infos.push((node.content_height, viewport_h));
let adj_y = node.pos.1.saturating_sub(y_offset);
data.scroll_rects
.push(Rect::new(node.pos.0, adj_y, node.size.0, node.size.1));
}
if let Some(id) = node.interaction_id {
let rect = if node.pos.1 + node.size.1 > y_offset {
Rect::new(
node.pos.0,
node.pos.1.saturating_sub(y_offset),
node.size.0,
node.size.1,
)
} else {
Rect::new(0, 0, 0, 0)
};
if id >= data.hit_areas.len() {
data.hit_areas.resize(id + 1, Rect::new(0, 0, 0, 0));
}
data.hit_areas[id] = rect;
}
if let NodeKind::RawDraw(draw_id) = node.kind {
let node_x = node.pos.0;
let node_w = node.size.0;
let node_h = node.size.1;
let screen_y = node.pos.1 as i64 - y_offset as i64;
if let Some(vp) = viewport {
let img_top = screen_y;
let img_bottom = screen_y + node_h as i64;
let vp_top = vp.y as i64;
let vp_bottom = vp.bottom() as i64;
if img_bottom > vp_top && img_top < vp_bottom {
let visible_top = img_top.max(vp_top) as u32;
let visible_bottom = img_bottom.min(vp_bottom) as u32;
let visible_height = visible_bottom.saturating_sub(visible_top);
let top_clip_rows = (vp_top - img_top).max(0) as u32;
data.raw_draw_rects.push(RawDrawRect {
draw_id,
rect: Rect::new(node_x, visible_top, node_w, visible_height),
top_clip_rows,
original_height: node_h,
});
}
} else {
data.raw_draw_rects.push(RawDrawRect {
draw_id,
rect: Rect::new(node_x, screen_y.max(0) as u32, node_w, node_h),
top_clip_rows: 0,
original_height: node_h,
});
}
}
let node_group_arc: Option<Arc<str>> = node.group_name.clone();
if let Some(name) = &node_group_arc {
if node.pos.1 + node.size.1 > y_offset {
data.group_rects.push((
Arc::clone(name),
Rect::new(
node.pos.0,
node.pos.1.saturating_sub(y_offset),
node.size.0,
node.size.1,
),
));
}
}
if matches!(node.kind, NodeKind::Container(_)) {
let adj_y = node.pos.1.saturating_sub(y_offset);
let full = Rect::new(node.pos.0, adj_y, node.size.0, node.size.1);
let inset_x = node.padding.left + node.border_left_inset();
let inset_y = node.padding.top + node.border_top_inset();
let inner_w = node.size.0.saturating_sub(node.frame_horizontal());
let inner_h = node.size.1.saturating_sub(node.frame_vertical());
let content = Rect::new(node.pos.0 + inset_x, adj_y + inset_y, inner_w, inner_h);
data.content_areas.push((full, content));
}
if let Some(id) = node.focus_id {
if node.pos.1 + node.size.1 > y_offset {
data.focus_rects.push((
id,
Rect::new(
node.pos.0,
node.pos.1.saturating_sub(y_offset),
node.size.0,
node.size.1,
),
));
}
}
let current_group = node_group_arc.as_ref().or(active_group);
if let Some(id) = node.focus_id {
if id >= data.focus_groups.len() {
data.focus_groups.resize(id + 1, None);
}
data.focus_groups[id] = current_group.cloned();
}
let (child_offset, child_viewport) = if node.is_scrollable {
let screen_y = node.pos.1.saturating_sub(y_offset);
let area = Rect::new(node.pos.0, screen_y, node.size.0, node.size.1);
let inner = inner_area(node, area);
(y_offset.saturating_add(node.scroll_offset), Some(inner))
} else {
(y_offset, viewport)
};
for child in &node.children {
collect_all_inner(
child,
data,
child_offset,
current_group,
child_viewport,
depth + 1,
);
}
}