Native Windows GUI: Controls

Controls are UI elements that can be interacted with. Native windows GUI wraps over 25 different controls in a safe rust-friendly interface. This section explains the basics of using NWG controls.

To learn how to use controls with native-windows-derive see the derive section

Control builder

Controls are created using a builder API. On each control type, the method builder can be called in order to instantiate a builder object. Builder names use the following format: [ControlName]Builder. Ex: (ButtonBuilder).

Each control documentation enumerates the properties accepted by the builder.

use native_windows_gui as nwg;
fn build_button(button: &mut nwg::Button, window: &nwg::Window, font: &nwg::Font) {
    nwg::Button::builder()
        .text("Hello")
        .flags(nwg::ButtonFlags::VISIBLE | nwg::ButtonFlags::CHECK)
        .font(Some(font))
        .parent(window)
        .build(button);
}

Control methods

NWG wraps almost every controls functionalities behind object methods. Fonctionnalities usually come in get/set pair. The syntax used is [property]() for getters and set_[property](value) for setters. Example:

fn test(button: &Button) {
    button.visible();
    button.set_visible(true);
}

Methods of NWG never takes a mutable borrow. This is because most methods maps directy to a single winapi call. And because the application data will almost certaintly be behind a Rc, having to put each control behind a refcell would be HELL.

Every NWG controls implements the Default trait. A control created from this is marked as uninitialized. Calling the methods of an uninitialized control will cause a panic.

Window / Container controls

Most controls in NWG cannot hold children. Those that can are called "container controls". This includes Window, Tab Container, and Tab. TabContainer should only contains tab controls.

Container controls can also use layouts. More on that in the layout section

The window control is be the base of any GUI application. It represents a top level system window. Every NWG application will have a window. This also includes system tray applications. In this case, the window should be a MessageWindow, an invisible window that only dispatch messages.

Window controls are highly customizable. System decorated window (aka normal window), popup window, and transparent window can be created by selecting the right WindowFlags.

nwg::Window::builder()
    .flags(nwg::WindowFlags::WINDOW | nwg::WindowFlags::VISIBLE)
    .size((300, 115))
    .position((300, 300))
    .title("Basic example")
    .build(&mut data.window)?;

Controls hierarchy

All non-window control require a parent. If no parent is defined when building a new control, the build function will return a NwgError::ControlCreationError.

Control events

Controls can raise Events when interacted with. The events that can be raised by a control are in the their documentation.

All controls have specific events (ex: OnButtonClick) and generic events that are shared (ex: OnPaint).

More on this in the next section

Implementation

To keep compile time low, most controls are feature-gated.

Every control is implemented as a wrapper over a low level window handle. As such, most* of them only take the size of a pointer.

Some built-in controls lacks critical feature on their own. To fix this, nwg extends those controls with raw events handlers.

Also, controls that support custom collections (ex: ListBox and ComboBox) also uses Rust collections internally to store the rust data.

Freeing

Controls implement Drop and are freed when they go out of scope.