Canvas
The paint-based designer surface — center tab “Designer”. Every element you add is rendered here in real time, with selection chrome, layout bounds, rulers, and the document area outline.
Table of contents
Why paint-based
The canvas does NOT use s&box’s runtime Razor renderer. It uses Editor.Paint (Qt-backed) to draw the document directly. Reasons (see PRD 15 for the long version):
- Zero hot-reload latency between edits (no engine compile)
- Selection chrome (handles, outline) renders over content without being overwritten by the runtime
- Predictable layout — the
SuiLayoutSolveris canonical, used by both canvas and generator - Custom overlays (rulers, grid, anchor markers, layout bounds) integrate naturally
The downside: the canvas has its own implementation of layout + image rendering, which can drift from the runtime. Several recent fixes (anchor-aware drag, rgba color parsing, image border-radius clipping) closed those gaps.
Coordinate systems
- Logical — pixels in the document’s drawable area (
0..PanelSize, e.g.0..1920) - Widget — pixels of the canvas widget’s local rect (Paint API native space)
Conversion functions: LogicalToWidget(Vector2) and WidgetToLogical(Vector2). Apply Paint.Translate + Paint.Scale then call Paint.ResetTransform() when switching to widget-pixel chrome (handles, rulers).
Selection
- Click any element → selects it (replaces selection)
- Shift+click → adds to selection
- Click on empty canvas → clears selection; starts a marquee drag-rectangle (releases to multi-select everything that intersects)
- Shift+click empty + drag → additive marquee (keeps existing selection)
Drag & resize
When an element is selected and its parent is Absolute:
- Drag the element body → moves it (
SuiMoveElementCommand). Math is anchor-aware: BottomCenter, TopRight, Stretch — drag-down always moves the element down on screen regardless of anchor. - Drag a handle (8 handles: corners + midpoints) → resizes (
SuiResizeElementCommand). HoldCtrlfor aspect-ratio lock on corner handles. - Shift while dragging → constrain to dominant axis (snap to horizontal-only or vertical-only)
When the parent is Flex, elements are flow-positioned — the canvas hides drag/resize handles. Edit the element’s order in the Hierarchy panel instead.
Pan & zoom
- Middle-mouse drag → pan
- Alt+left drag → pan (Unity / UMG convention)
- Mouse wheel → zoom (anchored at cursor)
Ctrl+0→ fit document to viewport
The zoom level and pan offset persist with the document (Settings.CanvasZoom, CanvasPanX, CanvasPanY).
Overlays
Toggle from the mini-toolbar (or the document Settings popover):
- Rulers — pixel rulers along the top + left edges, adapt to zoom
- Grid — dot grid at
Settings.GridSizeinterval (default 8 px). Auto-hides at low zoom - Layout Bounds — dashed outline around every element. ~12% alpha so they read as a hint not chrome
- Anchors — Quad Pin marker on the selected element’s anchor point. Stretch variants show a bracket + corner pins
- Safe Area — dashed rect inside the panel rect indicating “safe” content area for variable resolutions
- Responsive Debug — adds a top-right banner with detected layout issues (negative sizes, off-screen elements, oversized text, etc.)
- Widget Info — small pill on each element showing
name W×H
Status bar
Single line at the bottom of the canvas:
ElementName · Type · WxH @ X,Y · Anchor: …
Multi-selection: "N elements selected".
Empty selection: "Nothing selected".
Reference
- Source:
Editor/Widgets/SuiCanvasWidget.cs(mouse + hit-test) - Source:
Editor/Canvas/SuiCanvasViewport.cs(paint + overlays + zoom/pan) - Source:
Editor/Canvas/SuiCanvasRenderer.cs(per-element paint) - Source:
Editor/Canvas/SuiLayoutSolver.cs(layout math)