Skip to main content

vizia_core/views/
switch.rs

1use crate::prelude::*;
2
3/// A Switch used to display and toggle a boolean state.
4///
5/// Clicking on the Switch with the left mouse button triggers the `on_toggle` callback.
6///
7/// # Examples
8///
9/// ## Basic Switch
10///
11/// The Switch takes a boolean signal (for example `Signal<bool>`).
12///
13/// ```
14/// # use vizia_core::prelude::*;
15/// #
16/// #
17/// # struct AppData {
18/// #     value: bool,
19/// # }
20/// #
21/// # impl Model for AppData {}
22/// #
23/// # let cx = &mut Context::default();
24/// #
25/// # let value = Signal::new(false);
26/// #
27/// Switch::new(cx, value);
28/// ```
29///
30/// ## Switch with an action
31///
32/// A Switch can be used to trigger a callback when toggled. Usually this updates
33/// the underlying boolean state.
34///
35/// ```
36/// # use vizia_core::prelude::*;
37/// #
38/// #
39/// # struct AppData {
40/// #     value: bool,
41/// # }
42/// #
43/// # impl Model for AppData {}
44/// #
45/// # enum AppEvent {
46/// #     ToggleValue,
47/// # }
48/// #
49/// # let cx = &mut Context::default();
50/// #
51/// # let value = Signal::new(false);
52/// #
53/// Switch::new(cx, value).on_toggle(|cx| cx.emit(AppEvent::ToggleValue));
54/// ```
55///
56/// ## Switch with a label
57///
58/// A Switch is usually used with a label next to it describing what state the Switch
59/// controls or what the Switch does when pressed. This can be done, for example, by
60/// wrapping the Switch in an [`HStack`](crate::prelude::HStack) and adding a [`Label`](crate::prelude::Label)
61/// to it.
62///
63/// ```
64/// # use vizia_core::prelude::*;
65/// #
66/// #
67/// # struct AppData {
68/// #     value: bool,
69/// # }
70/// #
71/// # impl Model for AppData {}
72/// #
73/// # let cx = &mut Context::default();
74/// #
75/// # let value = Signal::new(false);
76/// #
77/// HStack::new(cx, |cx| {
78///     Switch::new(cx, value);
79///     Label::new(cx, "Press me");
80/// });
81/// ```
82pub struct Switch {
83    on_toggle: Option<Box<dyn Fn(&mut EventContext)>>,
84}
85
86impl Switch {
87    /// Creates a new Switch.
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// # use vizia_core::prelude::*;
93    /// #
94    /// #
95    /// # struct AppData {
96    /// #     value: bool,
97    /// # }
98    /// #
99    /// # impl Model for AppData {}
100    /// #
101    /// # let cx = &mut Context::default();
102    /// #
103    /// # let value = Signal::new(false);
104    /// #
105    /// Switch::new(cx, value);
106    /// ```
107    pub fn new(cx: &mut Context, checked: impl Res<bool>) -> Handle<Self> {
108        Self { on_toggle: None }
109            .build(cx, |cx| {
110                Element::new(cx)
111                    .class("thumb")
112                    .hoverable(false)
113                    .position_type(PositionType::Absolute);
114            })
115            .checked(checked)
116            .role(Role::Switch)
117            .navigable(true)
118    }
119}
120
121impl Handle<'_, Switch> {
122    /// Set the callback triggered when the Switch is pressed.
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// # use vizia_core::prelude::*;
128    /// #
129    /// #
130    /// # struct AppData {
131    /// #     value: bool,
132    /// # }
133    /// #
134    /// # impl Model for AppData {}
135    /// #
136    /// # enum AppEvent {
137    /// #     ToggleValue,
138    /// # }
139    /// #
140    /// # let cx = &mut Context::default();
141    /// #
142    /// # let value = Signal::new(false);
143    /// #
144    /// Switch::new(cx, value)
145    ///     .on_toggle(|cx| cx.emit(AppEvent::ToggleValue));
146    /// ```
147    pub fn on_toggle<F>(self, callback: F) -> Self
148    where
149        F: 'static + Fn(&mut EventContext),
150    {
151        self.modify(|switch| switch.on_toggle = Some(Box::new(callback)))
152    }
153}
154
155impl View for Switch {
156    fn element(&self) -> Option<&'static str> {
157        Some("switch")
158    }
159
160    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
161        event.map(|window_event, meta| match window_event {
162            WindowEvent::Press { mouse } => {
163                let over = if *mouse { cx.mouse.left.pressed } else { cx.focused() };
164                if over == cx.current() && meta.target == cx.current() && !cx.is_disabled() {
165                    if let Some(callback) = &self.on_toggle {
166                        (callback)(cx);
167                    }
168                }
169            }
170            WindowEvent::ActionRequest(action) => match action.action {
171                Action::Click if !cx.is_disabled() => {
172                    if let Some(callback) = &self.on_toggle {
173                        (callback)(cx);
174                    }
175                }
176                _ => {}
177            },
178            _ => {}
179        });
180    }
181}