Type Canvas

type Canvas struct {

	// path and triangulation scratch survives Reset: reused every paint.

	// spans is what an engine writes a selection into. The engine is an interface, so
	// a caller's own buffer passed to it would escape to the heap on every paint.

	// fits remembers the lines FitLine cut; a different measurement empties it.

	// The focus-scope depth each overlay claim was made at. A claim from a
	// deeper scope (a popup inside a dialog) wins over an outer one however
	// late the outer overlay finishes painting; equal depths let the later
	// claim win, which is painting order for sibling overlays.

	// clicked is the control whose click the event pass is delivering; listens
	// records that the last paint asked about clicks at all.

	// pointer is the pointer event the event pass is delivering; consumed records
	// that a control took it; listensForPointer that the last paint asked.

	// focusRequest asks the runtime to focus one hit even without a trap, as a
	// non-modal popover does. The runtime honors a given request once, so
	// painting it every frame never pulls focus back after the user leaves.

	// survives Reset; zero is referenceScale

	// motion survives Reset: transitions span many paint passes.

}

Canvas retains paint primitives and hit regions in deterministic back-to-front order.

Methods

AfterPaint#

func (canvas *Canvas) AfterPaint(fn func())

AfterPaint defers fn until the event pass has returned: the runtime calls it on the UI thread, outside any paint, and repaints afterwards, so the repaint shows what fn changed. It only takes effect in the event pass of a click and is ignored in an ordinary paint, so a read-only paint never schedules work. Use it when the handler of a click may block or open a native dialog, which must not run while a paint is in progress:

if id, ok := canvas.ClickTarget(); ok {
	canvas.AfterPaint(func() { model.Click(id) })
}

The functions run in the order they were deferred, once. A function that calls AfterPaint itself is ignored, because it no longer runs inside a paint. Nothing is allocated until a function is deferred.

AllTextRuns#

func (canvas *Canvas) AllTextRuns() []TextRun

AllTextRuns returns every run of the current paint pass; Text.RunFirst and Text.RunCount index into it. The slice is valid until the canvas is reset.

Animate#

func (canvas *Canvas) Animate(id ID, slot uint8, target float32, motion Motion) float32

Animate returns the displayed value of one animated scalar. Components pass the value their state asks for and paint with what comes back: a toggle passes 0 or 1 and positions its thumb with the result. A change of target starts a transition from the value currently shown, so reversing mid-flight is continuous. ID zero (static previews), a missing frame clock, reduced motion, and a zero duration all return target unchanged.

Slot distinguishes several animated values owned by one hit ID. State is kept per canvas, allocation-free once warm, and dropped as soon as a frame does not paint the widget.

Animating#

func (canvas *Canvas) Animating() bool

Animating reports whether the last frame left a transition in flight. The runtime must paint another frame; when it is false the canvas is at rest and nothing needs to be scheduled.

BeginEmbedded#

func (canvas *Canvas) BeginEmbedded(parent *Canvas, mount Scope)

BeginEmbedded prepares a canvas that is reused every frame to paint a view that parent will embed at mount. It discards the previous frame, hands the view the hover, press and focus of parent as its own identities, shares parent's identity tree, text measurer, clock and reduced-motion setting, and opens an animated paint pass (end it with EndFrame before Embed).

BeginEventPass#

func (canvas *Canvas) BeginEventPass(id ID)

BeginEventPass makes the next paint the event pass of a click on id: Clicked(id) is true in it. Pair it with EndEventPass around that one paint. The runtime does this; an application only calls Clicked.

BeginFrame#

func (canvas *Canvas) BeginFrame()

BeginFrame opens one animated paint pass. Runtimes call it once before the paint callback. The clock is read at most once per frame, and only when a widget actually animates, so every transition in the frame shares one time.

BeginPointerPass#

func (canvas *Canvas) BeginPointerPass(event PointerEvent)

BeginPointerPass makes the next paint the event pass of a pointer event.

BeginPresence#

func (canvas *Canvas) BeginPresence(id ID, slot uint8, presence Presence, pivot Point)

BeginPresence starts the group of primitives that make up one overlay (or one part of it, such as its backdrop) and gives it an entrance and an exit. id names the overlay and slot distinguishes the parts of one overlay that move differently; pivot is the point the overlay grows from. Everything the canvas paints until the matching EndPresence belongs to the group.

