⎇ devin/1784819829-clamp-overlarge-grids
tests/common/src/lib.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
Represent generated tests as XML rather than Rust code (#926)
1use taffy::{AvailableSpace, NodeId, Size, Style, TaffyTree};
2
3/// Creates a `TaffyTree` that uses `TestNodeContext`. The purpose of this function is
4/// to allow `TaffyTree` to be monomophised once in this crate rather than separately for
5/// each test crate.
6pub fn new_test_tree() -> TaffyTree<TestNodeContext> {
7 TaffyTree::new()
8}
9
10/// A sharednode context for tests which means that tests compiled with separate crates
11/// and using different styles of measure function. This saves on compile time when running tests.
12#[derive(Debug, Clone)]
13pub struct TestNodeContext {
14 /// How many times the measure function has been called
15 pub count: usize,
16 /// The measurement data to compute the intrinsic size from
17 pub measure_data: TestMeasureData,
18}
19
20impl TestNodeContext {
21 /// Create a new `TestNodeContext` from `TestMeasureData`
22 pub const fn new(measure_data: TestMeasureData) -> Self {
23 Self { count: 0, measure_data }
24 }
25
26 /// Create a `TestNodeContext` for a zero-sized node
27 pub const fn zero() -> Self {
28 Self::new(TestMeasureData::Zero)
29 }
30
31 /// Create a `TestNodeContext` for a fixed-sized node
32 pub const fn fixed(width: f32, height: f32) -> Self {
33 Self::new(TestMeasureData::Fixed(Size { width, height }))
34 }
35
36 /// Create a `TestNodeContext` for a node with a width and aspect-ratio
37 pub const fn aspect_ratio(width: f32, height_ratio: f32) -> Self {
38 let data = AspectRatioMeasureData { width, height_ratio };
39 Self::new(TestMeasureData::AspectRatio(data))
40 }
41
42 /// Create a `TestNodeContext` for a node with text using the Ahem font
43 pub const fn ahem_text(text_content: String, writing_mode: WritingMode) -> Self {
44 let data = AhemTextMeasureData { text_content, writing_mode };
45 Self::new(TestMeasureData::AhemText(data))
46 }
47}
48
49/// The measurement data for the node
50#[derive(Debug, Clone)]
51pub enum TestMeasureData {
52 /// A zero-sized node
53 Zero,
54 /// A node with a fixed size
55 Fixed(Size<f32>),
56 /// A node with a fixed size
57 AspectRatio(AspectRatioMeasureData),
58 /// A node with text using the Ahem font
59 AhemText(AhemTextMeasureData),
60}
61
62/// A measure function for tests that works with `TestNodeContext`
63pub fn test_measure_function(
64 known_dimensions: Size<Option<f32>>,
65 available_space: Size<AvailableSpace>,
66 _node_id: NodeId,
67 context: Option<&mut TestNodeContext>,
68 _style: &Style,
69) -> Size<f32> {
70 if let Size { width: Some(width), height: Some(height) } = known_dimensions {
71 return Size { width, height };
72 }
73
74 let Some(context) = context else { return known_dimensions.map(|d| d.unwrap_or(0.0)) };
75
76 // Increment count
77 context.count += 1;
78
79 let compute_size = match &context.measure_data {
80 TestMeasureData::Zero => Size::ZERO,
81 TestMeasureData::Fixed(size) => *size,
82 TestMeasureData::AspectRatio(data) => data.measure(known_dimensions),
83 TestMeasureData::AhemText(data) => data.measure(known_dimensions, available_space),
84 };
85
86 Size {
87 width: known_dimensions.width.unwrap_or(compute_size.width),
88 height: known_dimensions.height.unwrap_or(compute_size.height),
89 }
90}
91
92/// Measure data for nodes that returns results based on an intrinsic aspect ratio
93#[derive(Debug, Copy, Clone)]
94pub struct AspectRatioMeasureData {
95 width: f32,
96 height_ratio: f32,
97}
98impl AspectRatioMeasureData {
99 fn measure(&self, known_dimensions: Size<Option<f32>>) -> Size<f32> {
100 let width = known_dimensions.width.unwrap_or(self.width);
101 let height = known_dimensions.height.unwrap_or(width * self.height_ratio);
102 Size { width, height }
103 }
104}
105
106/// Whether text is horizontal or vertical
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
108pub enum WritingMode {
109 /// Horizontal text
110 #[default]
111 Horizontal,
112 /// Vertical text
113 Vertical,
114}
115
116impl core::str::FromStr for WritingMode {
117 type Err = ();
118 fn from_str(s: &str) -> Result<Self, Self::Err> {
119 if s.contains("vertical") {
120 Ok(Self::Vertical)
121 } else if s.contains("horizontal") {
122 Ok(Self::Horizontal)
123 } else {
124 Err(())
125 }
126 }
127}
128
129/// Measure data for nodes contain text using the Ahem testing font
130#[derive(Debug, Clone)]
131pub struct AhemTextMeasureData {
132 /// The text string
133 pub text_content: String,
134 /// The writing mode
135 pub writing_mode: WritingMode,
136}
137impl AhemTextMeasureData {
138 fn measure(
139 &self,
140 known_dimensions: taffy::Size<Option<f32>>,
141 available_space: taffy::Size<taffy::AvailableSpace>,
142 ) -> taffy::Size<f32> {
143 use taffy::prelude::*;
144 use taffy::AbsoluteAxis;
145
146 const ZWS: char = '\u{200B}';
147 const H_WIDTH: f32 = 10.0;
148 const H_HEIGHT: f32 = 10.0;
149
150 let inline_axis = match self.writing_mode {
151 WritingMode::Horizontal => AbsoluteAxis::Horizontal,
152 WritingMode::Vertical => AbsoluteAxis::Vertical,
153 };
154 let block_axis = inline_axis.other_axis();
155 let lines: Vec<&str> = self.text_content.split(ZWS).collect();
156
157 if lines.is_empty() {
158 return Size::ZERO;
159 }
160
161 let min_line_length: usize = lines.iter().map(|line| line.len()).max().unwrap_or(0);
162 let max_line_length: usize = lines.iter().map(|line| line.len()).sum();
163 let inline_size = known_dimensions
164 .get_abs(inline_axis)
165 .unwrap_or_else(|| match available_space.get_abs(inline_axis) {
166 AvailableSpace::MinContent => min_line_length as f32 * H_WIDTH,
167 AvailableSpace::MaxContent => max_line_length as f32 * H_WIDTH,
168 AvailableSpace::Definite(inline_size) => inline_size.min(max_line_length as f32 * H_WIDTH),
169 })
170 .max(min_line_length as f32 * H_WIDTH);
171 let block_size = known_dimensions.get_abs(block_axis).unwrap_or_else(|| {
172 let inline_line_length = (inline_size / H_WIDTH).floor() as usize;
173 let mut line_count = 1;
174 let mut current_line_length = 0;
175 for line in &lines {
176 if current_line_length + line.len() > inline_line_length {
177 if current_line_length > 0 {
178 line_count += 1
179 };
180 current_line_length = line.len();
181 } else {
182 current_line_length += line.len();
183 };
184 }
185 (line_count as f32) * H_HEIGHT
186 });
187
188 match self.writing_mode {
189 WritingMode::Horizontal => Size { width: inline_size, height: block_size },
190 WritingMode::Vertical => Size { width: block_size, height: inline_size },
191 }
192 }
193}