-
Notifications
You must be signed in to change notification settings - Fork 1
Getting Started
This page walks through the minimal go-gui application line by line. By the end you'll understand the window lifecycle, the view function, how state flows, and how widgets are composed.
Prerequisites: Installation complete, go run ./examples/get_started/ works.
package main
import (
"fmt"
"github.com/go-gui-org/go-gui/gui"
"github.com/go-gui-org/go-gui/gui/backend"
)
type App struct{ Clicks int }
func main() {
gui.SetTheme(gui.ThemeDarkBordered)
w := gui.NewWindow(gui.WindowCfg{
State: &App{},
Title: "get_started",
Width: 300,
Height: 300,
OnInit: func(w *gui.Window) { w.UpdateView(mainView) },
})
backend.Run(w)
}
func mainView(w *gui.Window) gui.View {
ww, wh := w.WindowSize()
app := gui.State[App](w)
return gui.Column(gui.ContainerCfg{
Width: float32(ww),
Height: float32(wh),
Sizing: gui.FixedFixed,
HAlign: gui.HAlignCenter,
VAlign: gui.VAlignMiddle,
Content: []gui.View{
gui.Text(gui.TextCfg{
Text: "Hello GUI!",
TextStyle: gui.CurrentTheme().B1,
}),
gui.Button(gui.ButtonCfg{
IDFocus: 1,
Content: []gui.View{
gui.Text(gui.TextCfg{
Text: fmt.Sprintf("%d Clicks", app.Clicks),
}),
},
OnClick: func(_ *gui.Layout, e *gui.Event, w *gui.Window) {
gui.State[App](w).Clicks++
e.IsHandled = true
},
}),
},
})
}type App struct{ Clicks int }This is plain Go. No framework base type, no special interface, no observable fields. Go-gui doesn't care what your state type looks like — it just stores a pointer to it and hands it back to you.
gui.SetTheme(gui.ThemeDarkBordered)Themes are global and set before any window is created. Built-in options include
ThemeDark, ThemeDarkBordered, ThemeLight, and others. See Theming for custom
themes.
w := gui.NewWindow(gui.WindowCfg{
State: &App{},
Title: "get_started",
Width: 300,
Height: 300,
OnInit: func(w *gui.Window) { w.UpdateView(mainView) },
})WindowCfg is the one configuration point for a window:
-
State— a pointer to your application state. Retrieved anywhere withgui.State[App](w). -
OnInit— called once after the window is created.w.UpdateView(mainView)registers the view function that will be called every frame to produce the UI.
backend.Run(w)This starts the event loop and blocks until the window is closed. backend.Run selects the
appropriate renderer automatically (Metal on macOS, OpenGL elsewhere). The call never returns
under normal operation.
func mainView(w *gui.Window) gui.View {This is the heart of a go-gui application. The framework calls this function every frame (or
whenever a redraw is needed). It returns a tree of gui.View values that describe the entire UI.
The function must be pure with respect to w — its output should depend only on the
window state and nothing else. No side effects, no I/O. Side effects belong in event callbacks.
app := gui.State[App](w)gui.State[T] is a generic function that retrieves the typed state pointer stored in the
window. This is the idiomatic way to read app state inside a view function or event callback.
No globals, no closures, no context passing — just the window.
gui.Column(gui.ContainerCfg{
Width: float32(ww),
Height: float32(wh),
Sizing: gui.FixedFixed,
HAlign: gui.HAlignCenter,
VAlign: gui.VAlignMiddle,
Content: []gui.View{ … },
})Column arranges its children vertically. ContainerCfg controls sizing and alignment:
-
Sizing: gui.FixedFixed— use the explicitWidthandHeightvalues (fill the window). -
HAlign: gui.HAlignCenter— center children horizontally. -
VAlign: gui.VAlignMiddle— center children vertically. -
Content— the child views, as a[]gui.Viewslice.
Other sizing modes: FillFill (grow on both axes), FillFit (grow horizontally, fit height),
FixedFit (fixed width, fit height).
gui.Text(gui.TextCfg{
Text: "Hello GUI!",
TextStyle: gui.CurrentTheme().B1,
})Text renders a string with the given style. gui.CurrentTheme().B1 is the theme's first
body text style. Theme typography tokens are named N1–N5 (neutral/UI) and B1–B5 (body).
gui.Button(gui.ButtonCfg{
IDFocus: 1,
Content: []gui.View{
gui.Text(gui.TextCfg{Text: fmt.Sprintf("%d Clicks", app.Clicks)}),
},
OnClick: func(_ *gui.Layout, e *gui.Event, w *gui.Window) {
gui.State[App](w).Clicks++
e.IsHandled = true
},
})A few things to notice:
IDFocus: 1 — This marks the button as keyboard-focusable and gives it tab-order position
- Any widget that should receive keyboard focus needs an
IDFocusvalue greater than zero. The value also determines tab order: lower numbers are focused first. This is the first introduction to go-gui's ID system — see Focus and Scrolling for the full explanation.
Content — Buttons in go-gui contain arbitrary View children. A button is a container
with click behavior, not a widget with a fixed text label. This makes it straightforward to
put icons, images, or complex layouts inside a button.
OnClick — The event callback signature is func(*gui.Layout, *gui.Event, *gui.Window).
All event callbacks in go-gui share this shape. The *gui.Layout argument is the layout
node that received the event, *gui.Event carries the event data, and *gui.Window is the
window — the same w available in the view function.
e.IsHandled = true — Setting this stops the event from propagating further up the
layout tree. It's idiomatic to set it in any callback that has fully handled the event.
Updating state — gui.State[App](w).Clicks++ mutates the state directly. The framework
does not need to be notified of the change; the view function is called again next frame and
will read the updated value naturally.
- An event arrives (or a redraw is scheduled).
- The framework calls
mainView(w), which readsapp.Clicksand returns a layout tree. - The layout engine sizes and positions every node in the tree.
- The renderer emits draw commands for each node.
- The backend (Metal or OpenGL) executes those commands.
The view function produces the complete UI every frame from current state. There is no incremental update, no diffing, no reconciliation — just a fresh layout tree each time.
The examples/ directory contains 35+ runnable applications. A good sequence:
| Example | What to look at |
|---|---|
examples/get_started/ |
This walkthrough (slightly extended with tooltip and shadow) |
examples/todo/ |
A realistic app: list state, input, conditional rendering |
examples/calculator/ |
Keyboard handling, custom button layouts |
examples/showcase/ |
Interactive reference for every widget in the framework |
Run the showcase and explore — every demo has a documentation button in the upper-right corner that explains the widget and its configuration.
go run ./examples/showcase/Getting Started
Widgets
Layout & Interaction
Visuals
Reference