While the application keeps painting the group, it fades and moves in from its first frame, and a frame later is simply there. When the application stops painting it, the canvas keeps drawing the last frame it painted, fading and moving it out, until it is gone: the application needs no state for a leaving overlay, and the leaving overlay is only a picture. It has no hit regions, no focus scope, no Escape target, so it never takes a press or the keyboard from what is under it. If the application paints the group again while it leaves, it turns around from where it is.

This is the one place the canvas keeps something its application no longer paints ("state follows what is painted"). It holds a copy of the group's last frame while the group is open, and drops it when the exit ends.

A group does not animate, and BeginPresence costs a stack push, with ID zero (a static preview), without a clock, outside BeginFrame and EndFrame, under reduced motion, and for a presence without a duration or without effect: the overlay is then just painted and just gone. Images are moved but not faded, for a Bitmap has no opacity.

Block#

func (canvas *Canvas) Block(bounds Rect)

Block declares an opaque region, such as the surface of a dialog or menu. Pointer presses inside it hit nothing declared before it, so a full-window dismiss target underneath a panel is not triggered by clicking the panel's padding. Hits declared after Block, like the panel's own buttons, still win.

Border#

func (canvas *Canvas) Border(bounds Rect, width float32, border, fill Color)

Border appends an outer border and inset fill.

CellSize#

func (canvas *Canvas) CellSize(size float32) (width, height float32)

CellSize is the size of one character cell of the fixed-pitch font at size, in logical pixels. The height is a whole number of pixels so rows of cells tile without seams.

ClickTarget#

func (canvas *Canvas) ClickTarget() (ID, bool)

ClickTarget returns the control whose click this paint is delivering, and false in every ordinary paint. It is Clicked for a central dispatcher: an application that keeps one handler for all of its controls reads the target once, at the start of its paint, before anything is drawn, and hands it on:

if id, ok := canvas.ClickTarget(); ok {
	model.Click(id) // runs once per click, before this paint draws
}

A component or a screen that owns its controls asks Clicked(id) of each one instead; use ClickTarget only where a single switch over identities is the application's design (and in the development reload host, which forwards the event pass to the process that runs the application). A handler that may block or open a native dialog is run through AfterPaint. Asking marks the paint as listening for clicks, exactly as Clicked does, so the runtime schedules the event pass for the clicks that follow.

Clicked#

func (canvas *Canvas) Clicked(id ID) bool

Clicked reports whether id is the control whose click this paint is delivering. It is true for one control, in one pass: when a click completes, the runtime paints once more in the event pass, and only there may a paint change the application's state. Every other paint is read-only and Clicked is false in it.

if components.BeginButton(canvas, theme, save, bounds).Default("Save").Clicked() {
	model.save() // runs once per click
}

Asking costs a comparison. A paint that never asks makes the event pass unnecessary, and the runtime skips it.

ClickedByKey#

func (canvas *Canvas) ClickedByKey(id ID) bool

ClickedByKey is Clicked for a click that came from the keyboard: Enter (or Space) on the focused control, not a press and release of the pointer. The pointer's click is delivered while the control is still held down (Pressed is its ID); the keyboard's is not. A text field uses it to tell "Enter was pressed in me" from "I was clicked".

ClickedIn#

func (canvas *Canvas) ClickedIn(list Scope) (int, bool)

ClickedIn reports which item of the list scope this paint is delivering a click on, and false in every other paint or when the click was elsewhere. It is Clicked for a whole list at once, for the component that numbers its own rows.

Clip#

func (canvas *Canvas) Clip(bounds Rect)

Clip restricts painting to bounds until the matching Unclip call. Clips nest; the effective region is the intersection with enclosing clips.

Clips#

func (canvas *Canvas) Clips() []Clip

Clips returns the retained clip stack. The result is invalidated by the next Reset.

Consume#

func (canvas *Canvas) Consume()

Consume says the control took the pointer event being delivered: the runtime does not also treat it as a click, and repaints. It does nothing outside the event pass.

Container#

func (canvas *Canvas) Container(bounds Rect, role Role, label string)

Container declares a semantic region that is not a control: a menu, menubar, listbox, grid, heading or status area. It has no target, takes no press, and appears in the accessibility tree with the given role and label.

