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::{
200 Brick, BrickAssertion, BrickBudget, BrickVerification, Canvas, Event, TypeId,
201 };
202 use std::any::Any;
203 use std::time::Duration;
204
205 struct TestWidget {
207 size: Size,
208 children: Vec<Box<dyn Widget>>,
209 }
210
211 impl TestWidget {
212 fn new(width: f32, height: f32) -> Self {
213 Self {
214 size: Size::new(width, height),
215 children: Vec::new(),
216 }
217 }
218
219 fn with_child(mut self, child: TestWidget) -> Self {
220 self.children.push(Box::new(child));
221 self
222 }
223 }
224
225 impl Brick for TestWidget {
226 fn brick_name(&self) -> &'static str {
227 "TestWidget"
228 }
229
230 fn assertions(&self) -> &[BrickAssertion] {
231 &[]
232 }
233
234 fn budget(&self) -> BrickBudget {
235 BrickBudget::uniform(16)
236 }
237
238 fn verify(&self) -> BrickVerification {
239 BrickVerification {
240 passed: vec![],
241 failed: vec![],
242 verification_time: Duration::from_micros(1),
243 }
244 }
245
246 fn to_html(&self) -> String {
247 String::new()
248 }
249
250 fn to_css(&self) -> String {
251 String::new()
252 }
253 }
254
255 impl Widget for TestWidget {
256 fn type_id(&self) -> TypeId {
257 TypeId::of::<Self>()
258 }
259
260 fn measure(&self, constraints: Constraints) -> Size {
261 constraints.constrain(self.size)
262 }
263
264 fn layout(&mut self, bounds: Rect) -> LayoutResult {
265 LayoutResult {
266 size: bounds.size(),
267 }
268 }
269
270 fn paint(&self, _canvas: &mut dyn Canvas) {}
271
272 fn event(&mut self, _event: &Event) -> Option<Box<dyn Any + Send>> {
273 None
274 }
275
276 fn children(&self) -> &[Box<dyn Widget>] {
277 &self.children
278 }
279
280 fn children_mut(&mut self) -> &mut [Box<dyn Widget>] {
281 &mut self.children
282 }
283
284 fn accessible_role(&self) -> AccessibleRole {
285 AccessibleRole::Generic
286 }
287 }
288
289 #[test]
290 fn test_layout_engine_new() {
291 let engine = LayoutEngine::new();
292 assert_eq!(engine.next_id, 0);
293 }
294
295 #[test]
296 fn test_layout_tree_default() {
297 let tree = LayoutTree::default();
298 assert!(tree.sizes.is_empty());
299 assert!(tree.positions.is_empty());
300 assert_eq!(tree.widget_count(), 0);
301 }
302
303 #[test]
304 fn test_layout_tree_get_size() {
305 let mut tree = LayoutTree::default();
306 tree.sizes.insert(0, Size::new(100.0, 50.0));
307 assert_eq!(tree.get_size(0), Some(Size::new(100.0, 50.0)));
308 assert_eq!(tree.get_size(1), None);
309 }
310
311 #[test]
312 fn test_layout_tree_get_position() {
313 let mut tree = LayoutTree::default();
314 tree.positions.insert(0, Rect::new(10.0, 20.0, 100.0, 50.0));
315 assert_eq!(
316 tree.get_position(0),
317 Some(Rect::new(10.0, 20.0, 100.0, 50.0))
318 );
319 assert_eq!(tree.get_position(1), None);
320 }
321
322 #[test]
323 fn test_layout_single_widget() {
324 let mut engine = LayoutEngine::new();
325 let mut widget = TestWidget::new(100.0, 50.0);
326 let viewport = Size::new(800.0, 600.0);
327
328 let tree = engine.compute(&mut widget, viewport);
329
330 assert_eq!(tree.widget_count(), 1);
331 assert!(tree.get_size(0).is_some());
332 }
333
334 #[test]
335 fn test_layout_widget_with_children() {
336 let mut engine = LayoutEngine::new();
337 let mut widget = TestWidget::new(200.0, 100.0)
338 .with_child(TestWidget::new(50.0, 50.0))
339 .with_child(TestWidget::new(50.0, 50.0));
340 let viewport = Size::new(800.0, 600.0);
341
342 let tree = engine.compute(&mut widget, viewport);
343
344 assert_eq!(tree.widget_count(), 3);
346 }
347
348 #[test]
349 fn test_layout_nested_children() {
350 let mut engine = LayoutEngine::new();
351 let mut widget = TestWidget::new(300.0, 200.0)
352 .with_child(TestWidget::new(100.0, 100.0).with_child(TestWidget::new(30.0, 30.0)));
353 let viewport = Size::new(800.0, 600.0);
354
355 let tree = engine.compute(&mut widget, viewport);
356
357 assert_eq!(tree.widget_count(), 3);
359 }
360
361 #[test]
362 fn test_layout_readonly() {
363 let mut engine = LayoutEngine::new();
364 let widget = TestWidget::new(100.0, 50.0);
365 let viewport = Size::new(800.0, 600.0);
366
367 let tree = engine.compute_readonly(&widget, viewport);
368
369 assert_eq!(tree.widget_count(), 1);
370 assert_eq!(tree.get_size(0), Some(Size::new(100.0, 50.0)));
371 }
372
373 #[test]
374 fn test_layout_cache_clear() {
375 let mut engine = LayoutEngine::new();
376 engine.clear_cache();
377 let (hits, misses) = engine.cache_stats();
378 assert_eq!(hits, 0);
379 assert_eq!(misses, 0);
380 }
381
382 #[test]
383 fn test_layout_viewport_constraint() {
384 let mut engine = LayoutEngine::new();
385 let mut widget = TestWidget::new(1000.0, 1000.0); let viewport = Size::new(400.0, 300.0);
387
388 let tree = engine.compute(&mut widget, viewport);
389
390 let size = tree.get_size(0).unwrap();
392 assert!(size.width <= viewport.width);
393 assert!(size.height <= viewport.height);
394 }
395
396 #[test]
397 fn test_layout_position_at_origin() {
398 let mut engine = LayoutEngine::new();
399 let mut widget = TestWidget::new(100.0, 50.0);
400 let viewport = Size::new(800.0, 600.0);
401
402 let tree = engine.compute(&mut widget, viewport);
403
404 let pos = tree.get_position(0).unwrap();
405 assert_eq!(pos.x, 0.0);
406 assert_eq!(pos.y, 0.0);
407 }
408
409 #[test]
414 fn test_layout_tree_widget_count() {
415 let mut tree = LayoutTree::default();
416 assert_eq!(tree.widget_count(), 0);
417
418 tree.positions.insert(0, Rect::default());
419 tree.positions.insert(1, Rect::default());
420 tree.positions.insert(2, Rect::default());
421
422 assert_eq!(tree.widget_count(), 3);
423 }
424
425 #[test]
426 fn test_layout_tree_sizes_and_positions() {
427 let mut tree = LayoutTree::default();
428
429 tree.sizes.insert(0, Size::new(100.0, 50.0));
430 tree.positions.insert(0, Rect::new(10.0, 20.0, 100.0, 50.0));
431
432 tree.sizes.insert(1, Size::new(200.0, 100.0));
433 tree.positions
434 .insert(1, Rect::new(120.0, 20.0, 200.0, 100.0));
435
436 assert_eq!(tree.sizes.len(), 2);
437 assert_eq!(tree.positions.len(), 2);
438 }
439
440 #[test]
441 fn test_layout_tree_debug() {
442 let tree = LayoutTree::default();
443 let debug = format!("{:?}", tree);
444 assert!(debug.contains("LayoutTree"));
445 }
446
447 #[test]
452 fn test_layout_engine_default() {
453 let engine = LayoutEngine::default();
454 assert_eq!(engine.next_id, 0);
455 }
456
457 #[test]
458 fn test_layout_engine_debug() {
459 let engine = LayoutEngine::new();
460 let debug = format!("{:?}", engine);
461 assert!(debug.contains("LayoutEngine"));
462 }
463
464 #[test]
469 fn test_layout_multiple_computes() {
470 let mut engine = LayoutEngine::new();
471 let viewport = Size::new(800.0, 600.0);
472
473 let mut widget1 = TestWidget::new(100.0, 50.0);
475 let tree1 = engine.compute(&mut widget1, viewport);
476 assert_eq!(tree1.widget_count(), 1);
477
478 let mut widget2 = TestWidget::new(200.0, 100.0);
480 let tree2 = engine.compute(&mut widget2, viewport);
481 assert_eq!(tree2.widget_count(), 1);
482 }
483
484 #[test]
485 fn test_layout_cache_cleared_on_compute() {
486 let mut engine = LayoutEngine::new();
487 let viewport = Size::new(800.0, 600.0);
488
489 let mut widget = TestWidget::new(100.0, 50.0);
490 engine.compute(&mut widget, viewport);
491
492 let (hits, misses) = engine.cache_stats();
493 assert_eq!(hits, 0);
494 assert_eq!(misses, 0);
495 }
496
497 #[test]
502 fn test_layout_zero_viewport() {
503 let mut engine = LayoutEngine::new();
504 let mut widget = TestWidget::new(100.0, 50.0);
505 let viewport = Size::new(0.0, 0.0);
506
507 let tree = engine.compute(&mut widget, viewport);
508 assert_eq!(tree.widget_count(), 1);
509 }
510
511 #[test]
512 fn test_layout_very_large_viewport() {
513 let mut engine = LayoutEngine::new();
514 let mut widget = TestWidget::new(100.0, 50.0);
515 let viewport = Size::new(10000.0, 10000.0);
516
517 let tree = engine.compute(&mut widget, viewport);
518 let size = tree.get_size(0).unwrap();
519
520 assert!(size.width <= 100.0);
522 assert!(size.height <= 50.0);
523 }
524
525 #[test]
526 fn test_layout_square_viewport() {
527 let mut engine = LayoutEngine::new();
528 let mut widget = TestWidget::new(100.0, 100.0);
529 let viewport = Size::new(500.0, 500.0);
530
531 let tree = engine.compute(&mut widget, viewport);
532 assert_eq!(tree.widget_count(), 1);
533 }
534
535 #[test]
540 fn test_layout_deeply_nested() {
541 let mut engine = LayoutEngine::new();
542 let viewport = Size::new(800.0, 600.0);
543
544 let mut widget =
545 TestWidget::new(100.0, 100.0).with_child(TestWidget::new(80.0, 80.0).with_child(
546 TestWidget::new(60.0, 60.0).with_child(
547 TestWidget::new(40.0, 40.0).with_child(TestWidget::new(20.0, 20.0)),
548 ),
549 ));
550
551 let tree = engine.compute(&mut widget, viewport);
552 assert_eq!(tree.widget_count(), 5); }
554
555 #[test]
556 fn test_layout_wide_tree() {
557 let mut engine = LayoutEngine::new();
558 let viewport = Size::new(800.0, 600.0);
559
560 let mut widget = TestWidget::new(200.0, 100.0)
561 .with_child(TestWidget::new(30.0, 30.0))
562 .with_child(TestWidget::new(30.0, 30.0))
563 .with_child(TestWidget::new(30.0, 30.0))
564 .with_child(TestWidget::new(30.0, 30.0))
565 .with_child(TestWidget::new(30.0, 30.0));
566
567 let tree = engine.compute(&mut widget, viewport);
568 assert_eq!(tree.widget_count(), 6); }
570
571 #[test]
572 fn test_layout_mixed_tree() {
573 let mut engine = LayoutEngine::new();
574 let viewport = Size::new(800.0, 600.0);
575
576 let mut widget = TestWidget::new(300.0, 200.0)
577 .with_child(
578 TestWidget::new(100.0, 100.0)
579 .with_child(TestWidget::new(30.0, 30.0))
580 .with_child(TestWidget::new(30.0, 30.0)),
581 )
582 .with_child(TestWidget::new(100.0, 100.0))
583 .with_child(TestWidget::new(100.0, 100.0).with_child(TestWidget::new(30.0, 30.0)));
584
585 let tree = engine.compute(&mut widget, viewport);
586 assert_eq!(tree.widget_count(), 7);
588 }
589
590 #[test]
595 fn test_layout_readonly_with_children() {
596 let mut engine = LayoutEngine::new();
597 let viewport = Size::new(800.0, 600.0);
598
599 let widget = TestWidget::new(200.0, 100.0)
600 .with_child(TestWidget::new(50.0, 50.0))
601 .with_child(TestWidget::new(50.0, 50.0));
602
603 let tree = engine.compute_readonly(&widget, viewport);
604 assert_eq!(tree.widget_count(), 3);
605 }
606
607 #[test]
608 fn test_layout_readonly_nested() {
609 let mut engine = LayoutEngine::new();
610 let viewport = Size::new(800.0, 600.0);
611
612 let widget = TestWidget::new(100.0, 100.0)
613 .with_child(TestWidget::new(80.0, 80.0).with_child(TestWidget::new(60.0, 60.0)));
614
615 let tree = engine.compute_readonly(&widget, viewport);
616 assert_eq!(tree.widget_count(), 3);
617 }
618
619 #[test]
624 fn test_cache_stats_initial() {
625 let engine = LayoutEngine::new();
626 let (hits, misses) = engine.cache_stats();
627 assert_eq!(hits, 0);
628 assert_eq!(misses, 0);
629 }
630
631 #[test]
632 fn test_cache_stats_after_clear() {
633 let mut engine = LayoutEngine::new();
634 engine.clear_cache();
635 let (hits, misses) = engine.cache_stats();
636 assert_eq!(hits, 0);
637 assert_eq!(misses, 0);
638 }
639
640 #[test]
645 fn test_layout_widget_larger_than_viewport() {
646 let mut engine = LayoutEngine::new();
647 let mut widget = TestWidget::new(2000.0, 1500.0);
648 let viewport = Size::new(800.0, 600.0);
649
650 let tree = engine.compute(&mut widget, viewport);
651 let size = tree.get_size(0).unwrap();
652
653 assert!(size.width <= viewport.width);
655 assert!(size.height <= viewport.height);
656 }
657
658 #[test]
659 fn test_layout_widget_fractional_size() {
660 let mut engine = LayoutEngine::new();
661 let mut widget = TestWidget::new(100.5, 50.25);
662 let viewport = Size::new(800.0, 600.0);
663
664 let tree = engine.compute(&mut widget, viewport);
665 assert_eq!(tree.widget_count(), 1);
666 }
667}