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