ContainerWithOptions#

func (canvas *Canvas) ContainerWithOptions(bounds Rect, role Role, label string, options HitOptions)

ContainerWithOptions is Container with the accessibility detail of a region: the Level of a heading, the Live setting of a status area, a Description. Other options have no meaning for a region that takes no press.

DeliverPointer#

func (canvas *Canvas) DeliverPointer(event PointerEvent, size Size, paint func(*Canvas, Size)) bool

DeliverPointer runs paint as the event pass of a pointer event and reports whether a control consumed it. It is what the runtime does for a window, for a test or a tool that drives a paint function without one:

if canvas.DeliverPointer(event, size, state.Paint) { ... }

It does nothing, and reports false, when the last paint never asked PointerOn or PointerIn.

DrawBackdrop#

func (canvas *Canvas) DrawBackdrop(bitmap Bitmap, source, bounds Rect)

DrawBackdrop appends a source region of bitmap sampled into bounds beneath everything else the frame paints: every rectangle, text and image of every layer, so that translucent surfaces let it show through. It is not clipped and belongs to no layer. At most one backdrop is meaningful; with several, they stack in the order given. Invalid or empty bitmaps are ignored.

DrawBitmap#

func (canvas *Canvas) DrawBitmap(bitmap Bitmap, bounds Rect)

DrawBitmap appends a complete bitmap draw. Invalid or empty bitmaps are ignored. Pixels are sampled linearly into bounds.

DrawBitmapRegion#

func (canvas *Canvas) DrawBitmapRegion(bitmap Bitmap, source, bounds Rect)

DrawBitmapRegion appends a source region of bitmap sampled into bounds. Source is clipped to the bitmap without allocating or copying pixels.

DrawIcon#

func (canvas *Canvas) DrawIcon(icon Icon, bounds Rect, tint Color)

DrawIcon paints one icon tinted with a single color, scaled uniformly to fit bounds and centered. The icon strokes scale with the icon, but its size and origin are rounded to whole device pixels and a stroke up to half a device pixel (see SetPixelScale), never thinner than one: a 16 point Lucide icon draws a 1.33 point stroke, which whole-pixel rounding turned into a single hairline at 100% but 1.5 points at 200%, so the same icon looked a third fainter on a 1x display.

A multi-color icon paints each of its layers in the layer's own color; the tint's alpha fades the whole glyph and its color is not used.

DrawPath#

func (canvas *Canvas) DrawPath(path Path, bounds Rect, style PathStyle)

DrawPath tessellates immutable path geometry into retained triangles. Fills support concave contours and holes under both fill rules; strokes tessellate into non-overlapping outlines so translucent stroke colors composite without seams.

Elevate#

func (canvas *Canvas) Elevate(bounds Rect, radius float32, level ShadowLevel)

Elevate appends the shadow of a level of the elevation scale, one or two layers cast by the rounded rectangle bounds. Draw it before the surface.

Embed#

func (canvas *Canvas) Embed(child *Canvas, origin Point, mount Scope)

Embed appends everything child painted: translated by origin, with hit IDs and focus scopes mounted at mount, on top of canvas's current layer and inside canvas's current clip. Hit regions are clipped like any other declaration, so a view scrolled partly out of sight keeps only the part that can be reached.

A view's modal state does not carry over: its focus trap, Escape target and initial focus stay on the child, where the host can read them (TrappedFocusScope, EscapeTarget, InitialFocus) and adopt them with TrapFocus and SetEscapeTarget, mounted with Tree.MountID, if the view should be modal for the whole host. Adopting by default would let one open dialog among many previews lock the rest of the page. A child still animating asks canvas for another frame.

EndEventPass#

func (canvas *Canvas) EndEventPass()

EndEventPass returns the canvas to ordinary, read-only painting.

EndFrame#

func (canvas *Canvas) EndFrame()

EndFrame closes the paint pass opened by BeginFrame and forgets transitions whose widgets were not painted.

EndPointerPass#

func (canvas *Canvas) EndPointerPass() bool

EndPointerPass ends the pointer event pass and reports whether a control consumed the event.

EndPresence#

func (canvas *Canvas) EndPresence()

