feat!(dialog): refactor and support rfd as file_chooser provider
This commit is contained in:
parent
b09b3db81a
commit
0bef593ba4
9 changed files with 618 additions and 362 deletions
|
|
@ -3,6 +3,11 @@ name = "open-dialog"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["xdg-portal"]
|
||||
rfd = ["libcosmic/rfd"]
|
||||
xdg-portal = ["libcosmic/xdg-portal"]
|
||||
|
||||
[dependencies]
|
||||
apply = "0.3.0"
|
||||
tokio = { version = "1.31", features = ["full"] }
|
||||
|
|
@ -13,4 +18,4 @@ url = "2.4.0"
|
|||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
default-features = false
|
||||
features = ["debug", "wayland", "tokio", "xdg-portal"]
|
||||
features = ["debug", "wayland", "tokio"]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use cosmic::dialog::file_chooser::{self, FileFilter};
|
|||
use cosmic::iced_core::Length;
|
||||
use cosmic::widget::button;
|
||||
use cosmic::{executor, iced, ApplicationExt, Element};
|
||||
use std::sync::Arc;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use url::Url;
|
||||
|
||||
|
|
@ -26,12 +27,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
/// Messages that are used specifically by our [`App`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
Cancelled,
|
||||
CloseError,
|
||||
DialogClosed,
|
||||
DialogInit(file_chooser::Sender),
|
||||
DialogOpened,
|
||||
Error(String),
|
||||
FileRead(Url, String),
|
||||
OpenError(Arc<file_chooser::Error>),
|
||||
OpenFile,
|
||||
Selected(Url),
|
||||
}
|
||||
|
|
@ -39,7 +39,6 @@ pub enum Message {
|
|||
/// The [`App`] stores application-specific state.
|
||||
pub struct App {
|
||||
core: Core,
|
||||
open_sender: Option<file_chooser::Sender>,
|
||||
file_contents: String,
|
||||
selected_file: Option<Url>,
|
||||
error_status: Option<String>,
|
||||
|
|
@ -70,7 +69,6 @@ impl cosmic::Application for App {
|
|||
fn init(core: Core, _input: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
let mut app = App {
|
||||
core,
|
||||
open_sender: None,
|
||||
file_contents: String::new(),
|
||||
selected_file: None,
|
||||
error_status: None,
|
||||
|
|
@ -90,41 +88,10 @@ impl cosmic::Application for App {
|
|||
vec![button::suggested("Open").on_press(Message::OpenFile).into()]
|
||||
}
|
||||
|
||||
fn subscription(&self) -> cosmic::iced_futures::Subscription<Self::Message> {
|
||||
// Creates a subscription for handling open dialogs.
|
||||
file_chooser::subscription(|response| match response {
|
||||
file_chooser::Message::Closed => Message::DialogClosed,
|
||||
file_chooser::Message::Opened => Message::DialogOpened,
|
||||
file_chooser::Message::Selected(files) => match files.uris().first() {
|
||||
Some(file) => Message::Selected(file.to_owned()),
|
||||
None => Message::DialogClosed,
|
||||
},
|
||||
file_chooser::Message::Init(sender) => Message::DialogInit(sender),
|
||||
file_chooser::Message::Err(why) => {
|
||||
let mut source: &dyn std::error::Error = &why;
|
||||
let mut string = format!("open dialog subscription errored\n cause: {source}");
|
||||
|
||||
while let Some(new_source) = source.source() {
|
||||
string.push_str(&format!("\n cause: {new_source}"));
|
||||
source = new_source;
|
||||
}
|
||||
|
||||
Message::Error(string)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
||||
match message {
|
||||
Message::DialogClosed => {
|
||||
eprintln!("dialog closed");
|
||||
}
|
||||
|
||||
Message::DialogOpened => {
|
||||
if let Some(sender) = self.open_sender.as_mut() {
|
||||
eprintln!("requesting selection");
|
||||
return sender.response().map(|_| cosmic::app::Message::None);
|
||||
}
|
||||
Message::Cancelled => {
|
||||
eprintln!("open file dialog cancelled");
|
||||
}
|
||||
|
||||
Message::FileRead(url, contents) => {
|
||||
|
|
@ -178,29 +145,30 @@ impl cosmic::Application for App {
|
|||
|
||||
// Creates a new open dialog.
|
||||
Message::OpenFile => {
|
||||
if let Some(sender) = self.open_sender.as_mut() {
|
||||
if let Some(dialog) = file_chooser::open_file() {
|
||||
eprintln!("opening new dialog");
|
||||
return cosmic::command::future(async move {
|
||||
eprintln!("opening new dialog");
|
||||
|
||||
return dialog
|
||||
// Sets title of the dialog window.
|
||||
.title("Choose a file".into())
|
||||
// Sets the label of the accept button.
|
||||
.accept_label("_Open".into())
|
||||
// Exclude directories from file selection.
|
||||
.include_directories(false)
|
||||
// Defines whether to block the main window while requesting input.
|
||||
.modal(false)
|
||||
// Only accept one file as input.
|
||||
.multiple_files(false)
|
||||
// Accept only plain text files
|
||||
.filter(FileFilter::new("Text files").mimetype("text/plain"))
|
||||
// Emits the dialog to our sender
|
||||
.create(sender)
|
||||
// Ignores the output because it's empty.
|
||||
.map(|_| cosmic::app::message::none());
|
||||
#[cfg(feature = "rfd")]
|
||||
let filter = FileFilter::new("Text files").extension("txt");
|
||||
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
let filter = FileFilter::new("Text files").glob("*.txt");
|
||||
|
||||
let dialog = file_chooser::open::Dialog::new()
|
||||
// Sets title of the dialog window.
|
||||
.title("Choose a file")
|
||||
// Accept only plain text files
|
||||
.filter(filter);
|
||||
|
||||
match dialog.open_file().await {
|
||||
Ok(response) => Message::Selected(response.url().to_owned()),
|
||||
|
||||
Err(file_chooser::Error::Cancelled) => Message::Cancelled,
|
||||
|
||||
Err(why) => Message::OpenError(Arc::new(why)),
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(cosmic::app::Message::App);
|
||||
}
|
||||
|
||||
// Displays an error in the application's warning bar.
|
||||
|
|
@ -208,15 +176,24 @@ impl cosmic::Application for App {
|
|||
self.error_status = Some(why);
|
||||
}
|
||||
|
||||
// Closes the warning bar, if it was shown.
|
||||
Message::CloseError => {
|
||||
self.error_status = None;
|
||||
// Displays an error in the application's warning bar.
|
||||
Message::OpenError(why) => {
|
||||
if let Some(why) = Arc::into_inner(why) {
|
||||
let mut source: &dyn std::error::Error = &why;
|
||||
let mut string =
|
||||
format!("open dialog subscription errored\n cause: {source}");
|
||||
|
||||
while let Some(new_source) = source.source() {
|
||||
string.push_str(&format!("\n cause: {new_source}"));
|
||||
source = new_source;
|
||||
}
|
||||
|
||||
self.error_status = Some(string);
|
||||
}
|
||||
}
|
||||
|
||||
// The open dialog. subscription provides this on register.
|
||||
Message::DialogInit(sender) => {
|
||||
eprintln!("dialog subscription enabled");
|
||||
self.open_sender = Some(sender);
|
||||
Message::CloseError => {
|
||||
self.error_status = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +209,8 @@ impl cosmic::Application for App {
|
|||
.on_close(Message::CloseError)
|
||||
.into(),
|
||||
);
|
||||
content.push(iced::widget::vertical_space(Length::Fixed(12.0)).into())
|
||||
|
||||
content.push(iced::widget::vertical_space(Length::Fixed(12.0)).into());
|
||||
}
|
||||
|
||||
content.push(if self.selected_file.is_none() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue