Getting started

In this first chapter, you will install NWG and then look at a very simple application. This tutorial will intoduce the concepts of Control Templates, Events and the macro-based template system. These three compose the foundation of NWG. The next chapter "Basics" will go deeper into these.

Installing NWG

NWG should work with any recent version of rust (64 bits or 32 bits). Any channel can be used (stable, beta, nightly). NWG was only tested using the MSVC ABI builds. on both stable and nightly
To use NWG in your project add it to cargo.toml:
[dependencies]
native-windows-gui = "0.2.0"
And then, in main.rs or lib.rs :
extern crate native_windows_gui as nwg;

Hello World!

Here is a minimalistic program that display a window with a text input and a button. When the button is clicked, a messagebox that says "Hello {your name}!" is created. This code is available in the NWG examples and uses the template system (scroll further down to see the code without the template).
/**
    Simple example on how to use the nwg template system.
*/

#[macro_use] extern crate native_windows_gui as nwg;

use nwg::{Event, Ui, simple_message, fatal_message, dispatch_events};

nwg_template!(
    head: setup_ui<&'static str>,
    controls: [
        ("MainWindow", nwg_window!( title="Template Example"; size=(280, 105) )),
        
        ("Label1", nwg_label!( 
           parent="MainWindow"; text="Your Name: ";
           position=(5,15); size=(80, 25); font=Some("TextFont") )),
        
        ("YourName", nwg_textinput!( 
           parent="MainWindow"; position=(85,13); 
           size=(185,22); font=Some("TextFont") )),
        
        ("HelloButton", nwg_button!( 
           parent="MainWindow"; text="Hello World!";
           position=(5, 45); size=(270, 50); font=Some("MainFont") ))
    ];
    events: [
        ("HelloButton", "SaySomething", Event::Click, |ui,_,_,_| {
            let your_name = nwg_get!(ui; ("YourName", nwg::TextInput));
            simple_message("Hello", &format!("Hello {}!", your_name.get_text()) );
        })
    ];
    resources: [
        ("MainFont", nwg_font!(family="Arial"; size=27)),
        ("TextFont", nwg_font!(family="Arial"; size=17))
    ];
    values: []
);

fn main() {
    let app: Ui<&'static str>;

    match Ui::new() {
        Ok(_app) => { app = _app; },
        Err(e) => { fatal_message("Fatal Error", &format!("{:?}", e) ); }
    }

    if let Err(e) = setup_ui(&app) {
        fatal_message("Fatal Error", &format!("{:?}", e));
    }

    dispatch_events();
}

The example details

UI creation

The Ui encapsulate the various components of NWG. Its role is to manage these components so that most of your energy is spent developing the actual application instead of the UI. As of BETA 1, an application can instance as many Ui as it requires. See the documentation for more information.

The Ui object is basically a generic HashMap, therefore the type of its key must be defined when it is declared. The "best" types are either &'static str if the ui is entirely static, or usize if new controls will be created at runtime. The hash values are UI components (ex: the controls).

Ui creation is done by calling the Ui::new() method. The constructor execute some system initialization and therefore can fail. Once the Ui is ready, it is now time to add stuff inside of it. The easiest way to do this is to use the built-in template system.

Template Base

The NWG template system is composed of multiple macros. It's role is to make the definition of static ui as painless as possible. There's absolutely no black magic behind it. For an example of how the last example could look like if it was done without using templates, see the example at the end of this page.

The base of the template system is the nwg_template! macro (documentation). Its role is to define a function that initialize the Ui components.

The first parameter head defines the type of the Ui that will be accepted and the name of the function The other parameters (controls, events, resources and values) are described in the following sections.

If the initialization of the Ui fails, an Error is returned with some information about what went wrong. The template content is ready to be used as soon as the function returns with success.

Controls

A control is an item (ex: a button) that can be added to a Ui.

The second argument in the nwg_template! macro is controls. Controls accepts a list of (Control_ID, Control_Template).

Control_ID is the unique ID that will identify the control in the Ui. Its type must match the type of the Ui.

Control_Template is the template of the control. NWG control templates are structs that define controls. By default, the templates do not have sane default. In order to ease the template definition, NWG has a macro named nwg_[control_name] (ex: button) for each built-in control.

The controls are added in the order defined in the template.

Events

An Event is a function that is called when a control is triggered by something (usually user actions).

The third argument in the nwg_template! macro is events. Events accepts a list of (Control_ID, Event_ID, Event_Type, Callback).

Control_ID is the ID of the control that will trigger the event.

Event_ID is a unique id that identify the event.

Event_Type Is the event type. See a list of all events.

Callback Is the function that will be called. The callback function signature is EventCallback

There are no limits to the number of callback on a control.

Resources

A Resource is a special element that can be used by controls, but do not accepts events. (ex: Font & Bitmap)

The fourth argument in the nwg_template! macro is resources.

Resources accepts a list of (Resource_ID, Resource_Template) that will be created in the order they were added.

Resources creation works like the controls.

Values

