#[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.