Native Windows GUI: Layouts

A layout resizes and moves the children controls of a Window control

Native windows GUI includes 2 type of layouts: BoxLayout and GridLayout.
To learn how to use layouts with native-windows-derive see the derive section

Basic usage

Just like most NWG objects, layouts are created using a builder api. A layout uses 1 parent control and 0 or more children control. For children or for the parent, the layout only accepts window-like controls (valid: buttons, edit, etc. not valid: menu, timer, notice, etc).

Layouts implement default. A default layout must first be initialized with a builder. Trying to call methods on default builders will cause a panic.

Layouts resize their children automatically when the parent control is resized. This also triggers a OnResize event.

GridLayout and flexbox have a similar builder API. With some exceptions when defining children.

Layouts uses interior mutability, as such it's always possible to edit their properties or their children

Gridlayout

The GridLayout splits the parent control into an equally spaced grid. Children are placed in that grid using a [row, column] index. Optionally, children controls can span across more than one row/column.

Children in a gridlayout can be added using the child method or the child_item method. The first one is a simpler interface when the children control do not span across multiple cells. child_item is used to specify the row span and the column span of the children.

A gridlayout can be manually resized with the resize method. Also, if one of the layout properties was updates, the fit method can be used to show the change and refit the children in the parent.

Lastly, check out the grid layout docs for the layout properties.

A grid layout example:
let grid = nwg::GridLayout::default();
let window: &nwg::Window = /*...*/;
let item1: &nwg::Button = /*...*/;
let item2: &nwg::Label = /*...*/;

nwg::GridLayout::builder()
    .parent(&window)
    .max_row(Some(6))
    .spacing(5)
    .margin([0,0,0,0])
    .child(0, 0, &item1)
    .child_item(nwg::GridLayoutItem::new(&item2, 1, 0, 2, 1))
    .build(&grid);


FlexboxLayout

Flexbox layout uses stretch to align children controls in a row or in column.

Internally, nwg controls (both the parent and the children) are associated with a stretch node/style. See the flexbox layout docs for the style methods.

At building time, a new children can be added to the layout using the child method. Following this method, any child_* method call will customize the style of this child. Another call to child will finalize the current child style

A flexbox layout example:
use native_windows_gui as nwg;

// native_windows_gui exports stretch
use nwg::stretch::{geometry::{Size, Rect}, style::{Dimension as D, FlexDirection}};

const FIFTY_PC: D = D::Percent(0.5);
const PT_10: D = D::Points(10.0);
const PT_5: D = D::Points(5.0);
const PADDING: Rect<D> = Rect{ start: PT_10, end: PT_10, top: PT_10, bottom: PT_10 };
const MARGIN: Rect<D> = Rect{ start: PT_5, end: PT_5, top: PT_5, bottom: PT_5 };

fn build_layout(window: &nwg::Window, item1: &nwg::Button, item2: &nwg::Button) {
    let layout = nwg::FlexboxLayout::default();

    nwg::FlexboxLayout::builder()
        .parent(&window)
        .flex_direction(FlexDirection::Row)
        .padding(PADDING)
        .child(item1)
            .child_margin(MARGIN)
            .child_max_size(Size { width: D::Points(200.0), height: D::Undefined })
            .child_size(Size { width: FIFTY_PC, height: D::Auto })
        .child(item2)
            .child_margin(MARGIN)
            .child_size(Size { width: D::Percent(0.25), height: FIFTY_PC })
        .build(&layout);

    layout
}

Flexbox with sub layouts:
use native_windows_gui as nwg;

// native_windows_gui exports stretch
use nwg::stretch::{
    geometry::Size,
    style::{Dimension as D, FlexDirection, JustifyContent},
};

const PC_50: D = D::Percent(0.5);
const PT_50: D = D::Points(50.0);

fn build_complex_layout(
    window: &nwg::Window,
    label_a: &nwg::Label,
    label_b: &nwg::Label,
    textbox_a: &nwg::TextBox,
    box_a: &nwg::Button,
    box_b: &nwg::Button,
) {
    let top_layout = Default::default();
    let middle_layout = Default::default();
    let bottom_layout = Default::default();

    let full_layout = Default::default();

    nwg::FlexboxLayout::builder()
        .parent(window)
        .flex_direction(FlexDirection::Column)
        .justify_content(JustifyContent::Center)
        .child(label_a)
            .child_size(Size { width: PC_50, height: PT_50 })
        .child(textbox_a)
            .child_flex_grow(1.0)
        .build_partial(&top_layout)
        .unwrap();

    nwg::FlexboxLayout::builder()
        .parent(window)
        .flex_direction(FlexDirection::Row)
        .child(box_a)
            .child_size(Size { width: PT_50, height: PT_50 })
        .child(box_b)
            .child_size(Size { width: PT_50, height: PT_50 })
        .build_partial(&middle_layout)
        .unwrap();

    nwg::FlexboxLayout::builder()
        .parent(window)
        .child(label_b)
            .child_size(Size { width: D::Percent(1.0), height: PT_50 })
        .build_partial(&bottom_layout)
        .unwrap();

    nwg::FlexboxLayout::builder()
        .parent(window)
        .flex_direction(FlexDirection::Column)
        .child_layout(&top_layout)
        .child_layout(&middle_layout)
            .child_flex_grow(1.0)
        .child_layout(&bottom_layout)
        .build(&full_layout)
        .unwrap();
}


DynLayout

DynLayout layout works like Dynamic Layout in MFC.

It uses ratios for moving and sizing a control horizontally and vertically, when its parent Window is resized.

Application can define the ratios and call fit function to move and resize a control. An example is to add a control with preferred ratio in OnInit and call the fit method in OnResize method to adjust the position and size of the control.

The ratios are in percents (0, 100), 0 means not to move or resize a control, 100 means to move or resize a control a hundre percent accordingly to Window size.

A dynamic layout example:
use native_windows_gui as nwg;
use native_windows_derive as nwd;

use nwd::{NwgUi, NwgPartial};
use nwg::NativeUi;

#[derive(Default, NwgUi)]
pub struct Application {
    #[nwg_control(size: (500, 400), position: (300, 300), title: "DynLayout")]
    #[nwg_events(OnInit: [Application::init], OnResize: [Application::size])]
    window: nwg::Window,
	
    #[nwg_layout(parent: window)]
    layout: nwg::DynLayout,

    #[nwg_control(text: "Ok", position: (120, 350), size: (100, 25))]
    ok_btn: nwg::Button,	
}

mpl Application {
    fn init(&self) {
        self.layout.add_child((0, 100), (0, 0), &self.ok_btn);

        self.layout.fit();
    }

    fn size(&self) {
        self.layout.fit();
    }
}

Examples