Macro relm4_macros::view

source ·
view!() { /* proc-macro */ }
Expand description

The view! macro allows you to construct your UI easily and cleanly.

It does the same as inside the component attribute macro, but with less features.

You can even use the relm4-macros crate independently from Relm4 to build your GTK4 UI.

use gtk::prelude::{BoxExt, ButtonExt};
use relm4::gtk;

// Creating a box with a button inside.
relm4_macros::view! {
    vbox = gtk::Box {
        gtk::Button {
            set_label: "Click me!",
            connect_clicked => |_| {
                println!("Hello world!");
            }
        },
        prepend: my_label = &gtk::Label::builder()
            .label("The view macro works!")
            .build(),
    }
}

// You can simply use the vbox created in the macro.
let spacing = vbox.spacing();

Also, the macro doesn’t rely on any special gtk4-rs features so you can even use the macro for other purposes.

In this example, we use it to construct a Command.

use std::process::Command;

let path = "/";

relm4_macros::view! {
    mut process = Command::new("ls") {
        args: ["-la"],
        current_dir = mut &String {
            push_str: path,
        },
        env: ("HOME", "/home/relm4"),
    }
}

// Output of "ls -la" at "/"
dbg!(process.output());

Macro expansion

Let’s have a look the this example:

// Creating a box with a button inside.
relm4_macros::view! {
    vbox = gtk::Box {
        gtk::Button {
            set_label: "Click me!",
            connect_clicked => |_| {
                println!("Hello world!");
            }
        },
        prepend: my_label = &gtk::Label::builder()
            .label("The view macro works!")
            .build(),
    }
}

The code generation for this example looks like this (plus comments):


// We've just used `gtk::Box` so we assume it has a `default()` method
let vbox = gtk::Box::default();
// `vbox` was named, yet the button doesn't have an explicit name and gets a generated one instead.
let _gtk_button_5 = gtk::Button::default();
// For the label, we used a manual constructor method, so no `default()` method is required.
let my_label = gtk::Label::builder().label("The view macro works!").build();

// Connect the signal
{
    _gtk_button_5.connect_clicked(|_| {
        println!("Hello world!");
    });
}

// The button was added without any further instructions, so we assume `container_add()` will work.
relm4::RelmContainerExt::container_add(&vbox, &_gtk_button_5);
_gtk_button_5.set_label("Click me!");
// For the label, we used the `prepend` method, so we don't need `container_add()` here.
vbox.prepend(&my_label);

The widgets are first initialized, then signals are connected and then properties and widgets are assigned to each other.

The nested structure of the UI is translated into regular Rust code.