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.
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_wrapper: The module containing the functions to communicate with the rustup binary
- rustup_ui: The module that contains the main UI template and its initialization.
- main: The program entry point.
rustup-ui/
-- rustup_wrapper.rs
-- rustup_ui.rs
-- main.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...
}
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)
}
}
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();
}
- Don't ever use globals
- If you need to pass a UI control to your application code, that's a bad sign
- If you need to pass a UI to your application code, that's just bad
- Ui ids definitions should be private in the UI modules
- Callbacks defined in a template should be kept small (<50 LOC)
- Custom controls should have their own module
- Don't ever use globals