mod collapse;
mod cross;
mod main_axis;
mod placement;
use rdom_core::{Dom, NodeId};
use crate::ext::TuiExt;
use crate::layout::{Direction, LayoutRect};
use crate::render::inline::compute_inline_layout;
use crate::style::ComputedStyle;
use super::ifc::is_ifc_block;
use super::{element_children_of, layout_node};
use collapse::SiblingOverlap;
use main_axis::{MainAxisBudget, collect_main_axis_items, resolve_flexible_lengths};
use placement::{AutoMainMargins, FlexLine, place_items};
pub(super) fn layout_children(
dom: &mut Dom<TuiExt>,
id: NodeId,
container: LayoutRect,
computed: &ComputedStyle,
) -> Option<super::block::BlockMeasurement> {
if let Some(ext) = dom.node_mut(id).ext_mut() {
ext.anonymous_blocks.clear();
}
if is_ifc_block(dom, id) {
for child in element_children_of(dom, id) {
if let Some(ext) = dom.node_mut(child).ext_mut() {
ext.layout = LayoutRect::new(container.x, container.y, 0, 0);
ext.content_layout = ext.layout;
ext.layout_dirty = false;
ext.margin_chain = None;
}
}
let inline_layout = compute_inline_layout(dom, id, container.width);
super::positioning::record_static_positions_in_ifc(
dom,
id,
&inline_layout,
crate::render::inline::scrolled_content_rect(dom, id).unwrap_or(container),
);
let atoms = crate::render::inline::atomic_placements(&inline_layout, container);
if let Some(ext) = dom.node_mut(id).ext_mut() {
ext.inline_layout = Some(inline_layout);
}
for (atom_id, atom_rect) in atoms {
layout_node(dom, atom_id, atom_rect, container.width);
}
return None;
}
let has_text_child = dom
.node(id)
.child_nodes()
.any(|c| c.node_type() == rdom_core::NodeType::Text);
let no_in_flow_element_children = element_children_of(dom, id)
.iter()
.all(|&c| !super::is_in_flow(dom, c));
if has_text_child && no_in_flow_element_children {
let inline_layout = compute_inline_layout(dom, id, container.width);
super::positioning::record_static_positions_in_ifc(
dom,
id,
&inline_layout,
crate::render::inline::scrolled_content_rect(dom, id).unwrap_or(container),
);
if let Some(ext) = dom.node_mut(id).ext_mut() {
ext.inline_layout = Some(inline_layout);
}
return None;
}
if let Some(ext) = dom.node_mut(id).ext_mut() {
ext.inline_layout = None;
}
match computed.flow {
crate::layout::Flow::Block => {
return Some(super::block::layout_block_children(
dom, id, container, computed,
));
}
crate::layout::Flow::Flex => {
}
}
let children: Vec<NodeId> = element_children_of(dom, id)
.into_iter()
.filter(|&c| super::is_in_flow(dom, c))
.collect();
let (static_x, static_y) = dom.node(id).ext().map_or((container.x, container.y), |e| {
(
container.x - e.scroll_x as i32,
container.y - e.scroll_y as i32,
)
});
for n in super::positioning::out_of_flow_positioned_children(dom, id) {
super::positioning::record_static_position(dom, n, static_x, static_y);
}
layout_flex_children(dom, &children, container, computed);
None
}
pub(super) fn layout_flex_children(
dom: &mut Dom<TuiExt>,
children: &[NodeId],
container: LayoutRect,
parent: &ComputedStyle,
) {
if children.is_empty() {
return;
}
let direction = parent.direction;
let gap = super::resolve_gap(parent, container, direction);
let container = collapse::inset_container_for_children(dom, children, parent, container);
let main_budget: u16 = match direction {
Direction::Row => container.width,
Direction::Column => container.height,
};
let cross_budget: u16 = match direction {
Direction::Row => container.height,
Direction::Column => container.width,
};
let line = collect_main_axis_items(dom, children, direction, main_budget, cross_budget);
let gap_total = gap.saturating_mul((children.len() as u16).saturating_sub(1));
let overlap = SiblingOverlap::new(parent, gap, direction);
let overlap_savings = overlap.savings(dom, children);
let remaining = (i32::from(main_budget) - line.consumed_fixed - i32::from(gap_total)
+ i32::from(overlap_savings))
.clamp(0, i32::from(u16::MAX)) as u16;
let auto_main_count = line.auto_main_count;
let auto_margins = AutoMainMargins {
share: (remaining as u32).checked_div(auto_main_count).unwrap_or(0) as u16,
remainder: (remaining as u32).checked_rem(auto_main_count).unwrap_or(0),
};
let flex_remaining: u16 = if auto_main_count > 0 { 0 } else { remaining };
let net_budget = (main_budget as i32) - (gap_total as i32) + (overlap_savings as i32);
let final_main = resolve_flexible_lengths(
dom,
&line.items,
direction,
MainAxisBudget {
main: main_budget,
cross: cross_budget,
flex_remaining,
net: net_budget,
},
);
place_items(
dom,
children,
FlexLine {
items: &line.items,
final_main: &final_main,
container,
direction,
gap,
cross_budget,
auto_margins,
overlap,
},
);
}