native_dialog/
file.rs

1use crate::dialog::{DialogImpl, OpenMultipleFile, OpenSingleDir, OpenSingleFile, SaveSingleFile};
2use crate::Result;
3use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
4use std::path::{Path, PathBuf};
5
6/// Represents a set of file extensions and their description.
7#[derive(Debug, Clone)]
8pub struct Filter<'a> {
9    pub(crate) description: &'a str,
10    pub(crate) extensions: &'a [&'a str],
11}
12
13/// Builds and shows file dialogs.
14#[derive(Debug, Clone)]
15pub struct FileDialog<'a> {
16    pub(crate) filename: Option<&'a str>,
17    pub(crate) location: Option<&'a Path>,
18    pub(crate) filters: Vec<Filter<'a>>,
19    pub(crate) owner: Option<RawWindowHandle>,
20    pub(crate) title: Option<&'a str>,
21}
22
23impl<'a> FileDialog<'a> {
24    /// Creates a file dialog builder.
25    pub fn new() -> Self {
26        FileDialog {
27            filename: None,
28            location: None,
29            filters: vec![],
30            owner: None,
31            title: None,
32        }
33    }
34
35    /// Sets the window title for the dialog.
36    pub fn set_title(mut self, title: &'a str) -> Self {
37        self.title = Some(title);
38        self
39    }
40
41    /// Sets the default value of the filename text field in the dialog. For open dialogs of macOS
42    /// and zenity, this is a no-op because there's no such text field on the dialog.
43    pub fn set_filename(mut self, filename: &'a str) -> Self {
44        self.filename = Some(filename);
45        self
46    }
47
48    /// Resets the default value of the filename field in the dialog.
49    pub fn reset_filename(mut self) -> Self {
50        self.filename = None;
51        self
52    }
53
54    /// Sets the default location that the dialog shows at open.
55    pub fn set_location<P: AsRef<Path> + ?Sized>(mut self, path: &'a P) -> Self {
56        self.location = Some(path.as_ref());
57        self
58    }
59
60    /// Resets the default location that the dialog shows at open. Without a default location set,
61    /// the dialog will probably use the current working directory as default location.
62    pub fn reset_location(mut self) -> Self {
63        self.location = None;
64        self
65    }
66
67    /// Adds a file type filter. The filter must contains at least one extension, otherwise this
68    /// method will panic. For dialogs that open directories, this is a no-op.
69    pub fn add_filter(mut self, description: &'a str, extensions: &'a [&'a str]) -> Self {
70        if extensions.is_empty() {
71            panic!("The file extensions of a filter must be specified.")
72        }
73        self.filters.push(Filter {
74            description,
75            extensions,
76        });
77        self
78    }
79
80    /// Removes all file type filters.
81    pub fn remove_all_filters(mut self) -> Self {
82        self.filters = vec![];
83        self
84    }
85
86    /// Sets the owner of the dialog. On Unix and GNU/Linux, this is a no-op.
87    pub fn set_owner<W: HasRawWindowHandle>(mut self, window: &W) -> Self {
88        self.owner = Some(window.raw_window_handle());
89        self
90    }
91
92    /// Sets the owner of the dialog by raw handle. On Unix and GNU/Linux, this is a no-op.
93    ///
94    /// # Safety
95    ///
96    /// It's the caller's responsibility that ensuring the handle is valid.
97    pub unsafe fn set_owner_handle(mut self, handle: RawWindowHandle) -> Self {
98        self.owner = Some(handle);
99        self
100    }
101
102    /// Resets the owner of the dialog to nothing.
103    pub fn reset_owner(mut self) -> Self {
104        self.owner = None;
105        self
106    }
107
108    /// Shows a dialog that let users to open one file.
109    pub fn show_open_single_file(self) -> Result<Option<PathBuf>> {
110        let mut dialog = OpenSingleFile {
111            filename: self.filename,
112            location: self.location,
113            filters: self.filters,
114            owner: self.owner,
115            title: self.title.unwrap_or("Open File"),
116        };
117        dialog.show()
118    }
119
120    /// Shows a dialog that let users to open multiple files.
121    pub fn show_open_multiple_file(self) -> Result<Vec<PathBuf>> {
122        let mut dialog = OpenMultipleFile {
123            filename: self.filename,
124            location: self.location,
125            filters: self.filters,
126            owner: self.owner,
127            title: self.title.unwrap_or("Open File"),
128        };
129        dialog.show()
130    }
131
132    /// Shows a dialog that let users to open one directory.
133    pub fn show_open_single_dir(self) -> Result<Option<PathBuf>> {
134        let mut dialog = OpenSingleDir {
135            filename: self.filename,
136            location: self.location,
137            owner: self.owner,
138            title: self.title.unwrap_or("Open Folder"),
139        };
140        dialog.show()
141    }
142
143    /// Shows a dialog that let users to save one file.
144    pub fn show_save_single_file(self) -> Result<Option<PathBuf>> {
145        let mut dialog = SaveSingleFile {
146            filename: self.filename,
147            location: self.location,
148            filters: self.filters,
149            owner: self.owner,
150            title: self.title.unwrap_or("Save As"),
151        };
152        dialog.show()
153    }
154}
155
156impl Default for FileDialog<'_> {
157    fn default() -> Self {
158        Self::new()
159    }
160}