1use std::any::Any;
6use std::sync::{Arc, Mutex};
7
8use super::component::Component;
9use super::layout_node::{LayoutNode, LayoutNodeProvider, StackLayoutEntry, VStackNode};
10
11#[derive(Debug, Clone, Default)]
13pub struct StackEntryOptions {
14 pub basis: Option<usize>,
16 pub grow: usize,
18 pub shrink: usize,
20 pub min_size: usize,
22 pub max_size: Option<usize>,
24}
25
26impl StackEntryOptions {
27 pub fn with_basis(size: usize) -> Self {
29 Self {
30 basis: Some(size),
31 ..Default::default()
32 }
33 }
34
35 pub fn auto() -> Self {
37 Self::default()
38 }
39
40 pub fn basis(mut self, size: usize) -> Self {
42 self.basis = Some(size);
43 self
44 }
45
46 pub fn grow(mut self, factor: usize) -> Self {
48 self.grow = factor;
49 self
50 }
51
52 pub fn shrink(mut self, factor: usize) -> Self {
54 self.shrink = factor;
55 self
56 }
57
58 pub fn min_size(mut self, size: usize) -> Self {
60 self.min_size = size;
61 self
62 }
63
64 pub fn max_size(mut self, size: usize) -> Self {
66 self.max_size = Some(size);
67 self
68 }
69}
70
71#[derive(Clone)]
73pub struct StackEntry {
74 pub component: Arc<dyn Component>,
75 pub options: StackEntryOptions,
76}
77
78impl StackEntry {
79 pub fn new(component: Arc<dyn Component>) -> Self {
81 Self {
82 component,
83 options: StackEntryOptions::default(),
84 }
85 }
86
87 pub fn with_options(component: Arc<dyn Component>, options: StackEntryOptions) -> Self {
89 Self { component, options }
90 }
91
92 pub fn basis(mut self, size: usize) -> Self {
94 self.options.basis = Some(size);
95 self
96 }
97
98 pub fn grow(mut self, factor: usize) -> Self {
100 self.options.grow = factor;
101 self
102 }
103
104 pub fn shrink(mut self, factor: usize) -> Self {
106 self.options.shrink = factor;
107 self
108 }
109
110 pub fn min_size(mut self, size: usize) -> Self {
112 self.options.min_size = size;
113 self
114 }
115
116 pub fn to_layout_entry(&self) -> StackLayoutEntry {
118 StackLayoutEntry {
119 component: self.component.clone(),
120 basis: self.options.basis,
121 grow: self.options.grow,
122 shrink: self.options.shrink,
123 min_size: self.options.min_size,
124 max_size: self.options.max_size.unwrap_or(usize::MAX),
125 visible: None,
126 }
127 }
128}
129
130pub enum StackChild {
132 Component(Arc<dyn Component>),
133 Entry(StackEntry),
134}
135
136impl From<Arc<dyn Component>> for StackChild {
137 fn from(component: Arc<dyn Component>) -> Self {
138 StackChild::Component(component)
139 }
140}
141
142impl From<StackEntry> for StackChild {
143 fn from(entry: StackEntry) -> Self {
144 StackChild::Entry(entry)
145 }
146}
147
148#[derive(Debug, Clone, Default)]
150pub struct StackOptions {
151 pub gap: usize,
153 pub align: StackAlign,
155}
156
157#[derive(Debug, Clone, Copy, Default)]
159pub enum StackAlign {
160 #[default]
161 Stretch,
162 Start,
163 Center,
164 End,
165}
166
167#[derive(Clone)]
171pub struct VStack {
172 children: Arc<Mutex<Vec<StackEntry>>>,
173 options: StackOptions,
174}
175
176impl VStack {
177 pub fn new() -> Self {
179 Self {
180 children: Arc::new(Mutex::new(Vec::new())),
181 options: StackOptions::default(),
182 }
183 }
184
185 pub fn with_options(options: StackOptions) -> Self {
187 Self {
188 children: Arc::new(Mutex::new(Vec::new())),
189 options,
190 }
191 }
192
193 pub fn from_children(children: Vec<StackChild>) -> Self {
195 let entries: Vec<StackEntry> = children
196 .into_iter()
197 .map(|child| match child {
198 StackChild::Component(c) => StackEntry::new(c),
199 StackChild::Entry(e) => e,
200 })
201 .collect();
202
203 Self {
204 children: Arc::new(Mutex::new(entries)),
205 options: StackOptions::default(),
206 }
207 }
208
209 pub fn from_entries(children: Vec<StackEntry>) -> Self {
211 Self {
212 children: Arc::new(Mutex::new(children)),
213 options: StackOptions::default(),
214 }
215 }
216
217 pub fn add_child(&self, component: Arc<dyn Component>) {
219 if let Ok(mut children) = self.children.lock() {
220 children.push(StackEntry::new(component));
221 }
222 }
223
224 pub fn add_child_with_options(
226 &self,
227 component: Arc<dyn Component>,
228 options: StackEntryOptions,
229 ) {
230 if let Ok(mut children) = self.children.lock() {
231 children.push(StackEntry::with_options(component, options));
232 }
233 }
234
235 pub fn remove_child(&self, component: &Arc<dyn Component>) {
237 if let Ok(mut children) = self.children.lock() {
238 children.retain(|e| !Arc::ptr_eq(&e.component, component));
239 }
240 }
241
242 pub fn clear(&self) {
244 if let Ok(mut children) = self.children.lock() {
245 children.clear();
246 }
247 }
248
249 pub fn gap(&self) -> usize {
251 self.options.gap
252 }
253
254 pub fn align(&self) -> StackAlign {
256 self.options.align
257 }
258
259 pub fn get_children(&self) -> Vec<StackEntry> {
261 self.children.lock().map(|c| c.clone()).unwrap_or_default()
262 }
263
264 pub fn get_layout_entries(&self) -> Vec<StackLayoutEntry> {
266 self.children
267 .lock()
268 .map(|c| c.iter().map(|e| e.to_layout_entry()).collect())
269 .unwrap_or_default()
270 }
271
272 #[allow(dead_code)]
274 fn intrinsic_height(&self, width: usize) -> usize {
275 if let Ok(children) = self.children.lock() {
276 let mut total = 0;
277 for (i, entry) in children.iter().enumerate() {
278 let child_height = entry.component.render(width).len();
279 total += child_height.max(entry.options.min_size);
280 if i > 0 && self.options.gap > 0 {
282 total += self.options.gap;
283 }
284 }
285 total
286 } else {
287 0
288 }
289 }
290}
291
292impl Default for VStack {
293 fn default() -> Self {
294 Self::new()
295 }
296}
297
298impl Component for VStack {
299 fn render(&self, width: usize) -> Vec<String> {
300 let mut lines = Vec::new();
301
302 if let Ok(children) = self.children.lock() {
303 for (i, entry) in children.iter().enumerate() {
306 let child_lines = entry.component.render(width);
307 lines.extend(child_lines);
308
309 if i < children.len() - 1 && self.options.gap > 0 {
311 for _ in 0..self.options.gap {
312 lines.push(String::new());
313 }
314 }
315 }
316 }
317
318 lines
319 }
320
321 fn invalidate(&self) {
322 if let Ok(children) = self.children.lock() {
323 for entry in children.iter() {
324 entry.component.invalidate();
325 }
326 }
327 }
328
329 fn as_any(&self) -> &dyn Any {
330 self
331 }
332}
333
334impl LayoutNodeProvider for VStack {
335 fn layout_node(&self) -> Option<LayoutNode> {
336 Some(LayoutNode::VStack(VStackNode {
337 entries: self.get_layout_entries(),
338 gap: self.options.gap,
339 align: self.options.align,
340 }))
341 }
342}
343
344pub fn layout_vstack_constrained(
353 children: &[StackEntry],
354 width: usize,
355 height: usize,
356 gap: usize,
357) -> Vec<(Arc<dyn Component>, usize)> {
358 layout_vstack_constrained_with(children, width, height, gap, |component, width| {
359 component.render(width).len()
360 })
361}
362
363pub(crate) fn layout_vstack_constrained_with<F>(
369 children: &[StackEntry],
370 width: usize,
371 height: usize,
372 gap: usize,
373 mut measure: F,
374) -> Vec<(Arc<dyn Component>, usize)>
375where
376 F: FnMut(&Arc<dyn Component>, usize) -> usize,
377{
378 let n = children.len();
379 if n == 0 {
380 return Vec::new();
381 }
382
383 let total_gap = if n > 1 { gap * (n - 1) } else { 0 };
384 let available = height.saturating_sub(total_gap);
385
386 let mut sizes: Vec<usize> = Vec::with_capacity(n);
388 let mut total_basis = 0usize;
389 let mut total_grow = 0usize;
390 let mut grow_indices = Vec::new();
391
392 for (i, entry) in children.iter().enumerate() {
393 let basis = entry
397 .options
398 .basis
399 .unwrap_or_else(|| measure(&entry.component, width));
400 let min_size = entry.options.min_size;
401 let max_size = entry.options.max_size.unwrap_or(usize::MAX);
402
403 let size = basis.clamp(min_size, max_size);
404 sizes.push(size);
405 total_basis += size;
406
407 if entry.options.grow > 0 {
408 total_grow += entry.options.grow;
409 grow_indices.push(i);
410 }
411 }
412
413 if total_basis < available && total_grow > 0 {
415 let remaining = available - total_basis;
417 let mut to_distribute = remaining;
418
419 for &i in &grow_indices {
420 if total_grow == 0 {
421 break;
422 }
423 let entry = &children[i];
424 let share = (remaining * entry.options.grow) / total_grow;
425 let max_size = entry.options.max_size.unwrap_or(usize::MAX);
426 let new_size = (sizes[i] + share).min(max_size);
427 let added = new_size - sizes[i];
428 sizes[i] = new_size;
429 to_distribute = to_distribute.saturating_sub(added);
430 }
431
432 for &i in &grow_indices {
434 if to_distribute == 0 {
435 break;
436 }
437 let entry = &children[i];
438 let max_size = entry.options.max_size.unwrap_or(usize::MAX);
439 if sizes[i] < max_size {
440 sizes[i] += 1;
441 to_distribute -= 1;
442 }
443 }
444 } else if total_basis > available {
445 let overflow = total_basis - available;
447 let mut to_shrink = overflow;
448
449 let shrinkable: Vec<(usize, usize)> = children
451 .iter()
452 .enumerate()
453 .filter(|(i, e)| e.options.shrink > 0 && sizes[*i] > e.options.min_size)
454 .map(|(i, e)| (i, e.options.shrink))
455 .collect();
456
457 let total_shrink: usize = shrinkable.iter().map(|(_, s)| *s).sum();
458
459 if total_shrink > 0 {
460 for (i, shrink) in &shrinkable {
461 if to_shrink == 0 {
462 break;
463 }
464 let share = (overflow * *shrink) / total_shrink;
465 let min_size = children[*i].options.min_size;
466 let new_size = sizes[*i].saturating_sub(share).max(min_size);
467 let removed = sizes[*i] - new_size;
468 sizes[*i] = new_size;
469 to_shrink = to_shrink.saturating_sub(removed);
470 }
471 }
472 }
473
474 children
476 .iter()
477 .zip(sizes.into_iter())
478 .map(|(entry, size)| (entry.component.clone(), size))
479 .collect()
480}