The component macro reference

There are quite a few of examples where the component macro is used in this book. Still, we haven't covered everything in the previous chapters, and it's also nice to have all the information in one place. This chapter serves as an exhaustive reference for the component macro syntax.

The component attribute macro expects a trait implementation of SimpleComponent, Component, or FactoryComponent on a user-provided struct that holds the component's state.

Public widgets

If you want to make the Widgets struct generated by the macro public, you can provide pub (or any other visibility) as an argument to the attribute macro.

#[relm4::component(pub)]

Nested view! macro

The nested view! macro allows us to easily define widgets and mutate their properties.

Constructing widgets

All components must have a root widget in their view! macro, such as gtk::Window or gtk::Box. Widgets are constructed by providing the type of the widget followed by curly braces. This will construct the widget using its Default implementation.

view! {
    gtk::Window {
        ...
    }
}

Sometimes, there's no Default implementation for a widget, or it may be more convenient to construct it manually. If the function is associated with a type, you can use this syntax:

gtk::Label::new(Some("Label from constructor")),

// ...

gtk::Label::builder()
    .label("Label from builder")
    .selectable(true)
    .build(),

Otherwise, you can provide the type:

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

Child widgets

Child widgets are added by nesting the declarations. This will automatically pass the child widget to set_child on the parent:

view! {
    gtk::Window {
        gtk::Box {

        }
    }
}

If another method is needed to assign a child, you can call it instead like this:

gtk::Box {
    append = &gtk::Label { ... }
}

Use & in front of the widget type to assign a reference.

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

If the widget needs to be wrapped in another type (commonly Option), use the wrap attribute:

#[wrap(Some)]
set_property_name = gtk::Box { ... }

Sometimes you need to pass additional arguments along with the widget, for example when calling gtk::Grid::attach. You can do this by providing the additional arguments in square brackets after the method:

set_property_name[value1, value2, ...] = gtk::Box { ... }

This will expand to parent.set_property_name(box_widget, value1, value2, ...).

Naming widgets

Widgets may be given a name with the name attribute. These names are accessible as fields on the Widgets struct generated by the macro.

#[name = "important_label"]
gtk::Label { ... }

// ...

let widgets = view_output!();
let label = &widgets.important_label;

Names can also be assigned with this syntax:

set_child: important_label = gtk::Label { ... }

Returned widgets

Sometimes, methods used for assigning widgets return another widget. For example, gtk::Stack::add_child() allows you to add a widget to the stack, but also returns a gtk::StackPage widget. To get access to this widget, you can use a special syntax of the view macro:

gtk::Stack {
    add_child = &gtk::Label {
        set_label: "placeholder",
    } -> {
        // Set the title of the stack page
        set_title: "page title",
    }
}

Properties

Properties are initialized and mutated by calling methods within the widget types. Check the documentation for each widget type to see what methods are available. Generally properties are set via setter methods, but any methods on the widget can also be called. Many of these methods are part of an extension trait associated with the widget type. These traits must be in scope to call their methods. For example, if you want to use the set_default_width method from the GtkWindowExt trait, you must import the trait directly or glob import it from the prelude (use gtk::prelude::*;).

To initialize a property with a value:

set_property_name: value,

Initialize a property only if its value is Some, and do nothing if it's None:

set_property_name?: value,

Call a method that has multiple arguments:

set_property_name: (value1, value2, ...),

Initialize and automatically update a property.

#[watch]
set_property_name: (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 method should be called:

#[track(track_expression)]
set_property_name: (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.

#[iterate]
set_property_name: iterator,

Trait disambiguation

It is possible that several traits implement the same method for a type. If both traits are in scope, when using the duplicated method name we need to tell Rust which trait implementation it should use. Otherwise, the Rust compiler will raise an error due to the ambiguity. To specify the intended trait, use the TraitName::method syntax, similar to Rust's fully qualified syntax for trait disambiguation.

You can also use the full path of the trait if desired.

Signals

When connecting signals emitted by widgets you can clone fields that you need in the closure (for example, the component sender) by listing the corresponding fields in square brackets.

connect_name[cloned_var1, cloned_var2, ...] => move |arg1, arg2, ...| { ... }

Manual code

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::component]
impl SimpleComponent for App {
    // ...

    view! {
        // ...
    }

    additional_fields! {
        // ...
    }

    fn pre_view() {
        // ...
    }

    fn post_view() {
        // ...
    }
}

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,
}

Initialize the variable in the init function by naming a local variable like your custom field.

let test = 0;

let widgets = view_output!();

Manual view

You can also implement your own view logic, which will be added to the view code that the view macro generates. Code inside pre_view() will run before the view update, while post_view() will run after.

Code inside these "functions" isn't like a normal function! The macro disallows returning early in pre_view to ensure that the view update will always execute.