Skip to content
UrushiDocumentation

Define a custom CanvasItem

Implement CanvasItem when drawing should be reusable or depend on the final Canvas size. Keep the value immutable; record all frame commands in draw.

use std::io;
use urushi::{
Canvas, CanvasContext, CanvasItem, Grapheme, Position, Size, TextStyle,
View,
};
#[derive(Debug, Clone, PartialEq)]
struct BottomRightLabel {
text: String,
}
impl CanvasItem for BottomRightLabel {
fn draw(&self, canvas: &mut CanvasContext) {
let size = canvas.size();
canvas.rectangle(
Position::new(0, 0),
size.width(),
size.height(),
Grapheme::new("·"),
TextStyle::new(),
);
let width = urushi::PrintableText::new(&self.text).width();
let x = size.width().saturating_sub(width) as i64;
let y = size.height().saturating_sub(1) as i64;
canvas.text(
Position::new(x, y),
self.text.clone(),
TextStyle::new().bold(),
);
}
}
fn main() -> io::Result<()> {
let view = View::canvas(
Canvas::new()
.extent(Size::new(12, 4))
.item(BottomRightLabel {
text: "ready".into(),
}),
);
urushi::println_view(&view)
}

Rendered output

············
·          ·
·          ·
·······ready

The item reads the settled 12×4 size, draws its boundary, and derives the label’s bottom-right position from that size. A different parent allocation moves both without changing the item type.

Canvas::item additionally requires the concrete value to implement Clone and PartialEq. Together with the CanvasItem bounds, stored items are Debug + Clone + PartialEq + Send + Sync + 'static. This keeps a complete View cloneable, comparable, and safe to move between runtime owners.

An adapter that owns a concrete item type can inspect those values without fallible downcasting:

let canvas = Canvas::new().item(BottomRightLabel {
text: "ready".into(),
});
for label in canvas.items::<BottomRightLabel>() {
assert_eq!(label.text, "ready");
}
Verification
items::<BottomRightLabel>() yields the stored label with text "ready".

Core layout does not inspect item meaning. items::<T>() is intended for an adapter that owns both the item type and its specialized rendering behavior.

A Canvas item represents immutable frame data, not a nested application. Input handling, animation state, focus, and event subscriptions belong to the caller. Build a new item value when that state changes and return a new View from the next render.