use std::{
fs::{canonicalize, read_dir},
marker::PhantomData,
path::{Path, PathBuf},
};
use egui::{
PopupCloseBehavior, Response, ScrollArea, TextBuffer, Ui, Widget,
containers::menu::{MenuButton, MenuConfig},
};
use super::icon_provider::IconProvider;
pub struct PathPicker<'input, 'path, S: TextBuffer, I: IconProvider> {
input: &'input mut S,
default_path: &'path Path,
provider: PhantomData<I>,
}
impl<'input, 'path, S: TextBuffer, I: IconProvider> PathPicker<'input, 'path, S, I> {
pub fn new<P: AsRef<Path>>(input: &'input mut S, default_path: &'path P) -> Self {
Self {
input,
default_path: default_path.as_ref(),
provider: PhantomData::default(),
}
}
fn picker_widget(self, ui: &mut Ui) {
ui.set_max_height(200.);
ui.set_max_width(200.);
ScrollArea::vertical().show(ui, |ui| {
let mut search_path = PathBuf::from(self.input.as_str());
if !search_path.exists() {
search_path = self.default_path.to_owned();
} else if !search_path.is_dir() {
search_path = search_path.parent().unwrap_or(self.default_path).to_owned();
}
if let Ok(search_path) = canonicalize(search_path) {
if let Some(parent) = search_path.parent() {
if ui.button(I::BACK_ICON).clicked() {
self.input.replace_with(parent.to_string_lossy().as_ref());
}
}
if let Ok(iter) = read_dir(search_path) {
for ent in iter {
if let Ok(ent) = ent {
let path = ent.path();
let icon = if path.is_file() {
I::FILE_ICON
} else {
I::FOLDER_ICON
};
if ui
.button(format!("{} {}", icon, ent.file_name().display()))
.clicked()
{
self.input.replace_with(path.to_string_lossy().as_ref());
if path.is_file() {
ui.close();
}
}
}
}
}
}
});
}
}
impl<S: TextBuffer, I: IconProvider> Widget for PathPicker<'_, '_, S, I> {
fn ui(self, ui: &mut Ui) -> Response {
ui.horizontal(|ui| {
ui.text_edit_singleline(self.input);
MenuButton::new(I::OPEN_ICON)
.config(
MenuConfig::default().close_behavior(PopupCloseBehavior::CloseOnClickOutside),
)
.ui(ui, |ui| {
self.picker_widget(ui);
});
})
.response
}
}