1use crate::flex::*;
4use crate::util::*;
5
6const MAX_MEASURES_COUNT: usize = 6;
7
8#[derive(Debug, Clone)]
9pub struct MeasureResult {
10 pub available_size: TaitankSize,
11 pub result_size: TaitankSize,
12 pub width_measure_mode: MeasureMode,
13 pub height_measure_mode: MeasureMode,
14 pub layout_action: FlexLayoutAction,
15}
16
17#[derive(Debug)]
18pub struct TaitankLayoutCache {
19 cached_layout: Option<MeasureResult>,
20 cached_measures: Vec<MeasureResult>,
21 next_measure_index: usize,
22}
23
24impl Default for TaitankLayoutCache {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl TaitankLayoutCache {
31 pub fn new() -> Self {
32 let mut cache = Self {
33 cached_layout: None,
34 cached_measures: Vec::with_capacity(MAX_MEASURES_COUNT),
35 next_measure_index: 0,
36 };
37 cache.init_cache();
38 cache
39 }
40
41 fn init_cache(&mut self) {
42 self.cached_layout = None;
43 self.cached_measures.clear();
44 self.cached_measures.resize(
45 MAX_MEASURES_COUNT,
46 MeasureResult {
47 available_size: TaitankSize::new(VALUE_UNDEFINED, VALUE_UNDEFINED),
48 result_size: TaitankSize::new(VALUE_UNDEFINED, VALUE_UNDEFINED),
49 width_measure_mode: MeasureMode::Undefined,
50 height_measure_mode: MeasureMode::Undefined,
51 layout_action: FlexLayoutAction::Layout,
52 },
53 );
54 self.next_measure_index = 0;
55 }
56
57 pub fn cache_result(
58 &mut self,
59 available_size: TaitankSize,
60 result_size: TaitankSize,
61 measure_mode: TaitankSizeMode,
62 layout_action: FlexLayoutAction,
63 ) {
64 if layout_action == FlexLayoutAction::Layout {
65 self.cached_layout = Some(MeasureResult {
66 available_size,
67 result_size,
68 width_measure_mode: measure_mode.width_measure_mode,
69 height_measure_mode: measure_mode.height_measure_mode,
70 layout_action,
71 });
72 } else {
73 if self.next_measure_index < self.cached_measures.len() {
74 self.cached_measures[self.next_measure_index] = MeasureResult {
75 available_size,
76 result_size,
77 width_measure_mode: measure_mode.width_measure_mode,
78 height_measure_mode: measure_mode.height_measure_mode,
79 layout_action,
80 };
81 } else {
82 self.cached_measures.push(MeasureResult {
83 available_size,
84 result_size,
85 width_measure_mode: measure_mode.width_measure_mode,
86 height_measure_mode: measure_mode.height_measure_mode,
87 layout_action,
88 });
89 }
90 self.next_measure_index = (self.next_measure_index + 1) % MAX_MEASURES_COUNT;
91 }
92 }
93
94 fn size_is_exact_and_matches_old_measured_size(
95 size_mode: MeasureMode,
96 size: f32,
97 last_result_size: f32,
98 ) -> bool {
99 size_mode == MeasureMode::Exactly && float_is_equal(size, last_result_size)
100 }
101
102 fn old_size_is_undefined_and_still_fits(
103 size_mode: MeasureMode,
104 size: f32,
105 last_size_mode: MeasureMode,
106 last_result_size: f32,
107 ) -> bool {
108 size_mode == MeasureMode::AtMost
109 && last_size_mode == MeasureMode::Undefined
110 && (size >= last_result_size || float_is_equal(size, last_result_size))
111 }
112
113 fn new_measure_size_is_stricter_and_still_valid(
114 size_mode: MeasureMode,
115 size: f32,
116 last_size_mode: MeasureMode,
117 last_size: f32,
118 last_result_size: f32,
119 ) -> bool {
120 last_size_mode == MeasureMode::AtMost
121 && size_mode == MeasureMode::AtMost
122 && last_size > size
123 && (last_result_size <= size || float_is_equal(size, last_result_size))
124 }
125
126 fn use_measure_cache_if_possible(
127 &self,
128 available_size: TaitankSize,
129 measure_mode: TaitankSizeMode,
130 layout_action: FlexLayoutAction,
131 is_measure_node: bool,
132 ) -> Option<&MeasureResult> {
133 if self.next_measure_index == 0 {
134 return None;
135 }
136
137 let check_count = self.next_measure_index.min(self.cached_measures.len());
138 for i in 0..check_count {
139 let cache_measure = &self.cached_measures[i];
140 if layout_action != cache_measure.layout_action && !is_measure_node {
141 continue;
142 }
143
144 let mut width_can_use = cache_measure.width_measure_mode
145 == measure_mode.width_measure_mode
146 && float_is_equal(cache_measure.available_size.width, available_size.width);
147
148 if is_measure_node {
149 if !width_can_use {
150 width_can_use = Self::size_is_exact_and_matches_old_measured_size(
151 measure_mode.width_measure_mode,
152 available_size.width,
153 cache_measure.result_size.width,
154 );
155 }
156 if !width_can_use {
157 width_can_use = Self::old_size_is_undefined_and_still_fits(
158 measure_mode.width_measure_mode,
159 available_size.width,
160 cache_measure.width_measure_mode,
161 cache_measure.result_size.width,
162 );
163 }
164 if !width_can_use {
165 width_can_use = Self::new_measure_size_is_stricter_and_still_valid(
166 measure_mode.width_measure_mode,
167 available_size.width,
168 cache_measure.width_measure_mode,
169 cache_measure.available_size.width,
170 cache_measure.result_size.width,
171 );
172 }
173 }
174
175 let mut height_can_use = cache_measure.height_measure_mode
176 == measure_mode.height_measure_mode
177 && float_is_equal(cache_measure.available_size.height, available_size.height);
178
179 if is_measure_node {
180 if !height_can_use {
181 height_can_use = Self::size_is_exact_and_matches_old_measured_size(
182 measure_mode.height_measure_mode,
183 available_size.height,
184 cache_measure.result_size.height,
185 );
186 }
187 if !height_can_use {
188 height_can_use = Self::old_size_is_undefined_and_still_fits(
189 measure_mode.height_measure_mode,
190 available_size.height,
191 cache_measure.height_measure_mode,
192 cache_measure.result_size.height,
193 );
194 }
195 if !height_can_use {
196 height_can_use = Self::new_measure_size_is_stricter_and_still_valid(
197 measure_mode.height_measure_mode,
198 available_size.height,
199 cache_measure.height_measure_mode,
200 cache_measure.available_size.height,
201 cache_measure.result_size.height,
202 );
203 }
204 }
205
206 if width_can_use && height_can_use {
207 return Some(cache_measure);
208 }
209 }
210
211 None
212 }
213
214 fn use_layout_cache_if_possible(
215 &self,
216 available_size: TaitankSize,
217 measure_mode: TaitankSizeMode,
218 ) -> Option<&MeasureResult> {
219 if let Some(ref cached) = self.cached_layout {
220 if is_undefined(cached.available_size.width)
221 || is_undefined(cached.available_size.height)
222 {
223 return None;
224 }
225 if size_is_equal(cached.available_size, available_size)
226 && cached.width_measure_mode == measure_mode.width_measure_mode
227 && cached.height_measure_mode == measure_mode.height_measure_mode
228 {
229 return Some(cached);
230 }
231 }
232 None
233 }
234
235 pub fn get_cached_measure_result(
236 &self,
237 available_size: TaitankSize,
238 measure_mode: TaitankSizeMode,
239 layout_action: FlexLayoutAction,
240 is_measure_node: bool,
241 ) -> Option<&MeasureResult> {
242 if is_measure_node {
243 if let Some(result) = self.use_layout_cache_if_possible(available_size, measure_mode) {
244 return Some(result);
245 }
246 self.use_measure_cache_if_possible(
247 available_size,
248 measure_mode,
249 layout_action,
250 is_measure_node,
251 )
252 } else if layout_action == FlexLayoutAction::Layout {
253 self.use_layout_cache_if_possible(available_size, measure_mode)
254 } else {
255 self.use_measure_cache_if_possible(
256 available_size,
257 measure_mode,
258 layout_action,
259 is_measure_node,
260 )
261 }
262 }
263
264 pub fn get_cached_layout(&self) -> Option<&MeasureResult> {
265 self.cached_layout.as_ref()
266 }
267
268 pub fn clear_cache(&mut self) {
269 self.init_cache();
270 }
271}