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, Actions and Events: the foundations 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.
To use NWG in your project add it to cargo.toml:
[dependencies]
native-windows-gui = "0.1.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.
extern crate native_windows_gui as nwg;

use nwg::Ui;
use nwg::events::EventCallback;
use nwg::controls::{Button, Window, TextInput};
use nwg::actions::{Action, ActionReturn};
use nwg::actions::helper as actions_help;
use nwg::constants::{HTextAlign, VTextAlign, MessageButtons, MessageIcons};

fn setup_controls(ui: &mut Ui<&'static str>) {
    let main_window = Window {
        caption: "Hello".to_string(),
        size: (200, 200),
        position: (100, 100),
        visible: true,
        resizable: false,
        exit_on_close: true
    };

    let name_input = TextInput {
        text: "".to_string(),
        size: (180, 20),
        position: (10, 10),
        parent: "MainWindow",
        placeholder: Some("Your name Here".to_string()),
        text_align: HTextAlign::Left,
        password: false,
        readonly: false
    };

    let hello_button = Button {
        text: "Say Hello!".to_string(),
        size: (180, 130),
        position: (10, 50),
        parent: "MainWindow",
        text_align: (HTextAlign::Center, VTextAlign::Center),
    };

    ui.new_control("MainWindow", main_window).unwrap();
    ui.new_control("Name", name_input).unwrap();
    ui.new_control("Hello", hello_button).unwrap();
}

fn main() {
    let mut ui: Ui<&'static str> = Ui::new();

    setup_controls(&mut ui);

    ui.bind("Hello", "SayHello", EventCallback::Click(Box::new(|ui, caller|{
        println!("Caller is: {:?}", caller);
        if let Ok(ActionReturn::Text(name)) = ui.exec("Name", Action::GetText) {
            let msg = actions_help::message(
                "Hello!", format!("Hello {}!", name),
                MessageButtons::Ok, MessageIcons::None
            );
            ui.exec("MainWindow", msg).unwrap();
        }
        
    }))).unwrap();

    nwg::dispatch_events();
}

The example details

UI creation

The first line executed in the main function creates the Ui object. You can think of it as some kind of service. In fact, NWG Ui abstract the whole UI, this means that, as a developer, you don't have access to the actual controls.

In order to expose the controls to the developer, NWG uses controls IDS. This way, controls can be accessed from anywhere in the code without you having drag some kind of data along. The type of the ID is choosen by the developer, but the it must implement the Eq+Clone+Hash traits.

For this example the control id type is &'static str. Using str is great for visibility, but it has one big inconvenient: you can't create controls on the fly.

Yup, with NWG all created controls must have a unique name. Because of this, using int with const is usually a better idea. You could also use enums.

Note 1: Ui objects are single threaded (!Send + !Sync). This is a OS restriction, so it will never go away.

Note 2: Currently, there can only be one Ui object per thread. This restriction might be lifted in the future in the BETA/STABLE release.

Creating controls

Now that the Ui object is created, it's time to create the controls! Creating controls is done with Control Templates. Control templates are transparent structures that implements the ControlTemplate trait. NWG offers a wide variety of built-in controls. For more details see: The Controls dictionnary.

Once a template is filled, it can be added to the GUI by calling ui.new_control. The first argument is the unique control ID and the second is the template. The creation can fail if the ID already exists or if the template configurations contains invalid values.

With the exception of the Window control, all controls must have a parent. A parent must have been created before its children. If a children control is created before its parent, a CONTROL_NOT_FOUND error will be returned.

Note 1: As you can see, control templates are very verbose. Because of this it is a good idea to put the controls creation in its own function. This is a design choice. Most GUI are now done though some kind of markup language or WYSIWYG editors (which I plan to work on once BETA is reached). Templates were designed to make the creation process of those tools easier.

Note 3: Templates fields are unstable. I will most likely add new fields before the BETA release.

Binding events

Now that the controls are created, it's time to make the GUI respond to the user. This is done though callbacks and events.

You can bind an event to a widget using the bind method of Ui. The first argument is the widget identifer, the second is a user indentifier for the callback and the third is the EventCallback.

Every event that can be bound to a control is defined under the EventCallback enum. The enum either take a boxed fn or a boxed closure. The first two parameters are always the Ui and the caller ID. Depending on the callback, more values can be passed.

Note 1: Two events callback of the same type on the same control cannot have the same identifier. Duplicate are possible in any other combinations (ex: two different events on the same control can have a callback named "foo").

Note 2: Not all control support all events; if an event is bound to a control that do not support it, an error is returned.

Note 3: The events API is mostly stable. The only thing that is subject to change is the EventCallback definition. (I want to remove the box).

Executing actions

The last thing left to explain is how to interact with the controls from the code. This is done by sending Actions to the ui using the exec method and then reading the returned ActionReturn. The first parameter is the identifier of the control that will receive the event, the second is the action.

An action sent can return different kind of ActionReturn. An action that don't return stuff (ex: SetText) will always return ActionReturn::None, furthermore, sending an unsupported action to a control (ex: GetReadOnly of a label) is not considered an error. Instead ActionReturn::NotSupported will be returned.

Just like the events, all Actions/ActionReturn are located under an enum: the Action/ActionReturn enums.

Note 1: The existing actions are stable and will most likely not change. Obviously, more will be added.

Note 2: An Action/ActionReturn size is limited to 16 bytes. If the data contained is bigger than this, it must be boxed. Boxed actions have helpers to not reduce the code readability.

Listening for events

Once your setup is completed, you must call nwg::dispatch_events in order to block the thread and process the system events. The function will return once a Quit event is received.