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 directoryNot selectable. Not traversable.Selectable. Traversable.
Open fileSelectable. Not traversable.Not selectable. Traversable.
Save fileOS 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

source

pub fn new() -> FileDialogOptions

Create a new set of options.

Examples found in repository?
examples/open_save.rs (line 46)
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)
}
source

pub fn show_hidden(self) -> Self

Set hidden files and directories to be visible.

source

pub fn select_directories(self) -> Self

Set directories to be selectable instead of files.

This is only relevant for open dialogs.

source

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.

source

pub fn multi_selection(self) -> Self

Set multiple items to be selectable.

This is only relevant for open dialogs.

source

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?
examples/open_save.rs (line 47)
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)
}
source

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?
examples/open_save.rs (line 48)
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)
}
source

pub fn default_name(self, default_name: impl Into<String>) -> Self

Set the default filename that appears in the dialog.

Examples found in repository?
examples/open_save.rs (line 49)
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)
}
source

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?
examples/open_save.rs (line 50)
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)
}
source

pub fn title(self, title: impl Into<String>) -> Self

Set the title text of the dialog.

Examples found in repository?
examples/open_save.rs (line 51)
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)
}
source

pub fn button_text(self, text: impl Into<String>) -> Self

Set the text of the Open/Save button.

Examples found in repository?
examples/open_save.rs (line 52)
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)
}
source

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.

source

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.

source

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.

source

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

source§

fn clone(&self) -> FileDialogOptions

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for FileDialogOptions

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for FileDialogOptions

source§

fn default() -> FileDialogOptions

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for Twhere U: From<T>,

const: unstable · source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> RoundFrom<T> for T

§

fn round_from(x: T) -> T

Performs the conversion.
§

impl<T, U> RoundInto<U> for Twhere U: RoundFrom<T>,

§

fn round_into(self) -> U

Performs the conversion.
source§

impl<T> Same<T> for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
const: unstable · source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more