cosmic/dialog/file_chooser/
mod.rs

1// Copyright 2023 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4//! Dialogs for opening and save files.
5//!
6//! # Features
7//!
8//! - On Linux, the `xdg-portal` feature will use XDG Portal dialogs.
9//! - Alternatively, `rfd` can be used for platform support beyond Linux.
10//!
11//! # Open a file
12//!
13//! ```no_run
14//! cosmic::task::future(async {
15//!     use cosmic::dialog::file_chooser;
16//!
17//!     let dialog = file_chooser::open::Dialog::new()
18//!         .title("Choose a file");
19//!
20//!     match dialog.open_file().await {
21//!         Ok(response) => println!("selected to open {:?}", response.url()),
22//!
23//!         Err(file_chooser::Error::Cancelled) => (),
24//!
25//!         Err(why) => eprintln!("error selecting file to open: {why:?}")
26//!     }
27//! });
28//! ```
29//!
30//! # Open multiple files
31//!
32//! ```no_run
33//! cosmic::task::future(async {
34//!     use cosmic::dialog::file_chooser;
35//!
36//!     let dialog = file_chooser::open::Dialog::new()
37//!         .title("Choose multiple files");
38//!
39//!     match dialog.open_files().await {
40//!         Ok(response) => println!("selected to open {:?}", response.urls()),
41//!
42//!         Err(file_chooser::Error::Cancelled) => (),
43//!
44//!         Err(why) => eprintln!("error selecting file(s) to open: {why:?}")
45//!     }
46//! });
47//! ```
48//!
49//! # Open a folder
50//!
51//! ```no_run
52//! cosmic::task::future(async {
53//!     use cosmic::dialog::file_chooser;
54//!
55//!     let dialog = file_chooser::open::Dialog::new()
56//!         .title("Choose a folder");
57//!
58//!     match dialog.open_folder().await {
59//!         Ok(response) => println!("selected to open {:?}", response.url()),
60//!
61//!         Err(file_chooser::Error::Cancelled) => (),
62//!
63//!         Err(why) => eprintln!("error selecting folder to open: {why:?}")
64//!     }
65//! });
66//! ```
67//!
68//! # Open multiple folders
69//!
70//! ```no_run
71//! cosmic::task::future(async {
72//!     use cosmic::dialog::file_chooser;
73//!
74//!     let dialog = file_chooser::open::Dialog::new()
75//!         .title("Choose a folder");
76//!
77//!     match dialog.open_folders().await {
78//!         Ok(response) => println!("selected to open {:?}", response.urls()),
79//!
80//!         Err(file_chooser::Error::Cancelled) => (),
81//!
82//!         Err(why) => eprintln!("error selecting folder(s) to open: {why:?}")
83//!     }
84//! });
85//! ```
86
87/// Open file dialog.
88pub mod open;
89
90/// Save file dialog.
91pub mod save;
92
93#[cfg(feature = "xdg-portal")]
94pub use ashpd::desktop::file_chooser::{Choice, FileFilter};
95
96use thiserror::Error;
97
98/// A file filter, to limit the available file choices to certain extensions.
99#[cfg(feature = "rfd")]
100#[must_use]
101pub struct FileFilter {
102    description: String,
103    extensions: Vec<String>,
104}
105
106#[cfg(feature = "rfd")]
107impl FileFilter {
108    pub fn new(description: impl Into<String>) -> Self {
109        Self {
110            description: description.into(),
111            extensions: Vec::new(),
112        }
113    }
114
115    pub fn extension(mut self, extension: impl Into<String>) -> Self {
116        self.extensions.push(extension.into());
117        self
118    }
119}
120
121/// Errors that my occur when interacting with the file chooser subscription
122#[derive(Debug, Error)]
123pub enum Error {
124    #[error("dialog request cancelled")]
125    Cancelled,
126    #[error("dialog close failed")]
127    Close(#[source] DialogError),
128    #[error("open dialog failed")]
129    Open(#[source] DialogError),
130    #[error("dialog response failed")]
131    Response(#[source] DialogError),
132    #[error("save dialog failed")]
133    Save(#[source] DialogError),
134    #[error("could not set directory")]
135    SetDirectory(#[source] DialogError),
136    #[error("could not set absolute path for file name")]
137    SetAbsolutePath(#[source] DialogError),
138    #[error("path from dialog was not absolute")]
139    UrlAbsolute,
140}
141
142#[cfg(feature = "xdg-portal")]
143pub type DialogError = ashpd::Error;
144
145#[cfg(feature = "rfd")]
146#[derive(Debug, Error)]
147#[error("no file selected")]
148pub struct DialogError {}