use crate as deft;
use crate::style::YogaNode;
use deft_macros::mrc_object;
use yoga::{
Align, Context, Direction, Display, Edge, FlexDirection, Justify, MeasureMode, Node, NodeRef,
Overflow, PositionType, Size, StyleUnit, Wrap,
};
use crate::layout::{LayoutNode};
pub struct MeasureParams {
pub width: f32,
pub width_mode: MeasureMode,
pub height: f32,
pub height_mode: MeasureMode,
}
#[mrc_object]
struct CustomMeasureFn {
callback: Box<dyn FnMut(MeasureParams) -> Size>,
}
extern "C" fn custom_measure_shadow(
node_ref: NodeRef,
width: f32,
_width_mode: MeasureMode,
height: f32,
_height_mode: MeasureMode,
) -> Size {
if let Some(ctx) = Node::get_context_mut(&node_ref) {
if let Some(node_item) = ctx.downcast_mut::<NodeItem>() {
node_item.calculate_shadow_layout(width, height, Direction::LTR);
let (width, height) = match node_item.layout.get_shadow_size() {
Ok(r) => r.unwrap_or_default(),
Err(_) => (0.0, 0.0)
};
return yoga::Size { width, height };
}
}
unreachable!()
}
extern "C" fn custom_measure_fn(
node_ref: NodeRef,
width: f32,
width_mode: MeasureMode,
height: f32,
height_mode: MeasureMode,
) -> Size {
if let Some(ctx) = Node::get_context_mut(&node_ref) {
if let Some(func) = ctx.downcast_mut::<CustomMeasureFn>() {
let params = MeasureParams {
width,
width_mode,
height,
height_mode,
};
return (func.callback)(params);
}
}
unreachable!()
}
#[mrc_object]
pub struct NodeItem {
pub layout: LayoutNode,
pub padding_top: StyleUnit,
pub padding_bottom: StyleUnit,
pub padding_left: StyleUnit,
pub padding_right: StyleUnit,
pub justify_content: Justify,
pub flex_direction: FlexDirection,
pub align_content: Align,
pub align_items: Align,
pub flex_wrap: Wrap,
pub column_gap: f32,
pub row_gap: f32,
pub position_type: PositionType,
pub display: Display,
pub width: StyleUnit,
pub height: StyleUnit,
pub max_width: StyleUnit,
pub max_height: StyleUnit,
pub min_width: StyleUnit,
pub min_height: StyleUnit,
pub margin_top: StyleUnit,
pub margin_right: StyleUnit,
pub margin_bottom: StyleUnit,
pub margin_left: StyleUnit,
pub flex_basis: StyleUnit,
pub top: StyleUnit,
pub right: StyleUnit,
pub bottom: StyleUnit,
pub left: StyleUnit,
pub overflow: Overflow,
pub flex: f32,
pub flex_grow: f32,
pub flex_shrink: f32,
pub align_self: Align,
pub direction: Direction,
pub border_top: f32,
pub border_right: f32,
pub border_bottom: f32,
pub border_left: f32,
pub measure_fn: Option<CustomMeasureFn>,
pub children: Vec<NodeItem>,
}
impl NodeItem {
pub fn new() -> Self {
let mut std_node = YogaNode::new();
let layout = LayoutNode::new(YogaNode::new());
NodeItemData {
layout,
padding_top: std_node.get_style_padding_top(),
padding_bottom: std_node.get_style_padding_bottom(),
padding_left: std_node.get_style_padding_left(),
padding_right: std_node.get_style_padding_right(),
justify_content: std_node.get_justify_content(),
flex_direction: std_node.get_flex_direction(),
align_content: std_node.get_align_content(),
align_items: std_node.get_align_items(),
flex_wrap: std_node.get_flex_wrap(),
column_gap: std_node.get_column_gap(),
row_gap: std_node.get_row_gap(),
position_type: PositionType::Static,
display: Display::Flex,
width: std_node.get_style_width(),
height: std_node.get_style_height(),
max_width: std_node.get_style_max_width(),
max_height: std_node.get_style_max_height(),
min_width: std_node.get_style_min_width(),
min_height: std_node.get_style_min_height(),
margin_top: std_node.get_style_margin_top(),
margin_right: std_node.get_style_margin_right(),
margin_bottom: std_node.get_style_margin_bottom(),
margin_left: std_node.get_style_margin_left(),
flex_basis: std_node.get_flex_basis(),
top: std_node.get_style_position_top(),
right: std_node.get_style_position_right(),
bottom: std_node.get_style_position_bottom(),
left: std_node.get_style_position_left(),
overflow: Overflow::Hidden,
flex: std_node.get_flex(),
flex_grow: f32::NAN,
flex_shrink: f32::NAN,
align_self: std_node.get_align_self(),
direction: std_node.get_style_direction(),
border_top: 0.0,
border_right: 0.0,
border_bottom: 0.0,
border_left: 0.0,
measure_fn: None,
children: vec![],
}
.to_ref()
}
pub fn is_layout_boundary(&self) -> bool {
match self.overflow {
Overflow::Visible => false,
Overflow::Hidden => false,
Overflow::Scroll => true,
}
}
pub fn calculate_layout(
&mut self,
available_width: f32,
available_height: f32,
direction: Direction,
) {
let _ = self.layout.calculate_layout(available_width, available_height, direction);
}
pub fn calculate_shadow_layout(
&mut self,
available_width: f32,
available_height: f32,
direction: Direction,
) {
let _ = self.layout.calculate_shadow_layout(available_width, available_height, direction);
}
pub fn build_yn(&mut self) {
for c in &mut self.children {
c.build_yn();
}
let mut n = YogaNode::new();
n.set_position_type(self.position_type);
n.set_display(self.display);
n.set_width(self.width);
n.set_height(self.height);
n.set_max_width(self.max_width);
n.set_max_height(self.max_height);
n.set_min_width(self.min_width);
n.set_min_height(self.min_height);
n.set_margin(Edge::Top, self.margin_top);
n.set_margin(Edge::Bottom, self.margin_bottom);
n.set_margin(Edge::Left, self.margin_left);
n.set_margin(Edge::Right, self.margin_right);
n.set_flex_basis(self.flex_basis);
n.set_position(Edge::Top, self.top);
n.set_position(Edge::Bottom, self.bottom);
n.set_position(Edge::Left, self.left);
n.set_position(Edge::Right, self.right);
n.set_overflow(self.overflow);
n.set_flex(self.flex);
n.set_flex_grow(self.flex_grow);
n.set_flex_shrink(self.flex_shrink);
n.set_align_self(self.align_self);
n.set_border(Edge::Top, self.border_top);
n.set_border(Edge::Right, self.border_right);
n.set_border(Edge::Bottom, self.border_bottom);
n.set_border(Edge::Left, self.border_left);
let _ = self.layout.update_yn(n.clone());
if self.is_layout_boundary() {
n.set_context(Some(Context::new(self.clone())));
n.set_measure_func(Some(custom_measure_shadow));
let mut s = YogaNode::new();
self.apply_inner_style(&mut s);
self.bind_children(&mut s);
let _ = self.layout.update_shadow_yn(Some(s));
} else {
if let Some(measure_func) = self.measure_fn.clone() {
n.set_context(Some(Context::new(measure_func)));
n.set_measure_func(Some(custom_measure_fn));
}
self.apply_inner_style(&mut n);
self.bind_children(&mut n);
let _ = self.layout.update_shadow_yn(None);
}
}
fn apply_inner_style(&self, container: &mut YogaNode) {
container.set_padding(Edge::Top, self.padding_top);
container.set_padding(Edge::Bottom, self.padding_bottom);
container.set_padding(Edge::Left, self.padding_left);
container.set_padding(Edge::Right, self.padding_right);
container.set_justify_content(self.justify_content);
container.set_align_items(self.align_items);
container.set_align_content(self.align_content);
container.set_flex_direction(self.flex_direction);
container.set_align_items(self.align_items);
container.set_flex_wrap(self.flex_wrap);
container.set_column_gap(self.column_gap);
container.set_row_gap(self.row_gap);
container.set_direction(self.direction);
}
fn bind_children(&self, n: &mut YogaNode) {
let mut idx = 0;
for c in &self.children {
if let Ok(mut yn) = c.layout.get_yn() {
n.insert_child(&mut yn, idx);
idx += 1;
}
}
}
pub fn set_measure_func<C: 'static, F: FnMut(&mut C, MeasureParams) -> Size + 'static>(
&mut self,
mut context: C,
mut measure_func: F,
) {
let func = CustomMeasureFnData {
callback: Box::new(move |params| measure_func(&mut context, params)),
}
.to_ref();
self.measure_fn = Some(func);
}
}
#[cfg(test)]
mod tests {
use crate::style::node_item::NodeItem;
use ordered_float::OrderedFloat;
use yoga::{Direction, Overflow, StyleUnit};
#[test]
fn test_layout() {
let mut root = NodeItem::new();
let mut child1 = NodeItem::new();
let mut child2 = NodeItem::new();
child1.width = StyleUnit::Percent(OrderedFloat(50.0));
child1.height = StyleUnit::Point(OrderedFloat(10.0));
child2.width = StyleUnit::Percent(OrderedFloat(50.0));
child2.height = StyleUnit::Point(OrderedFloat(20.0));
root.layout.calculate_layout(100.0, 100.0, Direction::LTR).unwrap();
let layout = root.layout.get_layout().unwrap();
assert_eq!(layout.width(), 100.0);
assert_eq!(layout.height(), 100.0);
}
#[test]
fn test_overflow() {
let mut root = NodeItem::new();
let mut child1 = NodeItem::new();
let mut child2 = NodeItem::new();
root.overflow = Overflow::Scroll;
child1.width = StyleUnit::Percent(OrderedFloat(50.0));
child1.height = StyleUnit::Point(OrderedFloat(1000.0));
child2.width = StyleUnit::Percent(OrderedFloat(50.0));
child2.height = StyleUnit::Point(OrderedFloat(20.0));
root.calculate_layout(100.0, 100.0, Direction::LTR);
root.calculate_shadow_layout(100.0, 100.0, Direction::LTR);
}
}