Skip to main content

vizia_core/views/
toggle_button.rs

1use crate::prelude::*;
2
3/// A button which can be toggled between two states.
4pub struct ToggleButton {
5    on_toggle: Option<Box<dyn Fn(&mut EventContext)>>,
6}
7
8impl ToggleButton {
9    /// Create a new [ToggleButton] view.
10    pub fn new<V: View>(
11        cx: &mut Context,
12        checked: impl Res<bool>,
13        content: impl Fn(&mut Context) -> Handle<V> + 'static,
14    ) -> Handle<Self> {
15        Self { on_toggle: None }
16            .build(cx, |cx| {
17                (content)(cx).hoverable(false);
18            })
19            .role(Role::Button)
20            .navigable(true)
21            .checkable(true) // To let the accesskit know button is toggleable
22            .checked(checked)
23    }
24
25    /// Create a new [ToggleButton] view with distinct content for unchecked and checked states.
26    pub fn with_contents<V1: View, V2: View>(
27        cx: &mut Context,
28        checked: impl Res<bool> + Copy + 'static,
29        content_unchecked: impl Fn(&mut Context) -> Handle<V1> + 'static,
30        content_checked: impl Fn(&mut Context) -> Handle<V2> + 'static,
31    ) -> Handle<Self> {
32        Self { on_toggle: None }
33            .build(cx, move |cx| {
34                checked.set_or_bind(cx, move |cx, checked| {
35                    if checked.get_value(cx) {
36                        (content_checked)(cx).hoverable(false);
37                    } else {
38                        (content_unchecked)(cx).hoverable(false);
39                    }
40                });
41            })
42            .role(Role::Button)
43            .navigable(true)
44            .checkable(true) // To let the accesskit know button is toggleable
45            .checked(checked)
46    }
47}
48
49impl View for ToggleButton {
50    fn element(&self) -> Option<&'static str> {
51        Some("toggle-button")
52    }
53
54    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
55        event.map(|window_event, meta| match window_event {
56            WindowEvent::PressDown { mouse } => {
57                if *mouse {
58                    cx.capture()
59                }
60                cx.focus();
61            }
62
63            WindowEvent::Press { mouse } => {
64                let over = if *mouse { cx.mouse().left.pressed } else { cx.focused() };
65                if over == cx.current() && meta.target == cx.current() && !cx.is_disabled() {
66                    if let Some(callback) = &self.on_toggle {
67                        (callback)(cx);
68                    }
69                }
70            }
71
72            WindowEvent::MouseUp(button) if *button == MouseButton::Left => {
73                cx.release();
74            }
75
76            WindowEvent::ActionRequest(action) => match action.action {
77                Action::Click => {
78                    if let Some(callback) = &self.on_toggle {
79                        (callback)(cx);
80                    }
81                }
82
83                _ => {}
84            },
85
86            _ => {}
87        });
88    }
89}
90
91impl Handle<'_, ToggleButton> {
92    /// Sets the callback triggered when the [ToggleButton] is toggled.
93    pub fn on_toggle(self, callback: impl Fn(&mut EventContext) + 'static) -> Self {
94        self.modify(|toggle_button| toggle_button.on_toggle = Some(Box::new(callback)))
95    }
96}