1use frust_core::{
10 AnyView, BoxConstraints, BuildCtx, ChangeFlags, ChildPod, EventCtx, EventResult, InputEvent,
11 LayoutCtx, PaintCtx, PaintScene, SemanticsCtx, View, ViewSeq, Widget, any,
12};
13use kurbo::{Point, Size};
14
15pub struct StackView<State: 'static> {
17 children: Vec<AnyView<State>>,
18}
19
20#[allow(non_snake_case)]
38pub fn Stack<State: 'static, M>(children: impl ViewSeq<State, M>) -> StackView<State> {
39 stack().children(children)
40}
41
42pub fn stack<State: 'static>() -> StackView<State> {
44 StackView {
45 children: Vec::new(),
46 }
47}
48
49impl<State: 'static> StackView<State> {
50 pub fn child<V: View<State>>(mut self, view: V) -> Self {
52 self.children.push(any(view));
53 self
54 }
55
56 pub fn children<M>(mut self, children: impl ViewSeq<State, M>) -> Self {
61 children.extend_views(&mut self.children);
62 self
63 }
64
65 pub fn when(self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self {
67 if cond { f(self) } else { self }
68 }
69
70 pub fn when_some<T>(self, opt: Option<T>, f: impl FnOnce(Self, T) -> Self) -> Self {
72 match opt {
73 Some(value) => f(self, value),
74 None => self,
75 }
76 }
77}
78
79pub struct StackWidget {
84 children: Vec<ChildPod>,
85}
86
87impl<State: 'static> View<State> for StackView<State> {
88 type Element = StackWidget;
89
90 fn build(&self, ctx: &mut BuildCtx<'_>) -> StackWidget {
91 StackWidget {
92 children: self
93 .children
94 .iter()
95 .map(|view| crate::authoring::build_child(view, ctx))
96 .collect(),
97 }
98 }
99
100 fn rebuild(
101 &self,
102 prev: &Self,
103 element: &mut StackWidget,
104 ctx: &mut BuildCtx<'_>,
105 ) -> ChangeFlags {
106 crate::authoring::rebuild_children(
110 &prev.children,
111 &self.children,
112 &mut element.children,
113 ctx,
114 |view| view,
115 |_| None,
116 )
117 }
118
119 fn teardown(&self, element: &mut StackWidget, ctx: &mut BuildCtx<'_>) {
120 for (view, pod) in self.children.iter().zip(element.children.iter_mut()) {
121 crate::authoring::teardown_child(view, pod, ctx);
122 }
123 }
124}
125
126impl Widget for StackWidget {
127 fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
128 let loose = bc.loosen();
129 let mut max = Size::ZERO;
130 for pod in &mut self.children {
131 let size = pod.layout_child(ctx, &loose);
132 pod.set_origin(Point::ZERO);
133 max = Size::new(max.width.max(size.width), max.height.max(size.height));
134 }
135 bc.constrain(max)
136 }
137
138 fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut dyn PaintScene) {
139 for pod in &mut self.children {
140 pod.paint_child(ctx, scene);
141 }
142 }
143
144 fn event(&mut self, ctx: &mut EventCtx, event: &InputEvent) -> EventResult {
145 crate::authoring::route_event(&mut self.children, ctx, event)
146 }
147
148 fn semantics(&self, ctx: &mut SemanticsCtx) {
149 for pod in &self.children {
151 pod.semantics_child(ctx);
152 }
153 }
154
155 crate::authoring::visit_children!(children);
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::test_support::{leaf_any, probe};
162 use frust_core::{BuildCtx, LayoutCtx, PointerButton, PointerEvent, PointerPhase};
163
164 fn build<S: 'static>(view: &StackView<S>) -> StackWidget {
165 let mut counter = 0u64;
166 view.build(&mut BuildCtx::new(&mut counter))
167 }
168
169 fn down(x: f64, y: f64) -> InputEvent {
170 InputEvent::Pointer(PointerEvent {
171 phase: PointerPhase::Down,
172 position: Point::new(x, y),
173 button: PointerButton::Primary,
174 })
175 }
176
177 #[test]
178 fn sizes_to_largest_child() {
179 let view: StackView<()> = stack()
180 .child(leaf_any(30.0, 60.0))
181 .child(leaf_any(80.0, 20.0));
182 let mut w = build(&view);
183 let mut lctx = LayoutCtx::new();
184 let size = w.layout(&mut lctx, &BoxConstraints::loose(Size::new(500.0, 500.0)));
186 assert_eq!(size, Size::new(80.0, 60.0));
187 assert_eq!(w.children[0].origin(), Point::ZERO);
189 assert_eq!(w.children[1].origin(), Point::ZERO);
190 }
191
192 #[test]
193 fn hit_test_prefers_topmost_child() {
194 let view: StackView<Vec<u32>> = stack()
196 .child(probe(0).into_any())
197 .child(probe(1).into_any());
198 let mut w = build(&view);
199 let mut lctx = LayoutCtx::new();
200 w.layout(&mut lctx, &BoxConstraints::tight(Size::new(100.0, 100.0)));
201
202 let mut log: Vec<u32> = Vec::new();
203 let mut ectx = frust_core::EventCtx::new(&mut log, Point::ZERO, Size::new(100.0, 100.0));
204 let result = w.event(&mut ectx, &down(50.0, 50.0));
205
206 assert_eq!(result, EventResult::Handled);
207 assert_eq!(log, vec![1]);
209 }
210}