use crate::core::{Backend, DialogMode, FileDialog, FileDialogError, Selection};
#[cfg(feature = "tracing")]
use tracing::trace;
impl FileDialog {
fn to_rfd(&self) -> rfd::FileDialog {
let mut d = rfd::FileDialog::new();
if let Some(dir) = &self.start_dir {
d = d.set_directory(dir);
}
if let Some(name) = &self.default_name {
d = d.set_file_name(name);
}
for f in &self.filters {
let exts_owned: Vec<String> = f
.extensions
.iter()
.filter_map(|s| plain_extension_for_native(s))
.collect();
let exts: Vec<&str> = exts_owned.iter().map(|s| s.as_str()).collect();
if !exts.is_empty() {
d = d.add_filter(&f.name, &exts);
}
}
d
}
fn to_rfd_async(&self) -> rfd::AsyncFileDialog {
let mut a = rfd::AsyncFileDialog::new();
if let Some(dir) = self.start_dir.as_deref() {
a = a.set_directory(dir);
}
if let Some(name) = self.default_name.as_deref() {
a = a.set_file_name(name);
}
for f in &self.filters {
let exts_owned: Vec<String> = f
.extensions
.iter()
.filter_map(|s| plain_extension_for_native(s))
.collect();
if !exts_owned.is_empty() {
a = a.add_filter(&f.name, &exts_owned);
}
}
a
}
pub fn open_blocking(self) -> Result<Selection, FileDialogError> {
match self.effective_backend() {
Backend::Native => self.open_blocking_native(),
Backend::ImGui => Err(FileDialogError::Unsupported),
Backend::Auto => unreachable!("resolved in effective_backend"),
}
}
fn open_blocking_native(self) -> Result<Selection, FileDialogError> {
#[cfg(feature = "tracing")]
trace!(?self.mode, "rfd blocking open");
let mut sel = Selection::default();
match self.mode {
DialogMode::OpenFile => {
if let Some(p) = self.to_rfd().pick_file() {
sel.paths.push(p);
}
}
DialogMode::OpenFiles => {
if !self.allow_multi {
if let Some(p) = self.to_rfd().pick_file() {
sel.paths.push(p);
}
} else if let Some(v) = self.to_rfd().pick_files() {
sel.paths.extend(v);
}
}
DialogMode::PickFolder => {
if let Some(p) = self.to_rfd().pick_folder() {
sel.paths.push(p);
}
}
DialogMode::SaveFile => {
if let Some(p) = self.to_rfd().save_file() {
sel.paths.push(p);
}
}
}
if let Some(max) = self.max_selection.filter(|&m| m > 0) {
sel.paths.truncate(max);
}
if sel.paths.is_empty() {
Err(FileDialogError::Cancelled)
} else {
Ok(sel)
}
}
pub async fn open_async(self) -> Result<Selection, FileDialogError> {
use rfd::AsyncFileDialog as A;
#[cfg(feature = "tracing")]
trace!(?self.mode, "rfd async open");
let mut sel = Selection::default();
match self.mode {
DialogMode::OpenFile => {
let a = self.to_rfd_async();
let f = a.pick_file().await;
if let Some(h) = f {
sel.paths.push(h.path().to_path_buf());
}
}
DialogMode::OpenFiles => {
let a = self.to_rfd_async();
if !self.allow_multi {
let f = a.pick_file().await;
if let Some(h) = f {
sel.paths.push(h.path().to_path_buf());
}
} else {
let v = a.pick_files().await;
if let Some(v) = v {
sel.paths
.extend(v.into_iter().map(|h| h.path().to_path_buf()));
}
}
}
DialogMode::PickFolder => {
let mut a = A::new();
if let Some(dir) = self.start_dir.as_deref() {
a = a.set_directory(dir);
}
let f = a.pick_folder().await;
if let Some(h) = f {
sel.paths.push(h.path().to_path_buf());
}
}
DialogMode::SaveFile => {
let a = self.to_rfd_async();
let f = a.save_file().await;
if let Some(h) = f {
sel.paths.push(h.path().to_path_buf());
}
}
}
if let Some(max) = self.max_selection.filter(|&m| m > 0) {
sel.paths.truncate(max);
}
if sel.paths.is_empty() {
Err(FileDialogError::Cancelled)
} else {
Ok(sel)
}
}
}
fn is_plain_extension_token(token: &str) -> bool {
let t = token.trim();
if t.is_empty() {
return false;
}
if t.starts_with("((") && t.ends_with("))") {
return false;
}
!(t.contains('*') || t.contains('?'))
}
fn plain_extension_for_native(token: &str) -> Option<String> {
if !is_plain_extension_token(token) {
return None;
}
let t = token.trim().trim_start_matches('.');
if t.is_empty() {
return None;
}
Some(t.to_lowercase())
}