Hello
I have a Gtk-rs (v4) application. The application has multiple windows and also uses Tokio to initialize a websocket, Tokio runs on a separate thread.
The most important code of my Gtk:
fn main() {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(2) // Set the number of Tokio worker threads
.enable_all()
.build()
.unwrap();
let _guard = rt.enter();
gtk();
}
fn gtk() {
gtk::init().expect("Failed to initialize GTK.");
let app = gtk::Application::builder().application_id("123").build();
// Connect to "activate" signal of `app`
app.connect_startup(|_| load_css());
app.connect_activate(build_ui);
// Run the application
app.run();
}
fn build_ui(app: >k::Application) {
// Show login form if not logged in
match futures::executor::block_on(session::restaurantuser()) {
Some(u) => {
let join_handle = tokio::spawn(initialize_tokio());
ui::dashboard_mainview(app);
},
None => ui::login_view(app),
};
}
I am also using Wry to show a webview to the user. The Wry-webview is opened in a separate window:
pub fn dashboard_webview() -> wry::Result<()> {
//...
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Dashboard webview")
.with_maximized(true)
.with_window_icon(Some(icon))
.build(&event_loop).unwrap();
let _webview = WebViewBuilder::new(window).unwrap()
.with_url_and_headers(&*format!("https://{}/order-dashboard/panel?desktop_app=true", *config::URL), headers).unwrap()
.build()
.unwrap();
_webview.clear_all_browsing_data().unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::NewEvents(StartCause::Init) => {
println!("Wry has started!");
},
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
_webview.clear_all_browsing_data().unwrap();
*control_flow = ControlFlow::Exit
},
_ => (),
}
});
}
The window with the Wry-webview is opened from a Gtk-window when a button is clicked.
item_dashboard.connect_clicked(move | _button| {
ui::dashboard_webview().unwrap();
});
When the button item_dashboard
is clicked the window with Wry opens and works fine. But the problem is that now my Gtk-windows are totally frozen. They do not respond at all. Closing the Wry-window closes the whole app as well, which is not the expected behavior.
I want the user to be able to open the Wry-window while still be able to use the Gtk-window from where it was opened. And closing the Wry-window should not terminate the application.
I think the reason the Gtk-part of my application blocks when Wry is opened, is because Wry uses some kind of event-loop:
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::NewEvents(StartCause::Init) => {
println!("Wry has started!");
},
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
_webview.clear_all_browsing_data().unwrap();
*control_flow = ControlFlow::Exit
},
_ => (),
}
});
I think that stalls the rest of my application. Note: Opening other Gtk-windows from my app does not stall my application.
I tried multiple solutions. I thought that I could launch ui::dashboard_webview()
in a thread so it would run separately from my app:
item_dashboard.connect_clicked(move | _button| {
std::thread::spawn(|| {
ui::dashboard_webview().unwrap();
});
});
Runtime error:
thread '<unnamed>' panicked at 'Initializing the event loop outside of the main thread is a significant cross-platform compatibility hazard. If you absolutely need to create an EventLoop on a different thread, you can use the `EventLoopBuilderExtWindows::any_thread` function.', C:\Users\vboxuser\.cargo\registry\src\index.crates.io-6f17d22bba15001f\tao-0.22.2\src\platform_impl\windows\event_loop.rs:172:7
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
I also tried making ui::dashboard_webview()
async and calling it like this:
item_dashboard.connect_clicked(move | _button| {
glib::MainContext::default().spawn_local(async move {
let join_handle = tokio::spawn(ui::dashboard_webview());
});
});
It yields the same runtime-error. How would I fix this?