tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 520HTML 244Markdown 7TOML 41339 Definitions

⎇ 0.5.x examples/custom_tree_owned_partial.rs Rust · 181 lines · 5.6 KB History Who touched what raw

Call measure function on leaf nodes even if the node does not have context (#598) 563d5dcee · Nico Burns ·

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}