1use azul_core::{
6 callbacks::{CoreCallbackData, Update},
7 dom::{Dom, IdOrClass, IdOrClass::Class, IdOrClassVec, TabIndex},
8 refany::RefAny,
9};
10use azul_css::dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec};
11#[allow(clippy::wildcard_imports)] use azul_css::{
13 props::{
14 basic::{color::ColorU, *},
15 layout::*,
16 property::{CssProperty, *},
17 style::*,
18 },
19 *,
20};
21
22use crate::callbacks::{Callback, CallbackInfo};
23
24static CHECKBOX_CONTAINER_CLASS: &[IdOrClass] = &[Class(AzString::from_const_str(
25 "__azul-native-checkbox-container",
26))];
27static CHECKBOX_CONTENT_CLASS: &[IdOrClass] = &[Class(AzString::from_const_str(
28 "__azul-native-checkbox-content",
29))];
30
31pub type CheckBoxOnToggleCallbackType =
33 extern "C" fn(RefAny, CallbackInfo, CheckBoxState) -> Update;
34impl_widget_callback!(
35 CheckBoxOnToggle,
36 OptionCheckBoxOnToggle,
37 CheckBoxOnToggleCallback,
38 CheckBoxOnToggleCallbackType
39);
40
41azul_core::impl_managed_callback! {
42 wrapper: CheckBoxOnToggleCallback,
43 info_ty: CallbackInfo,
44 return_ty: Update,
45 default_ret: Update::DoNothing,
46 invoker_static: CHECK_BOX_ON_TOGGLE_INVOKER,
47 invoker_ty: AzCheckBoxOnToggleCallbackInvoker,
48 thunk_fn: az_check_box_on_toggle_callback_thunk,
49 setter_fn: AzApp_setCheckBoxOnToggleCallbackInvoker,
50 from_handle_fn: AzCheckBoxOnToggleCallback_createFromHostHandle,
51 extra_args: [ state: CheckBoxState ],
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56#[repr(C)]
57pub struct CheckBox {
58 pub check_box_state: CheckBoxStateWrapper,
59 pub container_style: CssPropertyWithConditionsVec,
61 pub content_style: CssPropertyWithConditionsVec,
63}
64
65#[derive(Debug, Default, Clone, PartialEq, Eq)]
66#[repr(C)]
67pub struct CheckBoxStateWrapper {
68 pub inner: CheckBoxState,
70 pub on_toggle: OptionCheckBoxOnToggle,
72}
73
74#[derive(Copy, Debug, Default, Clone, PartialEq, Eq)]
76#[repr(C)]
77pub struct CheckBoxState {
78 pub checked: bool,
79}
80
81const BACKGROUND_COLOR: ColorU = ColorU {
82 r: 255,
83 g: 255,
84 b: 255,
85 a: 255,
86}; const BACKGROUND_THEME_LIGHT: &[StyleBackgroundContent] =
88 &[StyleBackgroundContent::Color(BACKGROUND_COLOR)];
89const BACKGROUND_COLOR_LIGHT: StyleBackgroundContentVec =
90 StyleBackgroundContentVec::from_const_slice(BACKGROUND_THEME_LIGHT);
91const COLOR_9B9B9B: ColorU = ColorU {
92 r: 155,
93 g: 155,
94 b: 155,
95 a: 255,
96}; const FILL_THEME: &[StyleBackgroundContent] = &[StyleBackgroundContent::Color(COLOR_9B9B9B)];
99const FILL_COLOR_BACKGROUND: StyleBackgroundContentVec =
100 StyleBackgroundContentVec::from_const_slice(FILL_THEME);
101
102static DEFAULT_CHECKBOX_CONTAINER_STYLE: &[CssPropertyWithConditions] = &[
103 CssPropertyWithConditions::simple(CssProperty::const_background_content(
104 BACKGROUND_COLOR_LIGHT,
105 )),
106 CssPropertyWithConditions::simple(CssProperty::const_display(LayoutDisplay::Block)),
107 CssPropertyWithConditions::simple(CssProperty::const_width(LayoutWidth::const_px(14))),
108 CssPropertyWithConditions::simple(CssProperty::const_height(LayoutHeight::const_px(14))),
109 CssPropertyWithConditions::simple(CssProperty::const_padding_left(
111 LayoutPaddingLeft::const_px(2),
112 )),
113 CssPropertyWithConditions::simple(CssProperty::const_padding_right(
114 LayoutPaddingRight::const_px(2),
115 )),
116 CssPropertyWithConditions::simple(CssProperty::const_padding_top(LayoutPaddingTop::const_px(
117 2,
118 ))),
119 CssPropertyWithConditions::simple(CssProperty::const_padding_bottom(
120 LayoutPaddingBottom::const_px(2),
121 )),
122 CssPropertyWithConditions::simple(CssProperty::const_border_top_width(
124 LayoutBorderTopWidth::const_px(1),
125 )),
126 CssPropertyWithConditions::simple(CssProperty::const_border_bottom_width(
127 LayoutBorderBottomWidth::const_px(1),
128 )),
129 CssPropertyWithConditions::simple(CssProperty::const_border_left_width(
130 LayoutBorderLeftWidth::const_px(1),
131 )),
132 CssPropertyWithConditions::simple(CssProperty::const_border_right_width(
133 LayoutBorderRightWidth::const_px(1),
134 )),
135 CssPropertyWithConditions::simple(CssProperty::const_border_top_style(StyleBorderTopStyle {
136 inner: BorderStyle::Inset,
137 })),
138 CssPropertyWithConditions::simple(CssProperty::const_border_bottom_style(
139 StyleBorderBottomStyle {
140 inner: BorderStyle::Inset,
141 },
142 )),
143 CssPropertyWithConditions::simple(CssProperty::const_border_left_style(StyleBorderLeftStyle {
144 inner: BorderStyle::Inset,
145 })),
146 CssPropertyWithConditions::simple(CssProperty::const_border_right_style(
147 StyleBorderRightStyle {
148 inner: BorderStyle::Inset,
149 },
150 )),
151 CssPropertyWithConditions::simple(CssProperty::const_border_top_color(StyleBorderTopColor {
152 inner: COLOR_9B9B9B,
153 })),
154 CssPropertyWithConditions::simple(CssProperty::const_border_bottom_color(
155 StyleBorderBottomColor {
156 inner: COLOR_9B9B9B,
157 },
158 )),
159 CssPropertyWithConditions::simple(CssProperty::const_border_left_color(StyleBorderLeftColor {
160 inner: COLOR_9B9B9B,
161 })),
162 CssPropertyWithConditions::simple(CssProperty::const_border_right_color(
163 StyleBorderRightColor {
164 inner: COLOR_9B9B9B,
165 },
166 )),
167 CssPropertyWithConditions::simple(CssProperty::const_cursor(StyleCursor::Pointer)),
168];
169
170static DEFAULT_CHECKBOX_CONTENT_STYLE_CHECKED: &[CssPropertyWithConditions] = &[
171 CssPropertyWithConditions::simple(CssProperty::const_width(LayoutWidth::const_px(8))),
172 CssPropertyWithConditions::simple(CssProperty::const_height(LayoutHeight::const_px(8))),
173 CssPropertyWithConditions::simple(CssProperty::const_background_content(FILL_COLOR_BACKGROUND)),
174 CssPropertyWithConditions::simple(CssProperty::const_opacity(StyleOpacity::const_new(100))),
175];
176
177static DEFAULT_CHECKBOX_CONTENT_STYLE_UNCHECKED: &[CssPropertyWithConditions] = &[
178 CssPropertyWithConditions::simple(CssProperty::const_width(LayoutWidth::const_px(8))),
179 CssPropertyWithConditions::simple(CssProperty::const_height(LayoutHeight::const_px(8))),
180 CssPropertyWithConditions::simple(CssProperty::const_background_content(FILL_COLOR_BACKGROUND)),
181 CssPropertyWithConditions::simple(CssProperty::const_opacity(StyleOpacity::const_new(0))),
182];
183
184impl CheckBox {
185 #[must_use] pub fn create(checked: bool) -> Self {
186 Self {
187 check_box_state: CheckBoxStateWrapper {
188 inner: CheckBoxState { checked },
189 ..Default::default()
190 },
191 container_style: CssPropertyWithConditionsVec::from_const_slice(
192 DEFAULT_CHECKBOX_CONTAINER_STYLE,
193 ),
194 content_style: if checked {
195 CssPropertyWithConditionsVec::from_const_slice(
196 DEFAULT_CHECKBOX_CONTENT_STYLE_CHECKED,
197 )
198 } else {
199 CssPropertyWithConditionsVec::from_const_slice(
200 DEFAULT_CHECKBOX_CONTENT_STYLE_UNCHECKED,
201 )
202 },
203 }
204 }
205
206 #[inline]
207 #[must_use]
208 pub fn swap_with_default(&mut self) -> Self {
209 let mut s = Self::create(false);
210 core::mem::swap(&mut s, self);
211 s
212 }
213
214 #[inline]
215 pub fn set_on_toggle<C: Into<CheckBoxOnToggleCallback>>(&mut self, data: RefAny, on_toggle: C) {
216 self.check_box_state.on_toggle = Some(CheckBoxOnToggle {
217 callback: on_toggle.into(),
218 refany: data,
219 })
220 .into();
221 }
222
223 #[inline]
224 #[must_use]
225 pub fn with_on_toggle<C: Into<CheckBoxOnToggleCallback>>(
226 mut self,
227 data: RefAny,
228 on_toggle: C,
229 ) -> Self {
230 self.set_on_toggle(data, on_toggle);
231 self
232 }
233
234 #[inline]
235 #[must_use] pub fn dom(self) -> Dom {
236 use azul_core::{
237 callbacks::{CoreCallback, CoreCallbackData},
238 dom::{Dom, EventFilter, HoverEventFilter},
239 };
240
241 Dom::create_div()
242 .with_ids_and_classes(IdOrClassVec::from(CHECKBOX_CONTAINER_CLASS))
243 .with_css_props(self.container_style)
244 .with_callbacks(
245 vec![CoreCallbackData {
246 event: EventFilter::Hover(HoverEventFilter::MouseUp),
247 callback: CoreCallback {
248 cb: input::default_on_checkbox_clicked as usize,
249 ctx: azul_core::refany::OptionRefAny::None,
250 },
251 refany: RefAny::new(self.check_box_state),
252 }]
253 .into(),
254 )
255 .with_tab_index(TabIndex::Auto)
256 .with_children(
257 vec![Dom::create_div()
258 .with_ids_and_classes(IdOrClassVec::from(CHECKBOX_CONTENT_CLASS))
259 .with_css_props(self.content_style)]
260 .into(),
261 )
262 }
263}
264
265mod input {
267
268 use azul_core::{callbacks::Update, refany::RefAny};
269 use azul_css::props::{property::CssProperty, style::effects::StyleOpacity};
270
271 use super::{CheckBoxOnToggle, CheckBoxStateWrapper};
272 use crate::callbacks::CallbackInfo;
273
274 pub(super) extern "C" fn default_on_checkbox_clicked(
275 mut check_box: RefAny,
276 mut info: CallbackInfo,
277 ) -> Update {
278 let Some(mut check_box) = check_box.downcast_mut::<CheckBoxStateWrapper>() else {
279 return Update::DoNothing;
280 };
281
282 let Some(checkbox_content_id) = info.get_first_child(info.get_hit_node()) else {
283 return Update::DoNothing;
284 };
285
286 check_box.inner.checked = !check_box.inner.checked;
287
288 let result = {
289 let check_box = &mut *check_box;
291 let ontoggle = &mut check_box.on_toggle;
292 let inner = check_box.inner;
293
294 match ontoggle.as_mut() {
295 Some(CheckBoxOnToggle {
296 callback,
297 refany: data,
298 }) => (callback.cb)(data.clone(), info, inner),
299 None => Update::DoNothing,
300 }
301 };
302
303 if check_box.inner.checked {
304 info.set_css_property(
305 checkbox_content_id,
306 CssProperty::const_opacity(StyleOpacity::const_new(100)),
307 );
308 } else {
309 info.set_css_property(
310 checkbox_content_id,
311 CssProperty::const_opacity(StyleOpacity::const_new(0)),
312 );
313 }
314
315 result
316 }
317}
318
319impl From<CheckBox> for Dom {
320 fn from(b: CheckBox) -> Self {
321 b.dom()
322 }
323}