Struct druid::FileDialogOptions
source · pub struct FileDialogOptions { /* private fields */ }
Expand description
Options for file dialogs.
File dialogs let the user choose a specific path to open or save.
By default the file dialogs operate in files mode where the user can only choose files. Importantly these are files from the user’s perspective, but technically the returned path will be a directory when the user chooses a package. You can read more about packages below. It’s also possible for users to manually specify a path which they might otherwise not be able to choose. Thus it is important to verify that all the returned paths match your expectations.
The open dialog can also be switched to directories mode via select_directories
.
Cross-platform compatibility
You could write platform specific code that really makes the best use of each platform. However if you want to write universal code that will work on all platforms then you have to keep some restrictions in mind.
Don’t depend on directories with extensions
Your application should avoid having to deal with directories that have extensions
in their name, e.g. my_stuff.pkg
. This clashes with packages on macOS and you
will either need platform specific code or a degraded user experience on macOS
via packages_as_directories
.
Use the save dialog only for new paths
Don’t direct the user to choose an existing file with the save dialog. Selecting existing files for overwriting is possible but extremely cumbersome on macOS. The much more optimized flow is to have the user select a file with the open dialog and then keep saving to that file without showing a save dialog. Use the save dialog only for selecting a new location.
macOS
The file dialog works a bit differently on macOS. For a lot of applications this doesn’t matter and you don’t need to know the details. However if your application makes extensive use of file dialogs and you target macOS then you should understand the macOS specifics.
Packages
On macOS directories with known extensions are considered to be packages, e.g. app_files.pkg
.
Furthermore the packages are divided into two groups based on their extension.
First there are packages that have been defined at the OS level, and secondly there are
packages that are defined at the file dialog level based on allowed_types
.
These two types have slightly different behavior in the file dialogs. Generally packages
behave similarly to regular files in many contexts, including the file dialogs.
This package concept can be turned off in the file dialog via packages_as_directories
.
| Packages as files. File filters apply to packages. | Packages as directories. |
---|---|---|
Open directory | Not selectable. Not traversable. | Selectable. Traversable. |
Open file | Selectable. Not traversable. | Not selectable. Traversable. |
Save file | OS packages clickable but not traversable. Dialog packages traversable but not selectable. | Not selectable. Traversable. |
Keep in mind that the file dialog may start inside any package if the user has traversed into one just recently. The user might also manually specify a path inside a package.
Generally this behavior should be kept, because it’s least surprising to macOS users.
However if your application requires selecting directories with extensions as directories
or the user needs to be able to traverse into them to select a specific file,
then you can change the default behavior via packages_as_directories
to force macOS to behave like other platforms and not give special treatment to packages.
Selecting files for overwriting in the save dialog is cumbersome
Existing files can be clicked on in the save dialog, but that only copies their base file name.
If the clicked file’s extension is different than the first extension of the default type
then the returned path does not actually match the path of the file that was clicked on.
Clicking on a file doesn’t change the base path either. Keep in mind that the macOS file dialog
can have several directories open at once. So if a user has traversed into /Users/Joe/foo/
and then clicks on an existing file /Users/Joe/old.txt
in another directory then the returned
path will actually be /Users/Joe/foo/old.rtf
if the default type’s first extension is rtf
.
Have a really good save dialog default type
There is no way for the user to choose which extension they want to save a file as via the UI. They have no way of knowing which extensions are even supported and must manually type it out.
Hopefully it’s a temporary problem and we can find a way to show the file formats in the UI. This is being tracked in druid#998.
Implementations§
source§impl FileDialogOptions
impl FileDialogOptions
sourcepub fn new() -> FileDialogOptions
pub fn new() -> FileDialogOptions
Create a new set of options.
Examples found in repository?
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
fn ui_builder() -> impl Widget<String> {
let rs = FileSpec::new("Rust source", &["rs"]);
let txt = FileSpec::new("Text file", &["txt"]);
let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
// The options can also be generated at runtime,
// so to show that off we create a String for the default save name.
let default_save_name = String::from("MyFile.txt");
let save_dialog_options = FileDialogOptions::new()
.allowed_types(vec![rs, txt, other])
.default_type(txt)
.default_name(default_save_name)
.name_label("Target")
.title("Choose a target for this lovely file")
.button_text("Export");
let open_dialog_options = save_dialog_options
.clone()
.default_name("MySavedFile.txt")
.name_label("Source")
.title("Where did you put that file?")
.button_text("Import");
let input = TextBox::new();
let save = Button::new("Save").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
});
let open = Button::new("Open").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
});
let mut col = Flex::column();
col.add_child(input);
col.add_spacer(8.0);
col.add_child(save);
col.add_child(open);
Align::centered(col)
}
Set hidden files and directories to be visible.
sourcepub fn select_directories(self) -> Self
pub fn select_directories(self) -> Self
Set directories to be selectable instead of files.
This is only relevant for open dialogs.
sourcepub fn packages_as_directories(self) -> Self
pub fn packages_as_directories(self) -> Self
Set packages to be treated as directories instead of files.
This allows for writing more universal cross-platform code at the cost of user experience.
This is only relevant on macOS.
sourcepub fn multi_selection(self) -> Self
pub fn multi_selection(self) -> Self
Set multiple items to be selectable.
This is only relevant for open dialogs.
sourcepub fn allowed_types(self, types: Vec<FileSpec>) -> Self
pub fn allowed_types(self, types: Vec<FileSpec>) -> Self
Set the file types the user is allowed to select.
This filter is only applied to files and packages, but not to directories.
An empty collection is treated as no filter.
macOS
These file types also apply to directories to define packages. Which means the directories that match the filter are no longer considered directories. The packages are defined by this collection even in directories mode.
Examples found in repository?
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
fn ui_builder() -> impl Widget<String> {
let rs = FileSpec::new("Rust source", &["rs"]);
let txt = FileSpec::new("Text file", &["txt"]);
let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
// The options can also be generated at runtime,
// so to show that off we create a String for the default save name.
let default_save_name = String::from("MyFile.txt");
let save_dialog_options = FileDialogOptions::new()
.allowed_types(vec![rs, txt, other])
.default_type(txt)
.default_name(default_save_name)
.name_label("Target")
.title("Choose a target for this lovely file")
.button_text("Export");
let open_dialog_options = save_dialog_options
.clone()
.default_name("MySavedFile.txt")
.name_label("Source")
.title("Where did you put that file?")
.button_text("Import");
let input = TextBox::new();
let save = Button::new("Save").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
});
let open = Button::new("Open").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
});
let mut col = Flex::column();
col.add_child(input);
col.add_spacer(8.0);
col.add_child(save);
col.add_child(open);
Align::centered(col)
}
sourcepub fn default_type(self, default_type: FileSpec) -> Self
pub fn default_type(self, default_type: FileSpec) -> Self
Set the default file type.
The provided default_type
must also be present in allowed_types
.
If it’s None
then the first entry in allowed_types
will be used as the default.
This is only relevant in files mode.
Examples found in repository?
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
fn ui_builder() -> impl Widget<String> {
let rs = FileSpec::new("Rust source", &["rs"]);
let txt = FileSpec::new("Text file", &["txt"]);
let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
// The options can also be generated at runtime,
// so to show that off we create a String for the default save name.
let default_save_name = String::from("MyFile.txt");
let save_dialog_options = FileDialogOptions::new()
.allowed_types(vec![rs, txt, other])
.default_type(txt)
.default_name(default_save_name)
.name_label("Target")
.title("Choose a target for this lovely file")
.button_text("Export");
let open_dialog_options = save_dialog_options
.clone()
.default_name("MySavedFile.txt")
.name_label("Source")
.title("Where did you put that file?")
.button_text("Import");
let input = TextBox::new();
let save = Button::new("Save").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
});
let open = Button::new("Open").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
});
let mut col = Flex::column();
col.add_child(input);
col.add_spacer(8.0);
col.add_child(save);
col.add_child(open);
Align::centered(col)
}
sourcepub fn default_name(self, default_name: impl Into<String>) -> Self
pub fn default_name(self, default_name: impl Into<String>) -> Self
Set the default filename that appears in the dialog.
Examples found in repository?
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
fn ui_builder() -> impl Widget<String> {
let rs = FileSpec::new("Rust source", &["rs"]);
let txt = FileSpec::new("Text file", &["txt"]);
let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
// The options can also be generated at runtime,
// so to show that off we create a String for the default save name.
let default_save_name = String::from("MyFile.txt");
let save_dialog_options = FileDialogOptions::new()
.allowed_types(vec![rs, txt, other])
.default_type(txt)
.default_name(default_save_name)
.name_label("Target")
.title("Choose a target for this lovely file")
.button_text("Export");
let open_dialog_options = save_dialog_options
.clone()
.default_name("MySavedFile.txt")
.name_label("Source")
.title("Where did you put that file?")
.button_text("Import");
let input = TextBox::new();
let save = Button::new("Save").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
});
let open = Button::new("Open").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
});
let mut col = Flex::column();
col.add_child(input);
col.add_spacer(8.0);
col.add_child(save);
col.add_child(open);
Align::centered(col)
}
sourcepub fn name_label(self, name_label: impl Into<String>) -> Self
pub fn name_label(self, name_label: impl Into<String>) -> Self
Set the text in the label next to the filename editbox.
Examples found in repository?
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
fn ui_builder() -> impl Widget<String> {
let rs = FileSpec::new("Rust source", &["rs"]);
let txt = FileSpec::new("Text file", &["txt"]);
let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
// The options can also be generated at runtime,
// so to show that off we create a String for the default save name.
let default_save_name = String::from("MyFile.txt");
let save_dialog_options = FileDialogOptions::new()
.allowed_types(vec![rs, txt, other])
.default_type(txt)
.default_name(default_save_name)
.name_label("Target")
.title("Choose a target for this lovely file")
.button_text("Export");
let open_dialog_options = save_dialog_options
.clone()
.default_name("MySavedFile.txt")
.name_label("Source")
.title("Where did you put that file?")
.button_text("Import");
let input = TextBox::new();
let save = Button::new("Save").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
});
let open = Button::new("Open").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
});
let mut col = Flex::column();
col.add_child(input);
col.add_spacer(8.0);
col.add_child(save);
col.add_child(open);
Align::centered(col)
}
sourcepub fn title(self, title: impl Into<String>) -> Self
pub fn title(self, title: impl Into<String>) -> Self
Set the title text of the dialog.
Examples found in repository?
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
fn ui_builder() -> impl Widget<String> {
let rs = FileSpec::new("Rust source", &["rs"]);
let txt = FileSpec::new("Text file", &["txt"]);
let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
// The options can also be generated at runtime,
// so to show that off we create a String for the default save name.
let default_save_name = String::from("MyFile.txt");
let save_dialog_options = FileDialogOptions::new()
.allowed_types(vec![rs, txt, other])
.default_type(txt)
.default_name(default_save_name)
.name_label("Target")
.title("Choose a target for this lovely file")
.button_text("Export");
let open_dialog_options = save_dialog_options
.clone()
.default_name("MySavedFile.txt")
.name_label("Source")
.title("Where did you put that file?")
.button_text("Import");
let input = TextBox::new();
let save = Button::new("Save").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
});
let open = Button::new("Open").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
});
let mut col = Flex::column();
col.add_child(input);
col.add_spacer(8.0);
col.add_child(save);
col.add_child(open);
Align::centered(col)
}
Set the text of the Open/Save button.
Examples found in repository?
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
fn ui_builder() -> impl Widget<String> {
let rs = FileSpec::new("Rust source", &["rs"]);
let txt = FileSpec::new("Text file", &["txt"]);
let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
// The options can also be generated at runtime,
// so to show that off we create a String for the default save name.
let default_save_name = String::from("MyFile.txt");
let save_dialog_options = FileDialogOptions::new()
.allowed_types(vec![rs, txt, other])
.default_type(txt)
.default_name(default_save_name)
.name_label("Target")
.title("Choose a target for this lovely file")
.button_text("Export");
let open_dialog_options = save_dialog_options
.clone()
.default_name("MySavedFile.txt")
.name_label("Source")
.title("Where did you put that file?")
.button_text("Import");
let input = TextBox::new();
let save = Button::new("Save").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
});
let open = Button::new("Open").on_click(move |ctx, _, _| {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
});
let mut col = Flex::column();
col.add_child(input);
col.add_spacer(8.0);
col.add_child(save);
col.add_child(open);
Align::centered(col)
}
sourcepub fn force_starting_directory(self, path: impl Into<PathBuf>) -> Self
pub fn force_starting_directory(self, path: impl Into<PathBuf>) -> Self
Force the starting directory to the specified path
.
User experience
This should almost never be used because it overrides the OS choice, which will usually be a directory that the user recently visited.
sourcepub fn accept_command(self, cmd: Selector<FileInfo>) -> Self
pub fn accept_command(self, cmd: Selector<FileInfo>) -> Self
Sets a custom command to use when the file dialog succeeds.
By default, an “open” dialog sends the OPEN_FILE
command when it succeeds, and a “save”
dialog sends the SAVE_FILE_AS
command. Using this method, you can configure a different
command to be used.
sourcepub fn accept_multiple_command(self, cmd: Selector<Vec<FileInfo>>) -> Self
pub fn accept_multiple_command(self, cmd: Selector<Vec<FileInfo>>) -> Self
Sets a custom command to use when the file dialog succeeds with multi selection.
This only works for “open” dialogs configured for multiselection.
sourcepub fn cancel_command(self, cmd: Selector<()>) -> Self
pub fn cancel_command(self, cmd: Selector<()>) -> Self
Sets a custom command to use when the file dialog is cancelled.
By default, an “open” dialog sends the OPEN_PANEL_CANCELLED
command when it is cancelled, and a “save”
dialog sends the SAVE_PANEL_CANCELLED
command. Using this method, you can configure a different
command to be used.
Trait Implementations§
source§impl Clone for FileDialogOptions
impl Clone for FileDialogOptions
source§fn clone(&self) -> FileDialogOptions
fn clone(&self) -> FileDialogOptions
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read more