1use ansiq_core::{Alignment, ElementKind, Node, Rect, Style};
2use ansiq_render::FrameBuffer;
3
4use crate::draw_border::draw_border;
5use crate::draw_common::{
6 border_style, fill_surface, fill_surface_in_regions, intersects_any, single_title, title_style,
7};
8use crate::draw_cursor::find_cursor_position;
9use crate::draw_scrollbar::draw_scrollbar;
10use crate::draw_table::draw_table;
11use crate::draw_text::{draw_paragraph, draw_rich_text, draw_scroll_text, draw_text, text_content};
12use crate::draw_widgets::{
13 draw_bar_chart, draw_canvas, draw_chart, draw_gauge, draw_line_gauge, draw_list, draw_monthly,
14 draw_sparkline, draw_tabs,
15};
16
17pub fn draw_tree<Message>(tree: &Node<Message>, focused: Option<usize>, buffer: &mut FrameBuffer) {
18 buffer.fill_rect(
19 Rect::new(0, 0, buffer.width(), buffer.height()),
20 ' ',
21 tree.element.style,
22 );
23 draw_node(tree, focused, buffer);
24}
25
26pub fn draw_tree_in_regions<Message>(
27 tree: &Node<Message>,
28 focused: Option<usize>,
29 buffer: &mut FrameBuffer,
30 regions: &[Rect],
31) {
32 if regions.is_empty() {
33 return;
34 }
35
36 for region in regions {
37 if let Some(clip) = region.intersection(Rect::new(0, 0, buffer.width(), buffer.height())) {
38 buffer.fill_rect(clip, ' ', tree.element.style);
39 }
40 }
41
42 draw_node_in_regions(tree, focused, buffer, regions);
43}
44
45pub fn cursor_position<Message>(
46 tree: &Node<Message>,
47 focused: Option<usize>,
48) -> Option<(u16, u16)> {
49 let focused = focused?;
50 find_cursor_position(tree, focused)
51}
52
53fn draw_node<Message>(node: &Node<Message>, focused: Option<usize>, buffer: &mut FrameBuffer) {
54 draw_node_impl(node, focused, buffer, None);
55}
56
57fn draw_node_in_regions<Message>(
58 node: &Node<Message>,
59 focused: Option<usize>,
60 buffer: &mut FrameBuffer,
61 regions: &[Rect],
62) {
63 draw_node_impl(node, focused, buffer, Some(regions));
64}
65
66fn draw_node_impl<Message>(
67 node: &Node<Message>,
68 focused: Option<usize>,
69 buffer: &mut FrameBuffer,
70 regions: Option<&[Rect]>,
71) {
72 if regions.is_some_and(|regions| !intersects_any(node.rect, regions)) {
73 return;
74 }
75
76 match &node.element.kind {
77 ElementKind::Box(_) | ElementKind::Shell(_) => {
78 fill_node_surface(buffer, node.rect, node.element.style, regions);
79 draw_children(node, focused, buffer, regions);
80 }
81 ElementKind::Component(_) => {
82 draw_children(node, focused, buffer, regions);
83 }
84 ElementKind::Pane(props) => {
85 fill_node_surface(buffer, node.rect, node.element.style, regions);
86 let titles = single_title(props.title.as_deref());
87 draw_border(
88 buffer,
89 node.rect,
90 &titles,
91 Alignment::Left,
92 ansiq_core::TitlePosition::Top,
93 ansiq_core::Borders::ALL,
94 ansiq_core::BorderType::Rounded,
95 None,
96 border_style(node.element.style, Style::default(), false),
97 title_style(node.element.style, Style::default()),
98 );
99 draw_children(node, focused, buffer, regions);
100 }
101 ElementKind::Block(props) => {
102 fill_node_surface(buffer, node.rect, node.element.style, regions);
103 if !props.borders.is_empty() || !props.titles.is_empty() {
104 draw_border(
105 buffer,
106 node.rect,
107 &props.titles,
108 props.title_alignment,
109 props.title_position,
110 props.borders,
111 props.border_type,
112 props.border_set,
113 border_style(node.element.style, props.border_style, false),
114 title_style(node.element.style, props.title_style),
115 );
116 }
117 draw_children(node, focused, buffer, regions);
118 }
119 ElementKind::ScrollView(props) => {
120 fill_node_surface(buffer, node.rect, node.element.style, regions);
121 if let Some(child) = node.children.first() {
122 if let Some((content, style)) = text_content(child) {
123 draw_scroll_text(
124 buffer,
125 node.rect,
126 &content,
127 style,
128 props.follow_bottom,
129 props.offset,
130 );
131 return;
132 }
133 }
134
135 draw_children(node, focused, buffer, regions);
136 }
137 ElementKind::StreamingText(props) => {
138 fill_node_surface(buffer, node.rect, node.element.style, regions);
139 draw_text(buffer, node.rect, &props.content, node.element.style);
140 }
141 ElementKind::Text(props) => {
142 fill_node_surface(buffer, node.rect, node.element.style, regions);
143 draw_text(buffer, node.rect, &props.content, node.element.style);
144 }
145 ElementKind::Paragraph(props) => {
146 fill_node_surface(buffer, node.rect, node.element.style, regions);
147 draw_paragraph(buffer, node.rect, props, node.element.style);
148 }
149 ElementKind::RichText(props) => {
150 fill_node_surface(buffer, node.rect, node.element.style, regions);
151 draw_rich_text(buffer, node.rect, &props.block);
152 }
153 ElementKind::List(props) => {
154 fill_node_surface(buffer, node.rect, node.element.style, regions);
155 draw_list(buffer, node.rect, props, node.element.style);
156 }
157 ElementKind::Tabs(props) => {
158 fill_node_surface(buffer, node.rect, node.element.style, regions);
159 draw_tabs(buffer, node.rect, props, node.element.style);
160 }
161 ElementKind::Gauge(props) => {
162 fill_node_surface(buffer, node.rect, node.element.style, regions);
163 draw_gauge(buffer, node.rect, props, node.element.style);
164 }
165 ElementKind::Clear(_) => {
166 clear_node_surface(buffer, node.rect, regions);
167 }
168 ElementKind::LineGauge(props) => {
169 fill_node_surface(buffer, node.rect, node.element.style, regions);
170 draw_line_gauge(buffer, node.rect, props, node.element.style);
171 }
172 ElementKind::Table(props) => {
173 fill_node_surface(buffer, node.rect, node.element.style, regions);
174 draw_table(buffer, node.rect, props, node.element.style);
175 }
176 ElementKind::Sparkline(props) => {
177 fill_node_surface(buffer, node.rect, node.element.style, regions);
178 draw_sparkline(buffer, node.rect, props, node.element.style);
179 }
180 ElementKind::BarChart(props) => {
181 fill_node_surface(buffer, node.rect, node.element.style, regions);
182 draw_bar_chart(buffer, node.rect, props, node.element.style);
183 }
184 ElementKind::Chart(props) => {
185 fill_node_surface(buffer, node.rect, node.element.style, regions);
186 draw_chart(buffer, node.rect, props, node.element.style);
187 }
188 ElementKind::Canvas(props) => {
189 fill_node_surface(buffer, node.rect, node.element.style, regions);
190 draw_canvas(buffer, node.rect, props, node.element.style);
191 }
192 ElementKind::Monthly(props) => {
193 fill_node_surface(buffer, node.rect, node.element.style, regions);
194 draw_monthly(buffer, node.rect, props, node.element.style);
195 }
196 ElementKind::Scrollbar(props) => {
197 fill_node_surface(buffer, node.rect, node.element.style, regions);
198 draw_scrollbar(buffer, node.rect, props, node.element.style);
199 }
200 ElementKind::StatusBar(props) => {
201 fill_node_surface(buffer, node.rect, node.element.style, regions);
202 draw_text(buffer, node.rect, &props.content, node.element.style);
203 }
204 ElementKind::Input(props) => {
205 fill_node_surface(buffer, node.rect, node.element.style, regions);
206 let titles = Vec::new();
207 draw_border(
208 buffer,
209 node.rect,
210 &titles,
211 Alignment::Left,
212 ansiq_core::TitlePosition::Top,
213 ansiq_core::Borders::ALL,
214 ansiq_core::BorderType::Rounded,
215 None,
216 border_style(
217 node.element.style,
218 Style::default(),
219 focused == Some(node.id),
220 ),
221 node.element.style,
222 );
223 let inner = node.rect.shrink(1);
224 let content = if props.value.is_empty() {
225 &props.placeholder
226 } else {
227 &props.value
228 };
229 let content_style = if props.value.is_empty() {
230 node.element.style.fg(ansiq_core::Color::Grey)
231 } else {
232 node.element.style
233 };
234 draw_text(buffer, inner, content, content_style);
235 }
236 }
237}
238
239fn draw_children<Message>(
240 node: &Node<Message>,
241 focused: Option<usize>,
242 buffer: &mut FrameBuffer,
243 regions: Option<&[Rect]>,
244) {
245 for child in &node.children {
246 match regions {
247 Some(regions) => draw_node_in_regions(child, focused, buffer, regions),
248 None => draw_node(child, focused, buffer),
249 }
250 }
251}
252
253fn fill_node_surface(buffer: &mut FrameBuffer, rect: Rect, style: Style, regions: Option<&[Rect]>) {
254 match regions {
255 Some(regions) => fill_surface_in_regions(buffer, rect, style, regions),
256 None => fill_surface(buffer, rect, style),
257 }
258}
259
260fn clear_node_surface(buffer: &mut FrameBuffer, rect: Rect, regions: Option<&[Rect]>) {
261 match regions {
262 Some(regions) => {
263 for region in regions {
264 if let Some(clip) = rect.intersection(*region) {
265 buffer.fill_rect(clip, ' ', Style::default());
266 }
267 }
268 }
269 None => buffer.fill_rect(rect, ' ', Style::default()),
270 }
271}