egui_path_picker/
path_picker.rs1use std::{
2 fs::{canonicalize, read_dir},
3 marker::PhantomData,
4 path::{Path, PathBuf},
5};
6
7use egui::{
8 PopupCloseBehavior, Response, ScrollArea, TextBuffer, Ui, Widget,
9 containers::menu::{MenuButton, MenuConfig},
10};
11
12use super::icon_provider::IconProvider;
13
14pub struct PathPicker<'input, 'path, S: TextBuffer, I: IconProvider> {
19 input: &'input mut S,
21 default_path: &'path Path,
23 provider: PhantomData<I>,
24}
25
26impl<'input, 'path, S: TextBuffer, I: IconProvider> PathPicker<'input, 'path, S, I> {
27 pub fn new<P: AsRef<Path>>(input: &'input mut S, default_path: &'path P) -> Self {
34 Self {
35 input,
36 default_path: default_path.as_ref(),
37 provider: PhantomData::default(),
38 }
39 }
40
41 fn picker_widget(self, ui: &mut Ui) {
43 ui.set_max_height(200.);
44 ui.set_max_width(200.);
45 ScrollArea::vertical().show(ui, |ui| {
46 let mut search_path = PathBuf::from(self.input.as_str());
47 if !search_path.exists() {
48 search_path = self.default_path.to_owned();
49 } else if !search_path.is_dir() {
50 search_path = search_path.parent().unwrap_or(self.default_path).to_owned();
51 }
52
53 if let Ok(search_path) = canonicalize(search_path) {
54 if let Some(parent) = search_path.parent() {
55 if ui.button(I::BACK_ICON).clicked() {
56 self.input.replace_with(parent.to_string_lossy().as_ref());
57 }
58 }
59
60 if let Ok(iter) = read_dir(search_path) {
61 for ent in iter {
62 if let Ok(ent) = ent {
63 let path = ent.path();
64
65 let icon = if path.is_file() {
66 I::FILE_ICON
67 } else {
68 I::FOLDER_ICON
69 };
70
71 if ui
72 .button(format!("{} {}", icon, ent.file_name().display()))
73 .clicked()
74 {
75 self.input.replace_with(path.to_string_lossy().as_ref());
76 if path.is_file() {
77 ui.close();
78 }
79 }
80 }
81 }
82 }
83 }
84 });
85 }
86}
87
88impl<S: TextBuffer, I: IconProvider> Widget for PathPicker<'_, '_, S, I> {
89 fn ui(self, ui: &mut Ui) -> Response {
90 ui.horizontal(|ui| {
91 ui.text_edit_singleline(self.input);
92 MenuButton::new(I::OPEN_ICON)
93 .config(
94 MenuConfig::default().close_behavior(PopupCloseBehavior::CloseOnClickOutside),
95 )
96 .ui(ui, |ui| {
97 self.picker_widget(ui);
98 });
99 })
100 .response
101 }
102}