use crate::node_id::NodeId;
use crate::style::BoxSizing;
use crate::widget_tree::WidgetTree;
use super::common::{ChildSpec, extract_child_spec, get_node_style};
use super::region::Region;
use super::resolve_1d::{Edge, layout_resolve_1d};
pub fn layout_vertical(
tree: &mut WidgetTree,
children: &[NodeId],
available: Region,
viewport: (u16, u16),
allow_h_overflow: bool,
) {
if children.is_empty() {
return;
}
let mut specs: Vec<ChildSpec> = Vec::with_capacity(children.len());
for &child in children {
let style = get_node_style(tree, child);
let width_is_auto =
matches!(style.width.as_ref(), None | Some(crate::style::Scalar::Auto));
let pre_intrinsic_w = tree
.get(child)
.and_then(|node| node.widget.content_width())
.and_then(|w| u16::try_from(w).ok());
let seed_spec = extract_child_spec(
&style,
available.width,
available.height,
viewport,
None,
None,
);
let mut seed_layout_w = available
.width
.saturating_sub(seed_spec.margin.left + seed_spec.margin.right);
if let Some(edge_w) = seed_spec.width_edge.size {
let explicit_w = edge_w.saturating_sub(seed_spec.margin.left + seed_spec.margin.right);
seed_layout_w = seed_layout_w.min(explicit_w);
}
if allow_h_overflow
&& width_is_auto
&& let Some(iw) = pre_intrinsic_w
{
let iw_outer = iw.saturating_add(
seed_spec.border_left
+ seed_spec.border_right
+ seed_spec.padding.left
+ seed_spec.padding.right,
);
seed_layout_w = seed_layout_w.max(iw_outer);
}
if let Some(max_w) = seed_spec.max_width_cells {
let max_w_outer = if seed_spec.box_sizing == BoxSizing::BorderBox {
max_w
} else {
max_w.saturating_add(
seed_spec.border_left
+ seed_spec.border_right
+ seed_spec.padding.left
+ seed_spec.padding.right,
)
};
seed_layout_w = seed_layout_w.min(max_w_outer);
}
let seed_content_w = seed_layout_w.saturating_sub(
seed_spec.border_left
+ seed_spec.border_right
+ seed_spec.padding.left
+ seed_spec.padding.right,
);
let seed_content_h = available.height.max(1);
if let Some(node) = tree.get_mut(child) {
node.widget.on_layout(seed_content_w.max(1), seed_content_h);
}
let intrinsic_height = tree
.get(child)
.and_then(|node| node.widget.layout_height())
.and_then(|h| u16::try_from(h).ok());
let intrinsic_width = tree
.get(child)
.and_then(|node| node.widget.content_width())
.and_then(|w| u16::try_from(w).ok());
let mut spec = extract_child_spec(
&style,
available.width,
available.height,
viewport,
intrinsic_height,
intrinsic_width,
);
if style.expand == Some(true) && spec.height_edge.size.is_some() {
spec.height_edge.size = None;
spec.height_edge.fraction = spec.height_edge.fraction.max(1);
}
specs.push(spec);
}
let edges: Vec<Edge> = specs.iter().map(|s| s.height_edge).collect();
let heights = layout_resolve_1d(available.height, &edges);
let mut y = available.y;
let mut prev_margin_bottom: u16 = 0;
for (i, &child) in children.iter().enumerate() {
let spec = &specs[i];
let total_h = heights[i];
let collapse = prev_margin_bottom.min(spec.margin.top);
y = y.saturating_sub(collapse);
let layout_x = available.x.saturating_add(spec.margin.left);
let layout_y = y.saturating_add(spec.margin.top);
let base_w = available
.width
.saturating_sub(spec.margin.left + spec.margin.right);
let mut layout_w = base_w;
let layout_h = total_h.saturating_sub(spec.margin.top + spec.margin.bottom);
if let Some(edge_w) = spec.width_edge.size {
let explicit_w = edge_w.saturating_sub(spec.margin.left + spec.margin.right);
if allow_h_overflow && spec.width_is_auto {
layout_w = explicit_w;
} else {
layout_w = base_w.min(explicit_w);
}
}
let layout_w = if let Some(max_w) = spec.max_width_cells {
let max_w_outer = if spec.box_sizing == BoxSizing::BorderBox {
max_w
} else {
max_w.saturating_add(
spec.border_left + spec.border_right + spec.padding.left + spec.padding.right,
)
};
layout_w.min(max_w_outer)
} else {
layout_w
};
let layout_h = if let Some(max_h) = spec.max_height_cells {
let max_h_outer = if spec.box_sizing == BoxSizing::BorderBox {
max_h
} else {
max_h.saturating_add(
spec.border_top + spec.border_bottom + spec.padding.top + spec.padding.bottom,
)
};
layout_h.min(max_h_outer)
} else {
layout_h
};
let content_x = layout_x.saturating_add(spec.border_left + spec.padding.left);
let content_y = layout_y.saturating_add(spec.border_top + spec.padding.top);
let content_w = layout_w.saturating_sub(
spec.border_left + spec.border_right + spec.padding.left + spec.padding.right,
);
let content_h = layout_h.saturating_sub(
spec.border_top + spec.border_bottom + spec.padding.top + spec.padding.bottom,
);
if let Some(node) = tree.get_mut(child) {
node.layout_rect = Region::new(layout_x, layout_y, layout_w, layout_h).to_rect();
node.content_rect = Region::new(content_x, content_y, content_w, content_h).to_rect();
}
y = y.saturating_add(total_h);
prev_margin_bottom = spec.margin.bottom;
}
}