1use std::string::String;
9
10use azul_core::{
11 callbacks::{CoreCallbackData, Update},
12 dom::Dom,
13 refany::RefAny,
14};
15#[allow(clippy::wildcard_imports)] use azul_css::{
17 dynamic_selector::CssPropertyWithConditionsVec,
18 props::{
19 basic::*,
20 layout::*,
21 property::{CssProperty, *},
22 style::*,
23 },
24 *,
25};
26
27use crate::{
28 callbacks::{Callback, CallbackInfo},
29 widgets::text_input::{
30 OnTextInputReturn, TextInput, TextInputOnFocusLostCallback,
31 TextInputOnFocusLostCallbackType, TextInputOnTextInputCallback,
32 TextInputOnTextInputCallbackType, TextInputOnVirtualKeyDownCallback,
33 TextInputOnVirtualKeyDownCallbackType, TextInputState, TextInputValid,
34 },
35};
36
37pub type NumberInputOnValueChangeCallbackType =
39 extern "C" fn(RefAny, CallbackInfo, NumberInputState) -> Update;
40impl_widget_callback!(
41 NumberInputOnValueChange,
42 OptionNumberInputOnValueChange,
43 NumberInputOnValueChangeCallback,
44 NumberInputOnValueChangeCallbackType
45);
46
47azul_core::impl_managed_callback! {
48 wrapper: NumberInputOnValueChangeCallback,
49 info_ty: CallbackInfo,
50 return_ty: Update,
51 default_ret: Update::DoNothing,
52 invoker_static: NUMBER_INPUT_ON_VALUE_CHANGE_INVOKER,
53 invoker_ty: AzNumberInputOnValueChangeCallbackInvoker,
54 thunk_fn: az_number_input_on_value_change_callback_thunk,
55 setter_fn: AzApp_setNumberInputOnValueChangeCallbackInvoker,
56 from_handle_fn: AzNumberInputOnValueChangeCallback_createFromHostHandle,
57 extra_args: [ state: NumberInputState ],
58}
59
60pub type NumberInputOnFocusLostCallbackType =
62 extern "C" fn(RefAny, CallbackInfo, NumberInputState) -> Update;
63impl_widget_callback!(
64 NumberInputOnFocusLost,
65 OptionNumberInputOnFocusLost,
66 NumberInputOnFocusLostCallback,
67 NumberInputOnFocusLostCallbackType
68);
69
70azul_core::impl_managed_callback! {
71 wrapper: NumberInputOnFocusLostCallback,
72 info_ty: CallbackInfo,
73 return_ty: Update,
74 default_ret: Update::DoNothing,
75 invoker_static: NUMBER_INPUT_ON_FOCUS_LOST_INVOKER,
76 invoker_ty: AzNumberInputOnFocusLostCallbackInvoker,
77 thunk_fn: az_number_input_on_focus_lost_callback_thunk,
78 setter_fn: AzApp_setNumberInputOnFocusLostCallbackInvoker,
79 from_handle_fn: AzNumberInputOnFocusLostCallback_createFromHostHandle,
80 extra_args: [ state: NumberInputState ],
81}
82
83#[derive(Debug, Default, Clone, PartialEq)]
85#[repr(C)]
86pub struct NumberInput {
87 pub number_input_state: NumberInputStateWrapper,
88 pub text_input: TextInput,
89 pub style: CssPropertyWithConditionsVec,
90}
91
92#[derive(Debug, Default, Clone, PartialEq)]
94#[repr(C)]
95pub struct NumberInputStateWrapper {
96 pub inner: NumberInputState,
97 pub on_value_change: OptionNumberInputOnValueChange,
98 pub on_focus_lost: OptionNumberInputOnFocusLost,
99}
100
101#[derive(Copy, Debug, Clone, PartialEq)]
103#[repr(C)]
104pub struct NumberInputState {
105 pub previous: f32,
107 pub number: f32,
109 pub min: f32,
111 pub max: f32,
113}
114
115impl Default for NumberInputState {
116 fn default() -> Self {
117 Self {
118 previous: 0.0,
119 number: 0.0,
120 min: core::f32::MIN,
121 max: core::f32::MAX,
122 }
123 }
124}
125
126impl NumberInput {
127 #[must_use] pub fn create(input: f32) -> Self {
129 Self {
130 number_input_state: NumberInputStateWrapper {
131 inner: NumberInputState {
132 number: input,
133 ..Default::default()
134 },
135 ..Default::default()
136 },
137 ..Default::default()
138 }
139 }
140
141 pub fn set_on_text_input<C: Into<TextInputOnTextInputCallback>>(
142 &mut self,
143 refany: RefAny,
144 callback: C,
145 ) {
146 self.text_input.set_on_text_input(refany, callback);
147 }
148
149 #[must_use]
150 pub fn with_on_text_input<C: Into<TextInputOnTextInputCallback>>(
151 mut self,
152 refany: RefAny,
153 callback: C,
154 ) -> Self {
155 self.set_on_text_input(refany, callback);
156 self
157 }
158
159 pub fn set_on_virtual_key_down<C: Into<TextInputOnVirtualKeyDownCallback>>(
160 &mut self,
161 refany: RefAny,
162 callback: C,
163 ) {
164 self.text_input.set_on_virtual_key_down(refany, callback);
165 }
166
167 #[must_use]
168 pub fn with_on_virtual_key_down<C: Into<TextInputOnVirtualKeyDownCallback>>(
169 mut self,
170 refany: RefAny,
171 callback: C,
172 ) -> Self {
173 self.set_on_virtual_key_down(refany, callback);
174 self
175 }
176
177 pub fn set_placeholder_style(&mut self, style: CssPropertyWithConditionsVec) {
178 self.text_input.placeholder_style = style;
179 }
180
181 #[must_use] pub fn with_placeholder_style(mut self, style: CssPropertyWithConditionsVec) -> Self {
182 self.set_placeholder_style(style);
183 self
184 }
185
186 pub fn set_container_style(&mut self, style: CssPropertyWithConditionsVec) {
187 self.text_input.container_style = style;
188 }
189
190 #[must_use] pub fn with_container_style(mut self, style: CssPropertyWithConditionsVec) -> Self {
191 self.set_container_style(style);
192 self
193 }
194
195 pub fn set_label_style(&mut self, style: CssPropertyWithConditionsVec) {
196 self.text_input.label_style = style;
197 }
198
199 #[must_use] pub fn with_label_style(mut self, style: CssPropertyWithConditionsVec) -> Self {
200 self.set_label_style(style);
201 self
202 }
203
204 pub fn set_on_value_change<C: Into<NumberInputOnValueChangeCallback>>(
206 &mut self,
207 refany: RefAny,
208 callback: C,
209 ) {
210 self.number_input_state.on_value_change = Some(NumberInputOnValueChange {
211 callback: callback.into(),
212 refany,
213 })
214 .into();
215 }
216
217 #[must_use]
218 pub fn with_on_value_change<C: Into<NumberInputOnValueChangeCallback>>(
219 mut self,
220 refany: RefAny,
221 callback: C,
222 ) -> Self {
223 self.set_on_value_change(refany, callback);
224 self
225 }
226
227 pub fn set_on_focus_lost<C: Into<NumberInputOnFocusLostCallback>>(
228 &mut self,
229 refany: RefAny,
230 callback: C,
231 ) {
232 self.number_input_state.on_focus_lost = Some(NumberInputOnFocusLost {
233 callback: callback.into(),
234 refany,
235 })
236 .into();
237 }
238
239 #[must_use]
240 pub fn with_on_focus_lost<C: Into<NumberInputOnFocusLostCallback>>(
241 mut self,
242 refany: RefAny,
243 callback: C,
244 ) -> Self {
245 self.set_on_focus_lost(refany, callback);
246 self
247 }
248
249 #[must_use]
250 pub fn swap_with_default(&mut self) -> Self {
251 let mut s = Self::create(0.0);
252 core::mem::swap(&mut s, self);
253 s
254 }
255
256 #[must_use] pub fn dom(mut self) -> Dom {
257 let number_string = format!("{}", self.number_input_state.inner.number);
258 self.text_input.text_input_state.inner.text = number_string
259 .chars()
260 .map(|s| s as u32)
261 .collect::<Vec<_>>()
262 .into();
263
264 let state = RefAny::new(self.number_input_state);
265
266 let validate: TextInputOnTextInputCallbackType = validate_text_input;
267 self.text_input.set_on_text_input(state.clone(), validate);
268 let focus_lost: TextInputOnFocusLostCallbackType = on_focus_lost;
269 self.text_input.set_on_focus_lost(state, focus_lost);
270 self.text_input.dom()
271 }
272}
273
274extern "C" fn on_focus_lost(
275 mut refany: RefAny,
276 info: CallbackInfo,
277 _state: TextInputState,
278) -> Update {
279 let Some(mut refany) = refany.downcast_mut::<NumberInputStateWrapper>() else {
280 return Update::DoNothing;
281 };
282
283 let number_input = &mut *refany;
284 let onfocuslost = &mut number_input.on_focus_lost;
285 let inner = number_input.inner;
286
287 match onfocuslost.as_mut() {
288 Some(NumberInputOnFocusLost { callback, refany }) => {
289 (callback.cb)(refany.clone(), info, inner)
290 }
291 None => Update::DoNothing,
292 }
293}
294
295extern "C" fn validate_text_input(
296 mut refany: RefAny,
297 info: CallbackInfo,
298 state: TextInputState,
299) -> OnTextInputReturn {
300 let Some(mut refany) = refany.downcast_mut::<NumberInputStateWrapper>() else {
301 return OnTextInputReturn {
302 update: Update::DoNothing,
303 valid: TextInputValid::Yes,
304 };
305 };
306
307 let validated_input: String = state
308 .text
309 .iter()
310 .filter_map(|c| core::char::from_u32(*c))
311 .map(|c| if c == ',' { '.' } else { c })
312 .collect();
313
314 let Ok(validated_f32) = validated_input.parse::<f32>() else {
315 return OnTextInputReturn {
318 update: Update::DoNothing,
319 valid: TextInputValid::No,
320 };
321 };
322
323 let number_input = &mut *refany;
324 let onvaluechange = &mut number_input.on_value_change;
325 let inner = &mut number_input.inner;
326
327 inner.previous = inner.number;
328 let clamped = validated_f32.clamp(inner.min, inner.max);
329 inner.number = clamped;
330 let inner_clone = *inner;
331
332 let update = match onvaluechange.as_mut() {
333 Some(NumberInputOnValueChange { callback, refany }) => {
334 (callback.cb)(refany.clone(), info, inner_clone)
335 }
336 None => Update::DoNothing,
337 };
338
339 OnTextInputReturn {
340 update,
341 valid: TextInputValid::Yes,
342 }
343}