EndPresence closes the group opened by BeginPresence. A group that is still entering is faded and moved here, in place, after its last primitive.

EscapeTarget#

func (canvas *Canvas) EscapeTarget() ID

EscapeTarget returns the application-owned dismiss action ID.

Fill#

func (canvas *Canvas) Fill(bounds Rect, color Color)

Fill appends a solid rectangle. On a canvas that knows the display scale (SetPixelScale) its edges land on whole device pixels, so a panel or a divider is not a blur of two.

FillLinearGradient#

func (canvas *Canvas) FillLinearGradient(bounds Rect, gradient LinearGradient)

FillLinearGradient fills a rectangle with a two-color gradient. Start and End use logical canvas coordinates, just like bounds. Colors outside that segment are held at its endpoints; a zero-length segment uses To. Shaded mesh vertices reuse the existing native, software and web renderers without a bitmap cache.

FitLine#

func (canvas *Canvas) FitLine(line string, size, weight, width float32) string

FitLine is line when it fits width at font size size and weight (TextStyle.Weight), else its start ending in an ellipsis, cut as WrapLines cuts the last line; nothing when width leaves no room. It is the one-line text of a table cell or a list row, and draws as one Label.

A cut line is remembered by the canvas, so painting the same cell at the same width again, frame after frame, builds no string: only the first paint of a line, or one that another line of the same slot replaced, allocates. Lines longer than 512 bytes are cut afresh every time.

FocusRequest#

func (canvas *Canvas) FocusRequest() ID

FocusRequest returns the non-modal focus request, or zero.

FocusVisible#

func (canvas *Canvas) FocusVisible() ID

Focused#

func (canvas *Canvas) Focused() ID

Focused returns the hit ID holding keyboard focus, or the zero ID.

Hit#

func (canvas *Canvas) Hit(id ID, bounds Rect)

Hit declares a pointer interaction region in paint order. Later hits win over earlier ones at the same point.

HitLabel#

func (canvas *Canvas) HitLabel(control ID, bounds Rect, text string, disabled bool)

HitLabel declares bounds as a label of the control with that identity, as an HTML `<label for>` is: pressing it focuses the control and clicks it (a checkbox or a switch toggles, a text field takes the keyboard, and Clicked reports the control), and it is hovered and pressed as the control is. It is not a tab stop and no node of its own; text, the label's words, names the control for assistive technology in place of the control's own label. A disabled label does nothing, as a disabled control does. Declare it over the text that is painted, after or before the control: only the control's own hit is focusable, so the keyboard visits it once.

canvas.HitLabel(subscribe, titleBounds, "Subscribe to updates", false)

HitWithLabel#

func (canvas *Canvas) HitWithLabel(id ID, bounds Rect, label string)

HitWithLabel declares an interaction region with an accessibility name.

HitWithOptions#

func (canvas *Canvas) HitWithOptions(id ID, bounds Rect, label string, options HitOptions)

HitWithOptions declares an interaction region with typed semantic state. The current focus scope and compositing layer are captured at declaration time.

Hits#

func (canvas *Canvas) Hits() []Hit

Hits returns the retained hit regions. The result is invalidated by the next Reset.

Hovered#

func (canvas *Canvas) Hovered() ID

Hovered returns the hit ID currently under the pointer, or the zero ID.

Images#

func (canvas *Canvas) Images() []Image

Images returns the retained bitmap paint list.

InitialFocus#

func (canvas *Canvas) InitialFocus() ID

InitialFocus returns the hit named by SetInitialFocus, or zero.

Label#

func (canvas *Canvas) Label(value string, bounds Rect, style TextStyle)

Label appends an immutable system-font line, aligned to the start of its text (AlignStart): the left edge, or the right edge for Hebrew, Arabic and other right-to-left text.

LabelAligned#

func (canvas *Canvas) LabelAligned(value string, bounds Rect, style TextStyle, alignment Alignment)

LabelAligned appends a system-font line with explicit horizontal placement. Placement uses the layout width of TextWidthWeight: the platform draws the line from there with its own advances, so a drawn line is off-center by half of the difference between the two (a pixel or so). A right-to-left line is drawn in the visual order UAX #9 gives it.

