1use presentar_core::{Constraints, Rect, Size, Widget};
4use std::collections::HashMap;
5
6use crate::cache::LayoutCache;
7
8#[derive(Debug, Default)]
10pub struct LayoutTree {
11 pub sizes: HashMap<u64, Size>,
13 pub positions: HashMap<u64, Rect>,
15}
16
17impl LayoutTree {
18 #[must_use]
20 pub fn get_size(&self, id: u64) -> Option<Size> {
21 self.sizes.get(&id).copied()
22 }
23
24 #[must_use]
26 pub fn get_position(&self, id: u64) -> Option<Rect> {
27 self.positions.get(&id).copied()
28 }
29
30 #[must_use]
32 pub fn widget_count(&self) -> usize {
33 self.positions.len()
34 }
35}
36
37#[derive(Debug, Default)]
39pub struct LayoutEngine {
40 cache: LayoutCache,
41 next_id: u64,
42}
43
44impl LayoutEngine {
45 #[must_use]
47 pub fn new() -> Self {
48 Self::default()
49 }
50
51 pub fn compute(&mut self, root: &mut dyn Widget, viewport: Size) -> LayoutTree {
57 self.cache.clear();
58 self.next_id = 0;
59
60 let constraints = Constraints::loose(viewport);
61
62 let mut sizes = HashMap::new();
64 self.measure_tree(root, constraints, &mut sizes);
65
66 self.next_id = 0;
68
69 let mut positions = HashMap::new();
71 let bounds = Rect::from_size(viewport);
72 self.layout_tree(root, bounds, &mut positions);
73
74 LayoutTree { sizes, positions }
75 }
76
77 pub fn compute_readonly(&mut self, root: &dyn Widget, viewport: Size) -> LayoutTree {
79 self.cache.clear();
80 self.next_id = 0;
81
82 let constraints = Constraints::loose(viewport);
83
84 let mut sizes = HashMap::new();
86 self.measure_tree(root, constraints, &mut sizes);
87
88 self.next_id = 0;
90
91 let mut positions = HashMap::new();
93 let bounds = Rect::from_size(viewport);
94 self.position_tree_readonly(root, bounds, &mut positions);
95
96 LayoutTree { sizes, positions }
97 }
98
99 fn measure_tree(
100 &mut self,
101 widget: &dyn Widget,
102 constraints: Constraints,
103 sizes: &mut HashMap<u64, Size>,
104 ) -> Size {
105 let id = self.next_id;
106 self.next_id += 1;
107
108 for child in widget.children() {
110 self.measure_tree(child.as_ref(), constraints, sizes);
111 }
112
113 let size = widget.measure(constraints);
115 sizes.insert(id, size);
116 size
117 }
118
119 fn layout_tree(
120 &mut self,
121 widget: &mut dyn Widget,
122 bounds: Rect,
123 positions: &mut HashMap<u64, Rect>,
124 ) {
125 let id = self.next_id;
126 self.next_id += 1;
127
128 let result = widget.layout(bounds);
130 positions.insert(
131 id,
132 Rect::new(bounds.x, bounds.y, result.size.width, result.size.height),
133 );
134
135 for child in widget.children_mut() {
137 self.collect_child_positions(child.as_mut(), positions);
140 }
141 }
142
143 fn collect_child_positions(
144 &mut self,
145 widget: &mut dyn Widget,
146 positions: &mut HashMap<u64, Rect>,
147 ) {
148 let id = self.next_id;
149 self.next_id += 1;
150
151 positions.insert(id, Rect::default());
159
160 for child in widget.children_mut() {
161 self.collect_child_positions(child.as_mut(), positions);
162 }
163 }
164
165 fn position_tree_readonly(
166 &mut self,
167 widget: &dyn Widget,
168 bounds: Rect,
169 positions: &mut HashMap<u64, Rect>,
170 ) {
171 let id = self.next_id;
172 self.next_id += 1;
173
174 positions.insert(id, bounds);
175
176 for child in widget.children() {
178 self.position_tree_readonly(child.as_ref(), bounds, positions);
180 }
181 }
182
183 pub fn clear_cache(&mut self) {
185 self.cache.clear();
186 }
187
188 #[must_use]
190 pub const fn cache_stats(&self) -> (usize, usize) {
191 (self.cache.hits(), self.cache.misses())
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use presentar_core::widget::{AccessibleRole, LayoutResult};
199 use presentar_core::{Canvas, Event, TypeId};
200 use std::any::Any;
201
202 struct TestWidget {
204 size: Size,
205 children: Vec<Box<dyn Widget>>,
206 }
207
208 impl TestWidget {
209 fn new(width: f32, height: f32) -> Self {
210 Self {
211 size: Size::new(width, height),
212 children: Vec::new(),
213 }
214 }
215
216 fn with_child(mut self, child: TestWidget) -> Self {
217 self.children.push(Box::new(child));
218 self
219 }
220 }
221
222 impl Widget for TestWidget {
223 fn type_id(&self) -> TypeId {
224 TypeId::of::<Self>()
225 }
226
227 fn measure(&self, constraints: Constraints) -> Size {
228 constraints.constrain(self.size)
229 }
230
231 fn layout(&mut self, bounds: Rect) -> LayoutResult {
232 LayoutResult {
233 size: bounds.size(),
234 }
235 }
236
237 fn paint(&self, _canvas: &mut dyn Canvas) {}
238
239 fn event(&mut self, _event: &Event) -> Option<Box<dyn Any + Send>> {
240 None
241 }
242
243 fn children(&self) -> &[Box<dyn Widget>] {
244 &self.children
245 }
246
247 fn children_mut(&mut self) -> &mut [Box<dyn Widget>] {
248 &mut self.children
249 }
250
251 fn accessible_role(&self) -> AccessibleRole {
252 AccessibleRole::Generic
253 }
254 }
255
256 #[test]
257 fn test_layout_engine_new() {
258 let engine = LayoutEngine::new();
259 assert_eq!(engine.next_id, 0);
260 }
261
262 #[test]
263 fn test_layout_tree_default() {
264 let tree = LayoutTree::default();
265 assert!(tree.sizes.is_empty());
266 assert!(tree.positions.is_empty());
267 assert_eq!(tree.widget_count(), 0);
268 }
269
270 #[test]
271 fn test_layout_tree_get_size() {
272 let mut tree = LayoutTree::default();
273 tree.sizes.insert(0, Size::new(100.0, 50.0));
274 assert_eq!(tree.get_size(0), Some(Size::new(100.0, 50.0)));
275 assert_eq!(tree.get_size(1), None);
276 }
277
278 #[test]
279 fn test_layout_tree_get_position() {
280 let mut tree = LayoutTree::default();
281 tree.positions.insert(0, Rect::new(10.0, 20.0, 100.0, 50.0));
282 assert_eq!(
283 tree.get_position(0),
284 Some(Rect::new(10.0, 20.0, 100.0, 50.0))
285 );
286 assert_eq!(tree.get_position(1), None);
287 }
288
289 #[test]
290 fn test_layout_single_widget() {
291 let mut engine = LayoutEngine::new();
292 let mut widget = TestWidget::new(100.0, 50.0);
293 let viewport = Size::new(800.0, 600.0);
294
295 let tree = engine.compute(&mut widget, viewport);
296
297 assert_eq!(tree.widget_count(), 1);
298 assert!(tree.get_size(0).is_some());
299 }
300
301 #[test]
302 fn test_layout_widget_with_children() {
303 let mut engine = LayoutEngine::new();
304 let mut widget = TestWidget::new(200.0, 100.0)
305 .with_child(TestWidget::new(50.0, 50.0))
306 .with_child(TestWidget::new(50.0, 50.0));
307 let viewport = Size::new(800.0, 600.0);
308
309 let tree = engine.compute(&mut widget, viewport);
310
311 assert_eq!(tree.widget_count(), 3);
313 }
314
315 #[test]
316 fn test_layout_nested_children() {
317 let mut engine = LayoutEngine::new();
318 let mut widget = TestWidget::new(300.0, 200.0)
319 .with_child(TestWidget::new(100.0, 100.0).with_child(TestWidget::new(30.0, 30.0)));
320 let viewport = Size::new(800.0, 600.0);
321
322 let tree = engine.compute(&mut widget, viewport);
323
324 assert_eq!(tree.widget_count(), 3);
326 }
327
328 #[test]
329 fn test_layout_readonly() {
330 let mut engine = LayoutEngine::new();
331 let widget = TestWidget::new(100.0, 50.0);
332 let viewport = Size::new(800.0, 600.0);
333
334 let tree = engine.compute_readonly(&widget, viewport);
335
336 assert_eq!(tree.widget_count(), 1);
337 assert_eq!(tree.get_size(0), Some(Size::new(100.0, 50.0)));
338 }
339
340 #[test]
341 fn test_layout_cache_clear() {
342 let mut engine = LayoutEngine::new();
343 engine.clear_cache();
344 let (hits, misses) = engine.cache_stats();
345 assert_eq!(hits, 0);
346 assert_eq!(misses, 0);
347 }
348
349 #[test]
350 fn test_layout_viewport_constraint() {
351 let mut engine = LayoutEngine::new();
352 let mut widget = TestWidget::new(1000.0, 1000.0); let viewport = Size::new(400.0, 300.0);
354
355 let tree = engine.compute(&mut widget, viewport);
356
357 let size = tree.get_size(0).unwrap();
359 assert!(size.width <= viewport.width);
360 assert!(size.height <= viewport.height);
361 }
362
363 #[test]
364 fn test_layout_position_at_origin() {
365 let mut engine = LayoutEngine::new();
366 let mut widget = TestWidget::new(100.0, 50.0);
367 let viewport = Size::new(800.0, 600.0);
368
369 let tree = engine.compute(&mut widget, viewport);
370
371 let pos = tree.get_position(0).unwrap();
372 assert_eq!(pos.x, 0.0);
373 assert_eq!(pos.y, 0.0);
374 }
375
376 #[test]
381 fn test_layout_tree_widget_count() {
382 let mut tree = LayoutTree::default();
383 assert_eq!(tree.widget_count(), 0);
384
385 tree.positions.insert(0, Rect::default());
386 tree.positions.insert(1, Rect::default());
387 tree.positions.insert(2, Rect::default());
388
389 assert_eq!(tree.widget_count(), 3);
390 }
391
392 #[test]
393 fn test_layout_tree_sizes_and_positions() {
394 let mut tree = LayoutTree::default();
395
396 tree.sizes.insert(0, Size::new(100.0, 50.0));
397 tree.positions.insert(0, Rect::new(10.0, 20.0, 100.0, 50.0));
398
399 tree.sizes.insert(1, Size::new(200.0, 100.0));
400 tree.positions
401 .insert(1, Rect::new(120.0, 20.0, 200.0, 100.0));
402
403 assert_eq!(tree.sizes.len(), 2);
404 assert_eq!(tree.positions.len(), 2);
405 }
406
407 #[test]
408 fn test_layout_tree_debug() {
409 let tree = LayoutTree::default();
410 let debug = format!("{:?}", tree);
411 assert!(debug.contains("LayoutTree"));
412 }
413
414 #[test]
419 fn test_layout_engine_default() {
420 let engine = LayoutEngine::default();
421 assert_eq!(engine.next_id, 0);
422 }
423
424 #[test]
425 fn test_layout_engine_debug() {
426 let engine = LayoutEngine::new();
427 let debug = format!("{:?}", engine);
428 assert!(debug.contains("LayoutEngine"));
429 }
430
431 #[test]
436 fn test_layout_multiple_computes() {
437 let mut engine = LayoutEngine::new();
438 let viewport = Size::new(800.0, 600.0);
439
440 let mut widget1 = TestWidget::new(100.0, 50.0);
442 let tree1 = engine.compute(&mut widget1, viewport);
443 assert_eq!(tree1.widget_count(), 1);
444
445 let mut widget2 = TestWidget::new(200.0, 100.0);
447 let tree2 = engine.compute(&mut widget2, viewport);
448 assert_eq!(tree2.widget_count(), 1);
449 }
450
451 #[test]
452 fn test_layout_cache_cleared_on_compute() {
453 let mut engine = LayoutEngine::new();
454 let viewport = Size::new(800.0, 600.0);
455
456 let mut widget = TestWidget::new(100.0, 50.0);
457 engine.compute(&mut widget, viewport);
458
459 let (hits, misses) = engine.cache_stats();
460 assert_eq!(hits, 0);
461 assert_eq!(misses, 0);
462 }
463
464 #[test]
469 fn test_layout_zero_viewport() {
470 let mut engine = LayoutEngine::new();
471 let mut widget = TestWidget::new(100.0, 50.0);
472 let viewport = Size::new(0.0, 0.0);
473
474 let tree = engine.compute(&mut widget, viewport);
475 assert_eq!(tree.widget_count(), 1);
476 }
477
478 #[test]
479 fn test_layout_very_large_viewport() {
480 let mut engine = LayoutEngine::new();
481 let mut widget = TestWidget::new(100.0, 50.0);
482 let viewport = Size::new(10000.0, 10000.0);
483
484 let tree = engine.compute(&mut widget, viewport);
485 let size = tree.get_size(0).unwrap();
486
487 assert!(size.width <= 100.0);
489 assert!(size.height <= 50.0);
490 }
491
492 #[test]
493 fn test_layout_square_viewport() {
494 let mut engine = LayoutEngine::new();
495 let mut widget = TestWidget::new(100.0, 100.0);
496 let viewport = Size::new(500.0, 500.0);
497
498 let tree = engine.compute(&mut widget, viewport);
499 assert_eq!(tree.widget_count(), 1);
500 }
501
502 #[test]
507 fn test_layout_deeply_nested() {
508 let mut engine = LayoutEngine::new();
509 let viewport = Size::new(800.0, 600.0);
510
511 let mut widget =
512 TestWidget::new(100.0, 100.0).with_child(TestWidget::new(80.0, 80.0).with_child(
513 TestWidget::new(60.0, 60.0).with_child(
514 TestWidget::new(40.0, 40.0).with_child(TestWidget::new(20.0, 20.0)),
515 ),
516 ));
517
518 let tree = engine.compute(&mut widget, viewport);
519 assert_eq!(tree.widget_count(), 5); }
521
522 #[test]
523 fn test_layout_wide_tree() {
524 let mut engine = LayoutEngine::new();
525 let viewport = Size::new(800.0, 600.0);
526
527 let mut widget = TestWidget::new(200.0, 100.0)
528 .with_child(TestWidget::new(30.0, 30.0))
529 .with_child(TestWidget::new(30.0, 30.0))
530 .with_child(TestWidget::new(30.0, 30.0))
531 .with_child(TestWidget::new(30.0, 30.0))
532 .with_child(TestWidget::new(30.0, 30.0));
533
534 let tree = engine.compute(&mut widget, viewport);
535 assert_eq!(tree.widget_count(), 6); }
537
538 #[test]
539 fn test_layout_mixed_tree() {
540 let mut engine = LayoutEngine::new();
541 let viewport = Size::new(800.0, 600.0);
542
543 let mut widget = TestWidget::new(300.0, 200.0)
544 .with_child(
545 TestWidget::new(100.0, 100.0)
546 .with_child(TestWidget::new(30.0, 30.0))
547 .with_child(TestWidget::new(30.0, 30.0)),
548 )
549 .with_child(TestWidget::new(100.0, 100.0))
550 .with_child(TestWidget::new(100.0, 100.0).with_child(TestWidget::new(30.0, 30.0)));
551
552 let tree = engine.compute(&mut widget, viewport);
553 assert_eq!(tree.widget_count(), 7);
555 }
556
557 #[test]
562 fn test_layout_readonly_with_children() {
563 let mut engine = LayoutEngine::new();
564 let viewport = Size::new(800.0, 600.0);
565
566 let widget = TestWidget::new(200.0, 100.0)
567 .with_child(TestWidget::new(50.0, 50.0))
568 .with_child(TestWidget::new(50.0, 50.0));
569
570 let tree = engine.compute_readonly(&widget, viewport);
571 assert_eq!(tree.widget_count(), 3);
572 }
573
574 #[test]
575 fn test_layout_readonly_nested() {
576 let mut engine = LayoutEngine::new();
577 let viewport = Size::new(800.0, 600.0);
578
579 let widget = TestWidget::new(100.0, 100.0)
580 .with_child(TestWidget::new(80.0, 80.0).with_child(TestWidget::new(60.0, 60.0)));
581
582 let tree = engine.compute_readonly(&widget, viewport);
583 assert_eq!(tree.widget_count(), 3);
584 }
585
586 #[test]
591 fn test_cache_stats_initial() {
592 let engine = LayoutEngine::new();
593 let (hits, misses) = engine.cache_stats();
594 assert_eq!(hits, 0);
595 assert_eq!(misses, 0);
596 }
597
598 #[test]
599 fn test_cache_stats_after_clear() {
600 let mut engine = LayoutEngine::new();
601 engine.clear_cache();
602 let (hits, misses) = engine.cache_stats();
603 assert_eq!(hits, 0);
604 assert_eq!(misses, 0);
605 }
606
607 #[test]
612 fn test_layout_widget_larger_than_viewport() {
613 let mut engine = LayoutEngine::new();
614 let mut widget = TestWidget::new(2000.0, 1500.0);
615 let viewport = Size::new(800.0, 600.0);
616
617 let tree = engine.compute(&mut widget, viewport);
618 let size = tree.get_size(0).unwrap();
619
620 assert!(size.width <= viewport.width);
622 assert!(size.height <= viewport.height);
623 }
624
625 #[test]
626 fn test_layout_widget_fractional_size() {
627 let mut engine = LayoutEngine::new();
628 let mut widget = TestWidget::new(100.5, 50.25);
629 let viewport = Size::new(800.0, 600.0);
630
631 let tree = engine.compute(&mut widget, viewport);
632 assert_eq!(tree.widget_count(), 1);
633 }
634}