freya_components/
switch.rs1use freya_animation::prelude::*;
2use freya_core::prelude::*;
3use torin::{
4 alignment::Alignment,
5 gaps::Gaps,
6 size::Size,
7};
8
9use crate::{
10 get_theme,
11 theming::component_themes::SwitchThemePartial,
12};
13
14#[cfg_attr(feature = "docs",
47 doc = embed_doc_image::embed_image!(
48 "gallery_toggled_switch",
49 "images/gallery_toggled_switch.png"
50 ),
51 doc = embed_doc_image::embed_image!("gallery_not_toggled_switch", "images/gallery_not_toggled_switch.png")
52)]
53#[derive(Clone, PartialEq)]
54pub struct Switch {
55 pub(crate) theme: Option<SwitchThemePartial>,
56 toggled: ReadState<bool>,
57 on_toggle: Option<EventHandler<()>>,
58 enabled: bool,
59 key: DiffKey,
60}
61
62impl KeyExt for Switch {
63 fn write_key(&mut self) -> &mut DiffKey {
64 &mut self.key
65 }
66}
67
68impl Default for Switch {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74impl Switch {
75 pub fn new() -> Self {
76 Self {
77 toggled: false.into(),
78 on_toggle: None,
79 theme: None,
80 enabled: true,
81 key: DiffKey::None,
82 }
83 }
84
85 pub fn toggled(mut self, toggled: impl Into<ReadState<bool>>) -> Self {
86 self.toggled = toggled.into();
87 self
88 }
89
90 pub fn on_toggle(mut self, on_toggle: impl FnMut(()) + 'static) -> Self {
91 self.on_toggle = Some(EventHandler::new(on_toggle));
92 self
93 }
94
95 pub fn enabled(mut self, enabled: impl Into<bool>) -> Self {
96 self.enabled = enabled.into();
97 self
98 }
99}
100
101impl Render for Switch {
102 fn render(self: &Switch) -> impl IntoElement {
103 let theme = get_theme!(&self.theme, switch);
104 let mut hovering = use_state(|| false);
105 let focus = use_focus();
106 let focus_status = use_focus_status(focus);
107
108 let toggled = *self.toggled.read();
109
110 let animation = use_animation_with_dependencies(
111 &(theme.clone(), toggled),
112 |conf, (switch_theme, toggled)| {
113 conf.on_creation(OnCreation::Finish);
114 conf.on_change(OnChange::Rerun);
115
116 let value = (
117 AnimNum::new(2., 22.)
118 .time(300)
119 .function(Function::Expo)
120 .ease(Ease::Out),
121 AnimNum::new(14., 18.)
122 .time(300)
123 .function(Function::Expo)
124 .ease(Ease::Out),
125 AnimColor::new(switch_theme.background, switch_theme.toggled_background)
126 .time(300)
127 .function(Function::Expo)
128 .ease(Ease::Out),
129 AnimColor::new(
130 switch_theme.thumb_background,
131 switch_theme.toggled_thumb_background,
132 )
133 .time(300)
134 .function(Function::Expo)
135 .ease(Ease::Out),
136 );
137
138 if *toggled {
139 value
140 } else {
141 value.into_reversed()
142 }
143 },
144 );
145
146 let enabled = use_reactive(&self.enabled);
147 use_drop(move || {
148 if hovering() && enabled() {
149 Cursor::set(CursorIcon::default());
150 }
151 });
152
153 let border = if focus_status() == FocusStatus::Keyboard {
154 Border::new()
155 .width(2.)
156 .alignment(BorderAlignment::Inner)
157 .fill(theme.focus_border_fill.mul_if(!self.enabled, 0.9))
158 } else {
159 Border::new()
160 };
161 let (offset_x, size, background, thumb) = animation.get().value();
162
163 rect()
164 .a11y_id(focus.a11y_id())
165 .a11y_focusable(self.enabled)
166 .width(Size::px(48.))
167 .height(Size::px(25.))
168 .padding(Gaps::new_all(4.0))
169 .main_align(Alignment::center())
170 .offset_x(offset_x)
171 .corner_radius(CornerRadius::new_all(50.))
172 .background(background.mul_if(!self.enabled, 0.85))
173 .border(border)
174 .maybe(self.enabled, |rect| {
175 rect.on_press({
176 let on_toggle = self.on_toggle.clone();
177 move |_| {
178 if let Some(on_toggle) = &on_toggle {
179 on_toggle.call(())
180 }
181 focus.request_focus();
182 }
183 })
184 })
185 .on_pointer_enter(move |_| {
186 hovering.set(true);
187 if enabled() {
188 Cursor::set(CursorIcon::Pointer);
189 } else {
190 Cursor::set(CursorIcon::NotAllowed);
191 }
192 })
193 .on_pointer_leave(move |_| {
194 if hovering() {
195 Cursor::set(CursorIcon::default());
196 hovering.set(false);
197 }
198 })
199 .child(
200 rect()
201 .width(Size::px(size))
202 .height(Size::px(size))
203 .background(thumb.mul_if(!self.enabled, 0.85))
204 .corner_radius(CornerRadius::new_all(50.)),
205 )
206 }
207
208 fn render_key(&self) -> DiffKey {
209 self.key.clone().or(self.default_key())
210 }
211}