~eliasnaur/gio-example

a444013efb43f71359b6078a8514e97ce1794890 — Chris Waldon 7 months ago f5d140d
multiwindow: fix concurrent use of material.Theme

text.Shapers are not safe for concurrent use, and thus material.Themes
(which embed them) are not either. This commit restructures the multiwindow
example to avoid this mistake and the nasty panics that it can trigger.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
3 files changed, 15 insertions(+), 16 deletions(-)

M multiwindow/letters.go
M multiwindow/log.go
M multiwindow/main.go
M multiwindow/letters.go => multiwindow/letters.go +6 -7
@@ 11,7 11,7 @@ import (

// Letters displays a clickable list of text items that open a new window.
type Letters struct {
	win *Window
	App *Application
	log *Log

	items []*LetterListItem


@@ 32,13 32,12 @@ func NewLetters(log *Log) *Letters {

// Run implements Window.Run method.
func (v *Letters) Run(w *Window) error {
	v.win = w
	v.App = w.App
	return WidgetView(v.Layout).Run(w)
}

// Layout handles drawing the letters view.
func (v *Letters) Layout(gtx layout.Context) layout.Dimensions {
	th := v.win.App.Theme
func (v *Letters) Layout(gtx layout.Context, th *material.Theme) layout.Dimensions {
	return material.List(th, &v.list).Layout(gtx, len(v.items), func(gtx layout.Context, index int) layout.Dimensions {
		item := v.items[index]
		for item.Click.Clicked() {


@@ 47,9 46,9 @@ func (v *Letters) Layout(gtx layout.Context) layout.Dimensions {
			bigText := material.H1(th, item.Text)
			size := bigText.TextSize
			size.V *= 2
			v.win.App.NewWindow(item.Text,
				WidgetView(func(gtx layout.Context) layout.Dimensions {
					return layout.Center.Layout(gtx, bigText.Layout)
			v.App.NewWindow(item.Text,
				WidgetView(func(gtx layout.Context, th *material.Theme) layout.Dimensions {
					return layout.Center.Layout(gtx, material.H1(th, item.Text).Layout)
				}),
				app.Size(size, size),
			)

M multiwindow/log.go => multiwindow/log.go +4 -1
@@ 5,6 5,7 @@ package main
import (
	"fmt"

	"gioui.org/font/gofont"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"


@@ 44,6 45,8 @@ func (log *Log) Printf(format string, args ...interface{}) {
func (log *Log) Run(w *Window) error {
	var ops op.Ops

	th := material.NewTheme(gofont.Collection())

	applicationClose := w.App.Context.Done()
	for {
		select {


@@ 59,7 62,7 @@ func (log *Log) Run(w *Window) error {
				return e.Err
			case system.FrameEvent:
				gtx := layout.NewContext(&ops, e)
				log.Layout(w, w.App.Theme, gtx)
				log.Layout(w, th, gtx)
				e.Frame(gtx.Ops)
			}
		}

M multiwindow/main.go => multiwindow/main.go +5 -8
@@ 16,12 16,11 @@ import (
	"sync"

	"gioui.org/app"
	"gioui.org/font/gofont"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/widget/material"

	"gioui.org/font/gofont"
)

func main() {


@@ 52,8 51,6 @@ type Application struct {
	Context context.Context
	// Shutdown shuts down all windows.
	Shutdown func()
	// Theme is the application wide theme.
	Theme *material.Theme
	// active keeps track the open windows, such that application
	// can shut down, when all of them are closed.
	active sync.WaitGroup


@@ 64,8 61,6 @@ func NewApplication(ctx context.Context) *Application {
	return &Application{
		Context:  ctx,
		Shutdown: cancel,

		Theme: material.NewTheme(gofont.Collection()),
	}
}



@@ 101,12 96,14 @@ type View interface {
}

// WidgetView allows to use layout.Widget as a view.
type WidgetView func(gtx layout.Context) layout.Dimensions
type WidgetView func(gtx layout.Context, th *material.Theme) layout.Dimensions

// Run displays the widget with default handling.
func (view WidgetView) Run(w *Window) error {
	var ops op.Ops

	th := material.NewTheme(gofont.Collection())

	applicationClose := w.App.Context.Done()
	for {
		select {


@@ 118,7 115,7 @@ func (view WidgetView) Run(w *Window) error {
				return e.Err
			case system.FrameEvent:
				gtx := layout.NewContext(&ops, e)
				view(gtx)
				view(gtx, th)
				e.Frame(gtx.Ops)
			}
		}