Skip to content

UI 9-Slice Demo

apps/ui_nineslice_demo shows the retained UI drawing a textured 9-slice panel driven entirely from data (engine PLM-139 / PLM-140, ADR 0038). Two panels of very different sizes reference the same image by path, and a sidecar next to that image marks it nine_slice with a 24px border — so both frames keep the same crisp corners while their edges and centers stretch to fit.

  • Image by path — each frame is a type = "panel" with image = "textures/panel.png" (a mounted PNG), not a bare integer texture handle. The engine resolves the path to a texture on the first render, through the PhysicsFS sandbox.
  • A .ui.component sidecartextures/panel.png.ui.component sits next to the PNG and carries the slicing metadata (nine_slice = true, border = [24, 24, 24, 24]), authored once with the asset. Both .ui elements pick it up automatically just by referencing the image.
  • 9-slice scaling — a small frame (160×120) and a large frame (500×400) share that one image. The four corners stay their fixed pixel size; the edges stretch on one axis and the center stretches both. Compare the two frames: the rounded corners are identical, only the middle grows.

The layout lives in apps/ui_nineslice_demo/ui/main.ui: a full-window background panel plus two child panels — frameSmall and frameLarge — that each set image = "textures/panel.png". Neither .ui element repeats the border; the sidecar supplies it. See the Ui API for the full field reference.

# textures/panel.png.ui.component — slicing authored once, with the image.
nine_slice = true
border = [24, 24, 24, 24] # L, R, T, B in texture pixels
# ui/main.ui — both frames reference the same PNG by path.
[[element]]
id = "frameSmall"
type = "panel"
parent = "bg"
image = "textures/panel.png"
[[element]]
id = "frameLarge"
type = "panel"
parent = "bg"
image = "textures/panel.png"
import "engine" for Engine, Logger, Window, Ui
class Game {
construct new() { _doc = null }
init() {
_doc = Ui.load("ui/main.ui")
if (_doc == null) Logger.error("failed to load ui/main.ui")
}
draw() {
if (_doc != null) Ui.render(_doc, 0, 0, Window.getWidth(), Window.getHeight())
}
quit() {}
}

The demo auto-captures ui_nineslice_demo.png after ~1.5 s so the render can be verified headlessly.

See the Ui API for the image field, 9-slice scaling, and the .ui.component sidecar, and the UI Demo for the retained-UI basics.