Closed fenhl closed 3 years ago
That's probably something threading related, I'll see what can be done in this regard.
Some questions:
pick_file
fn is called?
(you can run simple println!("{:?}", std::thread::current().id());
right before calling rfd to check this)I was pretty sure that iced always calls update on the main thread as it is direct result of winit event loop callback being called. But maybe I was wrong in this regard. (Judging by examples in iced repo, it is true)
Is this how you use it in iced
?
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Open(_) => {
return Command::perform(
rfd::AsyncFileDialog::new().pick_file(),
|ret| Message::Res(ret.map(|f| f.path().into())),
);
}
Message::Res(path) => {
println!("{:?}", path);
}
};
Command::none()
}
And one more question: Kinda related to previous question, when does the dialogue crashes? Does the dialogue open at all? Does it crash after selecting a file? After closing? etc. If it crashes after close then I probably know what's the problem, otherwise it will be quite tricky one to figure out.
Here's a minimal example, with the async code being called from a regular Command
, as is the usual case in iced apps.
Cargo.toml:
[package]
name = "rfd-crash-example"
version = "0.1.0"
authors = ["Fenhl <fenhl@fenhl.net>"]
edition = "2018"
[dependencies.iced]
git = "https://github.com/hecrj/iced"
rev = "12c0c18d662d2b817b559b94c71d18e122c76990"
[dependencies.rfd]
git = "https://github.com/PolyMeilex/rfd"
rev = "ee4e55f8d2cb0a307cea650a05deaf9d4aa142fc"
main.rs:
use iced::{
Application,
Command,
Element,
Settings,
widget::{
Text,
button::{
self,
Button,
},
},
};
#[derive(Debug, Clone)]
enum Message {
Click,
Nop,
}
#[derive(Default)]
struct App {
btn: button::State,
}
impl Application for App {
type Executor = iced::executor::Default;
type Message = Message;
type Flags = ();
fn new((): ()) -> (App, Command<Message>) { (App::default(), Command::none()) }
fn title(&self) -> String { format!("rfd crash example") }
fn update(&mut self, msg: Message) -> Command<Message> {
match msg {
Message::Click => async {
let dialog = rfd::AsyncFileDialog::new();
println!("{:?}", std::thread::current().id());
let _ = dialog.pick_file().await;
Message::Nop
}.into(),
Message::Nop => Command::none(),
}
}
fn view(&mut self) -> Element<'_, Message> {
Button::new(&mut self.btn, Text::new("Pick File"))
.on_press(Message::Click)
.into()
}
}
fn main() -> iced::Result {
App::run(Settings::default())
}
The file dialog window never appears, and the output is:
ThreadId(6)
fatal runtime error: Rust cannot catch foreign exceptions
So the crash happens in pick_file
, not new
.
My bad, I should have documented it better, there is no way to spawn dialogs from other threads in Mac OS, you have to spawn it on the main thread and await on it in the threaded executor. That's how Mac OS system API is designed nothing that I can do about it. So in this case dialog should be created in the update, and returned future should be moved into the async block where it can be awaited.
It is shown in async example
My solution:
@fenhl Does it solve your problem? If so, I'll proceed to add some additional checks and docs for this and close the issue
It fixes the minimal example above, I'll check if it works in the actual app I'm writing too.
Ok, thanks for the info.
I found a way to work around it, so if everything goes well, soon it will be possible to spawn async dialogues anywhere.
But it is only possible in windowed applications (those have internal macOS event loop, that I can use) So I don't want to make it a defacto/recommended way of doing things, as I want the API to be the same for all use-cases.
I will try to integrate it anyway, just in case main-thread limitation is unacceptable for someone (for example, there can be a GUI framework out there that does not allow users to control on which thread their code is executed)
Seems to be working fine now.
Nice, so for now I will just add a helpful error message, and proper documentation of this behaviour somewhere.
Version 0.2.0 released, RFD can now handle dialog spawn in other threads in iced, and in any other framework that uses winit
or SDL2
, and friendly panic message was added in case someone is trying to do this in unsupported (non-windowed) env.
I tried to use
AsyncFileDialog
in an iced app on macOS, but when trying to use the dialog, the app crashes, printing:This happens on 0.1.2 as well as master.