Components

Components can be used to easily compose widgets out of already existing ones without having to write your own Widget implementation.

They look similar to Applications but can be used as seperate widgets.

See the following code snippet for a counter component:

pub struct Counter {
    counter: ArcSignal<i32>,
    layout: MaybeSignal<LayoutStyle>,
}

impl Counter {
    pub fn new(counter: ArcSignal<i32>) -> Composed<Self> {
        Counter {
            counter,
            layout: LayoutStyle::default().into(),
        }
        .compose()
    }
}

impl Component for Counter {
    fn build(&self, context: AppContext) -> impl Widget + 'static {
        let counter = self.counter.clone();

        Container::new(vec![
            {
                let counter = counter.clone();

                Box::new(
                    Button::new(Text::new("Increase".to_string())).with_on_pressed(
                        EvalSignal::new(move || {
                            counter.set(*counter.get() + 1);
                            Update::DRAW
                        })
                        .hook(&context)
                        .maybe(),
                    ),
                )
            },
            {
                let counter = counter.clone();

                Box::new(
                    Button::new(Text::new("Decrease".to_string())).with_on_pressed(
                        EvalSignal::new(move || {
                            counter.set(*counter.get() - 1);
                            Update::DRAW
                        })
                        .hook(&context)
                        .maybe(),
                    ),
                )
            },
            Box::new(Text::new(
                MaybeSignal::signal(counter).map(|i| Ref::Owned(i.to_string())),
            )),
        ])
        .with_layout_style(self.layout.get().clone())
    }

    fn widget_id(&self) -> WidgetId {
        WidgetId::new("my-example", "Counter")
    }
}

impl WidgetLayoutExt for Counter {
    fn set_layout_style(&mut self, layout_style: impl Into<MaybeSignal<LayoutStyle>>) {
        self.layout = layout_style.into();
    }
}

Notice that the Counter::new method returns a Composed type which is a wrapper to turn the component into an actual widget. It's recommended to always return Composed<...> for cleaner code usage.

Composed also allows you to call methods of the inner component using deref coercion which is why Counter::new(...).with_layout_style(...) is valid code, even though it doesn't return a Counter instance.

See the component example for full usage.