azul_layout/widgets/
file_input.rs1use 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)]
82#[repr(C)]
83pub struct FileInputState {
84 pub path: OptionString,
85}
86
87impl Default for FileInputState {
88 fn default() -> Self {
89 Self { path: None.into() }
90 }
91}
92
93pub type FileInputOnPathChangeCallbackType =
94 extern "C" fn(RefAny, CallbackInfo, FileInputState) -> Update;
95
96impl_widget_callback!(
97 FileInputOnPathChange,
98 OptionFileInputOnPathChange,
99 FileInputOnPathChangeCallback,
100 FileInputOnPathChangeCallbackType
101);
102
103impl FileInput {
104 pub fn create(path: OptionString) -> Self {
105 Self {
106 file_input_state: FileInputStateWrapper {
107 inner: FileInputState {
108 path,
109 ..Default::default()
110 },
111 ..Default::default()
112 },
113 ..Default::default()
114 }
115 }
116
117 #[inline]
118 pub fn swap_with_default(&mut self) -> Self {
119 let mut s = Self::create(None.into());
120 core::mem::swap(&mut s, self);
121 s
122 }
123
124 #[inline]
125 pub fn set_default_text(&mut self, default_text: AzString) {
126 self.default_text = default_text;
127 }
128
129 #[inline]
130 pub fn with_default_text(mut self, default_text: AzString) -> Self {
131 self.set_default_text(default_text);
132 self
133 }
134
135 #[inline]
136 pub fn set_on_path_change<I: Into<FileInputOnPathChangeCallback>>(
137 &mut self,
138 refany: RefAny,
139 callback: I,
140 ) {
141 self.file_input_state.on_path_change = Some(FileInputOnPathChange {
142 callback: callback.into(),
143 refany,
144 })
145 .into();
146 }
147
148 #[inline]
149 pub fn with_on_path_change<I: Into<FileInputOnPathChangeCallback>>(
150 mut self,
151 refany: RefAny,
152 callback: I,
153 ) -> Self {
154 self.set_on_path_change(refany, callback);
155 self
156 }
157
158 #[inline]
159 pub fn dom(self) -> Dom {
160 let button_label = match self.file_input_state.inner.path.as_ref() {
163 Some(path) => std::path::Path::new(path.as_str())
164 .file_name()
165 .map(|s| s.to_string_lossy().to_string())
166 .unwrap_or(self.default_text.as_str().to_string())
167 .into(),
168 None => self.default_text.clone(),
169 };
170
171 Button {
172 label: button_label,
173 image: self.image,
174 container_style: self.container_style,
175 label_style: self.label_style,
176 image_style: self.image_style,
177 on_click: Some(ButtonOnClick {
178 refany: RefAny::new(self.file_input_state),
179 callback: ButtonOnClickCallback {
180 cb: fileinput_on_click,
181 ctx: azul_core::refany::OptionRefAny::None,
182 },
183 })
184 .into(),
185 }
186 .dom()
187 }
188}
189
190extern "C" fn fileinput_on_click(mut refany: RefAny, mut info: CallbackInfo) -> Update {
191 let mut fileinputstatewrapper = match refany.downcast_mut::<FileInputStateWrapper>() {
192 Some(s) => s,
193 None => return Update::DoNothing,
194 };
195 let fileinputstatewrapper = &mut *fileinputstatewrapper;
196
197 let inner = fileinputstatewrapper.inner.clone();
201 let mut result = match fileinputstatewrapper.on_path_change.as_mut() {
202 Some(FileInputOnPathChange { refany, callback }) => {
203 (callback.cb)(refany.clone(), info.clone(), inner)
204 }
205 None => return Update::DoNothing,
206 };
207
208 result.max_self(Update::RefreshDom);
209
210 result
211}