104 lines
2.9 KiB
Rust
104 lines
2.9 KiB
Rust
#[cfg(target_os = "linux")]
|
|
use std::fmt::Write as _;
|
|
use std::{
|
|
path::PathBuf,
|
|
sync::{Arc, Weak},
|
|
};
|
|
|
|
use winit::window::Window;
|
|
|
|
pub struct FilePicker {
|
|
window: Option<Weak<Window>>,
|
|
}
|
|
|
|
impl FilePicker {
|
|
pub fn new() -> Self {
|
|
Self { window: None }
|
|
}
|
|
|
|
pub fn set_window(&mut self, window: &Arc<Window>) {
|
|
self.window = Some(Arc::downgrade(window));
|
|
}
|
|
|
|
pub fn pick_file_dialog(&self) -> FileDialogBuilder {
|
|
self.new_dialog(FileDialogAction::PickFile, false)
|
|
}
|
|
|
|
pub fn save_file_dialog(&self) -> FileDialogBuilder {
|
|
self.new_dialog(FileDialogAction::SaveFile, cfg!(not(target_os = "windows")))
|
|
}
|
|
|
|
fn new_dialog(&self, action: FileDialogAction, attach_window: bool) -> FileDialogBuilder {
|
|
#[cfg(not(target_os = "macos"))]
|
|
let mut dialog = rfd::AsyncFileDialog::new();
|
|
#[cfg(target_os = "macos")]
|
|
let mut dialog = rfd::FileDialog::new();
|
|
|
|
if attach_window && let Some(window) = self.window.as_ref().and_then(|w| w.upgrade()) {
|
|
dialog = dialog.set_parent(&window);
|
|
}
|
|
FileDialogBuilder { dialog, action }
|
|
}
|
|
}
|
|
|
|
pub struct FileDialogBuilder {
|
|
#[cfg(not(target_os = "macos"))]
|
|
dialog: rfd::AsyncFileDialog,
|
|
#[cfg(target_os = "macos")]
|
|
dialog: rfd::FileDialog,
|
|
action: FileDialogAction,
|
|
}
|
|
|
|
impl FileDialogBuilder {
|
|
pub fn add_filter(self, name: impl Into<String>, extensions: &[impl ToString]) -> Self {
|
|
#[cfg(target_os = "linux")]
|
|
let exts = extensions
|
|
.iter()
|
|
.map(|ext| {
|
|
let mut normalized = String::new();
|
|
for char in ext.to_string().chars() {
|
|
if char.is_ascii_lowercase() {
|
|
let _ = write!(&mut normalized, "[{char}{}]", char.to_ascii_uppercase());
|
|
} else {
|
|
normalized.push(char);
|
|
}
|
|
}
|
|
normalized
|
|
})
|
|
.collect::<Vec<_>>();
|
|
#[cfg(target_os = "linux")]
|
|
let extensions = &exts;
|
|
Self {
|
|
dialog: self.dialog.add_filter(name, extensions),
|
|
..self
|
|
}
|
|
}
|
|
|
|
pub fn set_file_name(self, file_name: impl Into<String>) -> Self {
|
|
Self {
|
|
dialog: self.dialog.set_file_name(file_name),
|
|
..self
|
|
}
|
|
}
|
|
|
|
pub fn open(self) -> Option<PathBuf> {
|
|
#[cfg(not(target_os = "macos"))]
|
|
fn resolve(action: impl Future<Output = Option<rfd::FileHandle>>) -> Option<PathBuf> {
|
|
pollster::block_on(action).map(Into::into)
|
|
}
|
|
#[cfg(target_os = "macos")]
|
|
fn resolve(action: Option<PathBuf>) -> Option<PathBuf> {
|
|
action
|
|
}
|
|
match self.action {
|
|
FileDialogAction::PickFile => resolve(self.dialog.pick_file()),
|
|
FileDialogAction::SaveFile => resolve(self.dialog.save_file()),
|
|
}
|
|
}
|
|
}
|
|
|
|
enum FileDialogAction {
|
|
PickFile,
|
|
SaveFile,
|
|
}
|