1use ratatui::layout::Rect;
4use serde::{Deserialize, Serialize};
5
6use super::types::{Annotation, WidgetType};
7
8#[derive(Clone, Debug, Serialize, Deserialize)]
10pub struct RegionInfo {
11 pub area: SerializableRect,
13
14 pub annotation: Annotation,
16
17 pub parent: Option<usize>,
19
20 pub children: Vec<usize>,
22
23 pub depth: usize,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
29pub struct SerializableRect {
30 pub x: u16,
32 pub y: u16,
34 pub width: u16,
36 pub height: u16,
38}
39
40impl From<Rect> for SerializableRect {
41 fn from(rect: Rect) -> Self {
42 Self {
43 x: rect.x,
44 y: rect.y,
45 width: rect.width,
46 height: rect.height,
47 }
48 }
49}
50
51impl From<SerializableRect> for Rect {
52 fn from(rect: SerializableRect) -> Self {
53 Rect::new(rect.x, rect.y, rect.width, rect.height)
54 }
55}
56
57impl SerializableRect {
58 pub fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
60 Self {
61 x,
62 y,
63 width,
64 height,
65 }
66 }
67
68 pub fn contains(&self, x: u16, y: u16) -> bool {
70 x >= self.x
71 && x < self.x.saturating_add(self.width)
72 && y >= self.y
73 && y < self.y.saturating_add(self.height)
74 }
75
76 pub fn intersects(&self, other: &Self) -> bool {
78 self.x < other.x.saturating_add(other.width)
79 && self.x.saturating_add(self.width) > other.x
80 && self.y < other.y.saturating_add(other.height)
81 && self.y.saturating_add(self.height) > other.y
82 }
83}
84
85#[derive(Clone, Debug, Default, Serialize, Deserialize)]
91pub struct AnnotationRegistry {
92 regions: Vec<RegionInfo>,
94
95 #[serde(skip)]
97 open_stack: Vec<usize>,
98
99 #[serde(skip)]
101 current_depth: usize,
102}
103
104impl AnnotationRegistry {
105 pub fn new() -> Self {
107 Self::default()
108 }
109
110 pub fn clear(&mut self) {
112 self.regions.clear();
113 self.open_stack.clear();
114 self.current_depth = 0;
115 }
116
117 pub fn register(&mut self, area: Rect, annotation: Annotation) -> usize {
121 let parent = self.open_stack.last().copied();
122 let index = self.regions.len();
123
124 self.regions.push(RegionInfo {
125 area: area.into(),
126 annotation,
127 parent,
128 children: Vec::new(),
129 depth: self.current_depth,
130 });
131
132 if let Some(parent_idx) = parent {
134 self.regions[parent_idx].children.push(index);
135 }
136
137 index
138 }
139
140 pub fn open(&mut self, area: Rect, annotation: Annotation) -> usize {
144 let index = self.register(area, annotation);
145 self.open_stack.push(index);
146 self.current_depth += 1;
147 index
148 }
149
150 pub fn close(&mut self) {
152 self.open_stack.pop();
153 self.current_depth = self.current_depth.saturating_sub(1);
154 }
155
156 pub fn len(&self) -> usize {
158 self.regions.len()
159 }
160
161 pub fn is_empty(&self) -> bool {
163 self.regions.is_empty()
164 }
165
166 pub fn regions(&self) -> &[RegionInfo] {
168 &self.regions
169 }
170
171 pub fn get(&self, index: usize) -> Option<&RegionInfo> {
173 self.regions.get(index)
174 }
175
176 pub fn region_at(&self, x: u16, y: u16) -> Option<&RegionInfo> {
180 self.regions
181 .iter()
182 .filter(|r| r.area.contains(x, y))
183 .max_by_key(|r| r.depth)
184 }
185
186 pub fn regions_at(&self, x: u16, y: u16) -> Vec<&RegionInfo> {
188 self.regions
189 .iter()
190 .filter(|r| r.area.contains(x, y))
191 .collect()
192 }
193
194 pub fn find_by_id(&self, id: &str) -> Vec<&RegionInfo> {
196 self.regions
197 .iter()
198 .filter(|r| r.annotation.has_id(id))
199 .collect()
200 }
201
202 pub fn get_by_id(&self, id: &str) -> Option<&RegionInfo> {
204 self.regions.iter().find(|r| r.annotation.has_id(id))
205 }
206
207 pub fn find_by_type(&self, widget_type: &WidgetType) -> Vec<&RegionInfo> {
209 self.regions
210 .iter()
211 .filter(|r| r.annotation.is_type(widget_type))
212 .collect()
213 }
214
215 pub fn interactive_regions(&self) -> Vec<&RegionInfo> {
217 self.regions
218 .iter()
219 .filter(|r| r.annotation.is_interactive())
220 .collect()
221 }
222
223 pub fn focused_region(&self) -> Option<&RegionInfo> {
225 self.regions.iter().find(|r| r.annotation.focused)
226 }
227
228 pub fn root_regions(&self) -> Vec<&RegionInfo> {
230 self.regions.iter().filter(|r| r.depth == 0).collect()
231 }
232
233 pub fn children_of(&self, index: usize) -> Vec<&RegionInfo> {
235 if let Some(region) = self.regions.get(index) {
236 region
237 .children
238 .iter()
239 .filter_map(|&i| self.regions.get(i))
240 .collect()
241 } else {
242 Vec::new()
243 }
244 }
245
246 pub fn format_tree(&self) -> String {
248 let mut output = String::new();
249
250 for region in &self.regions {
251 if region.parent.is_none() {
252 self.format_region(&mut output, region, 0);
253 }
254 }
255
256 output
257 }
258
259 fn format_region(&self, output: &mut String, region: &RegionInfo, indent: usize) {
260 let prefix = " ".repeat(indent);
261 output.push_str(&format!(
262 "{}[{},{}+{}x{}] {}\n",
263 prefix,
264 region.area.x,
265 region.area.y,
266 region.area.width,
267 region.area.height,
268 region.annotation.description()
269 ));
270
271 for &child_idx in ®ion.children {
272 if let Some(child) = self.regions.get(child_idx) {
273 self.format_region(output, child, indent + 1);
274 }
275 }
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 #[test]
284 fn test_registry_register() {
285 let mut registry = AnnotationRegistry::new();
286
287 let idx = registry.register(Rect::new(0, 0, 80, 24), Annotation::container("main"));
288
289 assert_eq!(idx, 0);
290 assert_eq!(registry.len(), 1);
291 }
292
293 #[test]
294 fn test_registry_nesting() {
295 let mut registry = AnnotationRegistry::new();
296
297 let container = registry.open(Rect::new(0, 0, 80, 24), Annotation::container("main"));
299
300 let button = registry.register(Rect::new(10, 10, 20, 3), Annotation::button("submit"));
302
303 registry.close();
305
306 assert_eq!(registry.len(), 2);
307
308 let container_info = registry.get(container).unwrap();
309 assert_eq!(container_info.children, vec![button]);
310
311 let button_info = registry.get(button).unwrap();
312 assert_eq!(button_info.parent, Some(container));
313 assert_eq!(button_info.depth, 1);
314 }
315
316 #[test]
317 fn test_registry_region_at() {
318 let mut registry = AnnotationRegistry::new();
319
320 registry.open(Rect::new(0, 0, 80, 24), Annotation::container("main"));
322
323 registry.register(Rect::new(10, 10, 20, 3), Annotation::button("submit"));
325
326 registry.close();
327
328 let region = registry.region_at(15, 11).unwrap();
330 assert!(region.annotation.has_id("submit"));
331
332 let region = registry.region_at(5, 5).unwrap();
334 assert!(region.annotation.has_id("main"));
335
336 assert!(registry.region_at(100, 100).is_none());
338 }
339
340 #[test]
341 fn test_registry_find_by_id() {
342 let mut registry = AnnotationRegistry::new();
343
344 registry.register(Rect::new(0, 0, 10, 1), Annotation::input("username"));
345 registry.register(Rect::new(0, 2, 10, 1), Annotation::input("password"));
346 registry.register(Rect::new(0, 4, 10, 1), Annotation::button("submit"));
347
348 let found = registry.find_by_id("password");
349 assert_eq!(found.len(), 1);
350 assert!(found[0].annotation.has_id("password"));
351
352 let submit = registry.get_by_id("submit").unwrap();
353 assert_eq!(submit.annotation.widget_type, WidgetType::Button);
354 }
355
356 #[test]
357 fn test_registry_find_by_type() {
358 let mut registry = AnnotationRegistry::new();
359
360 registry.register(Rect::new(0, 0, 10, 1), Annotation::input("a"));
361 registry.register(Rect::new(0, 2, 10, 1), Annotation::input("b"));
362 registry.register(Rect::new(0, 4, 10, 1), Annotation::button("c"));
363
364 let inputs = registry.find_by_type(&WidgetType::Input);
365 assert_eq!(inputs.len(), 2);
366
367 let buttons = registry.find_by_type(&WidgetType::Button);
368 assert_eq!(buttons.len(), 1);
369 }
370
371 #[test]
372 fn test_registry_focused() {
373 let mut registry = AnnotationRegistry::new();
374
375 registry.register(Rect::new(0, 0, 10, 1), Annotation::input("a"));
376 registry.register(
377 Rect::new(0, 2, 10, 1),
378 Annotation::input("b").with_focus(true),
379 );
380
381 let focused = registry.focused_region().unwrap();
382 assert!(focused.annotation.has_id("b"));
383 }
384
385 #[test]
386 fn test_serializable_rect() {
387 let rect = SerializableRect::new(5, 10, 20, 30);
388
389 assert!(rect.contains(5, 10));
390 assert!(rect.contains(24, 39));
391 assert!(!rect.contains(25, 10));
392 assert!(!rect.contains(5, 40));
393 }
394
395 #[test]
396 fn test_rect_intersects() {
397 let a = SerializableRect::new(0, 0, 10, 10);
398 let b = SerializableRect::new(5, 5, 10, 10);
399 let c = SerializableRect::new(20, 20, 10, 10);
400
401 assert!(a.intersects(&b));
402 assert!(b.intersects(&a));
403 assert!(!a.intersects(&c));
404 }
405
406 #[test]
407 fn test_format_tree() {
408 let mut registry = AnnotationRegistry::new();
409
410 registry.open(Rect::new(0, 0, 80, 24), Annotation::dialog("Login"));
411 registry.register(Rect::new(5, 5, 30, 1), Annotation::input("username"));
412 registry.register(Rect::new(5, 7, 30, 1), Annotation::input("password"));
413 registry.register(Rect::new(5, 10, 10, 1), Annotation::button("submit"));
414 registry.close();
415
416 let tree = registry.format_tree();
417 assert!(tree.contains("Dialog"));
418 assert!(tree.contains("Input"));
419 assert!(tree.contains("Button"));
420 }
421
422 #[test]
423 fn test_registry_clear() {
424 let mut registry = AnnotationRegistry::new();
425
426 registry.register(Rect::new(0, 0, 10, 1), Annotation::button("a"));
427 registry.register(Rect::new(0, 2, 10, 1), Annotation::button("b"));
428
429 assert_eq!(registry.len(), 2);
430
431 registry.clear();
432
433 assert_eq!(registry.len(), 0);
434 assert!(registry.is_empty());
435 }
436
437 #[test]
438 fn test_registry_is_empty() {
439 let registry = AnnotationRegistry::new();
440 assert!(registry.is_empty());
441
442 let mut registry2 = AnnotationRegistry::new();
443 registry2.register(Rect::new(0, 0, 10, 1), Annotation::button("btn"));
444 assert!(!registry2.is_empty());
445 }
446
447 #[test]
448 fn test_registry_regions_at() {
449 let mut registry = AnnotationRegistry::new();
450
451 registry.open(Rect::new(0, 0, 80, 24), Annotation::container("main"));
453 registry.register(Rect::new(10, 10, 20, 3), Annotation::button("submit"));
455 registry.close();
456
457 let regions = registry.regions_at(15, 11);
459 assert_eq!(regions.len(), 2);
460 }
461
462 #[test]
463 fn test_registry_interactive_regions() {
464 let mut registry = AnnotationRegistry::new();
465
466 registry.register(Rect::new(0, 0, 80, 24), Annotation::container("main"));
468 registry.register(Rect::new(0, 0, 10, 1), Annotation::label("title"));
469
470 registry.register(Rect::new(0, 2, 10, 1), Annotation::button("btn"));
472 registry.register(Rect::new(0, 4, 10, 1), Annotation::input("input"));
473 registry.register(Rect::new(0, 6, 10, 1), Annotation::checkbox("checkbox"));
474
475 let interactive = registry.interactive_regions();
476 assert_eq!(interactive.len(), 3);
477 }
478
479 #[test]
480 fn test_registry_root_regions() {
481 let mut registry = AnnotationRegistry::new();
482
483 registry.open(Rect::new(0, 0, 40, 24), Annotation::container("left"));
485 registry.register(Rect::new(5, 5, 10, 1), Annotation::button("btn1"));
486 registry.close();
487
488 registry.open(Rect::new(40, 0, 40, 24), Annotation::container("right"));
489 registry.register(Rect::new(45, 5, 10, 1), Annotation::button("btn2"));
490 registry.close();
491
492 let roots = registry.root_regions();
493 assert_eq!(roots.len(), 2);
494 }
495
496 #[test]
497 fn test_registry_children_of() {
498 let mut registry = AnnotationRegistry::new();
499
500 let parent = registry.open(Rect::new(0, 0, 80, 24), Annotation::container("parent"));
501 registry.register(Rect::new(5, 5, 10, 1), Annotation::button("child1"));
502 registry.register(Rect::new(5, 8, 10, 1), Annotation::button("child2"));
503 registry.close();
504
505 let children = registry.children_of(parent);
506 assert_eq!(children.len(), 2);
507
508 let children = registry.children_of(999);
510 assert!(children.is_empty());
511 }
512
513 #[test]
514 fn test_registry_regions_accessor() {
515 let mut registry = AnnotationRegistry::new();
516
517 registry.register(Rect::new(0, 0, 10, 1), Annotation::button("a"));
518 registry.register(Rect::new(0, 2, 10, 1), Annotation::button("b"));
519
520 let regions = registry.regions();
521 assert_eq!(regions.len(), 2);
522 }
523
524 #[test]
525 fn test_serializable_rect_from_rect() {
526 let ratatui_rect = Rect::new(10, 20, 30, 40);
527 let serializable: SerializableRect = ratatui_rect.into();
528
529 assert_eq!(serializable.x, 10);
530 assert_eq!(serializable.y, 20);
531 assert_eq!(serializable.width, 30);
532 assert_eq!(serializable.height, 40);
533 }
534
535 #[test]
536 fn test_rect_from_serializable_rect() {
537 let serializable = SerializableRect::new(10, 20, 30, 40);
538 let ratatui_rect: Rect = serializable.into();
539
540 assert_eq!(ratatui_rect.x, 10);
541 assert_eq!(ratatui_rect.y, 20);
542 assert_eq!(ratatui_rect.width, 30);
543 assert_eq!(ratatui_rect.height, 40);
544 }
545
546 #[test]
547 fn test_registry_get_non_existent() {
548 let registry = AnnotationRegistry::new();
549 assert!(registry.get(0).is_none());
550 assert!(registry.get(999).is_none());
551 }
552
553 #[test]
554 fn test_registry_get_by_id_not_found() {
555 let mut registry = AnnotationRegistry::new();
556 registry.register(Rect::new(0, 0, 10, 1), Annotation::button("exists"));
557
558 assert!(registry.get_by_id("nonexistent").is_none());
559 }
560
561 #[test]
562 fn test_registry_focused_region_none() {
563 let mut registry = AnnotationRegistry::new();
564
565 registry.register(Rect::new(0, 0, 10, 1), Annotation::input("a"));
566 registry.register(Rect::new(0, 2, 10, 1), Annotation::input("b"));
567
568 assert!(registry.focused_region().is_none());
570 }
571
572 #[test]
573 fn test_registry_close_at_zero_depth() {
574 let mut registry = AnnotationRegistry::new();
575
576 registry.register(Rect::new(0, 0, 10, 1), Annotation::button("btn"));
578
579 registry.close();
581 registry.close(); assert_eq!(registry.len(), 1);
584 }
585
586 #[test]
587 fn test_registry_default() {
588 let registry = AnnotationRegistry::default();
589 assert!(registry.is_empty());
590 }
591
592 #[test]
593 fn test_region_info_fields() {
594 let mut registry = AnnotationRegistry::new();
595
596 let parent_idx = registry.open(Rect::new(0, 0, 80, 24), Annotation::container("parent"));
597 let child_idx = registry.register(Rect::new(5, 5, 10, 1), Annotation::button("child"));
598 registry.close();
599
600 let child = registry.get(child_idx).unwrap();
601 assert_eq!(child.area.x, 5);
602 assert_eq!(child.area.y, 5);
603 assert_eq!(child.area.width, 10);
604 assert_eq!(child.area.height, 1);
605 assert_eq!(child.parent, Some(parent_idx));
606 assert!(child.children.is_empty());
607 assert_eq!(child.depth, 1);
608 }
609}