The widget macro reference

There are quite a lot of examples where the widget macro is used in this book. Yet, we haven't covered everything in the previous chapters and having all the information in one place is nice, too.

Property names

The widget macros uses setter methods of gtk4-rs. You can find them at the gtk4-rs docs.

Many properties are also part of a trait. Make sure that this trait is in scope. In many cases you need to use gtk::prelude::TraitName.

For example, if you want to use the set_default_width method of the GtkWindowExt trait you need to use gtk::prelude::GtkWindowExt.

Trait disambiguation

Sometimes you use several traits that implement the same method for a type so you need to tell Rust which trait it should use. For example the set_child function is implemented by both gtk::prelude::GtkWindowExt and libadwaita::traits::ApplicationWindowExt. If we use the regular syntax, the Rust compiler will get confused and tells us to specify the trait. So instead we use the TraitName::method syntax that's similar to Rust's fully qualified syntax for trait disambiguation.

use libadwaita::traits::ApplicationWindowExt;
ApplicationWindowExt::set_child = Some(&gtk::Box) { ... }

You can also use the full path of the trait.

libadwaita::traits::ApplicationWindowExt::set_child = Some(&gtk::Box) { ... }

Public widgets

If you want to make the widgets struct generated by the macro public, you can simply use pub as an attribute for the macro.

#[relm4_macros::widget(pub)]

Assign properties

Initialize a property with a value:

property_name: value,

Initialize an optional property only if it's Some and ignore if it's none:

property_name?: value,

Initialize a property that has multiple properties:

property_name: args!(value1, value2, ...),

Initialize and automatically update a property:

property_name: watch!(value1, value2, ...),

Initialize and automatically update a property with a tracker. The track_expression can be any expression that returns a bool. If it's true, it indicates, that the property should be updated:

property_name: track!(track_expression, value1, value2, ...),

Initialize a property by iterating over an iterator. You can use this for repeated calls to setter functions, like add_class_name in case you have multiple class names in a Vec.

property_name: iterate!(iterator),

Add widgets

Without name:

property_name = gtk::Box { ... }

A common mistake is to accidentally use : instead of = for assigning widgets.

With name:

property_name: name = gtk::Box { ... }

As reference:

property_name = &gtk::Box { ... }

As Option:

property_name = Some(gtk::Box) { ... }

As reference in an Option:

property_name = Some(&gtk::Box) { ... }

Pass additional arguments with the widget. This will call widget.property_name(box_widget, value1, value2, ...) and can be used to call attach on a gtk::Grid for example.

property_name(value1, value2, ...) = gtk::Box { ... }
property_name(value1, value2, ...): name = gtk::Box { ... }

The type of the widget created in all the examples above will always be gtk::Box. However, some properties are set with references or references in Options where this syntax becomes handy.

Functions

Sometimes there's no default implementation for a widget, so you need a constructor or you want to pass a function that returns the widget.

If the function is associated with a type, you can simply use this syntax. The macro will assume the type of gtk::Box::new() is gtk::Box:

property_name = gtk::Box::new() { ... }

For some functions, the macro can't guess the type or might even assume a wrong type. In such a case, add the type your function:

property_name = new_box() -> gtk::Box { ... }

Connecting events

When connecting events you can clone elements you need in the closure by putting it into the parentheses.

connect_name(cloned_var1, cloned_var2, ...) => move |arg1, arg2, ...| { ... }

Connecting to components

For connecting events directly to components you need to use brackets. In the brackets you can create new sender variables from the senders of your components.

connect_name[sender1 = components.name1.sender(), 
    sender2 = components.name2.sender(), ...] => move |arg1, arg2, ...|

The send macro

The send macro simply provides some syntactical sugar. This code

send!(sender, AppMsg::Increment)

expands to this code:

sender.send(AppMsg::Increment).expect("Receiver was dropped!")

The unwrap is save because send errors should never happen, especially because Relm4 usually keeps the receiver alive.

Factories

property_name = gtk::Box { 
    factory!(model.data)
}

Manual

Sometimes the macro isn't flexible enough. In this case, you can always use manual code that will not be modified by the macro.

Here's a list of all the options available.

#[relm4_macros::widget]
impl Widgets<AppModel, ()> for AppWidgets {
    view! {
        // ...
    }

    additional_fields! {
        // ...
    }

    fn pre_init() {
        // ...
    }

    fn post_init() {
        // ...
    }

    fn manual_view() {
        // ...
    }
}

Run custom initialization code

You can use the pre_init function inside the widgets macro to run code before the initialization of the view macro starts. This is useful if you want to generate values you later use in the view macro.

fn pre_init() {
    // ...
}

You can use the post_init function to run code after the initialization of the view macro. This can be used to modify the widgets generated by the view macro for manual initialization. All variables and widget names used in the view macro and the pre_init function can still be used here.

fn post_init() {
    // ...
}

Add more fields to your widgets

The widgets struct is automatically generated by the macro, but sometimes you want to add your own fields. To do so, use the additional_fields! macro:

additional_fields! {
    test: u8,
}

To initialize the variable, you can use either pre_init or post_init. Simply name a local variable like your custom field. I this case we could simply do this:

fn post_init() {
    let test = 0;
}

The macro will then put all parts together to create the widgets struct and the init_view function.

struct AppWidgets {
    ...
    test: u8,
}

impl Widgets<AppModel, ()> for AppWidgets {
    ...
    fn init_view(model: &AppModel, parent_widgets: &(), sender: Sender<AppMsg>) -> Self {
        ...
        let test = 0;
        AppWidgets {
            ...
            test,
        }
    }
    ...
}

Manual view

You can also implement your own view logic that's added to the view code the view macro generates. To refer to the widgets, use self and model for the model.

fn manual_view() {
    // ...
}