vizia_core/views/
radio.rs

1use crate::prelude::*;
2
3/// A radio button used to display and toggle boolean state.
4///
5/// Clicking on the radio button with the left mouse button triggers the `on_select` callback.
6///
7/// # Examples
8///
9/// ## Basic radio button
10///
11/// The radio button must bound to some boolean data.
12///
13/// ```
14/// # use vizia_core::prelude::*;
15/// #
16/// # #[derive(Lens)]
17/// # struct AppData {
18/// #     value: bool,
19/// # }
20/// #
21/// # impl Model for AppData {}
22/// #
23/// # let cx = &mut Context::default();
24/// #
25/// # AppData { value: false }.build(cx);
26/// #
27/// RadioButton::new(cx, AppData::value);
28/// ```
29///
30/// ## Radio button with an action
31///
32/// A radio button can be used to trigger a callback when selected. Usually this is emitting an
33/// event responsible for changing the data the radio button is bound to.
34///
35/// ```
36/// # use vizia_core::prelude::*;
37/// #
38/// # #[derive(Lens)]
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/// # AppData { value: false }.build(cx);
52/// #
53/// RadioButton::new(cx, AppData::value).on_select(|cx| cx.emit(AppEvent::ToggleValue));
54/// ```
55///
56/// ## Radio button with a label
57///
58/// A radio button is usually used with a label next to it describing what data the radio button
59/// is bound to or what the radio button does when pressed. This can be done, for example, by
60/// wrapping the radio button 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/// # #[derive(Lens)]
67/// # struct AppData {
68/// #     value: bool,
69/// # }
70/// #
71/// # impl Model for AppData {}
72/// #
73/// # let cx = &mut Context::default();
74/// #
75/// # AppData { value: false }.build(cx);
76/// #
77/// HStack::new(cx, |cx| {
78///     RadioButton::new(cx, AppData::value);
79///     Label::new(cx, "Press me");
80/// });
81/// ```
82pub struct RadioButton {
83    on_select: Option<Box<dyn Fn(&mut EventContext)>>,
84}
85
86impl RadioButton {
87    /// Creates a new [RadioButton] view.
88    pub fn new(cx: &mut Context, checked: impl Lens<Target = bool>) -> Handle<Self> {
89        Self { on_select: None }
90            .build(cx, |cx| {
91                Element::new(cx).class("inner").hoverable(false);
92            })
93            .checked(checked)
94            .navigable(true)
95            .checkable(true)
96            .role(Role::RadioButton)
97    }
98}
99
100impl View for RadioButton {
101    fn element(&self) -> Option<&'static str> {
102        Some("radiobutton")
103    }
104
105    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
106        event.map(|window_event, meta| match window_event {
107            WindowEvent::Press { mouse } => {
108                let over = if *mouse { cx.mouse.left.pressed } else { cx.focused() };
109                if over == cx.current() && meta.target == cx.current() && !cx.is_disabled() {
110                    if let Some(callback) = &self.on_select {
111                        (callback)(cx);
112                    }
113                }
114            }
115
116            WindowEvent::ActionRequest(request) => match request.action {
117                Action::Click => {
118                    if let Some(callback) = &self.on_select {
119                        (callback)(cx);
120                    }
121                }
122
123                _ => {}
124            },
125
126            _ => {}
127        });
128    }
129}
130
131impl Handle<'_, RadioButton> {
132    /// Set the callback triggered when the radio button is selected.
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// # use vizia_core::prelude::*;
138    /// #
139    /// # #[derive(Lens)]
140    /// # struct AppData {
141    /// #     value: bool,
142    /// # }
143    /// #
144    /// # impl Model for AppData {}
145    /// #
146    /// # enum AppEvent {
147    /// #     ToggleValue,
148    /// # }
149    /// #
150    /// # let cx = &mut Context::default();
151    /// #
152    /// # AppData { value: false }.build(cx);
153    /// #
154    /// RadioButton::new(cx, AppData::value)
155    ///     .on_select(|cx| cx.emit(AppEvent::ToggleValue));
156    /// ```
157    pub fn on_select<F>(self, callback: F) -> Self
158    where
159        F: 'static + Fn(&mut EventContext),
160    {
161        self.modify(|radiobutton| radiobutton.on_select = Some(Box::new(callback)))
162    }
163}