1#![deny(missing_docs)]
2use crate::{
5 style_class,
6 view::IntoView,
7 views::{
8 self, create_value_container_signals, h_stack, svg, value_container, Decorators,
9 ValueContainer,
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)
44 .update_value(update_svg)
45 .class(CheckboxClass)
46 .keyboard_navigable()
47}
48
49pub struct Checkbox;
57
58impl Checkbox {
59 #[allow(clippy::new_ret_no_self)]
66 #[inline]
67 pub fn new(checked: impl Fn() -> bool + 'static) -> ValueContainer<bool> {
68 Self::new_custom(checked, DEFAULT_CHECKBOX_SVG)
69 }
70
71 pub fn new_custom(
77 checked: impl Fn() -> bool + 'static,
78 custom_check: impl Into<String> + Clone + 'static,
79 ) -> ValueContainer<bool> {
80 let (inbound_signal, outbound_signal) = create_value_container_signals(checked);
81
82 value_container(
83 checkbox_svg(inbound_signal.read_only(), custom_check).on_click_stop(move |_| {
84 let checked = inbound_signal.get_untracked();
85 outbound_signal.set(!checked);
86 }),
87 move || outbound_signal.get(),
88 )
89 }
90
91 #[inline]
96 pub fn new_rw(
97 checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
98 ) -> impl IntoView {
99 Self::new_rw_custom(checked, DEFAULT_CHECKBOX_SVG)
100 }
101
102 pub fn new_rw_custom(
106 checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
107 custom_check: impl Into<String> + Clone + 'static,
108 ) -> impl IntoView {
109 checkbox_svg(checked, custom_check).on_click_stop(move |_| {
110 checked.update(|val| *val = !*val);
111 })
112 }
113
114 #[inline]
119 pub fn labeled<S: Display + 'static>(
120 checked: impl Fn() -> bool + 'static,
121 label: impl Fn() -> S + 'static,
122 ) -> ValueContainer<bool> {
123 Self::custom_labeled(checked, label, DEFAULT_CHECKBOX_SVG)
124 }
125
126 pub fn custom_labeled<S: Display + 'static>(
130 checked: impl Fn() -> bool + 'static,
131 label: impl Fn() -> S + 'static,
132 custom_check: impl Into<String> + Clone + 'static,
133 ) -> ValueContainer<bool> {
134 let (inbound_signal, outbound_signal) = create_value_container_signals(checked);
135
136 value_container(
137 h_stack((
138 checkbox_svg(inbound_signal.read_only(), custom_check).on_click_stop(move |_| {
139 let checked = inbound_signal.get_untracked();
140 outbound_signal.set(!checked);
141 }),
142 views::label(label),
143 ))
144 .class(LabeledCheckboxClass)
145 .on_click_stop(move |_| {
146 let checked = inbound_signal.get_untracked();
147 outbound_signal.set(!checked);
148 })
149 .style(|s| s.items_center()),
150 move || outbound_signal.get(),
151 )
152 }
153
154 #[inline]
159 pub fn labeled_rw<S: Display + 'static>(
160 checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
161 label: impl Fn() -> S + 'static,
162 ) -> impl IntoView {
163 Self::custom_labeled_rw(checked, label, DEFAULT_CHECKBOX_SVG)
164 }
165
166 pub fn custom_labeled_rw<S: Display + 'static>(
170 checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
171 label: impl Fn() -> S + 'static,
172 custom_check: impl Into<String> + Clone + 'static,
173 ) -> impl IntoView {
174 h_stack((
175 checkbox_svg(checked, custom_check).on_click_stop(move |_| {
176 checked.update(|val| *val = !*val);
177 }),
178 views::label(label),
179 ))
180 .on_click_stop(move |_| {
181 checked.update(|val| *val = !*val);
182 })
183 .class(LabeledCheckboxClass)
184 .style(|s| s.items_center())
185 }
186}
187
188pub fn checkbox(checked: impl Fn() -> bool + 'static) -> ValueContainer<bool> {
190 Checkbox::new(checked)
191}
192
193pub fn custom_checkbox(
195 checked: impl Fn() -> bool + 'static,
196 custom_check: impl Into<String> + Clone + 'static,
197) -> ValueContainer<bool> {
198 Checkbox::new_custom(checked, custom_check)
199}
200
201pub fn labeled_checkbox<S: Display + 'static>(
203 checked: impl Fn() -> bool + 'static,
204 label: impl Fn() -> S + 'static,
205) -> ValueContainer<bool> {
206 Checkbox::labeled(checked, label)
207}
208
209pub fn custom_labeled_checkbox<S: Display + 'static>(
211 checked: impl Fn() -> bool + 'static,
212 label: impl Fn() -> S + 'static,
213 custom_check: impl Into<String> + Clone + 'static,
214) -> ValueContainer<bool> {
215 Checkbox::custom_labeled(checked, label, custom_check)
216}