Guides
Views
The view, a function of your state, events as questions, keys, and changing the state from other goroutines.
The view is a function from your app's state to its interface. MyGo calls it on the main thread to build every frame: after input, after you change the state (see below), and while something animates. Elements live for one frame: the state that lasts is yours, in your own types, plus what MyGo keeps for each element from frame to frame (focus, hover, scrolling, the text being edited, animations).
// todoList is the app's state, which lasts.
type todoList struct {
todos []Todo
draft string
}
// view builds the interface from it.
func (app *todoList) view(c *ui.Context) {
ui.Column(c).Fill().Padding(16).Gap(8).Children(func() {
for i := range app.todos {
ui.Checkbox(c, &app.todos[i].Done, app.todos[i].Title)
}
if ui.TextInput(c, &app.draft).Placeholder("New to-do").Submitted() {
app.todos = append(app.todos, Todo{Title: app.draft})
app.draft = ""
}
})
}Make the state once, as the app starts, and give its view to the window:
app := &todoList{}
mygo.NewWindow(mygo.WindowOptions{Title: "To-dos", Content: ui.View(app.view)})ui.View(app.view) is the window's content (mygo.WindowOptions.Content);
a Content can serve several windows, each with its own element state.
The examples in these guides are parts of such a view: c is the view's
*ui.Context, and app its receiver, the value of your own type that
holds the state, as todoList here. A field such as app.volume or a
method such as app.save() is one you declare on that type. Values that
last, such as a router, are fields made with the rest of
the state, never in the view, which runs for every frame.
Events are questions
Events are questions you ask while building: Clicked reports whether the
element was clicked since the last frame, so the code that handles a click
sits where the button is built:
if ui.Button(c, "Save").Clicked() {
app.save()
}When a handler changes the state while the view builds, MyGo builds the frame again, so it always shows the outcome.
The rest of the frame builds first, though, after the handler: a change to what the view is building pulls it from under the code building it, as deleting an item from the slice a loop builds rows from, from a button in one of the rows. The loop goes on over the items as they were, and reads past the end of the slice. Note the item instead, and change the slice once the loop is done:
deleted := -1
for i := range app.items {
item := app.items[i]
ui.Row(c).Key(item.ID).Children(func() {
ui.Text(c, item.Title).Grow(1)
if ui.Button(c, "Delete").Clicked() {
deleted = i
}
})
}
if deleted >= 0 {
app.items = slices.Delete(app.items, deleted, deleted+1)
}Widgets that change a value take a pointer to it, so they need no handler:
ui.Checkbox(c, &app.settings.Sync, "Sync") changes the field the moment
the user clicks. Changed reports that they did, for work that follows:
if ui.TextInput(c, &app.query).Placeholder("Search").Changed() {
app.results = search(app.query)
}Keys
MyGo tells elements apart by their position among their siblings. When the
siblings before an element can change, as in a list whose items you
insert, delete or reorder, give each item a Key so its state follows it:
for i := range app.todos {
todo := &app.todos[i]
ui.Row(c).Key(todo.ID).Children(func() {
ui.Checkbox(c, &todo.Done, todo.Title)
})
}Widgets that handle their input as they are created, such as Checkbox
here, take the key from an element around them: Key panics on them, as
their state would be lost.
Change the state from other goroutines
The view reads your state on the main thread. Change it from other
goroutines with Window.Update, which runs a function on the main thread
and then draws a new frame:
win := mygo.NewWindow(mygo.WindowOptions{Content: ui.View(app.view)})
go func() {
items, err := fetchItems()
win.Update(func() { app.items, app.err = items, err })
}()Window.Invalidate only draws a new frame, for state you guard yourself.
Within the view, c.Invalidate() asks for another frame and c.After(d) for
one after a delay, such as a clock's next second.
State of an element
An element keeps state of its own from frame to frame with ui.Local, for
widgets you build yourself; see custom widgets.