1#![deny(missing_docs)]
2use crate::{
5 style_class,
6 view::IntoView,
7 views::{
8 self, Decorators, Stack, ValueContainer, create_value_container_signals, svg,
9 value_container,
10 },
11};
12use floem_reactive::{SignalGet, SignalUpdate};
13use std::fmt::Display;
14
15style_class!(
16 pub CheckboxClass
18);
19
20style_class!(
21 pub LabeledCheckboxClass
23);
24
25pub const DEFAULT_CHECKBOX_SVG: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 16 16"><polygon points="5.19,11.83 0.18,7.44 1.82,5.56 4.81,8.17 10,1.25 12,2.75" /></svg>"#;
27
28fn checkbox_svg(
29 checked: impl SignalGet<bool> + 'static,
30 check_svg: impl Into<String> + 'static,
31) -> impl IntoView {
32 let check_svg: String = check_svg.into();
33 let update_svg = {
34 let check_svg = check_svg.clone();
35 move || {
36 if checked.get() {
37 check_svg.clone()
38 } else {
39 "".to_string()
40 }
41 }
42 };
43 svg(check_svg).update_value(update_svg).class(CheckboxClass)
44}
45
46pub struct Checkbox;
54
55impl Checkbox {
56 #[allow(clippy::new_ret_no_self)]
63 #[inline]
64 pub fn new(checked: impl Fn() -> bool + 'static) -> ValueContainer<bool> {
65 Self::new_custom(checked, DEFAULT_CHECKBOX_SVG)
66 }
67
68 pub fn new_custom(
74 checked: impl Fn() -> bool + 'static,
75 custom_check: impl Into<String> + Clone + 'static,
76 ) -> ValueContainer<bool> {
77 let (inbound_signal, outbound_signal) = create_value_container_signals(checked);
78
79 value_container(
80 checkbox_svg(inbound_signal.read_only(), custom_check).action(move || {
81 let checked = inbound_signal.get_untracked();
82 outbound_signal.set(!checked);
83 }),
84 move || outbound_signal.get(),
85 )
86 }
87
88 #[inline]
93 pub fn new_rw(
94 checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
95 ) -> impl IntoView {
96 Self::new_rw_custom(checked, DEFAULT_CHECKBOX_SVG)
97 }
98
99 pub fn new_rw_custom(
103 checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
104 custom_check: impl Into<String> + Clone + 'static,
105 ) -> impl IntoView {
106 checkbox_svg(checked, custom_check).action(move || {
107 checked.update(|val| *val = !*val);
108 })
109 }
110
111 #[inline]
116 pub fn labeled<S: Display + 'static>(
117 checked: impl Fn() -> bool + 'static,
118 label: impl Fn() -> S + 'static,
119 ) -> ValueContainer<bool> {
120 Self::custom_labeled(checked, label, DEFAULT_CHECKBOX_SVG)
121 }
122
123 pub fn custom_labeled<S: Display + 'static>(
127 checked: impl Fn() -> bool + 'static,
128 label: impl Fn() -> S + 'static,
129 custom_check: impl Into<String> + Clone + 'static,
130 ) -> ValueContainer<bool> {
131 let (inbound_signal, outbound_signal) = create_value_container_signals(checked);
132
133 value_container(
134 Stack::horizontal((
135 checkbox_svg(inbound_signal.read_only(), custom_check).action(move || {
136 let checked = inbound_signal.get_untracked();
137 outbound_signal.set(!checked);
138 }),
139 views::Label::derived(label),
140 ))
141 .class(LabeledCheckboxClass)
142 .action(move || {
143 let checked = inbound_signal.get_untracked();
144 outbound_signal.set(!checked);
145 })
146 .style(|s| s.items_center()),
147 move || outbound_signal.get(),
148 )
149 }
150
151 #[inline]
156 pub fn labeled_rw<S: Display + 'static>(
157 checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
158 label: impl Fn() -> S + 'static,
159 ) -> impl IntoView {
160 Self::custom_labeled_rw(checked, label, DEFAULT_CHECKBOX_SVG)
161 }
162
163 pub fn custom_labeled_rw<S: Display + 'static>(
167 checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
168 label: impl Fn() -> S + 'static,
169 custom_check: impl Into<String> + Clone + 'static,
170 ) -> impl IntoView {
171 Stack::horizontal((
172 checkbox_svg(checked, custom_check).action(move || {
173 checked.update(|val| *val = !*val);
174 }),
175 views::Label::derived(label),
176 ))
177 .action(move || {
178 checked.update(|val| *val = !*val);
179 })
180 .class(LabeledCheckboxClass)
181 .style(|s| s.items_center())
182 }
183}
184
185pub fn checkbox(checked: impl Fn() -> bool + 'static) -> ValueContainer<bool> {
187 Checkbox::new(checked)
188}
189
190pub fn custom_checkbox(
192 checked: impl Fn() -> bool + 'static,
193 custom_check: impl Into<String> + Clone + 'static,
194) -> ValueContainer<bool> {
195 Checkbox::new_custom(checked, custom_check)
196}
197
198pub fn labeled_checkbox<S: Display + 'static>(
200 checked: impl Fn() -> bool + 'static,
201 label: impl Fn() -> S + 'static,
202) -> ValueContainer<bool> {
203 Checkbox::labeled(checked, label)
204}
205
206pub fn custom_labeled_checkbox<S: Display + 'static>(
208 checked: impl Fn() -> bool + 'static,
209 label: impl Fn() -> S + 'static,
210 custom_check: impl Into<String> + Clone + 'static,
211) -> ValueContainer<bool> {
212 Checkbox::custom_labeled(checked, label, custom_check)
213}