a2ui_tui/components/
list.rs1use ratatui::{Frame, layout::{Direction, Rect}};
4
5use a2ui_base::model::component_context::ComponentContext;
6use a2ui_base::protocol::common_types::{Align, ChildList, Justify};
7use crate::component_impl::TuiComponent;
8use crate::components::row::{render_static_children, render_template_children};
9
10pub struct ListComponent;
15
16impl TuiComponent for ListComponent {
17 fn name(&self) -> &'static str {
18 "List"
19 }
20
21 fn render(
22 &self,
23 ctx: &ComponentContext,
24 area: Rect,
25 frame: &mut Frame,
26 render_child: &mut dyn FnMut(&str, Rect, &mut Frame, &str),
27 measure_child: &mut dyn FnMut(&str, &str, u16) -> Option<u16>,
28 ) {
29 let comp_model = match ctx.components.get(&ctx.component_id) {
30 Some(m) => m,
31 None => return,
32 };
33
34 let children = match comp_model.children() {
35 Some(c) => c,
36 None => return,
37 };
38
39 let dir = list_direction(comp_model);
41
42 let justify = comp_model
43 .get_property::<Justify>("justify")
44 .unwrap_or(Justify::Start);
45 let align = comp_model
46 .get_property::<Align>("align")
47 .unwrap_or(Align::Stretch);
48
49 match children {
50 ChildList::Static(ids) => {
51 render_static_children(
52 ctx, area, frame, render_child, measure_child,
53 &ids, justify, align, dir,
54 );
55 }
56 ChildList::Template { component_id, path } => {
57 render_template_children(
58 ctx, area, frame, render_child, measure_child,
59 &component_id, &path, justify, align, dir,
60 );
61 }
62 }
63 }
64
65 fn natural_height(
67 &self,
68 ctx: &ComponentContext,
69 available_width: u16,
70 measure_child: &mut dyn FnMut(&str, &str, u16) -> Option<u16>,
71 ) -> Option<u16> {
72 let comp_model = ctx.components.get(&ctx.component_id)?;
73 let dir = list_direction(comp_model);
74 let ids = match comp_model.children()? {
75 ChildList::Static(ids) => ids,
76 ChildList::Template { component_id, path } => {
77 let count = match ctx.data_context.get(&path) {
78 Some(serde_json::Value::Array(arr)) => arr.len(),
79 _ => return None,
80 };
81 if count == 0 {
82 return Some(0);
83 }
84 let item_path = format!("{}/{}", path, 0);
85 let one = measure_child(&component_id, &item_path, available_width)?;
86 return match dir {
87 Direction::Vertical => Some(one.saturating_mul(count as u16)),
88 Direction::Horizontal => Some(one),
89 };
90 }
91 };
92 if ids.is_empty() {
93 return Some(0);
94 }
95 let base = ctx.data_context.base_path();
98 match dir {
99 Direction::Vertical => {
100 let mut sum: u16 = 0;
101 for id in &ids {
102 sum = sum.saturating_add(measure_child(id, base, available_width)?);
103 }
104 Some(sum)
105 }
106 Direction::Horizontal => {
107 let mut max: u16 = 0;
108 for id in &ids {
109 max = max.max(measure_child(id, base, available_width)?);
110 }
111 Some(max)
112 }
113 }
114 }
115}
116
117fn list_direction(
119 comp_model: &a2ui_base::model::component_model::ComponentModel,
120) -> Direction {
121 let direction: Option<String> = comp_model.get_property("direction");
122 match direction.as_deref() {
123 Some("horizontal") => Direction::Horizontal,
124 _ => Direction::Vertical,
125 }
126}