Since a GUI library needs to talk to some sort of display system to display information:
var window app.Window
window.Option(app.Title(title))
var ops op.Ops
for {
switch e := window.Event().(type) {
case app.DestroyEvent:
// The window was closed.
return e.Err
case app.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.Window.Run
chooses the appropriate “driver” depending on the environment and build context. It might choose Wayland, Win32, or Cocoa among several others.
An app.Window
allows accessing events from the display with window.Event()
. There are other lifecycle events in the gioui.org/app
package such as app.DestroyEvent
and app.FrameEvent
.
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.