1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FileSpec {
/// A human readable name, describing this file type.
///
/// This is used in the Windows file dialog, where the user can select
/// from a dropdown the type of file they would like to choose.
///
/// This should not include the file extensions; they will be added automatically.
/// For instance, if we are describing Word documents, the name would be "Word Document",
/// and the displayed string would be "Word Document (*.doc)".
pub name: &'static str,
/// The file extensions used by this file type.
///
/// This should not include the leading '.'.
pub extensions: &'static [&'static str],
}
#[derive(Debug, Clone)]
pub struct FileInfo {
/// The path to the selected file.
///
/// On macOS, this is already rewritten to use the extension that the user selected
/// with the `file format` property.
pub path: Vec<PathBuf>,
/// The selected file format.
///
/// If there are multiple different formats available
/// this allows understanding the kind of format that the user expects the file
/// to be written in. Examples could be Blender 2.4 vs Blender 2.6 vs Blender 2.8.
/// The `path` above will already contain the appropriate extension chosen in the
/// `format` property, so it is not necessary to mutate `path` any further.
pub format: Option<FileSpec>,
}
impl FileInfo {
/// Returns the underlying path.
pub fn path(&self) -> &Vec<PathBuf> {
&self.path
}
}
#[derive(Debug, Clone, Default)]
pub struct FileDialogOptions {
pub(crate) title: Option<String>,
pub(crate) default_name: Option<String>,
pub(crate) allowed_types: Option<Vec<FileSpec>>,
pub(crate) select_directories: bool,
pub(crate) multi_selection: bool,
pub(crate) starting_directory: Option<PathBuf>,
}
impl FileDialogOptions {
/// Create a new set of options.
pub fn new() -> FileDialogOptions {
FileDialogOptions::default()
}
/// Set directories to be selectable instead of files.
///
/// This is only relevant for open dialogs.
pub fn select_directories(mut self) -> Self {
self.select_directories = true;
self
}
/// Set multiple items to be selectable.
///
/// This is only relevant for open dialogs.
pub fn multi_selection(mut self) -> Self {
self.multi_selection = true;
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*.
///
/// [packages]: #packages
pub fn allowed_types(mut self, types: Vec<FileSpec>) -> Self {
// An empty vector can cause platform issues, so treat it as no filter
if types.is_empty() {
self.allowed_types = None;
} else {
self.allowed_types = Some(types);
}
self
}
/// Set the default filename that appears in the dialog.
pub fn default_name(mut self, default_name: impl Into<String>) -> Self {
self.default_name = Some(default_name.into());
self
}
/// Set the title text of the dialog.
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
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.
pub fn force_starting_directory(mut self, path: impl Into<PathBuf>) -> Self {
self.starting_directory = Some(path.into());
self
}
}