Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Add `FileDialog::set_show_hidden_files` and `AsyncFileDialog::set_show_hidden_files` to control hidden file visibility. Supported on macOS, Windows, and Linux (GTK3).

## 0.17.2

- Lower MSRV back to 1.88 by @PolyMeilex in https://github.com/PolyMeilex/rfd/pull/303
Expand Down
18 changes: 18 additions & 0 deletions examples/show_hidden.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! Example demonstrating the show_hidden_files option.
//!
//! Run with: cargo run --example show_hidden

use rfd::FileDialog;

fn main() {
// Open a file dialog with hidden files shown
let file = FileDialog::new()
.set_title("Select a file (hidden files visible)")
.set_show_hidden_files(true)
.pick_file();

match file {
Some(path) => println!("Selected: {}", path.display()),
None => println!("No file selected"),
}
}
13 changes: 13 additions & 0 deletions src/backend/gtk3/file_dialog/dialog_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ impl GtkFileDialog {
}
}

fn set_show_hidden(&self, show: Option<bool>) {
if let Some(show) = show {
unsafe {
gtk_sys::gtk_file_chooser_set_show_hidden(self.ptr as _, show as i32);
}
}
}

pub fn get_result(&self) -> Option<PathBuf> {
let cstr = unsafe {
let chosen_filename = gtk_sys::gtk_file_chooser_get_filename(self.ptr as _);
Expand Down Expand Up @@ -162,6 +170,7 @@ impl GtkFileDialog {

dialog.add_filters(&opt.filters);
dialog.set_path(opt.starting_directory.as_deref());
dialog.set_show_hidden(opt.show_hidden_files);

if let (Some(mut path), Some(file_name)) =
(opt.starting_directory.to_owned(), opt.file_name.as_deref())
Expand All @@ -185,6 +194,7 @@ impl GtkFileDialog {

dialog.add_filters(&opt.filters);
dialog.set_path(opt.starting_directory.as_deref());
dialog.set_show_hidden(opt.show_hidden_files);

if let (Some(mut path), Some(file_name)) =
(opt.starting_directory.to_owned(), opt.file_name.as_deref())
Expand All @@ -211,6 +221,7 @@ impl GtkFileDialog {
GtkFileChooserAction::SelectFolder,
);
dialog.set_path(opt.starting_directory.as_deref());
dialog.set_show_hidden(opt.show_hidden_files);

if let (Some(mut path), Some(file_name)) =
(opt.starting_directory.to_owned(), opt.file_name.as_deref())
Expand All @@ -231,6 +242,7 @@ impl GtkFileDialog {
);
unsafe { gtk_sys::gtk_file_chooser_set_select_multiple(dialog.ptr as _, 1) };
dialog.set_path(opt.starting_directory.as_deref());
dialog.set_show_hidden(opt.show_hidden_files);

if let (Some(mut path), Some(file_name)) =
(opt.starting_directory.to_owned(), opt.file_name.as_deref())
Expand All @@ -253,6 +265,7 @@ impl GtkFileDialog {
unsafe { gtk_sys::gtk_file_chooser_set_select_multiple(dialog.ptr as _, 1) };
dialog.add_filters(&opt.filters);
dialog.set_path(opt.starting_directory.as_deref());
dialog.set_show_hidden(opt.show_hidden_files);

if let (Some(mut path), Some(file_name)) =
(opt.starting_directory.to_owned(), opt.file_name.as_deref())
Expand Down
32 changes: 32 additions & 0 deletions src/backend/macos/file_dialog/panel_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ trait PanelExt {
unsafe { self.panel().setCanCreateDirectories(can) }
}

fn set_shows_hidden_files(&self, show: bool) {
unsafe { self.panel().setShowsHiddenFiles(show) }
}

fn add_filters(&self, opt: &FileDialog) {
let mut exts: Vec<String> = Vec::new();

Expand Down Expand Up @@ -187,6 +191,10 @@ impl Panel {
panel.set_can_create_directories(can);
}

if let Some(show) = opt.show_hidden_files {
panel.set_shows_hidden_files(show);
}

unsafe { panel.setCanChooseDirectories(false) };
unsafe { panel.setCanChooseFiles(true) };

Expand Down Expand Up @@ -216,6 +224,10 @@ impl Panel {
panel.set_can_create_directories(can);
}

if let Some(show) = opt.show_hidden_files {
panel.set_shows_hidden_files(show);
}

Self::new(panel, opt.parent.as_ref())
}

Expand All @@ -233,6 +245,10 @@ impl Panel {
let can = opt.can_create_directories.unwrap_or(true);
panel.set_can_create_directories(can);

if let Some(show) = opt.show_hidden_files {
panel.set_shows_hidden_files(show);
}

unsafe { panel.setCanChooseDirectories(true) };
unsafe { panel.setCanChooseFiles(false) };

Expand All @@ -253,6 +269,10 @@ impl Panel {
let can = opt.can_create_directories.unwrap_or(true);
panel.set_can_create_directories(can);

if let Some(show) = opt.show_hidden_files {
panel.set_shows_hidden_files(show);
}

unsafe { panel.setCanChooseDirectories(true) };
unsafe { panel.setCanChooseFiles(false) };
unsafe { panel.setAllowsMultipleSelection(true) };
Expand All @@ -279,6 +299,10 @@ impl Panel {
panel.set_can_create_directories(can);
}

if let Some(show) = opt.show_hidden_files {
panel.set_shows_hidden_files(show);
}

unsafe { panel.setCanChooseDirectories(false) };
unsafe { panel.setCanChooseFiles(true) };
unsafe { panel.setAllowsMultipleSelection(true) };
Expand All @@ -304,6 +328,10 @@ impl Panel {
let can = opt.can_create_directories.unwrap_or(true);
panel.set_can_create_directories(can);

if let Some(show) = opt.show_hidden_files {
panel.set_shows_hidden_files(show);
}

unsafe { panel.setCanChooseDirectories(true) };
unsafe { panel.setCanChooseFiles(true) };
unsafe { panel.setAllowsMultipleSelection(false) };
Expand All @@ -329,6 +357,10 @@ impl Panel {
let can = opt.can_create_directories.unwrap_or(true);
panel.set_can_create_directories(can);

if let Some(show) = opt.show_hidden_files {
panel.set_shows_hidden_files(show);
}

unsafe { panel.setCanChooseDirectories(true) };
unsafe { panel.setCanChooseFiles(true) };
unsafe { panel.setAllowsMultipleSelection(true) };
Expand Down
2 changes: 1 addition & 1 deletion src/backend/win_cid/file_dialog/com.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ pub(super) struct IFileDialogV {
Unadvise: unsafe extern "system" fn(this: *mut c_void, dwcookie: u32) -> HRESULT,
pub(super) SetOptions:
unsafe extern "system" fn(this: *mut c_void, fos: FILEOPENDIALOGOPTIONS) -> HRESULT,
GetOptions:
pub(super) GetOptions:
unsafe extern "system" fn(this: *mut c_void, pfos: *mut FILEOPENDIALOGOPTIONS) -> HRESULT,
SetDefaultFolder: unsafe extern "system" fn(this: *mut c_void, psi: *mut c_void) -> HRESULT,
pub(super) SetFolder: unsafe extern "system" fn(this: *mut c_void, psi: *mut c_void) -> HRESULT,
Expand Down
47 changes: 43 additions & 4 deletions src/backend/win_cid/file_dialog/dialog_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use windows_sys::{
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
UI::Shell::{
FileOpenDialog, FileSaveDialog, SHCreateItemFromParsingName, FOS_ALLOWMULTISELECT,
FOS_PICKFOLDERS,
FOS_FORCESHOWHIDDEN, FOS_PICKFOLDERS,
},
},
};
Expand Down Expand Up @@ -81,6 +81,20 @@ impl DialogInner {
wrap_err((v.SetOptions)(d, opts))
}

#[inline]
unsafe fn get_options(&self) -> Result<FILEOPENDIALOGOPTIONS> {
let (d, v) = self.fd();
let mut opts = 0;
wrap_err((v.GetOptions)(d, &mut opts))?;
Ok(opts)
}

#[inline]
unsafe fn add_options(&self, opts: FILEOPENDIALOGOPTIONS) -> Result<()> {
let current = self.get_options()?;
self.set_options(current | opts)
}

#[inline]
unsafe fn set_title(&self, title: &[u16]) -> Result<()> {
let (d, v) = self.fd();
Expand Down Expand Up @@ -273,6 +287,15 @@ impl IDialog {
Ok(())
}

fn set_show_hidden_files(&self, show: Option<bool>) -> Result<()> {
if let Some(true) = show {
unsafe {
self.0.add_options(FOS_FORCESHOWHIDDEN)?;
}
}
Ok(())
}

pub fn get_results(&self) -> Result<Vec<PathBuf>> {
unsafe { self.0.get_results() }
}
Expand All @@ -294,6 +317,7 @@ impl IDialog {
dialog.set_path(&opt.starting_directory)?;
dialog.set_file_name(&opt.file_name)?;
dialog.set_title(&opt.title)?;
dialog.set_show_hidden_files(opt.show_hidden_files)?;

Ok(dialog)
}
Expand All @@ -305,6 +329,7 @@ impl IDialog {
dialog.set_path(&opt.starting_directory)?;
dialog.set_file_name(&opt.file_name)?;
dialog.set_title(&opt.title)?;
dialog.set_show_hidden_files(opt.show_hidden_files)?;

Ok(dialog)
}
Expand All @@ -315,8 +340,13 @@ impl IDialog {
dialog.set_path(&opt.starting_directory)?;
dialog.set_title(&opt.title)?;

let mut opts = FOS_PICKFOLDERS;
if let Some(true) = opt.show_hidden_files {
opts |= FOS_FORCESHOWHIDDEN;
}

unsafe {
dialog.0.set_options(FOS_PICKFOLDERS)?;
dialog.0.set_options(opts)?;
}

Ok(dialog)
Expand All @@ -327,7 +357,11 @@ impl IDialog {

dialog.set_path(&opt.starting_directory)?;
dialog.set_title(&opt.title)?;
let opts = FOS_PICKFOLDERS | FOS_ALLOWMULTISELECT;

let mut opts = FOS_PICKFOLDERS | FOS_ALLOWMULTISELECT;
if let Some(true) = opt.show_hidden_files {
opts |= FOS_FORCESHOWHIDDEN;
}

unsafe {
dialog.0.set_options(opts)?;
Expand All @@ -344,8 +378,13 @@ impl IDialog {
dialog.set_file_name(&opt.file_name)?;
dialog.set_title(&opt.title)?;

let mut opts = FOS_ALLOWMULTISELECT;
if let Some(true) = opt.show_hidden_files {
opts |= FOS_FORCESHOWHIDDEN;
}

unsafe {
dialog.0.set_options(FOS_ALLOWMULTISELECT)?;
dialog.0.set_options(opts)?;
}

Ok(dialog)
Expand Down
21 changes: 21 additions & 0 deletions src/file_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct FileDialog {
pub(crate) parent: Option<RawWindowHandle>,
pub(crate) parent_display: Option<RawDisplayHandle>,
pub(crate) can_create_directories: Option<bool>,
pub(crate) show_hidden_files: Option<bool>,
}

// Oh god, I don't like sending RawWindowHandle between threads but here we go anyways...
Expand Down Expand Up @@ -108,6 +109,16 @@ impl FileDialog {
self.can_create_directories.replace(can);
self
}

/// Show hidden files in the dialog.
/// Supported platforms:
/// * Windows
/// * Mac
/// * Linux (GTK3 only, not XDG Portal)
pub fn set_show_hidden_files(mut self, show: bool) -> Self {
self.show_hidden_files = Some(show);
self
}
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -252,6 +263,16 @@ impl AsyncFileDialog {
self.file_dialog = self.file_dialog.set_can_create_directories(can);
self
}

/// Show hidden files in the dialog.
/// Supported platforms:
/// * Windows
/// * Mac
/// * Linux (GTK3 only, not XDG Portal)
pub fn set_show_hidden_files(mut self, show: bool) -> Self {
self.file_dialog = self.file_dialog.set_show_hidden_files(show);
self
}
}

#[cfg(target_os = "macos")]
Expand Down