use imgui::Condition;
use std::cmp::Ordering;
use std::fs;
use std::path::{PathBuf};
pub struct FileDialog {
accept_text: String,
cancel_text: String,
title: String,
filename: String,
is_open: bool,
dirs_only: bool,
show_hidden_files: bool,
}
impl FileDialog {
#[inline]
pub fn new() -> Self {
Self {
accept_text: String::from("Open"),
cancel_text: String::from("Cancel"),
title: String::from("Open File"),
filename: String::new(),
is_open: true,
dirs_only: false,
show_hidden_files: false
}
}
#[inline]
pub fn title<S: Into<String>>(mut self, title: S) -> Self {
self.title = title.into();
self
}
#[inline]
pub fn accept_text<S: Into<String>>(mut self, accept_text: S) -> Self {
self.accept_text = accept_text.into();
self
}
#[inline]
pub fn cancel_text<S: Into<String>>(mut self, cancel_text: S) -> Self {
self.cancel_text = cancel_text.into();
self
}
#[inline]
pub fn dir_only(mut self) -> Self {
self.dirs_only = true;
self
}
#[inline]
pub fn for_save(mut self) -> Self {
self.is_open = false;
self.dirs_only = false;
self
}
pub fn spawn(mut self, ui: &imgui::Ui) -> Option<PathBuf> {
let mut path = None;
ui.window(self.title.clone())
.size([600.0, 400.0], Condition::FirstUseEver)
.build(|| {
ui.child_window("Path Selection")
.horizontal_scrollbar(false)
.border(true)
.size([0.0, 32.0])
.build(||{
ui.button("Path: ");
ui.same_line();
std::env::current_dir().unwrap().iter().for_each(|dir|{
if ui.button(dir.to_string_lossy()) {
std::env::set_current_dir(dir)
.map_err(|err| log::error!("Can't change directory to {}: {}", dir.to_string_lossy(), err.to_string()))
.ok();
}
if ui.is_item_hovered() {
ui.tooltip_text(format!("Directory: {}", dir.to_string_lossy()));
}
ui.same_line();
})
});
ui.child_window("Select file / directory")
.border(true)
.size([0.0, -32.0])
.build(|| {
let mut entries: Vec<_> = fs::read_dir(std::env::current_dir().unwrap())
.unwrap()
.filter_map(|entry| {
let entry = entry.expect("Filesystem entry error");
if self.show_hidden_files {
Some(entry)
} else {
if !entry.path().starts_with(".") {
Some(entry)
} else {
None
}
}
})
.collect();
entries.sort_by(|a, b| {
if a.path().is_dir() && !b.path().is_dir() {
Ordering::Less
} else if !a.path().is_dir() && b.path().is_dir() {
Ordering::Greater
} else {
a.path().cmp(&b.path())
}
});
for entry in entries {
if entry.path().is_file() && !self.dirs_only {
if ui.button(format!("[file]\t{}", PathBuf::from(entry.path().iter().last().unwrap()).display())) {
path = Some(entry.path());
}
} else if entry.path().is_dir() {
if ui.button(format!("[dir] \t{}", PathBuf::from(entry.path().iter().last().unwrap()).display())) {
std::env::set_current_dir(entry.path())
.map_err(|e|{
log::error!("Can't access '{}': {}", entry.path().display(), e.to_string());
path = None;
})
.ok();
}
}
}
});
ui.child_window("controls")
.border(false)
.build(||{
if !self.is_open {
ui.text(format!("Filename: {}", self.filename));
}
ui.same_line();
if ui.button("Back") {
let dir = {
let mut tmp = std::env::current_dir().unwrap();
tmp.pop();
tmp
};
std::env::set_current_dir(dir).ok();
}
ui.same_line();
ui.button("Open");
ui.same_line();
if ui.checkbox("Hidden Files", &mut self.show_hidden_files) {
self.show_hidden_files = !self.show_hidden_files;
}
})
});
path
}
}
impl Default for FileDialog {
fn default() -> Self {
Self::new()
}
}