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 Application
s 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.