use crate::core::ElementId;
use crate::geometry::Size;
use crate::layout::LayoutConstraint;
use crate::layout::box_model::ComputedLayout;
use crate::layout::flex_node::FlexNode;
use crate::layout::types::{
Direction as LayoutDirection, FlexDirection, NodeType as LayoutNodeType, VALUE_UNDEFINED,
};
use crate::runtime::element::ElementTree;
use crate::view::node::{NodeType, ViewNode};
#[derive(Debug, Clone, Copy, Default)]
pub struct LayoutContext;
impl LayoutContext {
pub fn compute(root_id: ElementId, tree: &ElementTree, viewport: Size, fill_parent: bool) {
let (pw, ph) = if fill_parent {
(
if viewport.width > 0.0 {
viewport.width
} else {
VALUE_UNDEFINED
},
if viewport.height > 0.0 {
viewport.height
} else {
VALUE_UNDEFINED
},
)
} else {
(VALUE_UNDEFINED, VALUE_UNDEFINED)
};
let mut flex_root = Self::build_flex(root_id, tree);
flex_root.layout(pw, ph, LayoutDirection::Ltr);
Self::write_layout(&flex_root, tree, root_id, 0.0, 0.0);
}
fn build_flex(id: ElementId, tree: &ElementTree) -> FlexNode {
let view = tree.get_node_ref(id).unwrap();
let mut fs = view.layout().clone();
fs.node_type = match view.node_type() {
NodeType::Text => LayoutNodeType::Text,
_ => LayoutNodeType::Default,
};
let children = tree.children_ref(id);
let is_leaf = children.is_empty();
let mut fn_node = FlexNode::new(0, fs);
if is_leaf {
let m = if !tree.is_dirty(id) {
tree.intrinsic(id)
} else {
let m = view.measure(&LayoutConstraint::default());
tree.set_intrinsic(id, m);
m
};
if let ViewNode::Text { content, style, .. } = view {
fn_node.measure_text = Some((content.clone(), style.clone()));
}
fn_node.intrinsic_size = Some((m.width, m.height));
}
for &cid in children {
let child_fn = Self::build_flex(cid, tree);
fn_node.children.push(child_fn);
}
if view.layout().overflow_scroll {
for c in fn_node.children.iter_mut() {
c.style.flex_shrink = 0.0;
c.style.flex_grow = 0.0;
}
}
fn_node
}
fn write_layout(
node: &FlexNode,
tree: &ElementTree,
id: ElementId,
offset_x: f32,
offset_y: f32,
) {
let global_x = offset_x + node.get_left();
let global_y = offset_y + node.get_top();
tree.set_layout(
id,
ComputedLayout {
x: global_x,
y: global_y,
width: node.get_width(),
height: node.get_height(),
overflow_scroll: node.style.overflow_scroll,
},
);
let children_ids = tree.children_ref(id);
let (child_origin_x, child_origin_y) = if node.style.overflow_scroll {
let (ox, oy) = tree.scroll_offset(id);
let cw = node.style.content_width.unwrap_or_else(|| {
node.children.iter().fold(0.0f32, |m, c| {
m.max(
c.get_left() + c.get_width() + c.get_layout_end_margin(FlexDirection::Row),
)
})
});
let ch = node.style.content_height.unwrap_or_else(|| {
node.children.iter().fold(0.0f32, |m, c| {
m.max(
c.get_top()
+ c.get_height()
+ c.get_layout_end_margin(FlexDirection::Column),
)
})
});
let vw = node.get_width();
let vh = node.get_height();
let max_x = (cw - vw).max(0.0);
let max_y = (ch - vh).max(0.0);
let nox = ox.clamp(0.0, max_x);
let noy = oy.clamp(0.0, max_y);
tree.set_scroll_offset(id, (nox, noy));
tree.set_content_size(id, (cw, ch));
(global_x - nox, global_y - noy)
} else {
(global_x, global_y)
};
for (child_fn, child_id) in node.children.iter().zip(children_ids.iter()) {
Self::write_layout(child_fn, tree, *child_id, child_origin_x, child_origin_y);
}
}
}