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 {
81 children: Vec<ChildPod>,
82}
83
84impl<State: 'static> View<State> for StackView<State> {
85 type Element = StackWidget;
86
87 fn build(&self, ctx: &mut BuildCtx<'_>) -> StackWidget {
88 StackWidget {
89 children: self
90 .children
91 .iter()
92 .map(|view| crate::authoring::build_child(view, ctx))
93 .collect(),
94 }
95 }
96
97 fn rebuild(
98 &self,
99 prev: &Self,
100 element: &mut StackWidget,
101 ctx: &mut BuildCtx<'_>,
102 ) -> ChangeFlags {
103 crate::authoring::rebuild_children(
107 &prev.children,
108 &self.children,
109 &mut element.children,
110 ctx,
111 |view| view,
112 |_| None,
113 )
114 }
115
116 fn teardown(&self, element: &mut StackWidget, ctx: &mut BuildCtx<'_>) {
117 for (view, pod) in self.children.iter().zip(element.children.iter_mut()) {
118 crate::authoring::teardown_child(view, pod, ctx);
119 }
120 }
121}
122
123impl Widget for StackWidget {
124 fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
125 let loose = bc.loosen();
126 let mut max = Size::ZERO;
127 for pod in &mut self.children {
128 let size = pod.layout_child(ctx, &loose);
129 pod.set_origin(Point::ZERO);
130 max = Size::new(max.width.max(size.width), max.height.max(size.height));
131 }
132 bc.constrain(max)
133 }
134
135 fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut dyn PaintScene) {
136 for pod in &mut self.children {
137 pod.paint_child(ctx, scene);
138 }
139 }
140
141 fn event(&mut self, ctx: &mut EventCtx, event: &InputEvent) -> EventResult {
142 crate::authoring::route_event(&mut self.children, ctx, event)
143 }
144
145 fn semantics(&self, ctx: &mut SemanticsCtx) {
146 for pod in &self.children {
148 pod.semantics_child(ctx);
149 }
150 }
151
152 crate::authoring::visit_children!(children);
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::test_support::{leaf_any, probe};
159 use frust_core::{BuildCtx, LayoutCtx, PointerButton, PointerEvent, PointerPhase};
160
161 fn build<S: 'static>(view: &StackView<S>) -> StackWidget {
162 let mut counter = 0u64;
163 view.build(&mut BuildCtx::new(&mut counter))
164 }
165
166 fn down(x: f64, y: f64) -> InputEvent {
167 InputEvent::Pointer(PointerEvent {
168 phase: PointerPhase::Down,
169 position: Point::new(x, y),
170 button: PointerButton::Primary,
171 })
172 }
173
174 #[test]
175 fn sizes_to_largest_child() {
176 let view: StackView<()> = stack()
177 .child(leaf_any(30.0, 60.0))
178 .child(leaf_any(80.0, 20.0));
179 let mut w = build(&view);
180 let mut lctx = LayoutCtx::new();
181 let size = w.layout(&mut lctx, &BoxConstraints::loose(Size::new(500.0, 500.0)));
183 assert_eq!(size, Size::new(80.0, 60.0));
184 assert_eq!(w.children[0].origin(), Point::ZERO);
186 assert_eq!(w.children[1].origin(), Point::ZERO);
187 }
188
189 #[test]
190 fn hit_test_prefers_topmost_child() {
191 let view: StackView<Vec<u32>> = stack()
193 .child(probe(0).into_any())
194 .child(probe(1).into_any());
195 let mut w = build(&view);
196 let mut lctx = LayoutCtx::new();
197 w.layout(&mut lctx, &BoxConstraints::tight(Size::new(100.0, 100.0)));
198
199 let mut log: Vec<u32> = Vec::new();
200 let mut ectx = frust_core::EventCtx::new(&mut log, Point::ZERO, Size::new(100.0, 100.0));
201 let result = w.event(&mut ectx, &down(50.0, 50.0));
202
203 assert_eq!(result, EventResult::Handled);
204 assert_eq!(log, vec![1]);
206 }
207}