zng::widget

Attribute Macro ui_node

source
#[ui_node]
Expand description

Expands an impl block into an UiNode trait implementation.

Missing UiNode methods are generated by this macro. The generation is configured in the macro arguments. The arguments can be a single keyword, a delegate or an entire struct declaration.

The general idea is you implement only the methods required by your node and configure this macro to generate the methods that are just boilerplate UI tree propagation, and in new node mode var and event handlers can be inited automatically as well.

§Delegate to single impl UiNode

If your node contains a single child node, you can configure the attribute to delegate the method calls for the child node.

use zng::prelude_wgt::*;

struct MyNode<C> {
    child: C
}
#[ui_node(delegate = &mut self.child)]
impl<C: UiNode> UiNode for MyNode<C> { }

If the child node is in a field named child you can use this shorthand to the same effect:

#[ui_node(child)]
impl<C: UiNode> UiNode for MyNode<C> { }

The generated code simply calls the same UiNode method in the child.

§Delegate to a impl UiNodeList

If your node contains multiple children nodes in a type that implements UiNodeList, you can configure the attribute to delegate to the equivalent list methods.

use zng::prelude_wgt::*;

struct MyNode<L> {
    children: L
}
#[ui_node(delegate_list = &mut self.children)]
impl<L: UiNodeList> UiNode for MyNode<L> { }

If the children list is a member named children you can use this shorthand to the same effect:

#[ui_node(children)]
impl<L: UiNodeList> UiNode for MyNode<L> { }

The generated code simply calls the equivalent UiNodeList method in the list. That is the same method name with the _all prefix. So UiNode::init maps to UiNodeList::init_all and so on.

§Don’t Delegate

If your node does not have any child nodes you can configure the attribute to generate empty missing methods.

#[ui_node(none)]
impl UiNode for MyNode { }

The generated measure and layout code returns the fill size.

The other generated methods are empty.

§Validation

If delegation is configured but no delegation occurs in the manually implemented methods you get the error "auto impl delegates call to `{}` but this manual impl does not".

To disable this error use #[allow_(zng::missing_delegate)] in the method or in the impl block. The error is also not shown if the method body contains a call to the todo!() macro.

The measure method is an exception to this and will not show the error, its ideal implementation is one where the entire sub-tree is skipped from the computation.

§Mixing Methods

You can use the same impl block to define UiNode methods and associated methods by using this attribute in a impl block without trait. The UiNode methods must be tagged with the #[UiNode] pseudo-attribute.

#[ui_node(child)]
impl MyNode {
    fn do_the_thing(&mut self) {
        // ..
    }

    #[UiNode]
    fn init(&mut self) {
        self.child.init();
        self.do_the_thing();
    }

    #[UiNode]
    fn update(&mut self, updates: &WidgetUpdates) {
        self.child.update(updates);
        self.do_the_thing();
    }
}

The above code expands to two impl blocks, one with the associated method and the other with the UiNode implementation.

This is particularly useful for nodes that have a large amount of generic constraints, you just type them once.

§New Node

In all the usage seen so far you must declare the struct type and the generic bounds to make it work in the impl block, and any var or event in it needs to be subscribed manually. You can avoid this extra boilerplate by declaring the node struct as an arg for the macro.

fn my_widget_node(child: impl UiNode, number: impl IntoVar<u32>) -> impl UiNode {
    #[ui_node(struct MyNode {
        child: impl UiNode,
        #[var] number: impl Var<u32>,
    })]
    impl UiNode for MyNode {
        fn update(&mut self, updates: &WidgetUpdates) {
            self.child.update(updates);
            if let Some(n) = self.number.get_new() {
                println!("new number: {n}");
            }
        }
    }
    MyNode {
        child,
        number: number.into_var(),
    }
}

In the example above the MyNode struct is declared with two generic params: T_child and T_var, the unimplemented node methods are delegated to child because of the name, and the number var is subscribed automatically because of the #[var] pseudo attribute.

Note that you can also use node::match_node to declare anonymous nodes, most of the properties are implemented using this node instead of the #[ui_node] macro.

§Generics

You can declare named generics in the struct, those are copied to the implement block, you can also have members with type impl Trait, a named generic is generated for these, the generated name is T_member. You can use named generics in the impl generics the same way as you would in a function.

§Impl Block

The impl block cannot have any generics, they are added automatically, the UiNode for part is optional, like in the delegating mode, if you omit the trait you must annotate each node method with the #[UiNode] pseudo attribute.

§Delegation

Delegation is limited to members named child or children, there is no way to declare a custom delegation in new node mode. If no specially named member is present the none delegation is used.

§Subscription

You can mark members with the #[var] or #[event] pseudo attributes to generate initialization code that subscribes the var or event to the WIDGET context. The init code is placed in a method with signature fn auto_subs(&mut self), if you manually implement the init node method you must call self.auto_subs(); in it, a compile time error is emitted if the call is missing.

§Limitations

The new node type must be private, you cannot set visibility modifiers. The struct cannot have any attribute set on it, but you can have attributes in members, the #[cfg] attribute is copied to generated generics. The impl Trait auto-generics only works for the entire type of a generic, you cannot declare a type Vec<impl Debug> for example.

Expands an impl into a UiNode impl.

§Full Documentation

Read the documentation in the zng::widget::ui_node page.