Skip to main content

native_dialog_fork/
file.rs

1use crate::dialog::{DialogImpl, OpenMultipleFile, OpenSingleDir, OpenSingleFile, SaveSingleFile};
2use crate::Result;
3use raw_window_handle::{HasWindowHandle, 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: HasWindowHandle>(mut self, window: Option<&W>) -> Self {
88        let handle = window
89            .map(W::window_handle)
90            .transpose()
91            .ok()
92            .flatten()
93            .map(|x| x.as_raw());
94        self.owner = handle;
95        self
96    }
97
98    /// Sets the owner of the dialog by raw handle. On Unix and GNU/Linux, this is a no-op.
99    ///
100    /// # Safety
101    ///
102    /// It's the caller's responsibility that ensuring the handle is valid.
103    pub unsafe fn set_owner_handle(mut self, handle: Option<RawWindowHandle>) -> Self {
104        self.owner = handle;
105        self
106    }
107
108    /// Resets the owner of the dialog to nothing.
109    pub fn reset_owner(mut self) -> Self {
110        self.owner = None;
111        self
112    }
113
114    /// Shows a dialog that let users to open one file.
115    pub fn show_open_single_file(self) -> Result<Option<PathBuf>> {
116        let mut dialog = OpenSingleFile {
117            filename: self.filename,
118            location: self.location,
119            filters: self.filters,
120            owner: self.owner,
121            title: self.title.unwrap_or("Open File"),
122        };
123        dialog.show()
124    }
125
126    /// Shows a dialog that let users to open multiple files.
127    pub fn show_open_multiple_file(self) -> Result<Vec<PathBuf>> {
128        let mut dialog = OpenMultipleFile {
129            filename: self.filename,
130            location: self.location,
131            filters: self.filters,
132            owner: self.owner,
133            title: self.title.unwrap_or("Open File"),
134        };
135        dialog.show()
136    }
137
138    /// Shows a dialog that let users to open one directory.
139    pub fn show_open_single_dir(self) -> Result<Option<PathBuf>> {
140        let mut dialog = OpenSingleDir {
141            filename: self.filename,
142            location: self.location,
143            owner: self.owner,
144            title: self.title.unwrap_or("Open Folder"),
145        };
146        dialog.show()
147    }
148
149    /// Shows a dialog that let users to save one file.
150    pub fn show_save_single_file(self) -> Result<Option<PathBuf>> {
151        let mut dialog = SaveSingleFile {
152            filename: self.filename,
153            location: self.location,
154            filters: self.filters,
155            owner: self.owner,
156            title: self.title.unwrap_or("Save As"),
157        };
158        dialog.show()
159    }
160}
161
162impl Default for FileDialog<'_> {
163    fn default() -> Self {
164        Self::new()
165    }
166}