-
Notifications
You must be signed in to change notification settings - Fork 80
use XDG Desktop Portal on Linux & BSDs #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e46df9b
9119652
6f42384
b74ff6f
35bb747
9e9fba6
bba1d87
72403bf
503aa10
7c78bb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
use std::path::PathBuf; | ||
|
||
use crate::backend::DialogFutureType; | ||
use crate::file_dialog::Filter; | ||
use crate::{FileDialog, FileHandle}; | ||
|
||
use ashpd::desktop::file_chooser::{ | ||
FileChooserProxy, FileFilter, OpenFileOptions, SaveFileOptions, | ||
}; | ||
// TODO: convert raw_window_handle::RawWindowHandle to ashpd::WindowIdentifier | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should provide a feature for that in ashpd There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be nice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
// https://github.com/bilelmoussaoui/ashpd/issues/40 | ||
use ashpd::{zbus, WindowIdentifier}; | ||
|
||
use log::warn; | ||
use pollster::block_on; | ||
|
||
// | ||
// Utility functions | ||
// | ||
|
||
fn add_filters_to_open_file_options( | ||
filters: Vec<Filter>, | ||
mut options: OpenFileOptions, | ||
) -> OpenFileOptions { | ||
for filter in &filters { | ||
let mut ashpd_filter = FileFilter::new(&filter.name); | ||
for file_extension in &filter.extensions { | ||
ashpd_filter = ashpd_filter.glob(&format!("*.{}", file_extension)); | ||
} | ||
options = options.add_filter(ashpd_filter); | ||
} | ||
options | ||
} | ||
|
||
fn add_filters_to_save_file_options( | ||
filters: Vec<Filter>, | ||
mut options: SaveFileOptions, | ||
) -> SaveFileOptions { | ||
for filter in &filters { | ||
let mut ashpd_filter = FileFilter::new(&filter.name); | ||
for file_extension in &filter.extensions { | ||
ashpd_filter = ashpd_filter.glob(&format!("*.{}", file_extension)); | ||
} | ||
options = options.add_filter(ashpd_filter); | ||
} | ||
options | ||
} | ||
|
||
// refer to https://github.com/flatpak/xdg-desktop-portal/issues/213 | ||
fn uri_to_pathbuf(uri: &str) -> Option<PathBuf> { | ||
uri.strip_prefix("file://").map(PathBuf::from) | ||
} | ||
|
||
fn ok_or_warn<T, E: std::fmt::Debug>(result: Result<T, E>) -> Option<T> { | ||
match result { | ||
Err(e) => { | ||
warn!("{:?}", e); | ||
None | ||
} | ||
Ok(t) => Some(t), | ||
} | ||
} | ||
|
||
async fn file_chooser_proxy<'a>() -> Option<FileChooserProxy<'a>> { | ||
let connection = ok_or_warn(zbus::Connection::session().await)?; | ||
ok_or_warn(FileChooserProxy::new(&connection).await) | ||
} | ||
|
||
// | ||
// File Picker | ||
// | ||
|
||
use crate::backend::FilePickerDialogImpl; | ||
impl FilePickerDialogImpl for FileDialog { | ||
fn pick_file(self) -> Option<PathBuf> { | ||
block_on(self.pick_file_async()).map(PathBuf::from) | ||
} | ||
|
||
fn pick_files(self) -> Option<Vec<PathBuf>> { | ||
block_on(self.pick_files_async()) | ||
.map(|vec_file_handle| vec_file_handle.iter().map(PathBuf::from).collect()) | ||
} | ||
} | ||
|
||
use crate::backend::AsyncFilePickerDialogImpl; | ||
impl AsyncFilePickerDialogImpl for FileDialog { | ||
fn pick_file_async(self) -> DialogFutureType<Option<FileHandle>> { | ||
Box::pin(async { | ||
let proxy = file_chooser_proxy().await?; | ||
let mut options = OpenFileOptions::default() | ||
.accept_label("Pick file") | ||
.multiple(false); | ||
options = add_filters_to_open_file_options(self.filters, options); | ||
let selected_files = proxy | ||
.open_file( | ||
&WindowIdentifier::default(), | ||
&self.title.unwrap_or_else(|| "Pick a file".to_string()), | ||
options, | ||
) | ||
.await; | ||
if selected_files.is_err() { | ||
return None; | ||
} | ||
uri_to_pathbuf(&selected_files.unwrap().uris()[0]).map(FileHandle::from) | ||
}) | ||
} | ||
|
||
fn pick_files_async(self) -> DialogFutureType<Option<Vec<FileHandle>>> { | ||
Box::pin(async { | ||
let proxy = file_chooser_proxy().await?; | ||
let mut options = OpenFileOptions::default() | ||
.accept_label("Pick file(s)") | ||
.multiple(true); | ||
options = add_filters_to_open_file_options(self.filters, options); | ||
let selected_files = proxy | ||
.open_file( | ||
&WindowIdentifier::default(), | ||
&self | ||
.title | ||
.unwrap_or_else(|| "Pick one or more files".to_string()), | ||
options, | ||
) | ||
.await; | ||
if selected_files.is_err() { | ||
return None; | ||
} | ||
let selected_files = selected_files | ||
.unwrap() | ||
.uris() | ||
.iter() | ||
.filter_map(|string| uri_to_pathbuf(string)) | ||
.map(FileHandle::from) | ||
.collect::<Vec<FileHandle>>(); | ||
if selected_files.is_empty() { | ||
return None; | ||
} | ||
Some(selected_files) | ||
}) | ||
} | ||
} | ||
|
||
// | ||
// Folder Picker | ||
// | ||
|
||
use crate::backend::FolderPickerDialogImpl; | ||
impl FolderPickerDialogImpl for FileDialog { | ||
fn pick_folder(self) -> Option<PathBuf> { | ||
block_on(self.pick_folder_async()).map(PathBuf::from) | ||
} | ||
} | ||
|
||
use crate::backend::AsyncFolderPickerDialogImpl; | ||
impl AsyncFolderPickerDialogImpl for FileDialog { | ||
fn pick_folder_async(self) -> DialogFutureType<Option<FileHandle>> { | ||
Box::pin(async { | ||
let proxy = file_chooser_proxy().await?; | ||
let mut options = OpenFileOptions::default() | ||
.accept_label("Pick folder") | ||
.multiple(false) | ||
.directory(true); | ||
options = add_filters_to_open_file_options(self.filters, options); | ||
let selected_files = proxy | ||
.open_file( | ||
&WindowIdentifier::default(), | ||
&self.title.unwrap_or_else(|| "Pick a folder".to_string()), | ||
options, | ||
) | ||
.await; | ||
if selected_files.is_err() { | ||
return None; | ||
} | ||
uri_to_pathbuf(&selected_files.unwrap().uris()[0]).map(FileHandle::from) | ||
}) | ||
} | ||
} | ||
|
||
// | ||
// File Save | ||
// | ||
|
||
use crate::backend::FileSaveDialogImpl; | ||
impl FileSaveDialogImpl for FileDialog { | ||
fn save_file(self) -> Option<PathBuf> { | ||
block_on(self.save_file_async()).map(PathBuf::from) | ||
} | ||
} | ||
|
||
use crate::backend::AsyncFileSaveDialogImpl; | ||
impl AsyncFileSaveDialogImpl for FileDialog { | ||
fn save_file_async(self) -> DialogFutureType<Option<FileHandle>> { | ||
Box::pin(async { | ||
let proxy = file_chooser_proxy().await?; | ||
let mut options = SaveFileOptions::default().accept_label("Save"); | ||
options = add_filters_to_save_file_options(self.filters, options); | ||
if let Some(file_name) = self.file_name { | ||
options = options.current_name(&file_name); | ||
} | ||
// TODO: impl zvariant::Type for PathBuf? | ||
// if let Some(dir) = self.starting_directory { | ||
// options.current_folder(dir); | ||
// } | ||
let selected_files = proxy | ||
.save_file( | ||
&WindowIdentifier::default(), | ||
&self.title.unwrap_or_else(|| "Save file".to_string()), | ||
options, | ||
) | ||
.await; | ||
if selected_files.is_err() { | ||
return None; | ||
} | ||
uri_to_pathbuf(&selected_files.unwrap().uris()[0]).map(FileHandle::from) | ||
}) | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.