pub trait GuiThread: GuiParent {
// Required methods
fn spawn_new_thread<F>(&self, func: F)
where F: FnOnce() -> AnyResult<()> + Send + 'static;
fn run_ui_thread<F>(&self, func: F)
where F: FnOnce() -> AnyResult<()> + Send + 'static;
}
kernel
and gui
only.Expand description
Allows a window to spawn new threads which can return errors, and run closures in the original UI thread.
Required Methods§
sourcefn spawn_new_thread<F>(&self, func: F)
fn spawn_new_thread<F>(&self, func: F)
This method calls std::thread::spawn
, but it allows the returning of
an error value. This error value will be forwarded to the original UI
thread, allowing it to be caught at
WindowMain::run_main
.
It’s a way to ensure that, upon an unexpected error, you application will be terminated gracefully.
§Examples
The example below shows the event of a button click which spawns a new thread.
use winsafe::{self as w, prelude::*, gui};
let wnd: gui::WindowMain; // initialized somewhere
let btn: gui::Button;
btn.on().bn_clicked({
let wnd = wnd.clone();
move || -> w::AnyResult<()> {
println!("Click event at {:#x}", w::GetCurrentThreadId());
wnd.spawn_new_thread({
let wnd = wnd.clone();
move || {
println!("This is another thread: {:#x}", w::GetCurrentThreadId());
if 1 != 2 {
Err("Unexpected condition, goodbye.".into())
} else {
Ok(())
}
}
});
Ok(())
}
});
sourcefn run_ui_thread<F>(&self, func: F)
fn run_ui_thread<F>(&self, func: F)
Runs a closure synchronously in the window’s original UI thread, allowing UI updates without the risk of a deadlock.
§Rationale
If you perform a very long task in the UI thread, the UI freezes until the task is complete – this may cause the impression that your application crashed. That’s why long tasks should be performed in parallel threads. However, at some point you’ll want to update the UI to reflect the task progress, but if you update the UI from another thread (different from the original UI thread), the UI may deadlock, and you application crashes.
This is what this run_ui_thread
does, step-by-step:
- blocks current thread;
- switches to the window’s original UI thread;
- runs the given
FnOnce
; - switches back to the first thread, which is then unblocked.
When working in a parallel thread, you must call run_ui_thread
to
update the UI.
§Examples
The example below shows the event of a button click which starts a long task in a parallel thread. As it progresses, the status is printed at the windows’s titlebar.
use winsafe::{self as w, prelude::*, gui};
let wnd: gui::WindowMain; // initialized somewhere
let btn: gui::Button;
btn.on().bn_clicked({
let wnd = wnd.clone();
move || -> w::AnyResult<()> {
println!("Click event at {:#x}", w::GetCurrentThreadId());
std::thread::spawn({
let wnd = wnd.clone();
move || {
println!("Parallel task starts at {:#x}", w::GetCurrentThreadId());
w::Sleep(2000);
wnd.run_ui_thread({
let wnd = wnd.clone();
move || -> w::AnyResult<()> {
println!("Updating UI at {:#x}", w::GetCurrentThreadId());
wnd.hwnd().SetWindowText("Status... 50%")?;
Ok(())
}
});
println!("Parallel task keeps going at {:#x}", w::GetCurrentThreadId());
w::Sleep(2000);
wnd.run_ui_thread({
let wnd = wnd.clone();
move || -> w::AnyResult<()> {
println!("Updating UI at {:#x}", w::GetCurrentThreadId());
wnd.hwnd().SetWindowText("Status... 100%")?;
Ok(())
}
});
}
});
Ok(())
}
});