package shared import ( "context" "sync" "gioui.org/app" "gioui.org/io/event" "gioui.org/widget/material" "github.com/rs/zerolog/log" ) type GlobalState struct { Window *app.Window Theme *material.Theme state WindowState } func Run(ctx context.Context, window *app.Window, initialState WindowState) error { // Set up global data first state := GlobalState{} theme := material.NewTheme() theme.Bg = BackgroundColor theme.Fg = TextColor state.Theme = theme state.Window = window state.state = initialState // Then start showing the window and polling for events // Though technically, I don't think window.Event will cause the window to display yet // since what should it display? It doesn't have anything to display. So first display happens at event.Frame eventChan := make(chan event.Event) go func() { for { eventChan <- window.Event() if ctx.Err() != nil { return } } }() for { select { case <-ctx.Done(): log.Info().Msg("Context cancelled, preparing shutdown") // Force one update for polling goroutine to exit state.Window.Invalidate() // Then keep awaiting more events. Since quit is just another event // and the event channel is unbuffered, at least one more event has to be read for the poller to exit case e := <-eventChan: // Let the current view handle the event, then update accordingly newState := state.state.Run(&state, e) if newState.NextState != nil { state.state = newState.NextState } if newState.ExitCode != nil { log.Info().Msg("Received shutdown request from window") wg := sync.WaitGroup{} wg.Add(1) go state.state.Exit(&state, &wg) wg.Wait() log.Info().Msg("Shutdown of window complete") return nil } } } }