Canvas

Canvas

A drawing surface for freeform 2D rendering using Core Graphics. Canvas accepts a list of declarative drawing commands (shapes, images, text) and renders them with GPU acceleration on macOS.

The canvas is reactive. Calling draw(), append(), or clear() triggers an immediate re-render. Gesture callbacks enable interactive drawing applications such as freehand sketching or annotation tools.

For the full list of drawing commands, see the Draw Module Reference.

Constructor

nib.Canvas(
    width=100,
    height=100,
    commands=None,
    background_color=None,
    enable_gestures=False,
    on_pan_start=None,
    on_pan_update=None,
    on_pan_end=None,
    on_hover=None,
    **modifiers,
)

Parameters

Parameter Type Default Description
width float 100 Width of the canvas drawing area in points.
height float 100 Height of the canvas drawing area in points.
commands list[DrawCommand] None Initial list of drawing commands to render.
background_color str None Background color as a hex string (e.g., "#1a1a1a"). None for transparent.
enable_gestures bool False Enable pan/hover gesture tracking. Automatically enabled when any gesture callback is set.
on_pan_start Callable[[PanEvent], None] None Callback invoked when mouse/pen is pressed down on the canvas.
on_pan_update Callable[[PanEvent], None] None Callback invoked when mouse/pen is dragged across the canvas.
on_pan_end Callable[[PanEvent], None] None Callback invoked when mouse/pen is released.
on_hover Callable[[PanEvent], None] None Callback invoked when the mouse moves over the canvas without pressing.
**modifiers Common view modifiers: padding, opacity, corner_radius, etc.

Mutable Properties

Property Type Description
canvas_width float Get or set the drawing area width. Setting triggers a re-render.
canvas_height float Get or set the drawing area height. Setting triggers a re-render.
background_color str | None Get or set the background color.
commands list[DrawCommand] Get the current list of drawing commands (read-only).
on_pan_start Callable | None Get or set the pan start callback. Setting a callback auto-enables gestures.
on_pan_update Callable | None Get or set the pan update callback.
on_pan_end Callable | None Get or set the pan end callback.
on_hover Callable | None Get or set the hover callback.

Methods

Method Signature Description
draw draw(commands: list[DrawCommand]) Replace all drawing commands and re-render.
append append(command: DrawCommand) Add a single drawing command and re-render.
clear clear() Remove all drawing commands and re-render.

PanEvent

A dataclass passed to gesture callbacks containing the pointer position.

Field Type Description
x float X coordinate of the event in canvas coordinates.
y float Y coordinate of the event in canvas coordinates.

Examples

Basic drawing

canvas_basic_drawing

import nib


def main(app: nib.App):
    app.title = "Canvas"
    app.icon = nib.SFSymbol("paintbrush.pointed")
    app.width = 360
    app.height = 240

    canvas = nib.Canvas(width=320, height=200, background_color="#1c1c1e")
    canvas.draw(
        [
            nib.draw.Rect(
                x=20, y=20, width=120, height=70, corner_radius=10, fill="#0a84ff"
            ),
            nib.draw.Circle(cx=230, cy=60, radius=40, fill="#ff453a"),
            nib.draw.Line(
                x1=20, y1=130, x2=300, y2=130, stroke="#30d158", stroke_width=4
            ),
            nib.draw.Text(
                "Core Graphics, from Python",
                x=160,
                y=155,
                fill="#ffffff",
                alignment=nib.HorizontalAlignment.CENTER,
            ),
        ]
    )

    app.build(nib.VStack(controls=[canvas], padding=16))


nib.run(main)

Freehand drawing with gestures

canvas_freehand_drawing_gestures

import nib


def main(app: nib.App):
    app.title = "Sketch"
    app.icon = nib.SFSymbol("pencil.tip")
    app.width = 360
    app.height = 300

    canvas = nib.Canvas(width=320, height=220, background_color="#ffffff")
    last = None

    def on_pan_start(e):
        nonlocal last
        last = (e.x, e.y)

    def on_pan_update(e):
        nonlocal last
        canvas.append(
            nib.draw.Line(
                x1=last[0], y1=last[1], x2=e.x, y2=e.y, stroke="#1c1c1e", stroke_width=3
            )
        )
        last = (e.x, e.y)

    canvas.on_pan_start = on_pan_start
    canvas.on_pan_update = on_pan_update

    app.build(
        nib.VStack(
            controls=[
                canvas,
                nib.HStack(
                    controls=[
                        nib.Text(
                            "Draw with the mouse", foreground_color=nib.Color.SECONDARY
                        ),
                        nib.Spacer(),
                        nib.Button("Clear", action=canvas.clear),
                    ]
                ),
            ],
            spacing=8,
            padding=16,
        )
    )


nib.run(main)