Since a GUI library needs to talk to some sort of display system to display information:
window := app.NewWindow()
var ops op.Ops
for e := range window.Events() {
switch e := e.(type) {
case system.DestroyEvent:
// The window was closed.
return e.Err
case system.FrameEvent:
// A request to draw the window state.
// Reset the operations back to zero.
ops.Reset()
// Draw the state into ops.
draw(&ops)
// Update the display.
e.Frame(&ops)
}
}
app.NewWindow
chooses the appropriate “driver” depending on the environment and build context. It might choose Wayland, Win32, Cocoa among several others.
An app.Window
sends events from the display system to the window.Events()
channel. The system events are listed in gioui.org/io/system
. The input events, such as gioui.org/io/pointer
and gioui.org/io/key
, are also sent into that channel.
Operations
All UI libraries need a way for the program to specify what to display and how to handle events. Gio programs use operations, serialized into one or more op.Ops
operation lists. Operation lists are in turn passed to the window driver through the FrameEvent.Frame
function.
By convention, each operation kind is represented by a Go type with an Add
method that records the operation into the Ops
argument. Like any Go struct literal, zero-valued fields can be useful to represent optional values.
For example, recording an operation that sets the current color to red:
func addColorOperation(ops *op.Ops) {
red := color.NRGBA{R: 0xFF, A: 0xFF}
paint.ColorOp{Color: red}.Add(ops)
}
You might be thinking that it would be more usual to have an ops.Add(ColorOp{Color: red})
method instead of using op.ColorOp{Color: red}.Add(ops)
. It’s like this so that the Add
method doesn’t have to take an interface-typed argument, which would often require an allocation to call. This is a key aspect of Gio’s “zero allocation” design.