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::density().scale.max(0.0001);
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 density_scale = locals::density().scale * locals::ui_scale().0;
70 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 need_layout = size_changed || !self.layout_valid || has_tree_mutation || locals_changed;
86
87 if size_changed {
89 for &node_id in self.text_cache.keys() {
91 if let Some(&taffy_id) = self.taffy_map.get(&node_id) {
92 let _ = self.taffy.mark_dirty(taffy_id);
93 }
94 }
95 self.text_cache.clear();
96 for (_, st) in &mut self.scope_trees {
98 for &node_id in st.text_cache.keys() {
99 if let Some(&tid) = st.taffy_map.get(&node_id) {
100 let _ = st.taffy.mark_dirty(tid);
101 }
102 }
103 st.text_cache.clear();
104 }
105 }
106 if locals_changed {
107 for (_, st) in &mut self.scope_trees {
108 st.text_cache.clear();
109 }
110 }
111
112 let px = |dp_val: f32| dp_to_px(dp_val);
114 let font_px = |dp_font: f32| dp_to_px(dp_font) * locals::text_scale().0;
115
116 self.sync_scope_trees(&font_px);
119 self.sync_taffy_tree(root_node_id, &font_px);
121
122 let taffy_root = self.taffy_map.get(&root_node_id).copied();
124 if let Some(taffy_root) = taffy_root {
125 if need_layout {
126 if let Ok(mut style) = self.taffy.style(taffy_root).cloned() {
127 style.size.width = length(size_px.0 as f32);
128 style.size.height = length(size_px.1 as f32);
129 let _ = self.taffy.set_style(taffy_root, style);
130 }
131
132 let available = taffy::geometry::Size {
133 width: AvailableSpace::Definite(size_px.0 as f32),
134 height: AvailableSpace::Definite(size_px.1 as f32),
135 };
136
137 Self::run_measure_pass(
138 &mut self.taffy,
139 taffy_root,
140 available,
141 &self.tree,
142 &mut self.text_cache,
143 &self.reverse_map,
144 &self.scope_root_map,
145 &self.node_to_scope,
146 &mut self.scope_trees,
147 &font_px,
148 &px,
149 );
150
151 for (&node_id, &taffy_id) in &self.taffy_map {
153 if let Ok(layout) = self.taffy.layout(taffy_id) {
154 let dp_w = layout.size.width / density_scale;
155 let dp_h = layout.size.height / density_scale;
156 let rect = repose_core::Rect {
157 x: 0.0,
158 y: 0.0,
159 w: dp_w,
160 h: dp_h,
161 };
162 self.tree
163 .set_layout(node_id, rect, rect, LayoutConstraints::default());
164 }
165 }
166
167 self.last_locals_stamp = Some(locals_stamp);
168
169 self.layout_valid = true;
170 self.last_size_px = Some(size_px);
171 self.stats.layout_misses += 1;
172 } else {
173 self.stats.layout_hits += 1;
174 }
175 }
176 self.stats.layout_time_ms = (web_time::Instant::now() - start).as_secs_f32() * 1000.0;
177
178 self.walk_tick(root_node_id);
180
181 let t_paint = web_time::Instant::now();
183 let (scene, hits, sems) = self.paint(
184 root_node_id,
185 textfield_states,
186 interactions,
187 focused,
188 &font_px,
189 );
190 self.stats.paint_time_ms = (web_time::Instant::now() - t_paint).as_secs_f32() * 1000.0;
191
192 if self.prev_focused != focused {
194 if let Some(old_id) = self.prev_focused
195 && let Some(cb) = self.focus_callbacks.get(&old_id)
196 {
197 (cb)(false);
198 }
199 if let Some(new_id) = focused
200 && let Some(cb) = self.focus_callbacks.get(&new_id)
201 {
202 (cb)(true);
203 }
204 self.prev_focused = focused;
205 }
206
207 for &node_id in &self.tree.removed_ids {
209 if let Some(&vid) = self.view_ids.get(&node_id) {
210 self.focus_callbacks.remove(&vid);
211 }
212 }
213
214 self.tree.clear_dirty();
215 (scene, hits, sems)
216 }
217
218 pub fn intrinsic_size(&mut self, view: &View, mode: IntrinsicSizeMode) -> (f32, f32) {
219 let px_closure = |dp_val: f32| dp_to_px(dp_val);
220 let font_px_closure = |dp_font: f32| dp_to_px(dp_font) * locals::text_scale().0;
221
222 let mut temp_taffy = taffy::TaffyTree::new();
223 let root_tid = self.build_taffy_subtree(view, &mut temp_taffy, &font_px_closure);
224
225 let avail = match mode {
226 IntrinsicSizeMode::MinContent => taffy::geometry::Size {
227 width: taffy::style::AvailableSpace::MinContent,
228 height: taffy::style::AvailableSpace::MinContent,
229 },
230 IntrinsicSizeMode::MaxContent => taffy::geometry::Size {
231 width: taffy::style::AvailableSpace::MaxContent,
232 height: taffy::style::AvailableSpace::MaxContent,
233 },
234 };
235
236 let mut text_cache: FxHashMap<NodeId, TextLayout> = FxHashMap::default();
237 let reverse_map: FxHashMap<taffy::NodeId, NodeId> = FxHashMap::default();
238
239 Self::run_measure_pass(
240 &mut temp_taffy,
241 root_tid,
242 avail,
243 &self.tree,
244 &mut text_cache,
245 &reverse_map,
246 &self.scope_root_map,
247 &self.node_to_scope,
248 &mut self.scope_trees,
249 &font_px_closure,
250 &px_closure,
251 );
252
253 let layout = temp_taffy.layout(root_tid).ok();
254 match layout {
255 Some(l) => (l.size.width, l.size.height),
256 None => (0.0, 0.0),
257 }
258 }
259
260 pub fn new() -> Self {
261 Self {
262 tree: ViewTree::new(),
263 taffy: TaffyTree::new(),
264 taffy_map: FxHashMap::default(),
265 reverse_map: FxHashMap::default(),
266 scope_trees: HashMap::new(),
267 scope_root_map: FxHashMap::default(),
268 node_to_scope: FxHashMap::default(),
269 text_cache: FxHashMap::default(),
270 last_size_px: None,
271 layout_valid: false,
272 paint_cache: FxHashMap::default(),
273 stats: LayoutStats::default(),
274 last_locals_stamp: None,
275 view_ids: FxHashMap::default(),
276 next_view_id: 1,
277 layer_id_counter: 0,
278 prev_focused: None,
279 focus_callbacks: FxHashMap::default(),
280 prev_observed_rects: FxHashMap::default(),
281 focus_group_stack: Vec::new(),
282 }
283 }
284
285 pub(crate) fn layout_for_node(&self, node_id: NodeId) -> taffy::prelude::Layout {
286 if self.scope_root_map.contains_key(&node_id) {
289 if let Some(&tid) = self.taffy_map.get(&node_id) {
290 return self.taffy.layout(tid).unwrap().clone();
291 }
292 if let Some(parent_id) = self.tree.get(node_id).and_then(|n| n.parent) {
296 if let Some(outer_key) = self.node_to_scope.get(&parent_id) {
297 if let Some(st) = self.scope_trees.get(outer_key) {
298 if let Some(&tid) = st.taffy_map.get(&node_id) {
299 return st.taffy.layout(tid).unwrap().clone();
300 }
301 }
302 }
303 }
304 if let Some(key) = self.node_to_scope.get(&node_id) {
305 if let Some(st) = self.scope_trees.get(key) {
306 if let Some(&tid) = st.taffy_map.get(&node_id) {
307 return st.taffy.layout(tid).unwrap().clone();
308 }
309 }
310 }
311 }
312 if let Some(key) = self.node_to_scope.get(&node_id) {
313 if let Some(st) = self.scope_trees.get(key) {
314 let tid = st.taffy_map[&node_id];
315 return st.taffy.layout(tid).unwrap().clone();
316 }
317 }
318 let tid = self.taffy_map[&node_id];
319 self.taffy.layout(tid).unwrap().clone()
320 }
321
322 pub(crate) fn taffy_children_for_node(&self, node_id: NodeId) -> Vec<taffy::NodeId> {
323 if let Some(key) = self.node_to_scope.get(&node_id) {
324 if let Some(st) = self.scope_trees.get(key) {
325 let tid = st.taffy_map[&node_id];
326 return st.taffy.children(tid).unwrap_or_default();
327 }
328 }
329 let tid = self.taffy_map[&node_id];
330 self.taffy.children(tid).unwrap_or_default()
331 }
332
333 pub(crate) fn ensure_view_id(&mut self, node_id: NodeId) -> u64 {
334 if let Some(&id) = self.view_ids.get(&node_id) {
335 return id;
336 }
337 let id = self.next_view_id;
338 self.next_view_id += 1;
339 self.view_ids.insert(node_id, id);
340 id
341 }
342
343 pub(crate) fn locals_stamp() -> u64 {
344 let mut h = FxHasher::default();
345
346 locals::density().scale.to_bits().hash(&mut h);
348 locals::text_scale().0.to_bits().hash(&mut h);
349
350 let dir_u8 = match locals::text_direction() {
351 locals::TextDirection::Ltr => 0u8,
352 locals::TextDirection::Rtl => 1u8,
353 };
354 dir_u8.hash(&mut h);
355
356 h.finish()
357 }
358}