1#![allow(non_snake_case)]
2
3use std::cell::RefCell;
4use std::collections::HashMap;
5use std::hash::{Hash, Hasher};
6use std::rc::Rc;
7
8use repose_core::*;
9use repose_tree::{LayoutConstraints, NodeId, ViewTree};
10use rustc_hash::{FxHashMap, FxHasher};
11use taffy::TaffyTree;
12use taffy::prelude::*;
13
14use crate::Interactions;
15use crate::textfield::TextFieldState;
16
17use super::*;
18impl Default for LayoutEngine {
19 fn default() -> Self {
20 Self::new()
21 }
22}
23
24impl LayoutEngine {
25 pub fn layout_frame(
26 &mut self,
27 root: &View,
28 size_px: (u32, u32),
29 textfield_states: &HashMap<u64, Rc<RefCell<TextFieldState>>>,
30 interactions: &Interactions,
31 focused: Option<u64>,
32 ) -> (Scene, Vec<HitRegion>, Vec<SemNode>) {
33 let start = web_time::Instant::now();
34 repose_text::begin_frame();
35 self.stats = LayoutStats::default();
36
37 self.focus_group_stack.clear();
39
40 let locals_stamp = Self::locals_stamp();
42 let locals_changed = self.last_locals_stamp != Some(locals_stamp);
43 if locals_changed {
44 self.layout_valid = false;
45 self.paint_cache.clear();
46 self.text_cache.clear();
47 }
48
49 let density_scale = locals::effective_density_scale();
51 let max_w_dp = size_px.0 as f32 / density_scale;
52 let max_h_dp = size_px.1 as f32 / density_scale;
53 self.tree
54 .set_subcompose_scope(repose_core::SubcomposeScope::new(
55 0.0, max_w_dp, 0.0, max_h_dp,
56 ));
57 let root_node_id = self.tree.update(root);
58 self.stats.tree = self.tree.stats.clone();
59
60 self.build_scope_maps();
62
63 let size_changed = self.last_size_px != Some(size_px);
65 let class = locals::calculate_window_size_class(size_px.0, size_px.1, density_scale);
71 if class != locals::window_size_class() {
72 locals::set_window_size_class_default(class);
73 }
74 let inv_density = if density_scale > 0.0 {
75 1.0 / density_scale
76 } else {
77 1.0
78 };
79 locals::set_window_container_size(
80 size_px.0 as f32 * inv_density,
81 size_px.1 as f32 * inv_density,
82 );
83 let has_tree_mutation =
84 !self.tree.dirty_nodes().is_empty() || !self.tree.removed_ids.is_empty();
85 let mut need_layout =
86 size_changed || !self.layout_valid || has_tree_mutation || locals_changed;
87
88 if locals_changed {
90 let all_ids: Vec<NodeId> = self.tree.iter_with_ids().map(|(id, _)| id).collect();
91 for id in all_ids {
92 self.tree.mark_dirty(id);
93 }
94 for st in self.scope_trees.values_mut() {
95 st.valid = false;
96 }
97 need_layout = true;
98 }
99
100 if size_changed {
102 for &node_id in self.text_cache.keys() {
104 if let Some(&taffy_id) = self.taffy_map.get(&node_id) {
105 let _ = self.taffy.mark_dirty(taffy_id);
106 }
107 }
108 self.text_cache.clear();
109 for st in self.scope_trees.values_mut() {
111 for &node_id in st.text_cache.keys() {
112 if let Some(&tid) = st.taffy_map.get(&node_id) {
113 let _ = st.taffy.mark_dirty(tid);
114 }
115 }
116 st.text_cache.clear();
117 }
118 }
119 if locals_changed {
120 for st in self.scope_trees.values_mut() {
121 st.text_cache.clear();
122 }
123 }
124
125 let px = |dp_val: f32| dp_to_px(dp_val);
127 let font_px = |dp_font: f32| dp_to_px(dp_font) * locals::text_scale().0;
128
129 self.sync_scope_trees(&font_px);
132 self.sync_taffy_tree(root_node_id, &font_px);
134
135 let taffy_root = self.taffy_map.get(&root_node_id).copied();
137 if let Some(taffy_root) = taffy_root {
138 if need_layout {
139 if let Ok(mut style) = self.taffy.style(taffy_root).cloned() {
140 style.size.width = length(size_px.0 as f32);
141 style.size.height = length(size_px.1 as f32);
142 let _ = self.taffy.set_style(taffy_root, style);
143 }
144
145 let available = taffy::geometry::Size {
146 width: AvailableSpace::Definite(size_px.0 as f32),
147 height: AvailableSpace::Definite(size_px.1 as f32),
148 };
149
150 Self::run_measure_pass(
151 &mut self.taffy,
152 taffy_root,
153 available,
154 &self.tree,
155 &mut self.text_cache,
156 &self.reverse_map,
157 &self.scope_root_map,
158 &self.node_to_scope,
159 &mut self.scope_trees,
160 &font_px,
161 &px,
162 );
163
164 for (&node_id, &taffy_id) in &self.taffy_map {
166 if let Ok(layout) = self.taffy.layout(taffy_id) {
167 let dp_w = layout.size.width / density_scale;
168 let dp_h = layout.size.height / density_scale;
169 let rect = repose_core::Rect {
170 x: 0.0,
171 y: 0.0,
172 w: dp_w,
173 h: dp_h,
174 };
175 self.tree
176 .set_layout(node_id, rect, rect, LayoutConstraints::default());
177 }
178 }
179
180 self.last_locals_stamp = Some(locals_stamp);
181
182 self.layout_valid = true;
183 self.last_size_px = Some(size_px);
184 self.stats.layout_misses += 1;
185 } else {
186 self.stats.layout_hits += 1;
187 }
188 }
189 self.stats.layout_time_ms = (web_time::Instant::now() - start).as_secs_f32() * 1000.0;
190
191 self.walk_tick(root_node_id);
193
194 let t_paint = web_time::Instant::now();
196 self.focus_interaction_sources.clear();
197 let (scene, hits, sems) = self.paint(
198 root_node_id,
199 textfield_states,
200 interactions,
201 focused,
202 &font_px,
203 );
204 self.stats.paint_time_ms = (web_time::Instant::now() - t_paint).as_secs_f32() * 1000.0;
205
206 if self.prev_focused != focused {
208 if let Some(old_id) = self.prev_focused {
209 if let Some(cb) = self.focus_callbacks.get(&old_id) {
210 (cb)(false);
211 }
212 if let Some(src) = self.focus_interaction_sources.get(&old_id) {
213 src.to_mutable().emit(Interaction::Unfocus);
214 }
215 }
216 if let Some(new_id) = focused {
217 if let Some(cb) = self.focus_callbacks.get(&new_id) {
218 (cb)(true);
219 }
220 if let Some(src) = self.focus_interaction_sources.get(&new_id) {
221 src.to_mutable().emit(Interaction::Focus);
222 }
223 }
224 self.prev_focused = focused;
225 }
226
227 for &node_id in &self.tree.removed_ids {
229 if let Some(&vid) = self.view_ids.get(&node_id) {
230 self.focus_callbacks.remove(&vid);
231 self.focus_interaction_sources.remove(&vid);
232 }
233 }
234
235 self.tree.clear_dirty();
236 (scene, hits, sems)
237 }
238
239 pub fn intrinsic_size(&mut self, view: &View, mode: IntrinsicSizeMode) -> (f32, f32) {
240 let px_closure = |dp_val: f32| dp_to_px(dp_val);
241 let font_px_closure = |dp_font: f32| dp_to_px(dp_font) * locals::text_scale().0;
242
243 let mut temp_taffy = taffy::TaffyTree::new();
244 let root_tid = self.build_taffy_subtree(view, &mut temp_taffy, &font_px_closure);
245
246 let avail = match mode {
247 IntrinsicSizeMode::MinContent => taffy::geometry::Size {
248 width: taffy::style::AvailableSpace::MinContent,
249 height: taffy::style::AvailableSpace::MinContent,
250 },
251 IntrinsicSizeMode::MaxContent => taffy::geometry::Size {
252 width: taffy::style::AvailableSpace::MaxContent,
253 height: taffy::style::AvailableSpace::MaxContent,
254 },
255 };
256
257 let mut text_cache: FxHashMap<NodeId, TextLayout> = FxHashMap::default();
258 let reverse_map: FxHashMap<taffy::NodeId, NodeId> = FxHashMap::default();
259
260 Self::run_measure_pass(
261 &mut temp_taffy,
262 root_tid,
263 avail,
264 &self.tree,
265 &mut text_cache,
266 &reverse_map,
267 &self.scope_root_map,
268 &self.node_to_scope,
269 &mut self.scope_trees,
270 &font_px_closure,
271 &px_closure,
272 );
273
274 let layout = temp_taffy.layout(root_tid).ok();
275 match layout {
276 Some(l) => (l.size.width, l.size.height),
277 None => (0.0, 0.0),
278 }
279 }
280
281 pub fn new() -> Self {
282 Self {
283 tree: ViewTree::new(),
284 taffy: TaffyTree::new(),
285 taffy_map: FxHashMap::default(),
286 reverse_map: FxHashMap::default(),
287 scope_trees: HashMap::new(),
288 scope_root_map: FxHashMap::default(),
289 node_to_scope: FxHashMap::default(),
290 text_cache: FxHashMap::default(),
291 last_size_px: None,
292 layout_valid: false,
293 paint_cache: FxHashMap::default(),
294 stats: LayoutStats::default(),
295 last_locals_stamp: None,
296 view_ids: FxHashMap::default(),
297 next_view_id: 1,
298 layer_id_counter: 0,
299 prev_focused: None,
300 focus_callbacks: FxHashMap::default(),
301 focus_interaction_sources: FxHashMap::default(),
302 prev_observed_rects: FxHashMap::default(),
303 focus_group_stack: Vec::new(),
304 }
305 }
306
307 pub(crate) fn layout_for_node(&self, node_id: NodeId) -> taffy::prelude::Layout {
308 if self.scope_root_map.contains_key(&node_id) {
311 if let Some(&tid) = self.taffy_map.get(&node_id) {
312 return *self.taffy.layout(tid).unwrap();
313 }
314 if let Some(parent_id) = self.tree.get(node_id).and_then(|n| n.parent)
318 && let Some(outer_key) = self.node_to_scope.get(&parent_id)
319 && let Some(st) = self.scope_trees.get(outer_key)
320 && let Some(&tid) = st.taffy_map.get(&node_id)
321 {
322 return *st.taffy.layout(tid).unwrap();
323 }
324 if let Some(key) = self.node_to_scope.get(&node_id)
325 && let Some(st) = self.scope_trees.get(key)
326 && let Some(&tid) = st.taffy_map.get(&node_id)
327 {
328 return *st.taffy.layout(tid).unwrap();
329 }
330 }
331 if let Some(key) = self.node_to_scope.get(&node_id)
332 && let Some(st) = self.scope_trees.get(key)
333 {
334 let tid = st.taffy_map[&node_id];
335 return *st.taffy.layout(tid).unwrap();
336 }
337 let tid = self.taffy_map[&node_id];
338 *self.taffy.layout(tid).unwrap()
339 }
340
341 pub(crate) fn ensure_view_id(&mut self, node_id: NodeId) -> u64 {
342 if let Some(&id) = self.view_ids.get(&node_id) {
343 return id;
344 }
345 let id = self.next_view_id;
346 self.next_view_id += 1;
347 self.view_ids.insert(node_id, id);
348 id
349 }
350
351 pub(crate) fn locals_stamp() -> u64 {
352 let mut h = FxHasher::default();
353
354 locals::density().scale.to_bits().hash(&mut h);
356 locals::ui_scale().0.to_bits().hash(&mut h);
357 locals::text_scale().0.to_bits().hash(&mut h);
358
359 let dir_u8 = match locals::text_direction() {
360 locals::TextDirection::Ltr => 0u8,
361 locals::TextDirection::Rtl => 1u8,
362 };
363 dir_u8.hash(&mut h);
364
365 h.finish()
366 }
367}