Skip to main content
ui.drawing is stateless draw helpers that append to a DefaultCommandList. Surface Description calls them internally. Reach for them when you need drawing outside the node tree:
  • Building custom nodes before upstream helpers exist
  • Drawing in legacy widget draw methods (otter-bar)
  • Composing one-off chrome (monitor.zig, auth_surface.zig)
Every function takes explicit parameters (colors, font, rect). drawing.zig does not look up theme globals. Pass colors from your loaded Theme.

Module map

SubmoduleExports
drawing.corepanel, border, control, listRow, wrappedText
drawing.frameroundedFill, surfacePanel, controlFrame, textBaselineY
drawing.controlsbutton, toggle, checkbox, tabBar, scrollbar, formRow, numberInput, progressBar, colorSwatch, dropdown
drawing.inputinputBox, wrappingInputBox, passwordInputBox
drawing.texttruncateText, textTruncated
drawing.selectSelect trigger rendering
drawing.dropdowndropdownOverlay, hit tests for menu rows
drawing.color_pickercolorPickerPopup, HSV, hit tests
Import from the root re-export:
const ui = @import("otter_ui");
ui.drawing.button(cmds, rect, "OK", hovered, pressed, style, font);

Common parameters

ParameterTypeNotes
cmds*DefaultCommandListCollects draw commands
rectgeo.RectDestination in logical pixels
font*render.FontRequired for text primitives
styleper-function structColors, padding, radius
themepassed by callerNot stored inside drawing
Set cmds.scale from the Wayland surface scale before drawing (Surface Description does this in begin()).

Controls (drawing.controls)

button

ui.drawing.button(cmds, rect, label, hovered, pressed, style, font);
ButtonStyle: background, hover_background, pressed_background, text_color, border_color, radius, padding, font_size, disabled.

toggle

Pill switch. ToggleStyle: on_color, off_color, thumb_color, disabled_color.

checkbox

Box + optional label. CheckboxStyle: box_color, check_color, text_color, font_size.

iconButton

Icon over label stack (logout menu pattern). IconButtonStyle: border, hover, icon_size, padding. Trigger row and floating option list. Pair with dropdownOverlayHitTest for manual hit routing outside Surface Description.

tabBar

Horizontal tabs. TabBarStyle mirrors TabsSpec colors.

scrollbar / roundedScrollbar

Track + thumb from ui.ScrollState. ScrollbarStyle: track/thumb colors, width, radius.

formRow / sectionHeader

Settings rows: fixed label column + value text.

numberInput

Decrement, value field, increment. Returns NumberInputResult with sub-rects. Use numberInputHitTest for three-zone clicks.

progressBar

Track + fill by value_0_100 (0..100 byte percent).

colorSwatch

Small filled square with optional checkerboard for alpha.

Text input (drawing.input)

inputBox

Single-line field with cursor, selection, placeholder, IME preedit underline.
ui.drawing.inputBox(
    cmds, rect,
    text, preedit,
    cursor_byte, sel_start, sel_end,
    placeholder,
    style, font,
);
InputBoxStyle: background, text_color, placeholder_color, cursor_color, selection_color, border_color, font_size, padding, border_width, radius. Helpers:
  • inputBoxCursorFromClick / wrappingInputBoxCursorFromClick
  • wrappingInputBox + wrappingInputBoxHeight for multiline
  • passwordInputBox + maskUtf8 for masked fields

Text (drawing.text)

  • truncateText / textTruncated: ellipsis overflow for narrow rects
  • wrappedText / wrappedTextHeight: multiline word wrap
Pass text_system through cmds when drawing RTL or CJK (same as Surface Description).

Panels and frames (drawing.frame, drawing.core)

  • surfacePanel: raised card with shadow (settings panels)
  • controlFrame: inset background inside a border (inputs, buttons)
  • roundedFill / roundedBorder: low-level rounded rects
  • panel, border, control: composable building blocks
  • listRow: selectable row with optional icon (launcher results)

Color picker (drawing.color_picker)

const hsv = ui.drawing.HSV.fromColor(color);
ui.drawing.colorPickerPopup(cmds, rect, hsv, alpha, style, font);
colorPickerHitTest returns which sub-region (SV, hue, alpha) was clicked. colorPickerUpdate maps drag position to new color.

When to use primitives vs nodes

Use SurfaceNodeUse drawing.* directly
App windows and formsBar widget draw specs
Anything needing hitTest / dispatchFully custom render loops
Settings, monitor, lock UImonitor.zig graph/table helpers
Prefer Surface Description for new code. Primitives remain the implementation layer and escape hatch.
  • ui.table: sortable tables for monitor-style UIs (uses drawing + direct cmds)
  • ui.monitor: sidebar, graphs, search box built on primitives
  • ui.auth_surface: lock/greeter shared auth layout
  • ui.overlay_chrome: loupe/crosshair for screenshot tools

Next