From a862841c1b97d2f819c050ecb83b9e924dda2aa8 Mon Sep 17 00:00:00 2001 From: Dominik Wilkowski Date: Sun, 18 Feb 2024 17:18:36 +1000 Subject: [PATCH 1/2] added filter to FileDialogOptions, passed on title, filter, name to rfd --- examples/files/Cargo.toml | 8 +++++ examples/files/src/main.rs | 71 ++++++++++++++++++++++++++++++++++++++ src/action.rs | 12 +++++++ src/file.rs | 7 ++++ 4 files changed, 98 insertions(+) create mode 100644 examples/files/Cargo.toml create mode 100644 examples/files/src/main.rs diff --git a/examples/files/Cargo.toml b/examples/files/Cargo.toml new file mode 100644 index 00000000..85c340ed --- /dev/null +++ b/examples/files/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fils" +version = "0.1.0" +edition = "2021" + +[dependencies] +im = "15.1.0" +floem = { path = "../.." } diff --git a/examples/files/src/main.rs b/examples/files/src/main.rs new file mode 100644 index 00000000..84b018f4 --- /dev/null +++ b/examples/files/src/main.rs @@ -0,0 +1,71 @@ +use floem::{ + action::{open_file, save_as}, + file::{FileDialogOptions, FileSpec}, + keyboard::{Key, ModifiersState, NamedKey}, + view::View, + views::{h_stack, Decorators}, + widgets::button, +}; + +fn app_view() -> impl View { + let view = h_stack(( + button(|| "Select file").on_click_cont(|_| { + open_file( + FileDialogOptions::new() + .force_starting_directory("/") + .title("Select file") + .filter(FileSpec { + name: "text", + extensions: &["txt", "rs", "md"], + }), + move |file_info| { + if let Some(file) = file_info { + println!("Selected file: {:?}", file.path); + } + }, + ); + }), + button(|| "Select folder").on_click_cont(|_| { + open_file( + FileDialogOptions::new() + .select_directories() + .title("Select Folder"), + move |file_info| { + if let Some(file) = file_info { + println!("Selected folder: {:?}", file.path); + } + }, + ); + }), + button(|| "Save file").on_click_cont(|_| { + save_as( + FileDialogOptions::new() + .default_name("floem.file") + .title("Save file"), + move |file_info| { + if let Some(file) = file_info { + println!("Save file to: {:?}", file.path); + } + }, + ); + }), + )) + .style(|s| { + s.gap(5, 0) + .width_full() + .height_full() + .items_center() + .justify_center() + }); + + let id = view.id(); + view.on_key_up( + Key::Named(NamedKey::F11), + ModifiersState::empty(), + move |_| id.inspect(), + ) +} + +fn main() { + floem::launch(app_view); +} diff --git a/src/action.rs b/src/action.rs index 9c94a246..1cf8df64 100644 --- a/src/action.rs +++ b/src/action.rs @@ -117,6 +117,12 @@ pub fn open_file( if let Some(path) = options.starting_directory.as_ref() { dialog = dialog.set_directory(path); } + if let Some(title) = options.title.as_ref() { + dialog = dialog.set_title(title); + } + if let Some(filter) = options.filter.as_ref() { + dialog = dialog.add_filter(filter.name, filter.extensions); + } let path = if options.select_directories { dialog.pick_folder() } else { @@ -135,6 +141,12 @@ pub fn save_as(options: FileDialogOptions, file_info_action: impl Fn(Option>, pub(crate) default_type: Option, + pub(crate) filter: Option, pub(crate) select_directories: bool, pub(crate) packages_as_directories: bool, pub(crate) multi_selection: bool, @@ -133,6 +134,12 @@ impl FileDialogOptions { self } + /// Set filter for file types + pub fn filter(mut self, filter: FileSpec) -> Self { + self.filter = Some(filter); + self + } + /// Set the default filename that appears in the dialog. pub fn default_name(mut self, default_name: impl Into) -> Self { self.default_name = Some(default_name.into()); From 9b90fb8578e1804fa7b6ae086934691d64be5e0b Mon Sep 17 00:00:00 2001 From: Dominik Wilkowski Date: Sun, 18 Feb 2024 18:13:23 +1000 Subject: [PATCH 2/2] replaced filter with already existing allowed_types, added multi_selection support --- examples/files/src/main.rs | 33 ++++++++++++++++++++++++++-- src/action.rs | 44 ++++++++++++++++++++++++++++---------- src/file.rs | 13 +++-------- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/examples/files/src/main.rs b/examples/files/src/main.rs index 84b018f4..602885e8 100644 --- a/examples/files/src/main.rs +++ b/examples/files/src/main.rs @@ -14,10 +14,26 @@ fn app_view() -> impl View { FileDialogOptions::new() .force_starting_directory("/") .title("Select file") - .filter(FileSpec { + .allowed_types(vec![FileSpec { name: "text", extensions: &["txt", "rs", "md"], - }), + }]), + move |file_info| { + if let Some(file) = file_info { + println!("Selected file: {:?}", file.path); + } + }, + ); + }), + button(|| "Select multiple files").on_click_cont(|_| { + open_file( + FileDialogOptions::new() + .multi_selection() + .title("Select file") + .allowed_types(vec![FileSpec { + name: "text", + extensions: &["txt", "rs", "md"], + }]), move |file_info| { if let Some(file) = file_info { println!("Selected file: {:?}", file.path); @@ -37,6 +53,19 @@ fn app_view() -> impl View { }, ); }), + button(|| "Select multiple folder").on_click_cont(|_| { + open_file( + FileDialogOptions::new() + .select_directories() + .multi_selection() + .title("Select multiple Folder"), + move |file_info| { + if let Some(file) = file_info { + println!("Selected folder: {:?}", file.path); + } + }, + ); + }), button(|| "Save file").on_click_cont(|_| { save_as( FileDialogOptions::new() diff --git a/src/action.rs b/src/action.rs index 1cf8df64..cd42c75e 100644 --- a/src/action.rs +++ b/src/action.rs @@ -109,9 +109,22 @@ pub fn open_file( options: FileDialogOptions, file_info_action: impl Fn(Option) + 'static, ) { - let send = create_ext_action(Scope::new(), move |path: Option| { - file_info_action(path.map(|path| FileInfo { path, format: None })) - }); + let send = create_ext_action( + Scope::new(), + move |(path, paths): (Option, Option>)| { + if paths.is_some() { + file_info_action(paths.map(|paths| FileInfo { + path: paths, + format: None, + })) + } else { + file_info_action(path.map(|path| FileInfo { + path: vec![path], + format: None, + })) + } + }, + ); std::thread::spawn(move || { let mut dialog = rfd::FileDialog::new(); if let Some(path) = options.starting_directory.as_ref() { @@ -120,21 +133,30 @@ pub fn open_file( if let Some(title) = options.title.as_ref() { dialog = dialog.set_title(title); } - if let Some(filter) = options.filter.as_ref() { - dialog = dialog.add_filter(filter.name, filter.extensions); + if let Some(allowed_types) = options.allowed_types.as_ref() { + dialog = allowed_types.iter().fold(dialog, |dialog, filter| { + dialog.add_filter(filter.name, filter.extensions) + }); } - let path = if options.select_directories { - dialog.pick_folder() + + if options.select_directories && options.multi_selection { + send((None, dialog.pick_folders())); + } else if options.select_directories && !options.multi_selection { + send((dialog.pick_folder(), None)); + } else if !options.select_directories && options.multi_selection { + send((None, dialog.pick_files())); } else { - dialog.pick_file() - }; - send(path); + send((dialog.pick_file(), None)); + } }); } pub fn save_as(options: FileDialogOptions, file_info_action: impl Fn(Option) + 'static) { let send = create_ext_action(Scope::new(), move |path: Option| { - file_info_action(path.map(|path| FileInfo { path, format: None })) + file_info_action(path.map(|path| FileInfo { + path: vec![path], + format: None, + })) }); std::thread::spawn(move || { let mut dialog = rfd::FileDialog::new(); diff --git a/src/file.rs b/src/file.rs index e6c66223..7877b32c 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct FileSpec { @@ -23,7 +23,7 @@ pub struct FileInfo { /// /// On macOS, this is already rewritten to use the extension that the user selected /// with the `file format` property. - pub path: PathBuf, + pub path: Vec, /// The selected file format. /// /// If there're multiple different formats available @@ -36,7 +36,7 @@ pub struct FileInfo { impl FileInfo { /// Returns the underlying path. - pub fn path(&self) -> &Path { + pub fn path(&self) -> &Vec { &self.path } } @@ -46,7 +46,6 @@ pub struct FileDialogOptions { pub(crate) show_hidden: bool, pub(crate) allowed_types: Option>, pub(crate) default_type: Option, - pub(crate) filter: Option, pub(crate) select_directories: bool, pub(crate) packages_as_directories: bool, pub(crate) multi_selection: bool, @@ -134,12 +133,6 @@ impl FileDialogOptions { self } - /// Set filter for file types - pub fn filter(mut self, filter: FileSpec) -> Self { - self.filter = Some(filter); - self - } - /// Set the default filename that appears in the dialog. pub fn default_name(mut self, default_name: impl Into) -> Self { self.default_name = Some(default_name.into());