A TextStyle.Direction that differs from the one the text has by itself is carried to the platform as an invisible LRM or RLM in front of the line, which the engines take as the paragraph direction too; the text of such a line is then not the caller's string, and costs one allocation.

LabelCenter#

func (canvas *Canvas) LabelCenter(value string, bounds Rect, style TextStyle)

LabelCenter appends a horizontally centered system-font line.

LabelOrigin#

func (canvas *Canvas) LabelOrigin(value string, bounds Rect, style TextStyle, alignment Alignment) float32

LabelOrigin is the x at which LabelAligned starts the line value in bounds: the left end of the line as drawn. The caret, selection and composition of a text field are placed from it with TextCaret and TextSelection.

LabelRuns#

func (canvas *Canvas) LabelRuns(value string, bounds Rect, style TextStyle, cellWidth float32, runs []TextRun)

LabelRuns appends one fixed-pitch line: value drawn in the system fixed-pitch font with every character on a grid of cellWidth, each run of it in its own color and weight. A terminal row is one such line, however many colors it holds, so the number and order of labels stays stable while its content changes. Bounds is the whole line; its width should be columns*cellWidth. runs must cover value (see TextRun); the canvas copies them, so the caller may reuse the slice as soon as the call returns.

ListensForClicks#

func (canvas *Canvas) ListensForClicks() bool

ListensForClicks reports whether the last paint asked Clicked about anything: whether a click needs the event pass.

ListensForPointer#

func (canvas *Canvas) ListensForPointer() bool

ListensForPointer reports whether the last paint asked PointerOn or PointerIn: whether a pointer event needs the event pass.

MonoCellSize#

func (canvas *Canvas) MonoCellSize(size float32, face MonoFace) (width, height float32)

MonoCellSize is the cell size of face at size: 0.6 of the size wide and 1.18 high on every platform, whatever the advance and line box of the font that draws the characters (the platforms place each character on this grid, so a terminal has the same columns and rows in the same window everywhere).

MoveRegion#

func (canvas *Canvas) MoveRegion(bounds Rect)

MoveRegion declares a place a custom-titlebar window is moved from: pressing and dragging it moves the window, and a double click zooms it. It is inert as Block is, with no ID, hover, focus or accessibility node, and it lies where it was declared in paint order: a control declared after it, such as a button of a tab bar laid over it, takes its own presses, and so does the surface of a dialog above it. An overlay's backdrop above it still receives a press (to dismiss the overlay), and the window then moves once the pointer has dragged. A window with no move region is moved from nowhere.

NextFrame#

func (canvas *Canvas) NextFrame() (delay time.Duration, ok bool)

NextFrame says when the runtime should paint again: after a zero delay, paced to the display, while a transition is in flight, after the requested delay when only RequestFrameIn asked, and not at all (ok false) when the canvas is at rest.

Now#

func (canvas *Canvas) Now() time.Duration

Now returns the monotonic time of the current frame, sampling the clock if no widget has yet. Painters that run transitions elsewhere, such as the development reload host, forward it to the process that does.

PixelScale#

func (canvas *Canvas) PixelScale() float32

PixelScale is the device pixels per logical pixel the canvas paints for.

PointerIn#

func (canvas *Canvas) PointerIn(list Scope) (int, PointerEvent, bool)

PointerIn is PointerOn for a whole list: the item number the event was aimed at.

PointerOn#

func (canvas *Canvas) PointerOn(id ID) (PointerEvent, bool)

PointerOn returns the pointer event this paint is delivering to id, and false in every other paint or when the event was aimed elsewhere. It is how a control reads a right click, a double click or a drag where it is declared:

if event, ok := canvas.PointerOn(row); ok && event.Button == ui.PointerButtonSecondary {
	menu.OpenAt(event.X, event.Y)
	canvas.Consume()
}

Like Clicked, it is answered only in the event pass, and a paint that never asks makes the pass unnecessary. Pointer moves that no press captured are not delivered this way: hover is Hovered.

PopFocusScope#

func (canvas *Canvas) PopFocusScope()

PopFocusScope closes the innermost focus scope.

PopLayer#

func (canvas *Canvas) PopLayer()

PopLayer closes the innermost compositing layer.

Pressed#

func (canvas *Canvas) Pressed() ID

Pressed returns the hit ID currently held down, or the zero ID.

PushFocusScope#

