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