1use azul_core::{
5 callbacks::{CoreCallbackData, Update},
6 dom::Dom,
7 refany::RefAny,
8 resources::OptionImageRef,
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::button::{Button, ButtonOnClick, ButtonOnClickCallback},
24};
25
26#[derive(Debug, Clone, PartialEq)]
27#[repr(C)]
28pub struct FileInput {
29 pub file_input_state: FileInputStateWrapper,
31 pub default_text: AzString,
34
35 pub image: OptionImageRef,
37 pub container_style: CssPropertyWithConditionsVec,
39 pub label_style: CssPropertyWithConditionsVec,
41 pub image_style: CssPropertyWithConditionsVec,
43}
44
45impl Default for FileInput {
46 fn default() -> Self {
47 let default_button = Button::create(AzString::from_const_str(""));
48 Self {
49 file_input_state: FileInputStateWrapper::default(),
50 default_text: "Select File...".into(),
51 image: None.into(),
52 container_style: default_button.container_style,
53 label_style: default_button.label_style,
54 image_style: default_button.image_style,
55 }
56 }
57}
58
59#[derive(Debug, Clone, PartialEq)]
60#[repr(C)]
61pub struct FileInputStateWrapper {
62 pub inner: FileInputState,
63 pub on_path_change: OptionFileInputOnPathChange,
64 pub file_dialog_title: AzString,
66 pub default_dir: OptionString,
68}
69
70impl Default for FileInputStateWrapper {
71 fn default() -> Self {
72 Self {
73 inner: FileInputState::default(),
74 on_path_change: None.into(),
75 file_dialog_title: "Select File".into(),
76 default_dir: None.into(),
77 }
78 }
79}
80
81#[derive(Debug, Clone, PartialEq)]
83#[repr(C)]
84pub struct FileInputState {
85 pub path: OptionString,
86}
87
88impl Default for FileInputState {
89 fn default() -> Self {
90 Self { path: None.into() }
91 }
92}
93
94pub type FileInputOnPathChangeCallbackType =
96 extern "C" fn(RefAny, CallbackInfo, FileInputState) -> Update;
97
98impl_widget_callback!(
99 FileInputOnPathChange,
100 OptionFileInputOnPathChange,
101 FileInputOnPathChangeCallback,
102 FileInputOnPathChangeCallbackType
103);
104
105azul_core::impl_managed_callback! {
106 wrapper: FileInputOnPathChangeCallback,
107 info_ty: CallbackInfo,
108 return_ty: Update,
109 default_ret: Update::DoNothing,
110 invoker_static: FILE_INPUT_ON_PATH_CHANGE_INVOKER,
111 invoker_ty: AzFileInputOnPathChangeCallbackInvoker,
112 thunk_fn: az_file_input_on_path_change_callback_thunk,
113 setter_fn: AzApp_setFileInputOnPathChangeCallbackInvoker,
114 from_handle_fn: AzFileInputOnPathChangeCallback_createFromHostHandle,
115 extra_args: [ state: FileInputState ],
116}
117
118impl FileInput {
119 pub fn create(path: OptionString) -> Self {
120 Self {
121 file_input_state: FileInputStateWrapper {
122 inner: FileInputState { path },
123 ..Default::default()
124 },
125 ..Default::default()
126 }
127 }
128
129 #[inline]
130 pub fn swap_with_default(&mut self) -> Self {
131 let mut s = Self::create(None.into());
132 core::mem::swap(&mut s, self);
133 s
134 }
135
136 #[inline]
137 pub fn set_default_text(&mut self, default_text: AzString) {
138 self.default_text = default_text;
139 }
140
141 #[inline]
142 pub fn with_default_text(mut self, default_text: AzString) -> Self {
143 self.set_default_text(default_text);
144 self
145 }
146
147 #[inline]
148 pub fn set_on_path_change<I: Into<FileInputOnPathChangeCallback>>(
149 &mut self,
150 refany: RefAny,
151 callback: I,
152 ) {
153 self.file_input_state.on_path_change = Some(FileInputOnPathChange {
154 callback: callback.into(),
155 refany,
156 })
157 .into();
158 }
159
160 #[inline]
161 pub fn with_on_path_change<I: Into<FileInputOnPathChangeCallback>>(
162 mut self,
163 refany: RefAny,
164 callback: I,
165 ) -> Self {
166 self.set_on_path_change(refany, callback);
167 self
168 }
169
170 #[inline]
171 pub fn dom(self) -> Dom {
172 let button_label = match self.file_input_state.inner.path.as_ref() {
175 Some(path) => std::path::Path::new(path.as_str())
176 .file_name()
177 .map(|s| s.to_string_lossy().to_string())
178 .unwrap_or(self.default_text.as_str().to_string())
179 .into(),
180 None => self.default_text.clone(),
181 };
182
183 Button {
184 label: button_label,
185 image: self.image,
186 button_type: crate::widgets::button::ButtonType::Default,
187 container_style: self.container_style,
188 label_style: self.label_style,
189 image_style: self.image_style,
190 on_click: Some(ButtonOnClick {
191 refany: RefAny::new(self.file_input_state),
192 callback: ButtonOnClickCallback {
193 cb: fileinput_on_click,
194 ctx: azul_core::refany::OptionRefAny::None,
195 },
196 })
197 .into(),
198 }
199 .dom()
200 }
201}
202
203extern "C" fn fileinput_on_click(mut refany: RefAny, mut info: CallbackInfo) -> Update {
204 let mut fileinputstatewrapper = match refany.downcast_mut::<FileInputStateWrapper>() {
205 Some(s) => s,
206 None => return Update::DoNothing,
207 };
208 let fileinputstatewrapper = &mut *fileinputstatewrapper;
209
210 let inner = fileinputstatewrapper.inner.clone();
214 let mut result = match fileinputstatewrapper.on_path_change.as_mut() {
215 Some(FileInputOnPathChange { refany, callback }) => {
216 (callback.cb)(refany.clone(), info.clone(), inner)
217 }
218 None => return Update::DoNothing,
219 };
220
221 result.max_self(Update::RefreshDom);
223
224 result
225}