Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commitdd348ac6c3

README.md

_viewport

Pan/zoom viewport helper for canvas-hosted charts. Persists { xMin, xMax, yMin, yMax } in ui.widgetState(plotId, "viewport") so the bounds survive across renders; the first render seeds from defaultBounds. Subsequent renders read the cached state; pan/zoom handlers (viewport:pan, viewport:zoom) mutate the state in place and write it back to widgetState. Underscore prefix marks it as a package-internal helper — not exported from zui.widget and not part of the Z.* surface.

Exports

  • Viewport.make(plotId: string?, rect: Rect?, defaultBounds: Bounds?) -> ViewportInstance — construct a viewport over rect. plotId enables widgetState persistence.
  • vp:dataToScreen(x: number, y: number) -> (number, number) — data → canvas-local pixels.
  • vp:screenToData(sx: number, sy: number) -> (number, number) — canvas-local pixels → data.
  • vp:pan(dxScreen: number, dyScreen: number, axisAllow: AxisFilter?) — pan and write-back.
  • vp:zoom(factor: number, anchorScreen: ScreenPoint?, axisAllow: AxisFilter?) — zoom around anchor and write-back.
  • vp:setBounds(bounds: Bounds) — replace bounds and write-back.
  • Viewport.bounds(vp) -> Bounds — snapshot bounds as a fresh table.

Types:

  • Rect = { x: number, y: number, width: number, height: number } — canvas-local plot body.
  • Bounds = { xMin: number, xMax: number, yMin: number, yMax: number } — data-space.
  • ScreenPoint = { x: number, y: number }
  • AxisFilter = { x: boolean?, y: boolean? }
  • ViewportInstance — instance shape; carries rect, bounds fields, invertX/Y, and the bound methods.

Usage

local Viewport = require("@builtin::modules.zui.widget._viewport")

local vp = Viewport.make("plot1",
    { x = 0, y = 0, width = 200, height = 100 },
    { xMin = 0, xMax = 100, yMin = 0, yMax = 1 })

-- Inside onDrag handler:
vp:pan(dragDx, dragDy)

-- Inside onScroll handler:
vp:zoom(1 + scrollAmount * 0.1, { x = mx, y = my })

-- Reset:
vp:setBounds({ xMin = 0, xMax = 100, yMin = 0, yMax = 1 })

Notes

  • Coordinate convention: origin top-left, +y down. yMax in data space maps to rect.y (top of viewport) so the cursor sits above the data point during pan.
  • Persistence is opt-in — passing plotId = nil skips widgetState reads / writes; bounds live only on the instance for the duration of the call.
  • Caller can reset by clearing widgetState explicitly: ui.widgetStateSet(plotId, "viewport", nil).
  • Viewport.bounds(vp) is a free function (no colon) — it's a thin snapshot helper, useful when you want a fresh bounds table without mutating the instance.
  • invertX / invertY flip the axis mapping in both dataToScreen and screenToData; default false for both.