pub trait GuiParent: GuiWindow + AsRef<Base> {
// Provided methods
fn on(&self) -> &WindowEvents { ... }
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
Any window which can host child controls.
Prefer importing this trait through the prelude:
use winsafe::prelude::*;
Provided Methods§
sourcefn on(&self) -> &WindowEvents
fn on(&self) -> &WindowEvents
Exposes methods to handle the basic window messages, plus timer and native control notifications.
§Panics
Panics if the window is already created. Events must be set before window creation.
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(())
}
});