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;
}
Available on crate features 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§

source

fn spawn_new_thread<F>(&self, func: F)
where F: FnOnce() -> AnyResult<()> + Send + 'static,

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(())
    }
});
source

fn run_ui_thread<F>(&self, func: F)
where F: FnOnce() -> AnyResult<()> + Send + 'static,

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:

  1. blocks current thread;
  2. switches to the window’s original UI thread;
  3. runs the given FnOnce;
  4. 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(())
    }
});

Object Safety§

This trait is not object safe.

Implementors§