Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Methods of the Widget trait

State (child states), Tag and Diff

A widget can have a state or be stateless. A widget state can contain data that should live longer than the widget itself. A widget itself lives quite short. After each view call, it gets recreated. The scrollable, for example, saves the scroll position in its state. That way, the scroll position stays after each view call.

If your widget has a state, you need to implement the tag and state methods; otherwise, you can just use the default implementation. If you reuse other widgets that have a state, you are required to implement the children method.

State method

At best, the state of your widget is created once and will be reused as much as possible. The state method returns the initial state of your widget if a new one needs to be created. It could look like this:

fn state(&self) -> iced::advanced::widget::tree::State {
	iced::advanced::widget::tree::State::new(YourCustomState::default())
}

Tag method

To identify the different states, the Tag is used. The tag method returns a Tag based on your state type. Internally, it uses the TypeId of your state to identify it.

Note: You are right if you think: "If if uses the type ID, can two of my widget states be accidentally swapped?", you are right. They can. For those cases the diff method should fix it.

For most use cases, the tag method will look like this:

fn tag(&self) -> iced::advanced::widget::tree::Tag {
	iced::advanced::widget::tree::Tag::of::<YourCustomState>()
}

If you have a widget that is stateless, you can just use the default implementation from the Widget trait.

diff

This function compares/reconciles the old state Tree, with what you would expect it to be. If there should be something different, you change it in the given tree.
There are two cases where this is needed. The first one is when two states with the same tag are accidentally swapped. In the second one, the state should change based on data passed to the widget in a view call.

If your widget has child widgets, it should contain something like this to diff the children:

fn diff(&self, tree: &mut Tree) {
	tree.diff_children(&self.children);
}

children

If your widget uses other children, you need to return their state Trees from the children method. The order of the child Trees returned determines the order of Tree.children in all other methods.

This method could look like this:

fn children(&self) -> Vec<Tree> {
	vec![Tree::new(&self.your_widget_as_element)]
}

size

The size method returns the size of the widget. That size can be used by other widgets to find a good layout. A good example of this is iced::advanced::layout::flex.

The size method could look like this:

fn size(&self) -> iced::Size<iced::Length> {
    iced::Size::new(iced::Length::Shrink, iced::Length::Fill)
}

layout

The layout method defines the layout of the widget and all of its child widgets. It returns a Node that represents the layout of the widget and all of its children.

To do that, you are given the Limits of the widget, meaning the minimum and maximum size that the widget can get, and the current state Tree.

If you have child widgets, you need to call their layout methods with their state from Tree.children, the Renderer (you can simply use the one that is given as a parameter) and limits you want to assign to the child. The returned Node shall be included in the returned Node.children of your widget.

draw

The draw function uses the given Renderer to draw the widget onto the screen. The renderer can implement different Renderer traits.

With the given viewport, you can know what region of your window is currently visible.

In general, your widget should use the theme provided colors to fit into the application style. The text color is provided by the style parameter.

You can access the position, area and layout of your children via the given Layout parameter. The order of child layouts provided in layout.children() is equal to the order of Node.children returned by the layout method.
To draw child widgets, you can simply call their draw methods with the appropriate state (from Tree.children) and layout (from Layout.children()).

Since you might want to draw some special effect or graphic depending on the mouse position, you can access it if available with the Cursor parameter.

by default, if the cursor position is available if it is over the window

operate

This function applies some given operation. The Operation can have different effects.

You should apply all the appropriate functions of the Operation trait. To do so, you pass your state to the function and an Id that identifies your state. Of course, you should only apply functions that are used. If your widget can not be focused, you should not call the focusable function.

If your widget has a child widget that does some operations, your operate method could look like this:

fn operate(
	&self,
	tree: &mut Tree,
	layout: Layout<'_>,
	renderer: &Renderer,
	operation: &mut dyn Operation,
) {
	operation.container(None, layout.bounds(), &mut |operation| {
		self.your_child_element.as_widget().operate(
			&mut tree.children[0],
			layout.children().next().unwrap(),
			renderer,
			operation,
		);
	});
}

If you are interested in implementing operations for your widget, see the operation section.

on_event

The on_event method processes an Event.

The Cursor parameter provides access to the current mouse position. The Clipboard parameter gives access to the clipboard of the user's system.

This is the only method of your widget that can emit messages to the application. For that, a Shell is provided as a parameter. But the Shell can do other things as well. You can check if the current layout is valid or invalidate it, request redraws, check if the widget tree is valid and invalidate the widget tree, etc.

If you have child widgets that you want to produce messages that are local to your widget, just like a component, you can create a new Shell and give it to them in the on_event method.

The method returns Status::Ignored if neither the widget nor its children have handled the event or Status::Captured else. For easier merging of the Statuses with child widgets, you can use the merge function on the Status and merge to Status into one. That could look this this:

[status1, status2, status3, ...].into_iter().fold(iced::event::Status::Ignored, iced::event::Status::merge)

If you want to do some animations, you can trigger/request redraws with the shell until the animation is over.

mouse interaction

This method returns the current mouse Interaction.
A mouse interaction is mostly how the cursor is displayed. You often see your cursor changes when you are resizing or moving some element on the screen. If your widget, for example, has some area that can be grabbed and moved, you can return the Interaction::Move while the cursor is over that area to change the look of the cursor.

overlay

This method returns the overlay of the widget if there is any. A good example of this can be found in the form of the tooltip widget.

If you have child widgets it could look something like this:

fn overlay<'a>(
    &'a mut self,
    tree: &'a mut Tree,
    layout: Layout<'_>,
    renderer: &Renderer,
    translation: Vector,
) -> Option<overlay::Element<'a, Message, Theme, Renderer>> {
    overlay::from_children(&mut self.children, tree, layout, renderer, translation)
}