1use std::{
2 borrow::Cow,
3 cmp,
4 fmt::Debug,
5 io::Error,
6 ops::Deref,
7 path::{Path, PathBuf},
8};
9
10use dyn_clone::clone_box;
11use egui::{Align2, Button, Context, Id, Key, Layout, Pos2, RichText, ScrollArea, TextEdit, Ui, Vec2, Vec2b, Window};
12use fs::FileInfo;
13use fs::Fs;
14
15mod fs;
16pub mod vfs;
17pub use vfs::Vfs;
18use vfs::VfsFile;
19
20pub type Filter<T> = Box<dyn Fn(&<T as Deref>::Target) -> bool + Send + Sync + 'static>;
22
23#[derive(Debug, PartialEq, Eq, Copy, Clone)]
24pub enum State {
26 Open,
28 Closed,
30 Cancelled,
32 Selected,
34}
35
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub enum DialogType {
39 SelectFolder,
40 OpenFile,
41 SaveFile,
42}
43
44pub struct FileDialog {
46 path: PathBuf,
48
49 path_edit: String,
51
52 selected_file: Option<Box<dyn VfsFile>>,
54
55 filename_edit: String,
57
58 title: Cow<'static, str>,
60
61 open_button_text: Cow<'static, str>,
63
64 save_button_text: Cow<'static, str>,
66
67 cancel_button_text: Cow<'static, str>,
69
70 new_folder_button_text: Cow<'static, str>,
72
73 new_folder_name_text: Cow<'static, str>,
75
76 rename_button_text: Cow<'static, str>,
78
79 refresh_button_hover_text: Cow<'static, str>,
81
82 parent_folder_button_hover_text: Cow<'static, str>,
84
85 file_label_text: Cow<'static, str>,
87
88 show_hidden_checkbox_text: Cow<'static, str>,
90
91 files: Result<Vec<Box<dyn VfsFile>>, Error>,
93
94 state: State,
96
97 dialog_type: DialogType,
99
100 id: Option<Id>,
101 current_pos: Option<Pos2>,
102 default_pos: Option<Pos2>,
103 default_size: Vec2,
104 anchor: Option<(Align2, Vec2)>,
105 show_files_filter: Filter<PathBuf>,
106 filename_filter: Filter<String>,
107 range_start: Option<usize>,
108 resizable: Vec2b,
109 rename: bool,
110 new_folder: bool,
111 multi_select_enabled: bool,
112 keep_on_top: bool,
113 show_system_files: bool,
114
115 #[cfg(windows)]
117 show_drives: bool,
118
119 #[cfg(unix)]
121 show_hidden: bool,
122
123 fs: Box<dyn Vfs + 'static>,
124}
125
126impl Debug for FileDialog {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 let mut dbg = f.debug_struct("FileDialog");
129 let dbg = dbg
130 .field("path", &self.path)
131 .field("path_edit", &self.path_edit)
132 .field("selected_file", &self.selected_file)
133 .field("filename_edit", &self.filename_edit)
134 .field("files", &self.files)
135 .field("state", &self.state)
136 .field("dialog_type", &self.dialog_type)
137 .field("current_pos", &self.current_pos)
138 .field("default_pos", &self.default_pos)
139 .field("default_size", &self.default_size)
140 .field("anchor", &self.anchor)
141 .field("resizable", &self.resizable)
142 .field("rename", &self.rename)
143 .field("new_folder", &self.new_folder)
144 .field("multi_select", &self.multi_select_enabled)
145 .field("range_start", &self.range_start)
146 .field("keep_on_top", &self.keep_on_top)
147 .field("show_system_files", &self.show_system_files);
148
149 #[cfg(unix)]
155 let dbg = dbg.field("show_hidden", &self.show_hidden);
156
157 #[cfg(windows)]
158 let dbg = dbg.field("show_drives", &self.show_drives);
159
160 dbg.finish()
161 }
162}
163
164impl FileDialog {
165 pub fn select_folder() -> Self {
167 FileDialog::new(DialogType::SelectFolder)
168 }
169
170 pub fn open_file() -> Self {
172 FileDialog::new(DialogType::OpenFile)
173 }
174
175 pub fn save_file() -> Self {
177 FileDialog::new(DialogType::SaveFile)
178 }
179
180 fn new(dialog_type: DialogType) -> Self {
183 let path = std::env::current_dir().unwrap_or_default();
184 let filename_edit = String::new();
185 let path_edit = path.to_str().unwrap_or_default().to_string();
186 Self {
187 path,
188 path_edit,
189 selected_file: None,
190 filename_edit,
191 title: match dialog_type {
192 DialogType::SelectFolder => "📁 Select Folder",
193 DialogType::OpenFile => "📂 Open File",
194 DialogType::SaveFile => "💾 Save File",
195 }
196 .into(),
197 open_button_text: "Open".into(),
198 save_button_text: "Save".into(),
199 cancel_button_text: "Cancel".into(),
200 new_folder_button_text: "New Folder".into(),
201 new_folder_name_text: "New folder".into(),
202 rename_button_text: "Rename".into(),
203 refresh_button_hover_text: "Refresh".into(),
204 parent_folder_button_hover_text: "Parent Folder".into(),
205 file_label_text: "File:".into(),
206 show_hidden_checkbox_text: "Show Hidden".into(),
207 files: Ok(Vec::new()),
208 state: State::Closed,
209 dialog_type,
210
211 id: None,
212 current_pos: None,
213 default_pos: None,
214 default_size: egui::vec2(512.0, 512.0),
215 anchor: None,
216 show_files_filter: Box::new(|_| true),
217 filename_filter: Box::new(|_| true),
218 resizable: true.into(),
219 rename: true,
220 new_folder: true,
221
222 #[cfg(windows)]
223 show_drives: true,
224
225 #[cfg(unix)]
226 show_hidden: false,
227 multi_select_enabled: false,
228 range_start: None,
229 keep_on_top: false,
230 show_system_files: false,
231 fs: Box::new(Fs {}),
232 }
233 }
234
235 pub fn initial_path(mut self, path: impl Into<PathBuf>) -> Self {
237 self.path = path.into();
238 self
239 }
240
241 pub fn default_filename(mut self, filename: impl Into<String>) -> Self {
243 self.filename_edit = filename.into();
244 self
245 }
246
247 pub fn title(mut self, title: &str) -> Self {
249 self.title = (match self.dialog_type {
250 DialogType::SelectFolder => "📁 ",
251 DialogType::OpenFile => "📂 ",
252 DialogType::SaveFile => "💾 ",
253 }
254 .to_string()
255 + title)
256 .into();
257 self
258 }
259
260 pub fn open_button_text(mut self, text: Cow<'static, str>) -> Self {
262 self.open_button_text = text;
263 self
264 }
265
266 pub fn save_button_text(mut self, text: Cow<'static, str>) -> Self {
268 self.save_button_text = text;
269 self
270 }
271
272 pub fn cancel_button_text(mut self, text: Cow<'static, str>) -> Self {
274 self.cancel_button_text = text;
275 self
276 }
277
278 pub fn new_folder_button_text(mut self, text: Cow<'static, str>) -> Self {
280 self.new_folder_button_text = text;
281 self
282 }
283
284 pub fn new_folder_name_text(mut self, text: Cow<'static, str>) -> Self {
286 self.new_folder_name_text = text;
287 self
288 }
289
290 pub fn refresh_button_hover_text(mut self, text: Cow<'static, str>) -> Self {
292 self.refresh_button_hover_text = text;
293 self
294 }
295
296 pub fn parent_folder_button_hover_text(mut self, text: Cow<'static, str>) -> Self {
298 self.parent_folder_button_hover_text = text;
299 self
300 }
301
302 pub fn rename_button_text(mut self, text: Cow<'static, str>) -> Self {
304 self.rename_button_text = text;
305 self
306 }
307
308 pub fn file_label_text(mut self, text: Cow<'static, str>) -> Self {
310 self.file_label_text = text;
311 self
312 }
313
314 pub fn show_hidden_checkbox_text(mut self, text: Cow<'static, str>) -> Self {
316 self.show_hidden_checkbox_text = text;
317 self
318 }
319
320 pub fn id(mut self, id: impl Into<Id>) -> Self {
322 self.id = Some(id.into());
323 self
324 }
325
326 pub fn anchor(mut self, align: Align2, offset: impl Into<Vec2>) -> Self {
328 self.anchor = Some((align, offset.into()));
329 self
330 }
331
332 pub fn current_pos(mut self, current_pos: impl Into<Pos2>) -> Self {
334 self.current_pos = Some(current_pos.into());
335 self
336 }
337
338 pub fn default_pos(mut self, default_pos: impl Into<Pos2>) -> Self {
340 self.default_pos = Some(default_pos.into());
341 self
342 }
343
344 pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {
346 self.default_size = default_size.into();
347 self
348 }
349
350 pub fn resizable(mut self, resizable: impl Into<Vec2b>) -> Self {
352 self.resizable = resizable.into();
353 self
354 }
355
356 pub fn show_rename(mut self, rename: bool) -> Self {
358 self.rename = rename;
359 self
360 }
361
362 pub fn show_new_folder(mut self, new_folder: bool) -> Self {
364 self.new_folder = new_folder;
365 self
366 }
367
368 pub fn multi_select(mut self, multi_select: bool) -> Self {
369 self.multi_select_enabled = multi_select;
370 self
371 }
372
373 pub fn has_multi_select(&self) -> bool {
374 self.multi_select_enabled
375 }
376
377 #[cfg(windows)]
379 pub fn show_drives(mut self, drives: bool) -> Self {
380 self.show_drives = drives;
381 self
382 }
383
384 pub fn show_files_filter(mut self, filter: Filter<PathBuf>) -> Self {
386 self.show_files_filter = filter;
387 self
388 }
389
390 pub fn filename_filter(mut self, filter: Filter<String>) -> Self {
392 self.filename_filter = filter;
393 self
394 }
395
396 pub fn keep_on_top(mut self, keep_on_top: bool) -> Self {
398 self.keep_on_top = keep_on_top;
399 self
400 }
401
402 pub fn with_fs(mut self, fs: Box<dyn Vfs>) -> Self {
403 self.fs = fs;
404 self
405 }
406
407 pub fn show_system_files(mut self, show_system_files: bool) -> Self {
409 self.show_system_files = show_system_files;
410 self
411 }
412
413 pub fn dialog_type(&self) -> DialogType {
415 self.dialog_type
416 }
417
418 pub fn visible(&self) -> bool {
420 self.state == State::Open
421 }
422
423 pub fn open(&mut self) {
425 self.state = State::Open;
426 self.refresh();
427 }
428
429 pub fn path(&self) -> Option<&Path> {
431 self.selected_file.as_ref().map(|info| info.path())
432 }
433
434 pub fn selection(&self) -> Vec<&Path> {
436 match self.files {
437 Ok(ref files) => files
438 .iter()
439 .filter_map(|info| if info.selected() { Some(info.path()) } else { None })
440 .collect(),
441 Err(_) => Vec::new(),
442 }
443 }
444
445 pub fn directory(&self) -> &Path {
447 self.path.as_path()
448 }
449
450 pub fn set_path(&mut self, path: impl Into<PathBuf>) {
452 self.path = path.into();
453 self.refresh();
454 }
455
456 pub fn state(&self) -> State {
458 self.state
459 }
460
461 pub fn selected(&self) -> bool {
463 self.state == State::Selected
464 }
465
466 fn open_selected(&mut self) {
467 if let Some(info) = &self.selected_file {
468 if info.is_dir() {
469 self.set_path(info.path().to_owned());
470 } else if self.dialog_type == DialogType::OpenFile {
471 self.confirm();
472 }
473 } else if self.multi_select_enabled && self.dialog_type == DialogType::OpenFile {
474 self.confirm();
475 }
476 }
477
478 fn confirm(&mut self) {
479 self.state = State::Selected;
480 }
481
482 fn refresh(&mut self) {
483 self.files = self.fs.read_folder(
484 &self.path,
485 self.show_system_files,
486 &self.show_files_filter,
487 #[cfg(unix)]
488 self.show_hidden,
489 #[cfg(windows)]
490 self.show_drives,
491 );
492 self.path_edit = String::from(self.path.to_str().unwrap_or_default());
493 self.select(None);
494 self.selected_file = None;
495 }
496
497 fn select(&mut self, file: Option<Box<dyn VfsFile>>) {
498 if let Some(info) = &file {
499 if !info.is_dir() {
500 info.get_file_name().clone_into(&mut self.filename_edit);
501 }
502 }
503 self.selected_file = file;
504 }
505
506 fn select_reset_multi(&mut self, idx: usize) {
507 if let Ok(files) = &mut self.files {
508 let selected_val = files[idx].selected();
509 for file in files.iter_mut() {
510 file.set_selected(false);
511 }
512 files[idx].set_selected(!selected_val);
513 self.range_start = Some(idx);
514 }
515 }
516
517 fn select_switch_multi(&mut self, idx: usize) {
518 if let Ok(files) = &mut self.files {
519 let old = !files[idx].selected();
520 files[idx].set_selected(old);
521 if files[idx].selected() {
522 self.range_start = Some(idx);
523 } else {
524 self.range_start = None;
525 }
526 } else {
527 self.range_start = None;
528 }
529 }
530
531 fn select_range(&mut self, idx: usize) {
532 if let Ok(files) = &mut self.files {
533 if let Some(range_start) = self.range_start {
534 let range = cmp::min(idx, range_start)..=cmp::max(idx, range_start);
535 for i in range {
536 files[i].set_selected(true);
537 }
538 }
539 }
540 }
541
542 fn can_save(&self) -> bool {
543 !self.filename_edit.is_empty() && (self.filename_filter)(self.filename_edit.as_str())
544 }
545
546 fn can_open(&self) -> bool {
547 if self.multi_select_enabled {
548 if let Ok(files) = &self.files {
549 for file in files {
550 if file.selected() && (self.filename_filter)(file.get_file_name()) {
551 return true;
552 }
553 }
554 }
555 false
556 } else {
557 !self.filename_edit.is_empty() && (self.filename_filter)(self.filename_edit.as_str())
558 }
559 }
560
561 fn can_rename(&self) -> bool {
562 if !self.filename_edit.is_empty() {
563 if let Some(file) = &self.selected_file {
564 return file.get_file_name() != self.filename_edit;
565 }
566 }
567 false
568 }
569
570 pub fn show(&mut self, ctx: &Context) -> &Self {
573 self.state = match self.state {
574 State::Open => {
575 if ctx.input(|state| state.key_pressed(Key::Escape)) {
576 self.state = State::Cancelled;
577 }
578
579 let mut is_open = true;
580 self.ui(ctx, &mut is_open);
581 match is_open {
582 true => self.state,
583 false => State::Cancelled,
584 }
585 }
586 _ => State::Closed,
587 };
588
589 self
590 }
591
592 fn ui(&mut self, ctx: &Context, is_open: &mut bool) {
593 let mut window = Window::new(RichText::new(self.title.as_ref()).strong())
594 .open(is_open)
595 .default_size(self.default_size)
596 .resizable(self.resizable)
597 .collapsible(false);
598
599 if let Some(id) = self.id {
600 window = window.id(id);
601 }
602
603 if let Some((align, offset)) = self.anchor {
604 window = window.anchor(align, offset);
605 }
606
607 if let Some(current_pos) = self.current_pos {
608 window = window.current_pos(current_pos);
609 }
610
611 if let Some(default_pos) = self.default_pos {
612 window = window.default_pos(default_pos);
613 }
614
615 window.show(ctx, |ui| {
616 if self.keep_on_top {
617 ui.ctx().move_to_top(ui.layer_id());
618 }
619 self.ui_in_window(ui)
620 });
621 }
622
623 fn ui_in_window(&mut self, ui: &mut Ui) {
624 enum Command {
625 Cancel,
626 CreateDirectory,
627 Folder,
628 Open(Box<dyn VfsFile>),
629 OpenSelected,
630 BrowseDirectory(Box<dyn VfsFile>),
631 Refresh,
632 Rename(PathBuf, PathBuf),
633 Save(Box<dyn VfsFile>),
634 Select(Box<dyn VfsFile>),
635 MultiSelectRange(usize),
636 MultiSelect(usize),
637 MultiSelectSwitch(usize),
638 UpDirectory,
639 }
640 let mut command: Option<Command> = None;
641
642 egui::TopBottomPanel::top("egui_file_top").show_inside(ui, |ui| {
644 ui.horizontal(|ui| {
645 ui.add_enabled_ui(self.path.parent().is_some(), |ui| {
646 let response = ui
647 .add_sized([23.0, 20.0], Button::new("⬆"))
648 .on_hover_text(self.parent_folder_button_hover_text.as_ref());
649 if response.clicked() {
650 command = Some(Command::UpDirectory);
651 }
652 });
653 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
654 let response = ui
655 .add_sized([23.0, 20.0], Button::new("⟲"))
656 .on_hover_text(self.refresh_button_hover_text.as_ref());
657 if response.clicked() {
658 command = Some(Command::Refresh);
659 }
660
661 let response = ui.add_sized(ui.available_size(), TextEdit::singleline(&mut self.path_edit));
662
663 if response.lost_focus() {
664 let path = PathBuf::from(&self.path_edit);
665 command = Some(Command::Open(Box::new(FileInfo::new(path))));
666 }
667 });
668 });
669 ui.add_space(ui.spacing().item_spacing.y);
670 });
671
672 egui::TopBottomPanel::bottom("egui_file_bottom").show_inside(ui, |ui| {
674 ui.add_space(ui.spacing().item_spacing.y * 2.0);
675 ui.horizontal(|ui| {
676 ui.label(self.file_label_text.as_ref());
677 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
678 if self.new_folder && ui.button(self.new_folder_button_text.as_ref()).clicked() {
679 command = Some(Command::CreateDirectory);
680 }
681
682 if self.rename {
683 ui.add_enabled_ui(self.can_rename(), |ui| {
684 if ui.button(self.rename_button_text.as_ref()).clicked() {
685 if let Some(from) = self.selected_file.clone() {
686 let to = from.path().with_file_name(&self.filename_edit);
687 command = Some(Command::Rename(from.path().to_owned(), to));
688 }
689 }
690 });
691 }
692
693 let response = ui.add_sized(ui.available_size(), TextEdit::singleline(&mut self.filename_edit));
694
695 if response.lost_focus() {
696 let ctx = response.ctx;
697 let enter_pressed = ctx.input(|state| state.key_pressed(Key::Enter));
698
699 if enter_pressed && (self.filename_filter)(self.filename_edit.as_str()) {
700 let path = self.path.join(&self.filename_edit);
701 match self.dialog_type {
702 DialogType::SelectFolder => command = Some(Command::Folder),
703 DialogType::OpenFile => {
704 if path.exists() {
705 command = Some(Command::Open(Box::new(FileInfo::new(path))));
706 }
707 }
708 DialogType::SaveFile => {
709 let file_info = Box::new(FileInfo::new(path));
710 command = Some(match file_info.is_dir() {
711 true => Command::Open(file_info),
712 false => Command::Save(file_info),
713 });
714 }
715 }
716 }
717 }
718 });
719 });
720
721 ui.add_space(ui.spacing().item_spacing.y);
722
723 ui.horizontal(|ui| {
725 #[cfg(unix)]
726 if ui
727 .checkbox(&mut self.show_hidden, self.show_hidden_checkbox_text.as_ref())
728 .changed()
729 {
730 self.refresh();
731 }
732
733 ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
734 if ui.button(self.cancel_button_text.as_ref()).clicked() {
735 command = Some(Command::Cancel);
736 }
737
738 match self.dialog_type {
739 DialogType::SelectFolder => {
740 ui.horizontal(|ui| {
741 if ui.button(self.open_button_text.as_ref()).clicked() {
742 command = Some(Command::Folder);
743 };
744 });
745 }
746 DialogType::OpenFile => {
747 ui.horizontal(|ui| {
748 if !self.can_open() {
749 ui.disable();
750 }
751
752 if ui.button(self.open_button_text.as_ref()).clicked() {
753 command = Some(Command::OpenSelected);
754 };
755 });
756 }
757 DialogType::SaveFile => {
758 let should_open_directory = match &self.selected_file {
759 Some(file) => file.is_dir(),
760 None => false,
761 };
762
763 if should_open_directory {
764 if ui.button(self.open_button_text.as_ref()).clicked() {
765 command = Some(Command::OpenSelected);
766 };
767 } else {
768 ui.horizontal(|ui| {
769 if !self.can_save() {
770 ui.disable();
771 }
772
773 if ui.button(self.save_button_text.as_ref()).clicked() {
774 let filename = &self.filename_edit;
775 let path = self.path.join(filename);
776 command = Some(Command::Save(Box::new(FileInfo::new(path))));
777 };
778 });
779 }
780 }
781 }
782 });
783 });
784 });
785
786 egui::CentralPanel::default().show_inside(ui, |ui| {
788 ScrollArea::vertical().show_rows(
789 ui,
790 ui.text_style_height(&egui::TextStyle::Body),
791 self.files.as_ref().map_or(0, |files| files.len()),
792 |ui, range| match self.files.as_ref() {
793 Ok(files) => {
794 ui.with_layout(ui.layout().with_cross_justify(true), |ui| {
795 let selected = self.selected_file.as_ref().map(|info| info.path());
796 let range_start = range.start;
797
798 for (n, info) in files[range].iter().enumerate() {
799 let idx = n + range_start;
800 let label = match info.is_dir() {
801 true => "🗀 ",
802 false => "🗋 ",
803 }
804 .to_string()
805 + info.get_file_name();
806
807 let is_selected = if self.multi_select_enabled {
808 files[idx].selected()
809 } else {
810 Some(info.path()) == selected
811 };
812 let response = ui.selectable_label(is_selected, label);
813 if response.clicked() {
814 if self.multi_select_enabled {
815 if ui.input(|i| i.modifiers.shift) {
816 command = Some(Command::MultiSelectRange(idx))
817 } else if ui.input(|i| i.modifiers.ctrl) {
818 command = Some(Command::MultiSelectSwitch(idx))
819 } else {
820 command = Some(Command::MultiSelect(idx))
821 }
822 } else {
823 command = Some(Command::Select(dyn_clone::clone_box(info.as_ref())));
824 }
825 }
826
827 if response.double_clicked() {
828 match self.dialog_type {
829 DialogType::SelectFolder => {
830 command = Some(Command::OpenSelected);
832 }
833 DialogType::OpenFile => {
835 if info.is_dir() {
836 command = Some(Command::BrowseDirectory(clone_box(info.as_ref())));
837 } else if (self.filename_filter)(self.filename_edit.as_str()) {
838 command = Some(Command::Open(clone_box(info.as_ref())));
839 }
840 }
841 DialogType::SaveFile => {
842 if info.is_dir() {
843 command = Some(Command::OpenSelected);
844 } else if (self.filename_filter)(self.filename_edit.as_str()) {
845 command = Some(Command::Save(info.clone()));
846 }
847 }
848 }
849 }
850 }
851 })
852 .response
853 }
854 Err(e) => ui.label(e.to_string()),
855 },
856 );
857 });
858
859 if let Some(command) = command {
860 match command {
861 Command::Select(info) => self.select(Some(info)),
862 Command::MultiSelect(idx) => self.select_reset_multi(idx),
863 Command::MultiSelectRange(idx) => self.select_range(idx),
864 Command::MultiSelectSwitch(idx) => self.select_switch_multi(idx),
865 Command::Folder => {
866 let path = self.get_folder().to_owned();
867 self.selected_file = Some(Box::new(FileInfo::new(path)));
868 self.confirm();
869 }
870 Command::Open(path) => {
871 self.select(Some(path));
872 self.open_selected();
873 }
874 Command::OpenSelected => self.open_selected(),
875 Command::BrowseDirectory(dir) => {
876 self.selected_file = Some(dir);
877 self.open_selected();
878 }
879 Command::Save(file) => {
880 self.selected_file = Some(file);
881 self.confirm();
882 }
883 Command::Cancel => self.state = State::Cancelled,
884 Command::Refresh => self.refresh(),
885 Command::UpDirectory => {
886 if self.path.pop() {
887 self.refresh();
888 }
889 }
890 Command::CreateDirectory => {
891 let mut path = self.path.clone();
892 let name = match self.filename_edit.is_empty() {
893 true => self.new_folder_name_text.as_ref(),
894 false => self.filename_edit.as_ref(),
895 };
896 path.push(name);
897 match self.fs.create_dir(&path) {
898 Ok(_) => {
899 self.refresh();
900 self.select(Some(Box::new(FileInfo::new(path))));
901 }
903 Err(err) => println!("Error while creating directory: {err}"),
904 }
905 }
906 Command::Rename(from, to) => match self.fs.rename(from.as_path(), to.as_path()) {
907 Ok(_) => {
908 self.refresh();
909 self.select(Some(Box::new(FileInfo::new(to))));
910 }
911 Err(err) => println!("Error while renaming: {err}"),
912 },
913 };
914 }
915 }
916
917 fn get_folder(&self) -> &Path {
918 if let Some(info) = &self.selected_file {
919 if info.is_dir() {
920 return info.path();
921 }
922 }
923
924 &self.path
926 }
927}