func (canvas *Canvas) PushFocusScope(scope ID)

PushFocusScope assigns subsequent hits to a stable component scope.

PushLayer#

func (canvas *Canvas) PushLayer(layer uint16)

PushLayer assigns subsequent paint primitives to a higher compositing layer. Layer zero is normal content; overlays should use monotonically increasing stable IDs and paint after their owning content.

Rectangles#

func (canvas *Canvas) Rectangles() []Rectangle

Rectangles returns the retained rectangle paint list.

ReduceMotion#

func (canvas *Canvas) ReduceMotion() bool

ReduceMotion reports whether transitions are suppressed.

RequestFocus#

func (canvas *Canvas) RequestFocus(id ID)

RequestFocus asks the runtime to focus id even when no focus trap is active, as a non-modal popover does. The runtime applies one request once; painting the same request every frame does not pull focus back after the user leaves.

RequestFrame#

func (canvas *Canvas) RequestFrame()

RequestFrame marks the current frame as unfinished for painters that run their transitions elsewhere, such as a development reload host mirroring a worker canvas. The runtime paints again at display cadence until a frame stops asking.

RequestFrameIn#

func (canvas *Canvas) RequestFrameIn(delay time.Duration)

RequestFrameIn asks for one more paint after at least delay, for a display that changes slowly: a spinner that steps every 125 ms, a pulse, a clock. Painting at display cadence for those would redraw a hundred times for every visible change. Like RequestFrame it has to be repeated by every frame that still needs the next one; a frame that does not ask leaves the application idle. A delay of zero or less is RequestFrame. When several are made in one frame the soonest wins.

Reset#

func (canvas *Canvas) Reset()

Reset discards all retained primitives while keeping the backing arrays for the next paint pass.

Rounded#

func (canvas *Canvas) Rounded(bounds Rect, radius float32, color Color)

Rounded appends a solid rounded rectangle, its edges snapped as Fill's are.

RoundedBorder#

func (canvas *Canvas) RoundedBorder(bounds Rect, radius, width float32, border, fill Color)

RoundedBorder appends a rounded border and inset fill. On a canvas that knows the display scale the outer edge lands on whole device pixels and the border is a whole number of them, the same on all four sides and at least one: a 1 point border at 150% is one pixel, not a pixel and a half blurred over two, as a browser draws it.

RunAfterPaint#

func (canvas *Canvas) RunAfterPaint()

RunAfterPaint runs and clears the functions deferred by AfterPaint. The runtime calls it once the event pass has ended; an application never does.

ScrollX#

func (canvas *Canvas) ScrollX() float32

ScrollX returns the horizontal scroll offset of the active scroll region.

ScrollY#

func (canvas *Canvas) ScrollY() float32

ScrollY returns the vertical scroll offset of the active scroll region.

SetCellMeasurer#

func (canvas *Canvas) SetCellMeasurer(measure func(size float32, face MonoFace) (width, height float32))

SetCellMeasurer replaces the reference fixed-pitch cell (0.6 of the size wide, 1.18 high) with a function returning the advance of one character cell and the height of one line at a size; nil restores it. Like SetMeasurer it is for tests and tools.

SetClock#

func (canvas *Canvas) SetClock(clock func() time.Duration)

SetClock installs the monotonic time source transitions are measured on. Without a clock, or outside BeginFrame and EndFrame, Animate is a pass-through and every component paints its final state, which keeps tests, static previews, and runtimes without a frame loop deterministic.

SetEscapeTarget#

func (canvas *Canvas) SetEscapeTarget(id ID)

SetEscapeTarget routes Escape to one explicit application-owned action ID. The deepest claim wins, as TrapFocus does, so an open popup owns Escape even when the dialog around it finishes painting later.

SetInitialFocus#

func (canvas *Canvas) SetInitialFocus(id ID)

SetInitialFocus names the hit that receives keyboard focus when the trapped scope opens, which is how a modal moves focus inside itself. It has no effect while the scope stays open, so painting it every frame never steals focus back from the user.

SetInteraction#

func (canvas *Canvas) SetInteraction(hovered, pressed, focused ID, focusVisible bool)

SetInteraction records the runtime pointer and focus state before a paint pass so components can render hover, pressed, and focus feedback. It is runtime-facing; application paint code should only read it.

