anathema_default_widgets/stacks/
vstack.rs1use std::ops::ControlFlow;
2
3use anathema_geometry::Size;
4use anathema_value_resolver::AttributeStorage;
5use anathema_widgets::error::Result;
6use anathema_widgets::layout::{Constraints, LayoutCtx, PositionCtx};
7use anathema_widgets::paint::{PaintCtx, SizePos};
8use anathema_widgets::{LayoutForEach, PaintChildren, PositionChildren, Widget, WidgetId};
9
10use super::Stack;
11use crate::layout::Axis;
12
13pub struct VStack(Stack);
14
15impl Default for VStack {
16 fn default() -> Self {
17 VStack(Stack(Axis::Vertical))
18 }
19}
20
21impl Widget for VStack {
22 fn layout<'bp>(
23 &mut self,
24 children: LayoutForEach<'_, 'bp>,
25 constraints: Constraints,
26 id: WidgetId,
27 ctx: &mut LayoutCtx<'_, 'bp>,
28 ) -> Result<Size> {
29 self.0.layout(children, constraints, id, ctx)
30 }
31
32 fn position<'bp>(
33 &mut self,
34 children: PositionChildren<'_, 'bp>,
35 attributes: WidgetId,
36 attribute_storage: &AttributeStorage<'bp>,
37 ctx: PositionCtx,
38 ) {
39 self.0.position(children, attributes, attribute_storage, ctx)
40 }
41
42 fn paint<'bp>(
43 &mut self,
44 mut children: PaintChildren<'_, 'bp>,
45 _id: WidgetId,
46 attribute_storage: &AttributeStorage<'bp>,
47 mut ctx: PaintCtx<'_, SizePos>,
48 ) {
49 _ = children.each(|child, children| {
50 let ctx = ctx.to_unsized();
51 child.paint(children, ctx, attribute_storage);
52 ControlFlow::Continue(())
53 });
54 }
55}
56
57#[cfg(test)]
58mod test {
59
60 use crate::testing::TestRunner;
61
62 #[test]
63 fn vstack() {
64 let tpl = "
65 vstack
66 border
67 text state.value
68 for i in [2]
69 border
70 text i
71 ";
72
73 let expected_first = "
74 ╔═══╗
75 ║┌─┐║
76 ║│0│║
77 ║└─┘║
78 ║┌─┐║
79 ║│2│║
80 ║└─┘║
81 ╚═══╝
82 ";
83
84 let expected_second = "
85 ╔═══╗
86 ║┌─┐║
87 ║│7│║
88 ║└─┘║
89 ║┌─┐║
90 ║│2│║
91 ║└─┘║
92 ╚═══╝
93 ";
94
95 TestRunner::new(tpl, (3, 6))
96 .instance()
97 .render_assert(expected_first)
98 .with_state(|state| *state.value.to_mut() = 7)
99 .render_assert(expected_second);
100 }
101
102 #[test]
103 fn fixed_height() {
104 let tpl = "
105 vstack [height: 2]
106 for i in [0, 1]
107 border
108 text i
109 ";
110
111 let expected = "
112 ╔══╗
113 ║┌┐║
114 ║└┘║
115 ╚══╝
116 ";
117
118 TestRunner::new(tpl, (2, 2)).instance().render_assert(expected);
119 }
120
121 #[test]
122 fn fixed_width() {
123 let tpl = "
124 vstack [width: 7, height: 3]
125 border
126 expand
127 text 'a'
128 ";
129
130 let expected = "
131 ╔═══════╗
132 ║┌─────┐║
133 ║│a │║
134 ║└─────┘║
135 ╚═══════╝
136 ";
137
138 TestRunner::new(tpl, (7, 3)).instance().render_assert(expected);
139 }
140
141 #[test]
142 fn bottom_up_vstack() {
143 let tpl = "
144 vstack [direction: 'back']
145 text 'b'
146 border
147 text 'a'
148 ";
149
150 let expected = "
151 ╔══════╗
152 ║ ║
153 ║┌─┐ ║
154 ║│a│ ║
155 ║└─┘ ║
156 ║b ║
157 ╚══════╝
158 ";
159
160 TestRunner::new(tpl, (6, 5)).instance().render_assert(expected);
161 }
162
163 #[test]
164 fn vstack_overflow() {
165 let tpl = "
166 vstack
167 text 'a'
168 text 'b'
169 text 'c'
170 text 'd'
171 ";
172
173 let expected = "
174 ╔══════╗
175 ║a ║
176 ║b ║
177 ╚══════╝
178 ";
179
180 let mut runner = TestRunner::new(tpl, (6, 2));
181 let mut runner = runner.instance();
182 runner.render_assert(expected);
183 }
184}