-
Notifications
You must be signed in to change notification settings - Fork 1
Graphics
Go-gui exposes two paths to custom rendering: DrawCanvas for immediate-mode procedural
2D drawing, and a set of container-level visual effects (gradients, shadows, blur, color
filters, custom GPU shaders). The SVG widget sits between the two — it is a widget like
any other, but it renders full vector assets through a built-in pipeline.
gui.DrawCanvas is a fixed-size drawing surface with a func(*DrawContext) callback that
fires every frame the canvas is visible. The framework tessellates geometry on the CPU and
uploads batched vertex buffers to the GPU.
type DrawCanvasCfg struct {
ID string
Version uint64 // increment to force a redraw
Width float32
Height float32
Color gui.Color // background fill
Radius float32 // corner radius
Clip bool // clip children to rounded rect
Sizing gui.SizingMode
IDFocus uint32
OnDraw func(dc *gui.DrawContext)
// ... event callbacks (OnClick, OnKeyDown, OnGesture, …)
}Version is a cache key. When Version is unchanged from the previous frame the
framework reuses the cached vertex buffer. Increment it whenever the drawing data changes:
gui.DrawCanvas(gui.DrawCanvasCfg{
ID: "my-canvas",
Version: uint64(app.Tick), // drives redraw from state
Width: 480,
Height: 300,
Color: gui.RGBA(20, 20, 30, 255),
Radius: 8,
Clip: true,
OnDraw: func(dc *gui.DrawContext) {
// draw crosshair guides
guide := gui.RGBA(80, 80, 100, 255)
dc.DashedLine(0, app.MarkerY, dc.Width, app.MarkerY, guide, 1, 6, 4)
dc.DashedLine(app.MarkerX, 0, app.MarkerX, dc.Height, guide, 1, 6, 4)
// draw marker
dc.FilledCircle(app.MarkerX, app.MarkerY, 10, gui.RGBA(255, 200, 80, 255))
dc.Circle(app.MarkerX, app.MarkerY, 10, gui.White, 1.5)
},
})The DrawContext also exposes dc.Width and dc.Height — the canvas dimensions after
padding — so drawing code doesn't need to hard-code sizes.
All coordinates are in canvas-local pixels (origin = top-left of the padded area).
| Method | Description |
|---|---|
FilledRect(x, y, w, h, color) |
Solid filled rectangle |
Line(x0, y0, x1, y1, color, width) |
Single stroked line segment |
Polyline(points, color, width) |
Stroked open path; points is []float32{x0,y0,x1,y1,...}
|
FilledPolygon(points, color) |
Filled closed polygon |
Circle(cx, cy, r, color, width) |
Stroked circle |
FilledCircle(cx, cy, r, color) |
Solid filled circle |
Arc(cx, cy, rx, ry, start, sweep, color, width) |
Stroked elliptic arc; angles in radians |
FilledArc(cx, cy, rx, ry, start, sweep, color) |
Filled arc sector |
CubicBezier(x0,y0, c1x,c1y, c2x,c2y, x1,y1, color, width) |
Stroked cubic Bézier |
DashedLine(x0, y0, x1, y1, color, width, dashLen, gapLen) |
Dashed line segment |
Text(x, y, text, style) |
Text at top-left (x, y) using gui.TextStyle
|
TextWidth(text, style) float32 |
Measured text width |
FontHeight(style) float32 |
Line height for the style |
Image(x, y, w, h, src, bgOpacity, bgColor) |
Stretch image to rect; src is a file path |
gui.Svg renders an SVG asset into a fixed-size widget. The framework parses SVG, applies
CSS cascading, tessellates curves, and uploads to the GPU — all without a browser engine.
gui.Svg(gui.SvgCfg{
ID: "logo",
Width: 100,
Height: 100,
Sizing: gui.FixedFixed,
SvgData: `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="40" fill="#3b82f6" opacity="0.8"/>
<circle cx="50" cy="50" r="10" fill="#ec4899"/>
</svg>`,
})Authoring tips:
- Use
fill="currentColor"so the widget'sColorconfig tints monochrome assets at render time. - Omit
width/heightfrom the root element and rely onviewBox— the layout decides the display size. - CSS
@keyframesanimations,var()custom properties, andcalc()are all supported.
The SVG pipeline is documented in the source at gui/svg/. The widget is also used by
gui.SvgSpinner for the 106 built-in spinner animations.
Set Gradient (or BorderGradient) on any ContainerCfg. The framework renders content
into an FBO and composites the gradient over it.
gui.Column(gui.ContainerCfg{
Width: 200,
Height: 80,
Sizing: gui.FixedFixed,
Radius: gui.SomeF(8),
Gradient: &gui.GradientDef{
Direction: gui.GradientToRight,
Stops: []gui.GradientStop{
{Pos: 0, Color: gui.ColorFromString("#3b82f6")},
{Pos: 1, Color: gui.ColorFromString("#8b5cf6")},
},
},
Content: headerViews,
})Linear gradient directions:
| Constant | Direction |
|---|---|
GradientToRight |
Left → right |
GradientToLeft |
Right → left |
GradientToBottom |
Top → bottom |
GradientToTop |
Bottom → top |
GradientToTopRight |
Diagonal |
GradientToBottomRight |
Diagonal |
GradientToBottomLeft |
Diagonal |
GradientToTopLeft |
Diagonal |
For a radial gradient, set Type: gui.GradientRadial (direction is ignored):
Gradient: &gui.GradientDef{
Type: gui.GradientRadial,
Stops: []gui.GradientStop{
{Pos: 0, Color: gui.RGBA(255, 200, 80, 255)},
{Pos: 1, Color: gui.RGBA(30, 30, 40, 0)},
},
},BorderGradient works the same way but applies to the border stroke instead of the fill.
Set Shadow on any container to paint a drop shadow behind it.
gui.Column(gui.ContainerCfg{
Width: 200,
Height: 80,
Sizing: gui.FixedFixed,
Color: t.ColorBackground,
Radius: gui.SomeF(10),
Shadow: &gui.BoxShadow{
Color: gui.RGBA(0, 0, 0, 55),
OffsetX: 0,
OffsetY: 10,
BlurRadius: 22,
},
Content: cardViews,
})The shadow renders outside the container's bounds; no extra padding is needed in the
parent. Colored shadows (e.g. RGBA(80, 120, 255, 85)) produce glow effects.
BlurRadius float32 on ContainerCfg renders the container's content into an off-screen
buffer and applies a Gaussian blur before compositing. Used for frosted-glass backgrounds
and bloom glows:
gui.Column(gui.ContainerCfg{
Width: 100,
Height: 100,
Sizing: gui.FixedFixed,
Color: gui.RGBA(0, 255, 128, 255),
BlurRadius: 15,
ColorFilter: gui.ColorFilterBrightness(1.3),
Radius: gui.SomeF(50),
Content: []gui.View{label},
})Combining BlurRadius with ColorFilterBrightness replicates a bloom glow.
ColorFilter *ColorFilter on ContainerCfg applies a 4×4 color matrix transform as a
post-processing pass on the container's rendered content.
| Constructor | Effect |
|---|---|
gui.ColorFilterGrayscale() |
Desaturate to greyscale |
gui.ColorFilterSepia() |
Warm sepia tone |
gui.ColorFilterContrast(v) |
Scale contrast; 1.0 = unchanged |
gui.ColorFilterBrightness(v) |
Scale brightness; 1.0 = unchanged |
gui.Column(gui.ContainerCfg{
Width: 120,
Sizing: gui.FixedFit,
Color: t.ColorPanel,
ColorFilter: gui.ColorFilterGrayscale(),
Content: photoViews,
})Multiple effects can be combined by layering containers — an outer blur with an inner contrast filter, for instance.
Shader *Shader on ContainerCfg replaces the backend's default fragment shader for
that container. Provide both Metal (macOS/iOS) and GLSL (other backends) source
strings. Pass uniform values via Params []float32.
The shader receives these built-in inputs:
-
uv— normalised texture coordinates (0,0 to 1,1) -
p0–p3—Params[0..15]packed as four vec4s -
tex— the container's rendered content before the shader
Use gui.BuildGLSLFragment(body string) string to wrap a GLSL body with the standard
preamble (version, uniforms, coordinate unpacking):
shader := &gui.Shader{
GLSL: gui.BuildGLSLFragment(`
float t = p0.x;
vec2 st = uv * 0.5 + 0.5;
vec3 c = 0.5 + 0.5 * cos(t + st.xyx + vec3(0, 2, 4));
frag_color = vec4(c, 1.0);
`),
Metal: `
float t = in.p0.x;
float2 st = in.uv * 0.5 + 0.5;
float3 c = 0.5 + 0.5 * cos(t + st.xyx + float3(0, 2, 4));
frag_color = float4(c, 1.0);
`,
Params: []float32{elapsed},
}
elapsed := float32(time.Since(startTime).Milliseconds()) / 1000.0
gui.Column(gui.ContainerCfg{
Width: 200,
Height: 200,
Sizing: gui.FixedFixed,
Radius: gui.SomeF(16),
Shader: shader,
})To keep elapsed updating every frame, drive it from a repeating Animate added via
w.QueueCommand (see the custom_shader example at examples/custom_shader/).
The w.Markdown() widget (see Display Widgets) supports two
extensions beyond standard CommonMark.
Inline $...$ and display $$...$$ math is rendered via the codecogs API. Math
expressions are cached by hash — identical equations fetch once. Blocked TeX commands
prevent server-side command execution.
w.Markdown(gui.MarkdownCfg{
Text: "# Heat equation\n\n$$\\frac{\\partial u}{\\partial t} "
+ "= \\alpha \\nabla^2 u$$",
MathEnabled: true,
})Fenced code blocks with mermaid language tag are rendered as PNG diagrams via the Kroki
API. Multiple diagrams fetch concurrently (up to 8 at a time, 30 s timeout, 10 MiB
response cap). Each diagram renders asynchronously — a placeholder displays while it
loads.
w.Markdown(gui.MarkdownCfg{
Text: "```mermaid\ngraph TD;\n A-->B;\n B-->C;\n```",
MermaidEnabled: true,
})Enable both for full extended Markdown support with MathEnabled and MermaidEnabled
on MarkdownCfg.
Getting Started
Widgets
Layout & Interaction
Visuals
Reference