SetMeasurer#

func (canvas *Canvas) SetMeasurer(measure func(value string, size, weight float32) float32)

SetMeasurer replaces the canvas's text measurement with a width function, in logical pixels, for a font size and weight; nil restores it. It takes precedence over the TextEngine and the reference metrics for every text query. An application does not need it: a backend installs the platform's engine (SetTextEngine). It exists for tests and tools that want exact, simple widths, such as one em per character.

SetPixelScale#

func (canvas *Canvas) SetPixelScale(scale float32)

SetPixelScale tells the canvas how many device pixels one logical pixel is on the display it paints for (1.25 at 125%), so that what must land on whole pixels, such as the edges of an icon, does at that scale, not only at 2x; an icon's stroke snaps up to half a device pixel at that scale. Zero is unspecified and restores the reference scale. It survives Reset.

SetReduceMotion#

func (canvas *Canvas) SetReduceMotion(reduce bool)

SetReduceMotion makes every transition jump straight to its target, honoring the operating system's reduced-motion accessibility preference.

SetScroll#

func (canvas *Canvas) SetScroll(x, y float32)

SetScroll records the active scroll offset before a paint pass. Positive values mean the viewport moved right/down, revealing content below.

SetTextEngine#

func (canvas *Canvas) SetTextEngine(engine TextEngine)

SetTextEngine installs the engine every text measurement of the canvas goes through; nil restores the reference metrics (internal/textmetrics). A backend installs its platform's engine; tests and tools that want fixed widths leave it unset, or install ReferenceTextEngine.

SetTree#

func (canvas *Canvas) SetTree(tree *Tree)

SetTree makes tree the identity tree of this canvas. An application that declares its identities in package-level variables owns the tree they are made in and gives it to every window's canvas; call it before the first paint.

Shadow#

func (canvas *Canvas) Shadow(bounds Rect, radius float32, style ShadowStyle, color Color)

Shadow appends one soft shadow layer cast by the rounded rectangle bounds. Draw it before the surface it belongs to: the rectangle itself is cut out of the shadow, so only the part that falls outside it is painted, and a shadow without Blur paints nothing. The shadow reaches style.Extent beyond bounds, and the canvas clip cuts it like any other shape.

Shadows#

func (canvas *Canvas) Shadows() []Shadow

Shadows returns the soft shadows painted so far. Each carries how many rectangles and triangles came before it, which is how they are merged with Rectangles and Triangles into paint order. The result is invalidated by Reset.

TextCaret#

func (canvas *Canvas) TextCaret(value string, size, weight float32, offset int) float32

TextCaret is the x, from the left end of the line value, of the caret at byte offset: in left-to-right text the width of what precedes it; in right-to-left text, or at a change of direction, where UAX #9 displays that boundary (the caret of Hebrew moves from the right).

TextOffsetAt#

func (canvas *Canvas) TextOffsetAt(value string, size, weight, x float32) int

TextOffsetAt is the byte offset of the character boundary nearest to x, measured from the left end of the line value: where a click puts the caret.

TextRuns#

func (canvas *Canvas) TextRuns(text Text) []TextRun

TextRuns returns the runs of a text produced by LabelRuns. The slice is valid until the canvas is reset.

TextSelection#

func (canvas *Canvas) TextSelection(dst []TextSpan, value string, size, weight float32, start, end int) []TextSpan

TextSelection appends the extents of the selection [start, end) of value, measured from the left end of the line: one span, or several where the selection crosses a change of direction (the highlight of a selection in mixed Hebrew and English is not contiguous).

TextWidth#

func (canvas *Canvas) TextWidth(value string, size float32) float32

TextWidth measures one regular-weight line. Text drawn in another weight is measured with TextWidthWeight.

TextWidthWeight#

func (canvas *Canvas) TextWidthWeight(value string, size, weight float32) float32

TextWidthWeight measures one line at a weight (TextStyle.Weight): with the platform's TextEngine when the backend installed one, the same shaped line that is drawn, so the width is the glyphs' width; with the reference metrics (internal/textmetrics) otherwise, the same on every platform whatever fonts are installed. A heavier weight is wider; measure a label at the weight it is drawn with.

Texts#

func (canvas *Canvas) Texts() []Text

