1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use crate::*;
use accesskit::Role;

pub const BUTTON_CORNER_RADIUS: f32 = 5.0;

/// Calls a function when the button is tapped.
pub fn button<A: 'static, F: Fn(&mut Context) -> A + 'static + Clone>(
    view: impl View + Clone,
    f: F,
) -> impl View {
    state(
        || false,
        move |hovering, cx| {
            let f = f.clone();
            view.clone()
                .padding(Auto)
                .background(rectangle().corner_radius(BUTTON_CORNER_RADIUS).color(
                    if cx[hovering] {
                        BUTTON_HOVER_COLOR
                    } else {
                        BUTTON_BACKGROUND_COLOR
                    },
                ))
                .tap(move |cx| f(cx))
                .hover(move |cx, inside| {
                    cx[hovering] = inside;
                })
                .role(Role::Button)
        },
    )
}

/// Version of button which emits an action directly instead of taking a callback.
pub fn button_a<A: Clone + 'static>(view: impl View + Clone, action: A) -> impl View {
    button(view, move |_| action.clone())
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_button() {
        let mut cx = Context::new();

        let ui = state(
            || false,
            |pushed, _| {
                button("button", move |cx| {
                    *pushed.get_mut(cx) = true;
                })
            },
        );
        let sz = [100.0, 100.0].into();

        let button_sz = ui.layout(
            cx.root_id,
            &mut LayoutArgs {
                sz,
                cx: &mut cx,
                text_bounds: &mut |_, _, _| LocalRect::new(LocalPoint::zero(), [90.0, 90.0].into()),
            },
        );

        assert_eq!(button_sz, sz);
        let s = StateHandle::<bool>::new(cx.root_id);
        assert!(!*s.get(&cx));

        let events = [
            Event::TouchBegin {
                id: 0,
                position: [50.0, 50.0].into(),
            },
            Event::TouchEnd {
                id: 0,
                position: [50.0, 50.0].into(),
            },
        ];

        let mut actions = vec![];
        for event in &events {
            ui.process(event, cx.root_id, &mut cx, &mut actions);
        }

        assert!(cx.state_map.contains_key(&cx.root_id));

        // State should have changed.
        assert!(*s.get(&cx));
    }
}