Skip to content

Commit bafa1b9

Browse files
xStromcmyr
authored andcommitted
Add default file extension for the file dialog in Windows.
1 parent bc6e0a3 commit bafa1b9

File tree

2 files changed

+30
-4
lines changed

2 files changed

+30
-4
lines changed

druid-shell/src/dialog.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub enum FileDialogType {
3535
pub struct FileDialogOptions {
3636
pub show_hidden: bool,
3737
pub allowed_types: Option<Vec<FileSpec>>,
38+
pub default_type: Option<FileSpec>,
3839
// we don't want a library user to be able to construct this type directly
3940
__non_exhaustive: (),
4041
// multi selection
@@ -49,7 +50,7 @@ pub struct FileDialogOptions {
4950
/// struct.
5051
///
5152
/// [`COMDLG_FILTERSPEC`]: https://docs.microsoft.com/en-ca/windows/win32/api/shtypes/ns-shtypes-comdlg_filterspec
52-
#[derive(Debug, Clone, Copy)]
53+
#[derive(Debug, Clone, Copy, PartialEq)]
5354
pub struct FileSpec {
5455
/// A human readable name, describing this filetype.
5556
///
@@ -85,11 +86,19 @@ impl FileDialogOptions {
8586
self
8687
}
8788

88-
/// Set the filetypes the user is allowed to select.
89+
/// Set the file types the user is allowed to select.
8990
pub fn allowed_types(mut self, types: Vec<FileSpec>) -> Self {
9091
self.allowed_types = Some(types);
9192
self
9293
}
94+
95+
/// Set the default file type.
96+
/// If it's `None` or not present in [`allowed_types`](#method.allowed_types)
97+
/// then the first entry in [`allowed_types`](#method.allowed_types) will be used as default.
98+
pub fn default_type(mut self, default_type: FileSpec) -> Self {
99+
self.default_type = Some(default_type);
100+
self
101+
}
93102
}
94103

95104
impl FileSpec {

druid-shell/src/platform/windows/dialog.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ use crate::dialog::{FileDialogOptions, FileDialogType, FileSpec};
3939
use std::ffi::OsString;
4040
use std::ptr::null_mut;
4141

42+
use std::convert::TryInto;
43+
4244
// TODO: remove these when they get added to winapi
4345
DEFINE_GUID! {CLSID_FileOpenDialog,
4446
0xDC1C_5A9C, 0xE88A, 0x4DDE, 0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7}
@@ -117,9 +119,24 @@ pub(crate) unsafe fn get_file_dialog_path(
117119
.collect::<Vec<_>>()
118120
});
119121

122+
// Having Some in raw_spec means that we can safely unwrap options.allowed_types.
123+
// Be careful when moving the unwraps out of this block or changing how raw_spec is made.
120124
if let Some(spec) = &raw_spec {
121-
as_result(file_dialog.SetFileTypes(spec.len() as u32, spec.as_ptr()))?;
122-
as_result(file_dialog.SetFileTypeIndex(1))?;
125+
// We also want to make sure that options.allowed_types wasn't Some(zero-length vector)
126+
if !spec.is_empty() {
127+
as_result(file_dialog.SetFileTypes(spec.len() as u32, spec.as_ptr()))?;
128+
129+
let index = match (&options.default_type, &options.allowed_types) {
130+
(Some(typ), Some(typs)) => typs.iter().position(|t| t == typ).unwrap_or(0),
131+
_ => 0,
132+
};
133+
134+
let default_allowed_type = options.allowed_types.as_ref().unwrap().get(index).unwrap();
135+
let default_extension = default_allowed_type.extensions.first().unwrap_or(&"");
136+
137+
as_result(file_dialog.SetFileTypeIndex((index + 1).try_into().unwrap()))?;
138+
as_result(file_dialog.SetDefaultExtension(default_extension.to_wide().as_ptr()))?;
139+
}
123140
}
124141

125142
as_result(file_dialog.SetOptions(flags))?;

0 commit comments

Comments
 (0)