Data model

NWG is a GUI framework, and not an application framework. As such, the application code and the UI code should be decoupled.

This chapter gives an example on how to structure small or moderately sized applications. The rustup utility is used as an example of application code.

Another chapter will be released once NWG will be ready for bigger applications.

Project Tree

The first step to have a nicely decouped application is to create two base modules: one for the ui and another for the application.
rustup-ui/
-- rustup_wrapper.rs
-- rustup_ui.rs
-- main.rs

rustup_wrapper.rs

For simplicity, lets say the rustup_wrapper file will only define a RustupWrapper structure with methods that call the original binary and return the output.
pub struct RustupWrapper {
 // private fields...
}

impl RustupWrapper {
  pub fn init() -> RustupWrapper;
  pub fn show(&self) -> Vec<String>;
  pub fn default(&self, toolchain: String) -> Result<(), String>;
  // etc...
}

rustup_ui.rs

The rustup_ui module will define the ui template using nwg_template and export a single functions: create_ui. Its function will be to create the ui, initalize it with the template, pack the RustupWrapper object and finally initalizing the ui components that depends on the wrapper object
const APP: usize = 0;
const ShowToolchainsBtn: usize = 1;
const ShowToolchainsCallback: usize = 2;
// other ids...

nwg_template!(
 head: setup_ui<usize>,
 controls: [
 //...
   (ShowToolchainsBtn, nwg_button!(/*..*/)),
 //...
  events: [
   (ShowToolchainsBtn, ShowToolchainsCallback, Event::Click, |ui,_,_,_| {
       let app = nwg_get!(ui, (RustupWrapper));
       let installed_toochains = app.show();
       // insert values into a listbox...
    }),
 //...
);

pub fn create_ui(app: RustupWrapper) -> Result<Ui<usize>, nwg::Error>  {
  let ui: Ui<usize>;

  match Ui::new() {
      Ok(_ui) => { ui = _ui; },
      Err(e) => { return Err(e); }
  }

  if let Err(e) = setup_ui(&ui) {
      return Err(e);
  }

  ui.pack_value(APP, app);
  ui.trigger(ShowToolchainsBtn, Event::Click, EventArgs::None);
 
  if let Err(e) = ui.commit() {
      Err(e)
  } else {
      Ok(ui)
  }
}

main.rs

Finally, main.rs will load both module and do the required initialization
extern crate native_windows_gui as nwg;
mod rustup_wrapper;
mod rustup_ui;

fn main() {
   let app = rustup_wrapper::RustupWrapper::init();
   let ui = rustup_ui::create_ui(app).expect("Something went wrong");

   nwg::dispatch_events();
}

Things to keep in mind