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 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/// Switch::new(cx, AppData::value);
28/// ```
29///
30/// ## Switch with an action
31///
32/// A Switch can be used to trigger a callback when toggled. Usually this is emitting an
33/// event responsible for changing the data the Switch 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/// Switch::new(cx, AppData::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 data the Switch
59/// is bound to 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/// # #[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/// Switch::new(cx, AppData::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 /// # #[derive(Lens)]
95 /// # struct AppData {
96 /// # value: bool,
97 /// # }
98 /// #
99 /// # impl Model for AppData {}
100 /// #
101 /// # let cx = &mut Context::default();
102 /// #
103 /// # AppData { value: false }.build(cx);
104 /// #
105 /// Switch::new(cx, AppData::value);
106 /// ```
107 pub fn new(cx: &mut Context, checked: impl Lens<Target = bool>) -> Handle<Self> {
108 Self { on_toggle: None }
109 .build(cx, |cx| {
110 Element::new(cx)
111 .class("switch-handle-bg")
112 .hoverable(false)
113 .position_type(PositionType::Absolute);
114 Element::new(cx)
115 .class("switch-handle")
116 .hoverable(false)
117 .position_type(PositionType::Absolute);
118 })
119 .checked(checked)
120 .role(Role::Switch)
121 .navigable(true)
122 }
123}
124
125impl Handle<'_, Switch> {
126 /// Set the callback triggered when the Switch is pressed.
127 ///
128 /// # Examples
129 ///
130 /// ```
131 /// # use vizia_core::prelude::*;
132 /// #
133 /// # #[derive(Lens)]
134 /// # struct AppData {
135 /// # value: bool,
136 /// # }
137 /// #
138 /// # impl Model for AppData {}
139 /// #
140 /// # enum AppEvent {
141 /// # ToggleValue,
142 /// # }
143 /// #
144 /// # let cx = &mut Context::default();
145 /// #
146 /// # AppData { value: false }.build(cx);
147 /// #
148 /// Switch::new(cx, AppData::value)
149 /// .on_toggle(|cx| cx.emit(AppEvent::ToggleValue));
150 /// ```
151 pub fn on_toggle<F>(self, callback: F) -> Self
152 where
153 F: 'static + Fn(&mut EventContext),
154 {
155 self.modify(|switch| switch.on_toggle = Some(Box::new(callback)))
156 }
157}
158
159impl View for Switch {
160 fn element(&self) -> Option<&'static str> {
161 Some("switch")
162 }
163
164 fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
165 event.map(|window_event, meta| match window_event {
166 WindowEvent::Press { mouse } => {
167 let over = if *mouse { cx.mouse.left.pressed } else { cx.focused() };
168 if over == cx.current() && meta.target == cx.current() && !cx.is_disabled() {
169 if let Some(callback) = &self.on_toggle {
170 (callback)(cx);
171 }
172 }
173 }
174 WindowEvent::ActionRequest(action) => match action.action {
175 Action::Click if !cx.is_disabled() => {
176 if let Some(callback) = &self.on_toggle {
177 (callback)(cx);
178 }
179 }
180 _ => {}
181 },
182 _ => {}
183 });
184 }
185}