A value can be any other object that needs to be packed in a Ui. For example, if you want to pack a SQL connection or a table of translations.

The last argument in the nwg_template! macro is values. Values accepts a list of (Value_ID, ValueObject). Resources creation works like controls and resources with the exception that ValueObject is the object itself and not some kind of template.

Listening for events

Once the Ui initialization is done, the threads needs to listen to the system events. In order to do that the dispatch_events function must be used. The method will dispatch the events to every instanced UI. As soon as a main window is closed (exit_on_close set to true) or if nwg::exit is called, the function will return.

Hello World! (no macro)

Here is the same example without the macro templates.
/**
    Simple example on how to use nwg without the template system.
    Unless your UI is built dynamically, the usage of the macro templates is highly recommended
*/

extern crate native_windows_gui as nwg;

use nwg::{Ui, Error, Event, simple_message, fatal_message, dispatch_events};

pub fn setup_ui(ui: &Ui<&'static str>) -> Result<(), Error> {

    // nwg_font!(family="Arial"; size=27)
    let f1 = nwg::FontT {
        family: "Arial", size: 27,
        weight: nwg::constants::FONT_WEIGHT_NORMAL,
        decoration: nwg::constants::FONT_DECO_NORMAL,
    };

    // nwg_font!(family="Arial"; size=17)
    let f2 = nwg::FontT {
        family: "Arial", size: 17,
        weight: nwg::constants::FONT_WEIGHT_NORMAL,
        decoration: nwg::constants::FONT_DECO_NORMAL,
    };

    // nwg_window!( title="Template Example"; size=(280, 105))
    let window = nwg::WindowT {
        title: "No template",
        position: (100, 100), size: (280, 105),
        resizable: false, visible: true, disabled: false,
        exit_on_close: true
    };

    // nwg_label!( parent="MainWindow"; [...] font=Some("TextFont") )
    let label = nwg::LabelT {
        text: "Your Name: ",
        position: (5,15), size: (80, 25),
        visible: true, disabled: false,
        align: nwg::constants::HTextAlign::Left,
        parent: "MainWindow", font: Some("TextFont")
    };

    // nwg_textinput!( parent="MainWindow"; [..] font=Some("TextFont") )
    let tedit = nwg::TextInputT::<_, &'static str, _> {
        text: "",
        position: (85,13), size: (185,22),
        visible: true, disabled: false, readonly: false, password: false,
        limit: 32_767, placeholder: None,
        parent: "MainWindow", font: Some("TextFont")
    };

    // nwg_button!( parent="MainWindow"; [..] font=Some("MainFont") )
    let hellbtn = nwg::ButtonT {
        text: "Hello World!",
        position: (5, 45), size: (270, 50),
        visible: true, disabled: false,
        parent: "MainWindow", font: Some("MainFont")
    };

    // resources: 
    ui.pack_resource(&"MainFont", f1);
    ui.pack_resource(&"TextFont", f2);

    // controls:
    ui.pack_control(&"MainWindow", window);
    ui.pack_control(&"Label1", label);
    ui.pack_control(&"YourName", tedit);
    ui.pack_control(&"HelloButton", hellbtn);

    // events:
    ui.bind(&"HelloButton", &"SaySomething", Event::Click, |ui,_,_,_| {
        if let Ok(your_name) = ui.get::<nwg::TextInput>(&"YourName") {
            simple_message("Hello", &format!("Hello {}!", your_name.get_text()) );
        } else {
            panic!()
        }
    });

    ui.commit()
}

fn main() {
    let app: Ui<&'static str>;

    match Ui::new() {
        Ok(_app) => { app = _app; },
        Err(e) => { fatal_message("Fatal Error", &format!("{:?}", e) ); }
    }

    if let Err(e) = setup_ui(&app) {
        fatal_message("Fatal Error", &format!("{:?}", e));
    }

    dispatch_events();
}

The big lines

Note that this section only scratch the non-macro features because they are explained in details in the Basics section.
For some technical info, see the UI api.

pack_*

pack_control, pack_resource and pack_value add an element to the UI. The parameters works like in the nwg_template! macro.

pack commands will not be executed right away (see commit, just below).

bind

Works just like the events fields of nwg_template!: bind a callback to a control.

The bind command will not be executed right away (see commit, just below).

get

The Ui object will always keep the ownership of the resources and the controls. In order to access a control, it must first be borrowed from the Ui.

ui.get and ui.get_mut either borrows a control, a resource or a user value from the Ui. Obviously, any element can be borrowed immutably as many time as required, but an element can only be borrowed mutably once.

If the UI tries to execute an action on a borrowed element, an error such as Error::ControlInUse will be raised.

commit

Some NWG ui commands (such as pack_control and bind) wont be executed right away. Many of these commands are used when creating a UI. With these conditions, they should never fail and validating each of them individually would be annoying.

When a lazy command is executed, it is stored in the windows event queue. ui.commit() forces the execution of the waiting commands and return an error if the initialization failed.

If commit is not called, the commands will be processed and executed just like any Windows messages. In this case, no error will be reported.