microui_redux/
file_dialog.rs1use std::path::Path;
2
3use crate::*;
33
34pub struct FileDialogState {
36 current_working_directory: String,
37 file_name: Option<String>,
38 tmp_file_name: Textbox,
39 selected_folder: Option<String>,
40 win: WindowHandle,
41 folder_panel: ContainerHandle,
42 file_panel: ContainerHandle,
43 folders: Vec<String>,
44 files: Vec<String>,
45 folder_items: Vec<ListItem>,
46 file_items: Vec<ListItem>,
47 ok_button: Button,
48 cancel_button: Button,
49}
50
51impl FileDialogState {
52 pub fn file_name(&self) -> &Option<String> { &self.file_name }
54
55 pub fn is_open(&self) -> bool { self.win.is_open() }
57
58 fn list_folders_files(p: &Path, folders: &mut Vec<String>, files: &mut Vec<String>) {
59 folders.clear();
60 files.clear();
61 if let Some(parent) = p.parent() {
62 folders.push(parent.to_string_lossy().to_string());
63 }
64 if let Ok(read_dir) = std::fs::read_dir(p) {
65 for entry in read_dir {
66 if let Ok(e) = entry {
67 let path = e.path();
68 if path.is_dir() {
69 folders.push(path.to_string_lossy().to_string());
70 } else {
71 files.push(e.file_name().to_string_lossy().to_string())
72 }
73 }
74 }
75 }
76 }
77
78 fn refresh_entries(&mut self) {
79 Self::list_folders_files(Path::new(&self.current_working_directory), &mut self.folders, &mut self.files);
80 self.rebuild_item_states();
81 }
82
83 fn rebuild_item_states(&mut self) {
84 let parent_path = Path::new(&self.current_working_directory)
85 .parent()
86 .map(|p| p.to_string_lossy().to_string());
87
88 self.folder_items.clear();
89 self.folder_items.reserve(self.folders.len());
90 for f in &self.folders {
91 let label = if parent_path.as_deref() == Some(f.as_str()) {
92 ".."
93 } else {
94 Path::new(f)
95 .file_name()
96 .and_then(|name| name.to_str())
97 .unwrap_or(f.as_str())
98 };
99 let icon = if self.selected_folder.as_deref() == Some(f.as_str()) {
100 OPEN_FOLDER_16_ICON
101 } else {
102 CLOSED_FOLDER_16_ICON
103 };
104 let mut state = ListItem::new(label);
105 state.icon = Some(icon);
106 self.folder_items.push(state);
107 }
108
109 self.file_items.clear();
110 self.file_items.reserve(self.files.len());
111 for f in &self.files {
112 let mut state = ListItem::new(f.as_str());
113 state.icon = Some(FILE_16_ICON);
114 self.file_items.push(state);
115 }
116 }
117
118 pub fn new<R: Renderer>(ctx: &mut Context<R>) -> Self {
120 let current_working_directory = std::env::current_dir()
121 .unwrap_or_else(|_| std::path::PathBuf::from("."))
122 .to_string_lossy()
123 .to_string();
124 let mut dialog = Self {
125 current_working_directory,
126 file_name: None,
127 tmp_file_name: Textbox::new(""),
128 selected_folder: None,
129 win: ctx.new_dialog("File Dialog", Recti::new(50, 50, 500, 500)),
130 folder_panel: ctx.new_panel("folders"),
131 file_panel: ctx.new_panel("files"),
132 folders: Vec::new(),
133 files: Vec::new(),
134 folder_items: Vec::new(),
135 file_items: Vec::new(),
136 ok_button: Button::new("Ok"),
137 cancel_button: Button::new("Cancel"),
138 };
139 dialog.refresh_entries();
140 dialog
141 }
142
143 pub fn open<R: Renderer>(&mut self, ctx: &mut Context<R>) { ctx.open_dialog(&mut self.win); }
145
146 pub fn eval<R: Renderer>(&mut self, ctx: &mut Context<R>) {
148 let mut needs_refresh = false;
149 {
150 let win = &mut self.win;
151 let folder_panel = &mut self.folder_panel;
152 let file_panel = &mut self.file_panel;
153 let folders = &self.folders;
154 let files = &self.files;
155 let folder_items = &mut self.folder_items;
156 let file_items = &mut self.file_items;
157 let current_working_directory = &mut self.current_working_directory;
158 let tmp_file_name = &mut self.tmp_file_name;
159 let selected_folder = &mut self.selected_folder;
160 let ok_button = &mut self.ok_button;
161 let cancel_button = &mut self.cancel_button;
162 let file_name = &mut self.file_name;
163
164 ctx.dialog(win, ContainerOption::NONE, WidgetBehaviourOption::NONE, |cont| {
165 let mut dialog_state = WindowState::Open;
166 let half_width = cont.body.width / 2;
167 cont.with_row(&[SizePolicy::Remainder(0)], SizePolicy::Auto, |cont| {
168 cont.label(current_working_directory.as_str());
169 cont.textbox_ex(tmp_file_name);
170 });
171 let left_column = if half_width > 0 {
172 SizePolicy::Remainder(half_width - 1)
173 } else {
174 SizePolicy::Auto
175 };
176 let top_row_widths = [left_column, SizePolicy::Remainder(0)];
177 cont.with_row(&top_row_widths, SizePolicy::Remainder(24), |cont| {
178 cont.panel(folder_panel, ContainerOption::NONE, WidgetBehaviourOption::NONE, |container_handle| {
179 let container = &mut container_handle.inner_mut();
180
181 container.with_row(&[SizePolicy::Remainder(0)], SizePolicy::Auto, |container| {
182 let mut refresh = false;
183 for index in 0..folder_items.len() {
184 let submitted = {
185 let item = &mut folder_items[index];
186 container.list_item(item).is_submitted()
187 };
188 if submitted {
189 if let Some(path) = folders.get(index) {
190 *current_working_directory = path.to_string();
191 *selected_folder = Some(path.to_string());
192 }
193 refresh = true;
194 }
195 }
196 if refresh {
197 needs_refresh = true;
198 }
199 });
200 });
201 cont.panel(file_panel, ContainerOption::NONE, WidgetBehaviourOption::NONE, |container_handle| {
202 let container = &mut container_handle.inner_mut();
203
204 container.with_row(&[SizePolicy::Remainder(0)], SizePolicy::Auto, |container| {
205 if !file_items.is_empty() {
206 for index in 0..file_items.len() {
207 let submitted = {
208 let item = &mut file_items[index];
209 container.list_item(item).is_submitted()
210 };
211 if submitted {
212 if let Some(name) = files.get(index) {
213 tmp_file_name.buf = name.to_string();
214 }
215 }
216 }
217 } else {
218 container.label("No Files");
219 }
220 });
221 });
222 });
223 let bottom_row_widths = [left_column, SizePolicy::Remainder(0)];
224 cont.with_row(&bottom_row_widths, SizePolicy::Remainder(0), |cont| {
225 if cont.button(ok_button).is_submitted() {
226 if tmp_file_name.buf.is_empty() {
227 *file_name = None;
228 } else {
229 *file_name = Some(tmp_file_name.buf.clone());
230 }
231 dialog_state = WindowState::Closed;
232 }
233 if cont.button(cancel_button).is_submitted() {
234 *file_name = None;
235 dialog_state = WindowState::Closed;
236 }
237 });
238 dialog_state
239 });
240 }
241
242 if needs_refresh {
243 self.refresh_entries();
244 }
245 }
246}