Native Windows Derive: Controls

The first thing to define in a derive UI are the controls. Native windows derive maps a list of attributes to the builder api. This way, definining the control parameters will only take a single line above the struct member definition.

Also, be sure to check out the controls docs.

Attributes

As mentioned in the controls docs, almost every components of NWG use the same builder based approach. Builders API are easy to understand at the cost of being verbose. In order to keep UI the initialization to a minimum, NWD translates the builder API into a list of

nwg_control(builder_field: builder_value,*)

This has the benefits of keeping boilerplate code to a minimal amount and it also keep the control parameters next to the type definition, making things easier to understand for anyone reading the code.

Example:
#[nwg_control(text: "Heisenberg", size: (280, 25), position: (10, 10))]
name_edit: nwg::TextInput,

// is the same as 

nwg::TextInput::builder()
    .text("Heisenberg")
    .size((280, 25))
    .position((10, 10))
    .build(&mut data.text_edit);

Parent detection

In most circumstances, the controls in a UI will have the same parent; the window. Because nobody wants to copy and paste "parent: window" on each children controls, the derive macro can guess the parent by looking up the fields defined before the control in the UI struct. Derive will choose the first control marked with nwg_control that supports children.

If no parent is defined in the attributes, and no parent can be guessed the parent value will be left empty. The derive macro will not panic, but the resulting code will raise a ControlCreationError("no parent") error.

Here is an example that shows how NWD guess the parents:
#[derive(NwgUi)]
struct MyUi {
    // No auto parent
    label: nwg::Label,

    // No auto parent
    window: nwg::Window,

    // parent: window
    label: nwg::Label,

    // parent: window
    frame: nwg::Frame,

    // parent: frame
    button: nwg::Button
}

Compressed flags

Defining flags can take alot of space. In order to fix this, NWG supports a "compressed" flags format. Where the flag enum is ignored. This only works with the flags parameters because it's possible for NWD to guess the right type. For example, here's the compressed flag definition VS the normal flag definition.

It's also worth noting that there is no need to import NWG in the local scope of your module when using native window values (such as flags definition).
#[derive(NwgUi)]
struct MyUi {
    #[nwg_control(flags: "WINDOW|VISIBLE")]
    window1: nwg::Window,

    #[nwg_control(flags: WindowFlags::WINDOW | WindowFlags::VISIBLE)]
    window1: nwg::Window,
}

Subclassed controls

By default, NWD can guess the control builder by looking up the structure member type. When a user type is used, this is no longer possible. To fix this, it's possible to pass the ty parameter to the nwg_control attribute. ty won't be evaluated as a builder parameter.

When not using ty and still using custom values, NWD will try to instance a custom builder object by calling MyType::builder. The custom builder must work in the same way built-in builders works.

For an example see: the subclassing example.
struct CustomButton1 {
    base: nwg::Button
}

struct CustomButton2 {
    base: nwg::Button
}

struct CustomButton2Builder {
    pub fn build(btn: &mut CustomButton2) -> Result<(), nwg::NwgError> {
        //...
    }
}

subclass_control!(CustomButton1, Button, base);
subclass_control!(CustomButton2, Button, base);

#[derive(NwgUi)]
struct MyUi {
    #[nwg_control]
    window1: nwg::Window,

    #[nwg_control(ty=Button)]
    test_button1: CustomButton1,

    #[nwg_control]
    test_button2: CustomButton2,
}