Texts returns the retained text paint list.

TrapFocus#

func (canvas *Canvas) TrapFocus(scope ID)

TrapFocus restricts hit testing and keyboard traversal to scope. A scope trapped from a deeper focus scope than the last one wins, so a popup painted inside a dialog keeps the trap even though the dialog finishes painting after it; two scopes at the same depth let the later one win, which is painting order for sibling overlays.

TrappedFocusScope#

func (canvas *Canvas) TrappedFocusScope() ID

TrappedFocusScope returns the top-most active focus trap.

Tree#

func (canvas *Canvas) Tree() *Tree

Tree returns the identity tree this canvas names its places in: the one the application gave it with SetTree, or else one of its own. Embedded canvases share their parent's.

Triangles#

func (canvas *Canvas) Triangles() []Triangle

Triangles returns the retained vector-mesh paint list.

Trim#

func (canvas *Canvas) Trim()

Trim releases backing arrays whose capacity has outgrown their content. Reset keeps the arrays for the next paint, which is right for a steady page but wrong after a one-off large one: without Trim, a page with 100,000 hits would keep that capacity for the life of the application. The painter calls it after publishing a frame. An application that owns a Canvas directly may call it after painting, once the previous frame has been handed to the platform; do not call it right after Reset, when the slices are empty, or a large steady page would reallocate every frame. It keeps the current contents, so it is safe between frames.

Unclip#

func (canvas *Canvas) Unclip()

Unclip closes the innermost open clip.

WrapLines#

func (canvas *Canvas) WrapLines(dst []string, value string, size, weight, width float32, maxLines int) []string

WrapLines breaks value into lines no wider than width at font size size and weight (TextStyle.Weight) and appends them to dst, at most maxLines of them.

Where a line may break follows UAX #14 (internal/linebreak): after spaces, hyphens and most punctuation as the standard's pair rules say, between ideographs, kana and emoji with the line-start and line-end rules of East Asian typesetting, between the syllables of the dictionary-less scripts (Thai, Lao, Khmer, Myanmar) at the places their writing allows, and at a newline in the text (a trailing CR is dropped). Two tailorings: Hangul syllables break at spaces as Korean text is set (a Korean word is kept whole when it fits a line), and a word wider than a whole line (a URL, a path, a hash) breaks after the last / \ - _ . & ? = that fits and failing that after the last grapheme cluster (UAX #29) that fits, so a base with its marks, an emoji sequence, a flag, a conjunct is never split.

A line always holds at least one character, however narrow the width. When the text needs more than maxLines, the last line ends with an ellipsis. The lines are slices of value, so wrapping text that fits builds no strings; only the ellipsized last line is new (see WrapLinesCached). Draw the result one Label per line.

lines := canvas.WrapLines(scratch[:0], message, 12, ui.WeightRegular, bounds.Width, 3)
for index, line := range lines {
	canvas.Label(line, ui.Rect{X: bounds.X, Y: bounds.Y + float32(index)*17, Width: bounds.Width, Height: 17}, style)
}

Widths are measured at weight, the one the lines are drawn with: a heavier line is wider.

WrapLinesCached#

func (canvas *Canvas) WrapLinesCached(dst []string, value string, size, weight, width float32, maxLines int, cache *EllipsisCache) []string

WrapLinesCached is WrapLines that takes the ellipsized last line from cache when it is the one wanted, and stores it there when it is not. A nil cache builds the string every time.

WrapRow#

func (canvas *Canvas) WrapRow(value string, from int, size, weight, width float32) (row TextRow, next int)

WrapRow returns the row of value that begins at from, and where the next one begins (-1 when this is the last). from is 0 or the next of a previous call. Rows break where WrapLines breaks lines (see there), at font size size and weight, and at every newline in the text, which belongs to no row: a text ending in a newline ends with an empty row. A width of zero or less never wraps, so each paragraph is a row. Nothing is allocated, so a caller that needs only the rows in view can ask for them one at a time.

WrapRows#

func (canvas *Canvas) WrapRows(dst []TextRow, value string, size, weight, width float32) []TextRow

WrapRows appends the rows of value to dst (see WrapRow). Painting a text one row to a Label, with the rows in hand, is how an editor maps between a byte offset and a place on the screen.