1use frust_core::{
10 AnyView, BoxConstraints, BuildCtx, ChangeFlags, ChildPod, EventCtx, EventResult, InputEvent,
11 LayoutCtx, PaintCtx, PaintScene, SemanticsCtx, View, Widget,
12};
13use kurbo::{Point, Size};
14
15pub struct StackView<State: 'static> {
17 children: Vec<AnyView<State>>,
18}
19
20#[allow(non_snake_case)]
22pub fn Stack<State: 'static>(children: Vec<AnyView<State>>) -> StackView<State> {
23 StackView { children }
24}
25
26pub struct StackWidget {
28 children: Vec<ChildPod>,
29}
30
31impl<State: 'static> View<State> for StackView<State> {
32 type Element = StackWidget;
33
34 fn build(&self, ctx: &mut BuildCtx<'_>) -> StackWidget {
35 StackWidget {
36 children: self
37 .children
38 .iter()
39 .map(|view| crate::authoring::build_child(view, ctx))
40 .collect(),
41 }
42 }
43
44 fn rebuild(
45 &self,
46 prev: &Self,
47 element: &mut StackWidget,
48 ctx: &mut BuildCtx<'_>,
49 ) -> ChangeFlags {
50 crate::authoring::rebuild_children(
54 &prev.children,
55 &self.children,
56 &mut element.children,
57 ctx,
58 |view| view,
59 |_| None,
60 )
61 }
62
63 fn teardown(&self, element: &mut StackWidget, ctx: &mut BuildCtx<'_>) {
64 for (view, pod) in self.children.iter().zip(element.children.iter_mut()) {
65 crate::authoring::teardown_child(view, pod, ctx);
66 }
67 }
68}
69
70impl Widget for StackWidget {
71 fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
72 let loose = bc.loosen();
73 let mut max = Size::ZERO;
74 for pod in &mut self.children {
75 let size = pod.layout_child(ctx, &loose);
76 pod.set_origin(Point::ZERO);
77 max = Size::new(max.width.max(size.width), max.height.max(size.height));
78 }
79 bc.constrain(max)
80 }
81
82 fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut dyn PaintScene) {
83 for pod in &mut self.children {
84 pod.paint_child(ctx, scene);
85 }
86 }
87
88 fn event(&mut self, ctx: &mut EventCtx, event: &InputEvent) -> EventResult {
89 crate::authoring::route_event(&mut self.children, ctx, event)
90 }
91
92 fn semantics(&self, ctx: &mut SemanticsCtx) {
93 for pod in &self.children {
95 pod.semantics_child(ctx);
96 }
97 }
98
99 crate::authoring::visit_children!(children);
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::test_support::{leaf_any, probe};
106 use frust_core::{BuildCtx, LayoutCtx, PointerButton, PointerEvent, PointerPhase};
107
108 fn build<S: 'static>(view: &StackView<S>) -> StackWidget {
109 let mut counter = 0u64;
110 view.build(&mut BuildCtx::new(&mut counter))
111 }
112
113 fn down(x: f64, y: f64) -> InputEvent {
114 InputEvent::Pointer(PointerEvent {
115 phase: PointerPhase::Down,
116 position: Point::new(x, y),
117 button: PointerButton::Primary,
118 })
119 }
120
121 #[test]
122 fn sizes_to_largest_child() {
123 let view: StackView<()> = Stack(vec![leaf_any(30.0, 60.0), leaf_any(80.0, 20.0)]);
124 let mut w = build(&view);
125 let mut lctx = LayoutCtx::new();
126 let size = w.layout(&mut lctx, &BoxConstraints::loose(Size::new(500.0, 500.0)));
128 assert_eq!(size, Size::new(80.0, 60.0));
129 assert_eq!(w.children[0].origin(), Point::ZERO);
131 assert_eq!(w.children[1].origin(), Point::ZERO);
132 }
133
134 #[test]
135 fn hit_test_prefers_topmost_child() {
136 let view: StackView<Vec<u32>> = Stack(vec![probe(0).into_any(), probe(1).into_any()]);
138 let mut w = build(&view);
139 let mut lctx = LayoutCtx::new();
140 w.layout(&mut lctx, &BoxConstraints::tight(Size::new(100.0, 100.0)));
141
142 let mut log: Vec<u32> = Vec::new();
143 let mut ectx = frust_core::EventCtx::new(&mut log, Point::ZERO, Size::new(100.0, 100.0));
144 let result = w.event(&mut ectx, &down(50.0, 50.0));
145
146 assert_eq!(result, EventResult::Handled);
147 assert_eq!(log, vec![1]);
149 }
150}