Canvas
Canvas is Urushi’s finite, renderer-neutral drawing surface. Use it when the
result depends on signed cell coordinates, overlap, clipping, sparse cells, or
connected line geometry.
Canvas is one kind of View. It can appear inside rows, columns, blocks, CLI
output, prompts, or full-screen applications.
The current Canvas is a cell-space surface: each integer coordinate addresses one terminal cell. It does not provide sampled, pixel-like, Braille, or other subcell drawing. Use the documented cell commands and line networks; sampled and subcell drawing are not implemented.
See what Canvas draws
Section titled “See what Canvas draws”Run the complete Canvas quickstart ↓
Choose the right drawing model
Section titled “Choose the right drawing model”| Need | Start with |
|---|---|
| Text and boxes in normal horizontal or vertical flow | Layout |
| A list, table, tree, or scrollbar with reusable meaning | Components |
Content at explicit (x, y) positions |
Canvas |
| Overlapping layers or custom cell composition | Canvas |
| Lines, polylines, rectangles, or connected junctions | Canvas |
These models compose. A component produces a View; a Canvas can place that
View at a coordinate; and the resulting Canvas remains an ordinary View.
How Canvas resolves and draws
Section titled “How Canvas resolves and draws”Canvas separates immutable frame data from drawing after layout:
- The surrounding
Viewlayout chooses a finite Canvas size. - Canvas calls each owned
CanvasItemonce, in insertion order. - Each item records commands into a frame-scoped
CanvasContext. - Urushi rasterizes, composes, and clips those commands into the settled rectangle.
An item can read CanvasContext::size() while it records commands. It therefore
sees the final size and can place content relative to the right or bottom edge.
Items do not measure the Canvas.
Coordinates and clipping
Section titled “Coordinates and clipping”Position::new(x, y) is relative to the Canvas’s top-left cell:
The origin is the top-left cell. Positive x moves right and positive y moves down; the highlighted cell is at (3, 2).
Negative coordinates are valid. Commands outside the finite surface are clipped rather than shifting the Canvas or increasing its size.
Drawing order and composition
Section titled “Drawing order and composition”Items and their commands draw in insertion order. Later commands combine with
existing cells using a Composition rule:
Replacewrites a complete cell;Overlaychanges only supplied symbol or style fields; andCustomcalls an application function for each affected cell.
view defaults to Replace. Text, paths, line networks, and sparse cells
default to Overlay.
Boundary with layout and components
Section titled “Boundary with layout and components”Rows, columns, grids, blocks, and viewports measure and place children. Canvas does not infer positions from its items or derive an intrinsic size from them. Application-defined items require a finite parent allocation or fallback extent.
Canvas also does not carry the meaning of a list, table, tree, or scrollbar.
Keep that meaning in a Component and place its resulting View into Canvas
when it needs explicit coordinates.
Quickstart
Section titled “Quickstart”This example draws a finite status panel and places text inside it:
cargo new canvas-democd canvas-democargo add urushiReplace src/main.rs with:
use std::io;
use urushi::{ Canvas, CanvasContext, CanvasItem, Grapheme, Position, Size, TextStyle, View,};
#[derive(Debug, Clone, PartialEq)]struct StatusPanel;
impl CanvasItem for StatusPanel { fn draw(&self, canvas: &mut CanvasContext) { canvas.rectangle( Position::new(0, 0), 9, 3, Grapheme::new("·"), TextStyle::new(), ); canvas.text( Position::new(2, 1), "ready", TextStyle::new().bold(), ); }}
fn main() -> io::Result<()> { let view = View::canvas( Canvas::new() .extent(Size::new(9, 3)) .item(StatusPanel), );
urushi::println_view(&view)}Run it with cargo run:
Rendered output
·········
· ready ·
·········
extent supplies a fallback size because standard output does not provide a
finite height. CanvasItem::draw receives the settled size and records signed
coordinate commands. View::canvas makes the result composable with every
other Urushi View.