use std::path::PathBuf;
use super::linux::zenity;
use crate::backend::DialogFutureType;
use crate::file_dialog::Filter;
use crate::message_dialog::MessageDialog;
use crate::{FileDialog, FileHandle, MessageButtons, MessageDialogResult};
use ashpd::desktop::file_chooser::{FileFilter, OpenFileRequest, SaveFileRequest};
use log::error;
use pollster::block_on;
impl From<&Filter> for FileFilter {
fn from(filter: &Filter) -> Self {
let mut ashpd_filter = FileFilter::new(&filter.name);
for file_extension in &filter.extensions {
ashpd_filter = ashpd_filter.glob(&format!("*.{file_extension}"));
}
ashpd_filter
}
}
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 move {
let res = OpenFileRequest::default()
.multiple(false)
.title(self.title.as_deref().or(None))
.filters(self.filters.iter().map(From::from))
.current_folder::<&PathBuf>(&self.starting_directory)
.expect("File path should not be nul-terminated")
.send()
.await;
if res.is_err() {
match zenity::pick_file(&self).await {
Ok(res) => res,
Err(err) => {
error!("pick_file error {err}");
return None;
}
}
} else {
res.ok()
.and_then(|request| request.response().ok())
.and_then(|response| {
response
.uris()
.first()
.and_then(|uri| uri.to_file_path().ok())
})
}
.map(FileHandle::from)
})
}
fn pick_files_async(self) -> DialogFutureType<Option<Vec<FileHandle>>> {
Box::pin(async move {
let res = OpenFileRequest::default()
.multiple(true)
.title(self.title.as_deref().or(None))
.filters(self.filters.iter().map(From::from))
.current_folder::<&PathBuf>(&self.starting_directory)
.expect("File path should not be nul-terminated")
.send()
.await;
if res.is_err() {
match zenity::pick_files(&self).await {
Ok(res) => Some(res.into_iter().map(FileHandle::from).collect::<Vec<_>>()),
Err(err) => {
error!("pick_files error {err}");
None
}
}
} else {
res.ok()
.and_then(|request| request.response().ok())
.map(|response| {
response
.uris()
.iter()
.filter_map(|uri| uri.to_file_path().ok())
.map(FileHandle::from)
.collect::<Vec<FileHandle>>()
})
}
})
}
}
use crate::backend::FolderPickerDialogImpl;
impl FolderPickerDialogImpl for FileDialog {
fn pick_folder(self) -> Option<PathBuf> {
block_on(self.pick_folder_async()).map(PathBuf::from)
}
fn pick_folders(self) -> Option<Vec<PathBuf>> {
block_on(self.pick_folders_async())
.map(|vec_file_handle| vec_file_handle.iter().map(PathBuf::from).collect())
}
}
use crate::backend::AsyncFolderPickerDialogImpl;
impl AsyncFolderPickerDialogImpl for FileDialog {
fn pick_folder_async(self) -> DialogFutureType<Option<FileHandle>> {
Box::pin(async move {
let res = OpenFileRequest::default()
.multiple(false)
.directory(true)
.title(self.title.as_deref().or(None))
.filters(self.filters.iter().map(From::from))
.current_folder::<&PathBuf>(&self.starting_directory)
.expect("File path should not be nul-terminated")
.send()
.await;
if res.is_err() {
match zenity::pick_folder(&self).await {
Ok(res) => res,
Err(err) => {
error!("pick_folder error {err}");
return None;
}
}
} else {
res.ok()
.and_then(|request| request.response().ok())
.and_then(|response| {
response
.uris()
.first()
.and_then(|uri| uri.to_file_path().ok())
})
}
.map(FileHandle::from)
})
}
fn pick_folders_async(self) -> DialogFutureType<Option<Vec<FileHandle>>> {
Box::pin(async move {
let res = OpenFileRequest::default()
.multiple(true)
.directory(true)
.title(self.title.as_deref().or(None))
.filters(self.filters.iter().map(From::from))
.current_folder::<&PathBuf>(&self.starting_directory)
.expect("File path should not be nul-terminated")
.send()
.await;
if res.is_err() {
match zenity::pick_folders(&self).await {
Ok(res) => Some(res.into_iter().map(FileHandle::from).collect::<Vec<_>>()),
Err(err) => {
error!("pick_files error {err}");
None
}
}
} else {
res.ok()
.and_then(|request| request.response().ok())
.map(|response| {
response
.uris()
.iter()
.filter_map(|uri| uri.to_file_path().ok())
.map(FileHandle::from)
.collect::<Vec<FileHandle>>()
})
}
})
}
}
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 move {
let res = SaveFileRequest::default()
.title(self.title.as_deref().or(None))
.current_name(self.file_name.as_deref())
.filters(self.filters.iter().map(From::from))
.current_folder::<&PathBuf>(&self.starting_directory)
.expect("File path should not be nul-terminated")
.send()
.await;
if res.is_err() {
match zenity::save_file(&self).await {
Ok(res) => res,
Err(err) => {
error!("pick_folder error {err}");
return None;
}
}
} else {
res.ok()
.and_then(|request| request.response().ok())
.and_then(|response| {
response
.uris()
.first()
.and_then(|uri| uri.to_file_path().ok())
})
}
.map(FileHandle::from)
})
}
}
use crate::backend::MessageDialogImpl;
impl MessageDialogImpl for MessageDialog {
fn show(self) -> MessageDialogResult {
block_on(self.show_async())
}
}
use crate::backend::AsyncMessageDialogImpl;
impl AsyncMessageDialogImpl for MessageDialog {
fn show_async(self) -> DialogFutureType<MessageDialogResult> {
Box::pin(async move {
match &self.buttons {
MessageButtons::Ok | MessageButtons::OkCustom(_) => {
let res = crate::backend::linux::zenity::message(
&self.level,
&self.buttons,
&self.title,
&self.description,
)
.await;
match res {
Ok(res) => res,
Err(err) => {
log::error!("Failed to open zenity dialog: {err}");
MessageDialogResult::Cancel
}
}
}
MessageButtons::OkCancel
| MessageButtons::YesNo
| MessageButtons::OkCancelCustom(..)
| MessageButtons::YesNoCancel
| MessageButtons::YesNoCancelCustom(..) => {
let res = crate::backend::linux::zenity::question(
&self.buttons,
&self.title,
&self.description,
)
.await;
match res {
Ok(res) => res,
Err(err) => {
log::error!("Failed to open zenity dialog: {err}");
MessageDialogResult::Cancel
}
}
}
}
})
}
}