use crate::{
maths::{Pose, Quat, Vec2, Vec3},
prelude::*,
sprite::Sprite,
tools::os_api::PathEntry,
ui::{Ui, UiBtnLayout, UiVisual, UiWin},
util::{Color128, PickerMode},
};
use std::path::PathBuf;
use super::os_api::get_files;
pub const FILE_BROWSER_OPEN: &str = "File_Browser_open";
pub const FILE_BROWSER_SAVE: &str = "File_Browser_save";
#[derive(IStepper)]
pub struct FileBrowser {
id: StepperId,
sk_info: Option<Rc<RefCell<SkInfo>>>,
pub picker_mode: PickerMode,
pub dir: PathBuf,
files_of_dir: Vec<PathEntry>,
pub exts: Vec<String>,
pub window_pose: Pose,
pub window_size: Vec2,
pub close_on_select: bool,
pub caller: StepperId,
pub dir_buttons_tint: Color128,
pub input_tint: Color128,
pub file_name_to_save: String,
start_dir: PathBuf,
replace_existing_file: bool,
file_selected: u32,
radio_off: Sprite,
radio_on: Sprite,
close: Sprite,
}
unsafe impl Send for FileBrowser {}
impl Default for FileBrowser {
fn default() -> Self {
let yellow = Color128::new(1.0, 0.0, 0.0, 1.0).to_gamma();
Self {
id: "FileBrowser".to_string(),
sk_info: None,
picker_mode: PickerMode::Open,
files_of_dir: vec![],
dir: PathBuf::new(),
exts: vec![],
window_pose: Pose::new(Vec3::new(0.5, 1.5, -0.5), Some(Quat::from_angles(0.0, 180.0, 0.0))),
window_size: Vec2::new(0.5, 0.0),
close_on_select: true,
caller: "".into(),
dir_buttons_tint: Ui::get_element_color(UiVisual::Separator, 0.0),
input_tint: yellow,
start_dir: PathBuf::new(),
file_name_to_save: String::with_capacity(255),
replace_existing_file: false,
file_selected: 0,
radio_off: Sprite::radio_off(),
radio_on: Sprite::radio_on(),
close: Sprite::close(),
}
}
}
impl FileBrowser {
fn start(&mut self) -> bool {
self.files_of_dir = get_files(&self.sk_info, self.dir.clone(), &self.exts, true);
if self.caller.is_empty() {
Log::err(
"FileBrowser must be called by an other stepper (FileBrowser::caller) it will notify of the selected file ",
);
return false;
}
if self.picker_mode == PickerMode::Save && self.close_on_select {
self.close_on_select = false;
}
Log::diag(format!("Browsing directory {:?}", self.dir));
self.start_dir = self.dir.clone();
true
}
fn check_event(&mut self, _id: &StepperId, _key: &str, _value: &str) {}
fn draw(&mut self, _token: &MainThreadToken) {
let mut dir_selected = None;
let mut window_text2 = String::with_capacity(2048);
let window_text = if self.exts.is_empty() {
format!("{:?}", self.dir)
} else {
format!("{:?} with type {:?}", self.dir, self.exts)
};
window_text2.push_str(&window_text);
Ui::window_begin(&window_text, &mut self.window_pose, Some(self.window_size), Some(UiWin::Normal), None);
if Ui::button_img_at(
"a",
&self.close,
None,
Vec3::new(self.window_size.x / 2.0 + 0.04, 0.03, 0.01),
Vec2::new(0.03, 0.03),
None,
) {
self.close_me();
}
if self.picker_mode == PickerMode::Save {
Ui::push_tint(self.input_tint);
Ui::label("File name: ", None, false);
Ui::same_line();
Ui::input("filename_to_save", &mut self.file_name_to_save, None, None);
let file = self.dir.join(&self.file_name_to_save);
let mut ok_to_save = false;
for ext in &self.exts {
if self.file_name_to_save.ends_with(ext) {
ok_to_save = true;
break;
}
}
if file.exists() && !self.file_name_to_save.is_empty() {
Ui::toggle("Replace existing file", &mut self.replace_existing_file, None);
} else {
self.replace_existing_file = false;
}
ok_to_save = ok_to_save && (!file.exists() || file.exists() && self.replace_existing_file);
Ui::push_enabled(ok_to_save, None);
Ui::same_line();
if Ui::button("Save", None) {
SkInfo::send_event(
&self.sk_info,
StepperAction::event(
self.caller.as_str(),
FILE_BROWSER_SAVE,
file.to_str().unwrap_or("problemo!!"),
),
);
self.close_me();
}
Ui::pop_enabled();
Ui::pop_tint();
Ui::next_line();
}
let mut i = 0;
for file_name in &self.files_of_dir {
i += 1;
if let PathEntry::File(name) = file_name {
let file_name_str = name.to_str().unwrap_or("OsString error!!");
Ui::same_line();
if Ui::radio_img(
file_name_str,
self.file_selected == i,
&self.radio_off,
&self.radio_on,
UiBtnLayout::Left,
None,
) {
self.file_selected = i;
if self.picker_mode == PickerMode::Save {
self.file_name_to_save = file_name_str.to_string();
self.replace_existing_file = false;
} else {
let file = self.dir.join(file_name_str);
SkInfo::send_event(
&self.sk_info,
StepperAction::event(
self.caller.as_str(),
FILE_BROWSER_OPEN,
file.to_str().unwrap_or("problemo!!"),
),
);
if self.close_on_select {
self.close_me()
}
}
}
}
}
Ui::next_line();
Ui::push_tint(self.dir_buttons_tint);
if let Some(sub_dir_name) = self.dir.to_str()
&& !sub_dir_name.is_empty()
{
Ui::push_enabled(self.dir != self.start_dir, None);
if Ui::button("..", None) {
self.dir.pop();
dir_selected = Some(get_files(&self.sk_info, self.dir.clone(), &self.exts, true));
}
Ui::pop_enabled();
}
let cur_dir = self.dir.clone();
let mut sub_dir: String = String::with_capacity(2048);
sub_dir += cur_dir.to_string_lossy().to_string().as_str();
if !sub_dir.is_empty() {
sub_dir.insert(sub_dir.len(), '/');
}
for file_name in &self.files_of_dir {
if let PathEntry::Dir(name) = file_name {
let dir_name = name.to_str().unwrap_or("OsString error!!");
Ui::same_line();
if Ui::button(dir_name, None) {
self.dir.push(dir_name);
dir_selected = Some(get_files(&self.sk_info, self.dir.clone(), &self.exts, true));
}
}
}
Ui::pop_tint();
if let Some(new_value) = dir_selected {
self.files_of_dir = new_value;
self.file_selected = 0;
}
Ui::window_end();
}
fn close_me(&self) {
SkInfo::send_event(&self.sk_info, StepperAction::remove(self.id.clone()));
}
}