Call measure function on leaf nodes even if the node does not have context (#598)
1mod common {
2 pub mod image;
3 pub mod text;
4}
5use common::image::{image_measure_function, ImageContext};
6use common::text::{text_measure_function, FontMetrics, TextContext, WritingMode, LOREM_IPSUM};
7use taffy::{
8 compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_leaf_layout, compute_root_layout,
9 prelude::*, Cache, Layout, Style,
10};
11
12#[derive(Debug, Copy, Clone)]
13#[allow(dead_code)]
14enum NodeKind {
15 Flexbox,
16 Grid,
17 Text,
18 Image,
19}
20
21struct Node {
22 kind: NodeKind,
23 style: Style,
24 text_data: Option<TextContext>,
25 image_data: Option<ImageContext>,
26 cache: Cache,
27 layout: Layout,
28 children: Vec<Node>,
29}
30
31impl Default for Node {
32 fn default() -> Self {
33 Node {
34 kind: NodeKind::Flexbox,
35 style: Style::default(),
36 text_data: None,
37 image_data: None,
38 cache: Cache::new(),
39 layout: Layout::with_order(0),
40 children: Vec::new(),
41 }
42 }
43}
44
45#[allow(dead_code)]
46impl Node {
47 pub fn new_row(style: Style) -> Node {
48 Node {
49 kind: NodeKind::Flexbox,
50 style: Style { display: Display::Flex, flex_direction: FlexDirection::Row, ..style },
51 ..Node::default()
52 }
53 }
54 pub fn new_column(style: Style) -> Node {
55 Node {
56 kind: NodeKind::Flexbox,
57 style: Style { display: Display::Flex, flex_direction: FlexDirection::Column, ..style },
58 ..Node::default()
59 }
60 }
61 pub fn new_grid(style: Style) -> Node {
62 Node { kind: NodeKind::Grid, style: Style { display: Display::Grid, ..style }, ..Node::default() }
63 }
64 pub fn new_text(style: Style, text_data: TextContext) -> Node {
65 Node { kind: NodeKind::Text, style, text_data: Some(text_data), ..Node::default() }
66 }
67 pub fn new_image(style: Style, image_data: ImageContext) -> Node {
68 Node { kind: NodeKind::Image, style, image_data: Some(image_data), ..Node::default() }
69 }
70 pub fn append_child(&mut self, node: Node) {
71 self.children.push(node);
72 }
73
74 pub fn compute_layout(&mut self, available_space: Size<AvailableSpace>) {
75 compute_root_layout(self, NodeId::from(usize::MAX), available_space);
76 }
77
78 /// The methods on LayoutPartialTree need to be able to access:
79 ///
80 /// - The node being laid out
81 /// - Direct children of the node being laid out
82 ///
83 /// Each must have an ID. For children we simply use it's index. For the node itself
84 /// we use usize::MAX on the assumption that there will never be that many children.
85 fn node_from_id(&self, node_id: NodeId) -> &Node {
86 let idx = usize::from(node_id);
87 if idx == usize::MAX {
88 self
89 } else {
90 &self.children[idx]
91 }
92 }
93
94 fn node_from_id_mut(&mut self, node_id: NodeId) -> &mut Node {
95 let idx = usize::from(node_id);
96 if idx == usize::MAX {
97 self
98 } else {
99 &mut self.children[idx]
100 }
101 }
102}
103
104struct ChildIter(std::ops::Range<usize>);
105impl Iterator for ChildIter {
106 type Item = NodeId;
107 fn next(&mut self) -> Option<Self::Item> {
108 self.0.next().map(|idx| NodeId::from(idx))
109 }
110}
111
112impl taffy::TraversePartialTree for Node {
113 type ChildIter<'a> = ChildIter;
114
115 fn child_ids(&self, _node_id: NodeId) -> Self::ChildIter<'_> {
116 ChildIter(0..self.children.len())
117 }
118
119 fn child_count(&self, _node_id: NodeId) -> usize {
120 self.children.len()
121 }
122
123 fn get_child_id(&self, _node_id: NodeId, index: usize) -> NodeId {
124 NodeId::from(index)
125 }
126}
127
128impl taffy::LayoutPartialTree for Node {
129 fn get_style(&self, node_id: NodeId) -> &Style {
130 &self.node_from_id(node_id).style
131 }
132
133 fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout) {
134 self.node_from_id_mut(node_id).layout = *layout
135 }
136
137 fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache {
138 &mut self.node_from_id_mut(node_id).cache
139 }
140
141 fn compute_child_layout(&mut self, node_id: NodeId, inputs: taffy::tree::LayoutInput) -> taffy::tree::LayoutOutput {
142 compute_cached_layout(self, node_id, inputs, |parent, node_id, inputs| {
143 let node = parent.node_from_id_mut(node_id);
144 let font_metrics = FontMetrics { char_width: 10.0, char_height: 10.0 };
145
146 match node.kind {
147 NodeKind::Flexbox => compute_flexbox_layout(node, node_id, inputs),
148 NodeKind::Grid => compute_grid_layout(node, node_id, inputs),
149 NodeKind::Text => compute_leaf_layout(inputs, &node.style, |known_dimensions, available_space| {
150 text_measure_function(
151 known_dimensions,
152 available_space,
153 node.text_data.as_ref().unwrap(),
154 &font_metrics,
155 )
156 }),
157 NodeKind::Image => compute_leaf_layout(inputs, &node.style, |known_dimensions, _available_space| {
158 image_measure_function(known_dimensions, node.image_data.as_ref().unwrap())
159 }),
160 }
161 })
162 }
163}
164
165fn main() -> Result<(), taffy::TaffyError> {
166 let mut root = Node::new_column(Style::DEFAULT);
167
168 let text_node = Node::new_text(
169 Style::default(),
170 TextContext { text_content: LOREM_IPSUM.into(), writing_mode: WritingMode::Horizontal },
171 );
172 root.append_child(text_node);
173
174 let image_node = Node::new_image(Style::default(), ImageContext { width: 400.0, height: 300.0 });
175 root.append_child(image_node);
176
177 // Compute layout
178 root.compute_layout(Size::MAX_CONTENT);
179
180 Ok(())
181}