~eliasnaur/gio

d8000880c3b437562d25d797d33f0531b86fdb38 — Elias Naur 5 days ago 2451750
example,cmd: bump gio version

Signed-off-by: Elias Naur <mail@eliasnaur.com>
M cmd/go.mod => cmd/go.mod +1 -1
@@ 3,7 3,7 @@ module gioui.org/cmd
go 1.13

require (
	gioui.org v0.0.0-20200521183411-67382bec3949
	gioui.org v0.0.0-20200523202849-2451750782b8
	github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4
	github.com/chromedp/chromedp v0.5.2
	golang.org/x/image v0.0.0-20190802002840-cff245a6509b

M cmd/go.sum => cmd/go.sum +2 -2
@@ 1,6 1,6 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20200521183411-67382bec3949 h1:Nvvyn28PT4kyRKBKg04QY2ZU6y9J1hFGJtBDV5VyBmM=
gioui.org v0.0.0-20200521183411-67382bec3949/go.mod h1:AHI9rFr6AEEHCb8EPVtb/p5M+NMJRKH58IOp8O3Je04=
gioui.org v0.0.0-20200523202849-2451750782b8 h1:TB+F3jDAVjNemdtgc8yfvsPy1xWkyn7JLOsm5r+YUuQ=
gioui.org v0.0.0-20200523202849-2451750782b8/go.mod h1:AHI9rFr6AEEHCb8EPVtb/p5M+NMJRKH58IOp8O3Je04=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4 h1:QD3KxSJ59L2lxG6MXBjNHxiQO2RmxTQ3XcK+wO44WOg=

M cmd/gogio/testdata/red.go => cmd/gogio/testdata/red.go +18 -12
@@ 14,6 14,7 @@ import (
	"gioui.org/io/pointer"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/paint"
)



@@ 39,6 40,11 @@ const (
// when a frame is ready. Initially we want to notify about the first frame.
var notify = notifyInvalidate

type (
	C = layout.Context
	D = layout.Dimensions
)

func loop(w *app.Window) error {
	topLeft := quarterWidget{
		color: color.RGBA{R: 0xde, G: 0xad, B: 0xbe, A: 0xff},


@@ 53,30 59,29 @@ func loop(w *app.Window) error {
		color: color.RGBA{R: 0x00, G: 0x00, B: 0x00, A: 0x80},
	}

	gtx := new(layout.Context)
	var ops op.Ops
	for {
		e := <-w.Events()
		switch e := e.(type) {
		case system.DestroyEvent:
			return e.Err
		case system.FrameEvent:

			gtx.Reset(e.Queue, e.Config, e.Size)
			gtx := layout.NewContext(&ops, e.Queue, e.Config, e.Size)
			layout.Flex{Axis: layout.Vertical}.Layout(gtx,
				layout.Flexed(0.5, func() {
					layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
				layout.Flexed(0.5, func(gtx C) D {
					return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
						// r1c1
						layout.Flexed(0.5, func() { topLeft.Layout(gtx) }),
						layout.Flexed(0.5, func(gtx C) D { return topLeft.Layout(gtx) }),
						// r1c2
						layout.Flexed(0.5, func() { topRight.Layout(gtx) }),
						layout.Flexed(0.5, func(gtx C) D { return topRight.Layout(gtx) }),
					)
				}),
				layout.Flexed(0.5, func() {
					layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
				layout.Flexed(0.5, func(gtx C) D {
					return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
						// r2c1
						layout.Flexed(0.5, func() { botLeft.Layout(gtx) }),
						layout.Flexed(0.5, func(gtx C) D { return botLeft.Layout(gtx) }),
						// r2c2
						layout.Flexed(0.5, func() { botRight.Layout(gtx) }),
						layout.Flexed(0.5, func(gtx C) D { return botRight.Layout(gtx) }),
					)
				}),
			)


@@ 105,7 110,7 @@ type quarterWidget struct {

var red = color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff}

func (w *quarterWidget) Layout(gtx *layout.Context) {
func (w *quarterWidget) Layout(gtx layout.Context) layout.Dimensions {
	if w.clicked {
		paint.ColorOp{Color: red}.Add(gtx.Ops)
	} else {


@@ 128,4 133,5 @@ func (w *quarterWidget) Layout(gtx *layout.Context) {
			notify = notifyInvalidate
		}
	}
	return layout.Dimensions{Size: gtx.Constraints.Max}
}

M example/glfw/main.go => example/glfw/main.go +7 -6
@@ 29,6 29,7 @@ import (
	"gioui.org/io/pointer"
	"gioui.org/io/router"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"


@@ 71,7 72,7 @@ func main() {
	gofont.Register()
	f := new(goglFunctions)
	var queue router.Router
	gtx := new(layout.Context)
	var ops op.Ops
	th := material.NewTheme()
	backend, err := giogl.NewBackend(f)
	if err != nil {


@@ 92,7 93,7 @@ func main() {
		}
		width, height := window.GetSize()
		sz := image.Point{X: width, Y: height}
		gtx.Reset(&queue, &glfwConfig{scale}, sz)
		gtx := layout.NewContext(&ops, &queue, &glfwConfig{scale}, sz)
		draw(gtx, th)
		gpu.Collect(sz, gtx.Ops)
		gpu.BeginFrame()


@@ 104,10 105,10 @@ func main() {

var button widget.Clickable

func draw(gtx *layout.Context, th *material.Theme) {
	layout.Center.Layout(gtx, func() {
		material.Button(th, "Button").Layout(gtx, &button)
	})
func draw(gtx layout.Context, th *material.Theme) layout.Dimensions {
	return layout.Center.Layout(gtx,
		material.Button(th, &button, "Button").Layout,
	)
}

func registerCallbacks(window *glfw.Window, q *router.Router) {

M example/go.mod => example/go.mod +1 -1
@@ 3,7 3,7 @@ module gioui.org/example
go 1.13

require (
	gioui.org v0.0.0-20200521183411-67382bec3949
	gioui.org v0.0.0-20200523202849-2451750782b8
	github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7
	github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72
	github.com/google/go-github/v24 v24.0.1

M example/go.sum => example/go.sum +2 -2
@@ 1,7 1,7 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20200521183411-67382bec3949 h1:Nvvyn28PT4kyRKBKg04QY2ZU6y9J1hFGJtBDV5VyBmM=
gioui.org v0.0.0-20200521183411-67382bec3949/go.mod h1:AHI9rFr6AEEHCb8EPVtb/p5M+NMJRKH58IOp8O3Je04=
gioui.org v0.0.0-20200523202849-2451750782b8 h1:TB+F3jDAVjNemdtgc8yfvsPy1xWkyn7JLOsm5r+YUuQ=
gioui.org v0.0.0-20200523202849-2451750782b8/go.mod h1:AHI9rFr6AEEHCb8EPVtb/p5M+NMJRKH58IOp8O3Je04=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=

M example/gophers/main.go => example/gophers/main.go +3 -2
@@ 20,6 20,7 @@ import (
	"gioui.org/io/key"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/unit"

	"github.com/google/go-github/v24/github"


@@ 77,7 78,7 @@ func initProfiling() {

func (a *App) run() error {
	a.ui.profiling = *stats
	gtx := new(layout.Context)
	var ops op.Ops
	for {
		select {
		case users := <-a.updateUsers:


@@ 125,7 126,7 @@ func (a *App) run() error {
					}
				}
			case system.FrameEvent:
				gtx.Reset(e.Queue, e.Config, e.Size)
				gtx := layout.NewContext(&ops, e.Queue, e.Config, e.Size)
				a.ui.Layout(gtx)
				e.Frame(gtx.Ops)
			}

M example/gophers/main_test.go => example/gophers/main_test.go +6 -2
@@ 7,14 7,18 @@ import (
	"testing"

	"gioui.org/layout"
	"gioui.org/op"
)

func BenchmarkUI(b *testing.B) {
	fetch := func(_ string) {}
	u := newUI(fetch)
	gtx := new(layout.Context)
	var ops op.Ops
	for i := 0; i < b.N; i++ {
		gtx.Reset(nil, nil, image.Point{800, 600})
		gtx := layout.Context{
			Ops:         &ops,
			Constraints: layout.Exact(image.Pt(800, 600)),
		}
		u.Layout(gtx)
	}
}

M example/gophers/ui.go => example/gophers/ui.go +74 -73
@@ 65,6 65,11 @@ type user struct {

var theme *material.Theme

type (
	C = layout.Context
	D = layout.Dimensions
)

func init() {
	gofont.Register()
	theme = material.NewTheme()


@@ 106,7 111,7 @@ func argb(c uint32) color.RGBA {
	return color.RGBA{A: uint8(c >> 24), R: uint8(c >> 16), G: uint8(c >> 8), B: uint8(c)}
}

func (u *UI) layoutTimings(gtx *layout.Context) {
func (u *UI) layoutTimings(gtx layout.Context) {
	if !u.profiling {
		return
	}


@@ 120,17 125,17 @@ func (u *UI) layoutTimings(gtx *layout.Context) {
	runtime.ReadMemStats(&mstats)
	mallocs := mstats.Mallocs - u.lastMallocs
	u.lastMallocs = mstats.Mallocs
	layout.NE.Layout(gtx, func() {
		layout.Inset{Top: unit.Dp(16)}.Layout(gtx, func() {
	layout.NE.Layout(gtx, func(gtx C) D {
		return layout.Inset{Top: unit.Dp(16)}.Layout(gtx, func(gtx C) D {
			txt := fmt.Sprintf("m: %d %s", mallocs, u.profile.Timings)
			lbl := material.Caption(theme, txt)
			lbl.Font.Variant = "Mono"
			lbl.Layout(gtx)
			return lbl.Layout(gtx)
		})
	})
}

func (u *UI) Layout(gtx *layout.Context) {
func (u *UI) Layout(gtx layout.Context) {
	for i := range u.userClicks {
		click := &u.userClicks[i]
		for _, e := range click.Events(gtx) {


@@ 156,159 161,155 @@ func (u *UI) newUserPage(user *user) *userPage {
	return up
}

func (up *userPage) Layout(gtx *layout.Context) {
func (up *userPage) Layout(gtx layout.Context) {
	l := up.commitsList
	if l.Dragging() {
		key.HideInputOp{}.Add(gtx.Ops)
	}
	l.Layout(gtx, len(up.commits), func(i int) {
		up.commit(gtx, i)
	l.Layout(gtx, len(up.commits), func(gtx C, i int) D {
		return up.commit(gtx, i)
	})
}

func (up *userPage) commit(gtx *layout.Context, index int) {
func (up *userPage) commit(gtx layout.Context, index int) layout.Dimensions {
	u := up.user
	msg := up.commits[index].GetMessage()
	label := material.Caption(theme, msg)
	in := layout.Inset{Top: unit.Dp(16), Right: unit.Dp(8), Left: unit.Dp(8)}
	in.Layout(gtx, func() {
		layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
			layout.Rigid(func() {
	return in.Layout(gtx, func(gtx C) D {
		return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
			layout.Rigid(func(gtx C) D {
				sz := gtx.Px(unit.Dp(48))
				cc := clipCircle{}
				cc.Layout(gtx, func() {
				return cc.Layout(gtx, func(gtx C) D {
					gtx.Constraints = layout.Exact(gtx.Constraints.Constrain(image.Point{X: sz, Y: sz}))
					u.layoutAvatar(gtx)
					return u.layoutAvatar(gtx)
				})
			}),
			layout.Flexed(1, func() {
			layout.Flexed(1, func(gtx C) D {
				gtx.Constraints.Min.X = gtx.Constraints.Max.X
				layout.Inset{Left: unit.Dp(8)}.Layout(gtx, func() {
					label.Layout(gtx)
				})
				return layout.Inset{Left: unit.Dp(8)}.Layout(gtx, label.Layout)
			}),
		)
	})
}

func (u *UI) layoutUsers(gtx *layout.Context) {
func (u *UI) layoutUsers(gtx layout.Context) {
	layout.Stack{Alignment: layout.SE}.Layout(gtx,
		layout.Expanded(func() {
			layout.Flex{Axis: layout.Vertical}.Layout(gtx,
				layout.Rigid(func() {
		layout.Expanded(func(gtx C) D {
			return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
				layout.Rigid(func(gtx C) D {
					gtx.Constraints.Min.X = gtx.Constraints.Max.X
					layout.UniformInset(unit.Dp(16)).Layout(gtx, func() {
					return layout.UniformInset(unit.Dp(16)).Layout(gtx, func(gtx C) D {
						sz := gtx.Px(unit.Dp(200))
						cs := gtx.Constraints
						gtx.Constraints = layout.Exact(cs.Constrain(image.Point{X: sz, Y: sz}))
						material.Editor(theme, "Hint").Layout(gtx, u.edit)
						return material.Editor(theme, u.edit, "Hint").Layout(gtx)
					})
				}),
				layout.Rigid(func() {
				layout.Rigid(func(gtx C) D {
					gtx.Constraints.Min.X = gtx.Constraints.Max.X
					in := layout.Inset{Bottom: unit.Dp(16), Left: unit.Dp(16), Right: unit.Dp(16)}
					in.Layout(gtx, func() {
						e := material.Editor(theme, "Hint")
					return in.Layout(gtx, func(gtx C) D {
						e := material.Editor(theme, u.edit2, "Hint")
						e.TextSize = unit.Sp(14)
						e.Font.Style = text.Italic
						e.Layout(gtx, u.edit2)
						return e.Layout(gtx)
					})
				}),
				layout.Rigid(func() {
					layout.Stack{}.Layout(gtx,
						layout.Expanded(func() {
				layout.Rigid(func(gtx C) D {
					return layout.Stack{}.Layout(gtx,
						layout.Expanded(func(gtx C) D {
							gtx.Constraints.Min.X = gtx.Constraints.Max.X
							fill{rgb(0xf2f2f2)}.Layout(gtx)
							return fill{rgb(0xf2f2f2)}.Layout(gtx)
						}),
						layout.Stacked(func() {
						layout.Stacked(func(gtx C) D {
							in := layout.Inset{Top: unit.Dp(16), Right: unit.Dp(8), Bottom: unit.Dp(8), Left: unit.Dp(8)}
							in.Layout(gtx, func() {
							return in.Layout(gtx, func(gtx C) D {
								lbl := material.Caption(theme, "GOPHERS")
								lbl.Color = rgb(0x888888)
								lbl.Layout(gtx)
								return lbl.Layout(gtx)
							})
						}),
					)
				}),
				layout.Flexed(1, func() {
				layout.Flexed(1, func(gtx C) D {
					gtx.Constraints.Min.X = gtx.Constraints.Max.X
					u.layoutContributors(gtx)
					return u.layoutContributors(gtx)
				}),
			)
		}),
		layout.Stacked(func() {
		layout.Stacked(func(gtx C) D {
			in := layout.UniformInset(unit.Dp(16))
			in.Layout(gtx, func() {
			return in.Layout(gtx, func(gtx C) D {
				for u.fab.Clicked(gtx) {
				}
				material.IconButton(theme, u.fabIcon).Layout(gtx, u.fab)
				return material.IconButton(theme, u.fab, u.fabIcon).Layout(gtx)
			})
		}),
	)
}

func (u *UI) layoutContributors(gtx *layout.Context) {
func (u *UI) layoutContributors(gtx layout.Context) layout.Dimensions {
	l := u.usersList
	if l.Dragging() {
		key.HideInputOp{}.Add(gtx.Ops)
	}
	l.Layout(gtx, len(u.users), func(i int) {
		u.user(gtx, i)
	return l.Layout(gtx, len(u.users), func(gtx C, i int) D {
		return u.user(gtx, i)
	})
}

func (u *UI) user(gtx *layout.Context, index int) {
func (u *UI) user(gtx layout.Context, index int) layout.Dimensions {
	user := u.users[index]
	in := layout.UniformInset(unit.Dp(8))
	in.Layout(gtx, func() {
		centerRowOpts().Layout(gtx,
			layout.Rigid(func() {
	dims := in.Layout(gtx, func(gtx C) D {
		return centerRowOpts().Layout(gtx,
			layout.Rigid(func(gtx C) D {
				in := layout.Inset{Right: unit.Dp(8)}
				cc := clipCircle{}
				in.Layout(gtx, func() {
					cc.Layout(gtx, func() {
				return in.Layout(gtx, func(gtx C) D {
					return cc.Layout(gtx, func(gtx C) D {
						dim := gtx.Px(unit.Dp(48))
						sz := image.Point{X: dim, Y: dim}
						gtx.Constraints = layout.Exact(gtx.Constraints.Constrain(sz))
						user.layoutAvatar(gtx)
						return user.layoutAvatar(gtx)
					})
				})
			}),
			layout.Rigid(func() {
				column().Layout(gtx,
					layout.Rigid(func() {
						baseline().Layout(gtx,
							layout.Rigid(func() {
								material.Body1(theme, user.name).Layout(gtx)
							}),
							layout.Flexed(1, func() {
			layout.Rigid(func(gtx C) D {
				return column().Layout(gtx,
					layout.Rigid(func(gtx C) D {
						return baseline().Layout(gtx,
							layout.Rigid(material.Body1(theme, user.name).Layout),
							layout.Flexed(1, func(gtx C) D {
								gtx.Constraints.Min.X = gtx.Constraints.Max.X
								layout.E.Layout(gtx, func() {
									layout.Inset{Left: unit.Dp(2)}.Layout(gtx, func() {
										material.Caption(theme, "3 hours ago").Layout(gtx)
									})
								return layout.E.Layout(gtx, func(gtx C) D {
									return layout.Inset{Left: unit.Dp(2)}.Layout(gtx,
										material.Caption(theme, "3 hours ago").Layout)
								})
							}),
						)
					}),
					layout.Rigid(func() {
					layout.Rigid(func(gtx C) D {
						in := layout.Inset{Top: unit.Dp(4)}
						in.Layout(gtx, func() {
						return in.Layout(gtx, func(gtx C) D {
							lbl := material.Caption(theme, user.company)
							lbl.Color = rgb(0xbbbbbb)
							lbl.Layout(gtx)
							return lbl.Layout(gtx)
						})
					}),
				)
			}),
		)
	})
	pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops)
	pointer.Rect(image.Rectangle{Max: dims.Size}).Add(gtx.Ops)
	click := &u.userClicks[index]
	click.Add(gtx.Ops)
	return dims
}

func (u *user) layoutAvatar(gtx *layout.Context) {
func (u *user) layoutAvatar(gtx layout.Context) layout.Dimensions {
	sz := gtx.Constraints.Min.X
	if u.avatarOp.Size().X != sz {
		img := image.NewRGBA(image.Rectangle{Max: image.Point{X: sz, Y: sz}})


@@ 317,14 318,14 @@ func (u *user) layoutAvatar(gtx *layout.Context) {
	}
	img := widget.Image{Src: u.avatarOp}
	img.Scale = float32(sz) / float32(gtx.Px(unit.Dp(float32(sz))))
	img.Layout(gtx)
	return img.Layout(gtx)
}

type fill struct {
	col color.RGBA
}

func (f fill) Layout(gtx *layout.Context) {
func (f fill) Layout(gtx layout.Context) layout.Dimensions {
	cs := gtx.Constraints
	d := cs.Min
	dr := f32.Rectangle{


@@ 332,7 333,7 @@ func (f fill) Layout(gtx *layout.Context) {
	}
	paint.ColorOp{Color: f.col}.Add(gtx.Ops)
	paint.PaintOp{Rect: dr}.Add(gtx.Ops)
	gtx.Dimensions = layout.Dimensions{Size: d}
	return layout.Dimensions{Size: d}
}

func column() layout.Flex {


@@ 350,12 351,11 @@ func baseline() layout.Flex {
type clipCircle struct {
}

func (c *clipCircle) Layout(gtx *layout.Context, w layout.Widget) {
func (c *clipCircle) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
	var m op.MacroOp
	m.Record(gtx.Ops)
	w()
	dims := w(gtx)
	m.Stop()
	dims := gtx.Dimensions
	max := dims.Size.X
	if dy := dims.Size.Y; dy > max {
		max = dy


@@ 370,6 370,7 @@ func (c *clipCircle) Layout(gtx *layout.Context, w layout.Widget) {
	}.Op(gtx.Ops).Add(gtx.Ops)
	m.Add()
	stack.Pop()
	return dims
}

const longTextSample = `1. I learned from my grandfather, Verus, to use good manners, and to

M example/hello/hello.go => example/hello/hello.go +3 -2
@@ 11,6 11,7 @@ import (
	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/text"
	"gioui.org/widget/material"



@@ 30,14 31,14 @@ func main() {
func loop(w *app.Window) error {
	gofont.Register()
	th := material.NewTheme()
	gtx := new(layout.Context)
	var ops op.Ops
	for {
		e := <-w.Events()
		switch e := e.(type) {
		case system.DestroyEvent:
			return e.Err
		case system.FrameEvent:
			gtx.Reset(e.Queue, e.Config, e.Size)
			gtx := layout.NewContext(&ops, e.Queue, e.Config, e.Size)
			l := material.H1(th, "Hello, Gio")
			maroon := color.RGBA{127, 0, 0, 255}
			l.Color = maroon

M example/kitchen/kitchen.go => example/kitchen/kitchen.go +75 -77
@@ 22,6 22,7 @@ import (
	"gioui.org/font/gofont"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"


@@ 37,7 38,10 @@ type scaledConfig struct {
}

type iconAndTextButton struct {
	theme *material.Theme
	theme  *material.Theme
	button *widget.Clickable
	icon   *widget.Icon
	word   string
}

func main() {


@@ 81,8 85,11 @@ func saveScreenshot(f string) error {
	if err != nil {
		return err
	}
	gtx := new(layout.Context)
	gtx.Reset(nil, &scaledConfig{scale}, sz)
	gtx := layout.Context{
		Ops:         new(op.Ops),
		Config:      &scaledConfig{scale},
		Constraints: layout.Exact(sz),
	}
	th := material.NewTheme()
	kitchen(gtx, th)
	w.Frame(gtx.Ops)


@@ 100,7 107,7 @@ func saveScreenshot(f string) error {
func loop(w *app.Window) error {
	th := material.NewTheme()

	gtx := new(layout.Context)
	var ops op.Ops
	for {
		select {
		case e := <-w.Events():


@@ 110,7 117,7 @@ func loop(w *app.Window) error {
			case system.DestroyEvent:
				return e.Err
			case system.FrameEvent:
				gtx.Reset(e.Queue, e.Config, e.Size)
				gtx := layout.NewContext(&ops, e.Queue, e.Config, e.Size)
				for iconButton.Clicked(gtx) {
					w.WriteClipboard(lineEditor.Text())
				}


@@ 154,134 161,125 @@ var (
	swtch               = new(widget.Bool)
)

func (b iconAndTextButton) Layout(gtx *layout.Context, button *widget.Clickable, icon *widget.Icon, word string) {
	material.ButtonLayout(b.theme).Layout(gtx, button, func() {
type (
	D = layout.Dimensions
	C = layout.Context
)

func (b iconAndTextButton) Layout(gtx layout.Context) layout.Dimensions {
	return material.ButtonLayout(b.theme, b.button).Layout(gtx, func(gtx C) D {
		iconAndLabel := layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}
		textIconSpacer := unit.Dp(5)

		layIcon := layout.Rigid(func() {
			layout.Inset{Right: textIconSpacer}.Layout(gtx, func() {
				size := gtx.Px(unit.Dp(56)) - 2*gtx.Px(unit.Dp(16))
		layIcon := layout.Rigid(func(gtx C) D {
			return layout.Inset{Right: textIconSpacer}.Layout(gtx, func(gtx C) D {
				var d D
				if icon != nil {
					icon.Layout(gtx, unit.Px(float32(size)))
					gtx.Dimensions = layout.Dimensions{
					size := gtx.Px(unit.Dp(56)) - 2*gtx.Px(unit.Dp(16))
					b.icon.Layout(gtx, unit.Px(float32(size)))
					d = layout.Dimensions{
						Size: image.Point{X: size, Y: size},
					}
				}
				return d
			})
		})

		layLabel := layout.Rigid(func() {
			layout.Inset{Left: textIconSpacer}.Layout(gtx, func() {
				l := material.Body1(b.theme, word)
		layLabel := layout.Rigid(func(gtx C) D {
			return layout.Inset{Left: textIconSpacer}.Layout(gtx, func(gtx C) D {
				l := material.Body1(b.theme, b.word)
				l.Color = b.theme.Color.InvText
				l.Layout(gtx)
				return l.Layout(gtx)
			})
		})

		iconAndLabel.Layout(gtx, layIcon, layLabel)
		return iconAndLabel.Layout(gtx, layIcon, layLabel)
	})
}

func kitchen(gtx *layout.Context, th *material.Theme) {
	widgets := []func(){
		func() {
			material.H3(th, topLabel).Layout(gtx)
		},
		func() {
func kitchen(gtx layout.Context, th *material.Theme) layout.Dimensions {
	widgets := []layout.Widget{
		material.H3(th, topLabel).Layout,
		func(gtx C) D {
			gtx.Constraints.Max.Y = gtx.Px(unit.Dp(200))
			material.Editor(th, "Hint").Layout(gtx, editor)
			return material.Editor(th, editor, "Hint").Layout(gtx)
		},
		func() {
			e := material.Editor(th, "Hint")
			e.Font.Style = text.Italic
			e.Layout(gtx, lineEditor)
		func(gtx C) D {
			for _, e := range lineEditor.Events(gtx) {
				if e, ok := e.(widget.SubmitEvent); ok {
					topLabel = e.Text
					lineEditor.SetText("")
				}
			}
			e := material.Editor(th, lineEditor, "Hint")
			e.Font.Style = text.Italic
			return e.Layout(gtx)
		},
		func() {
		func(gtx C) D {
			in := layout.UniformInset(unit.Dp(8))
			layout.Flex{Alignment: layout.Middle}.Layout(gtx,
				layout.Rigid(func() {
					in.Layout(gtx, func() {
						material.IconButton(th, icon).Layout(gtx, iconButton)
					})
			return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
				layout.Rigid(func(gtx C) D {
					return in.Layout(gtx, material.IconButton(th, iconButton, icon).Layout)
				}),
				layout.Rigid(func() {
					in.Layout(gtx, func() {
						iconAndTextButton{th}.Layout(gtx, iconTextButton, icon, "Icon")
					})
				layout.Rigid(func(gtx C) D {
					return in.Layout(gtx, iconAndTextButton{theme: th, icon: icon, word: "Icon", button: iconTextButton}.Layout)
				}),
				layout.Rigid(func() {
					in.Layout(gtx, func() {
				layout.Rigid(func(gtx C) D {
					return in.Layout(gtx, func(gtx C) D {
						for button.Clicked(gtx) {
							green = !green
						}
						material.Button(th, "Click me!").Layout(gtx, button)
						return material.Button(th, button, "Click me!").Layout(gtx)
					})
				}),
				layout.Rigid(func() {
					in.Layout(gtx, func() {
				layout.Rigid(func(gtx C) D {
					return in.Layout(gtx, func(gtx C) D {
						l := "Green"
						if !green {
							l = "Blue"
						}
						btn := material.Button(th, l)
						btn := material.Button(th, greenButton, l)
						if green {
							btn.Background = color.RGBA{A: 0xff, R: 0x9e, G: 0x9d, B: 0x24}
						}
						btn.Layout(gtx, greenButton)
						return btn.Layout(gtx)
					})
				}),
				layout.Rigid(func() {
					in.Layout(gtx, func() {
						material.Clickable(gtx, flatBtn, func() {
							layout.UniformInset(unit.Dp(12)).Layout(gtx, func() {
								layout.Center.Layout(gtx, func() {
									material.Body1(th, "Flat").Layout(gtx)
								})
				layout.Rigid(func(gtx C) D {
					return in.Layout(gtx, func(gtx C) D {
						return material.Clickable(gtx, flatBtn, func(gtx C) D {
							return layout.UniformInset(unit.Dp(12)).Layout(gtx, func(gtx C) D {
								return layout.Center.Layout(gtx, material.Body1(th, "Flat").Layout)
							})
						})
					})
				}),
			)
		},
		func() {
			material.ProgressBar(th).Layout(gtx, progress)
		},
		func() {
			layout.Flex{Alignment: layout.Middle}.Layout(gtx,
				layout.Rigid(func() {
					material.CheckBox(th, "Checkbox").Layout(gtx, checkbox)
				}),
				layout.Rigid(func() {
					layout.Inset{Left: unit.Dp(16)}.Layout(gtx, func() {
						material.Switch(th).Layout(gtx, swtch)
					})
		material.ProgressBar(th, progress).Layout,
		func(gtx C) D {
			return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
				layout.Rigid(
					material.CheckBox(th, checkbox, "Checkbox").Layout,
				),
				layout.Rigid(func(gtx C) D {
					return layout.Inset{Left: unit.Dp(16)}.Layout(gtx,
						material.Switch(th, swtch).Layout,
					)
				}),
			)
		},
		func() {
			layout.Flex{}.Layout(gtx,
				layout.Rigid(func() {
					material.RadioButton(th, "r1", "RadioButton1").Layout(gtx, radioButtonsGroup)
				}),
				layout.Rigid(func() {
					material.RadioButton(th, "r2", "RadioButton2").Layout(gtx, radioButtonsGroup)
				}),
				layout.Rigid(func() {
					material.RadioButton(th, "r3", "RadioButton3").Layout(gtx, radioButtonsGroup)
				}),
		func(gtx C) D {
			return layout.Flex{}.Layout(gtx,
				layout.Rigid(material.RadioButton(th, radioButtonsGroup, "r1", "RadioButton1").Layout),
				layout.Rigid(material.RadioButton(th, radioButtonsGroup, "r2", "RadioButton2").Layout),
				layout.Rigid(material.RadioButton(th, radioButtonsGroup, "r3", "RadioButton3").Layout),
			)
		},
	}

	list.Layout(gtx, len(widgets), func(i int) {
		layout.UniformInset(unit.Dp(16)).Layout(gtx, widgets[i])
	return list.Layout(gtx, len(widgets), func(gtx C, i int) D {
		return layout.UniformInset(unit.Dp(16)).Layout(gtx, widgets[i])
	})
}


M example/tabs/slider.go => example/tabs/slider.go +12 -11
@@ 26,13 26,13 @@ type Slider struct {
}

// PushLeft pushes the existing widget to the left.
func (s *Slider) PushLeft(gtx *layout.Context) { s.push = 1 }
func (s *Slider) PushLeft() { s.push = 1 }

// PushRight pushes the existing widget to the right.
func (s *Slider) PushRight(gtx *layout.Context) { s.push = -1 }
func (s *Slider) PushRight() { s.push = -1 }

// Layout lays out widget that can be pushed.
func (s *Slider) Layout(gtx *layout.Context, w layout.Widget) {
func (s *Slider) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
	if s.push != 0 {
		s.last, s.next = s.next, new(op.Ops)
		s.offset = float32(s.push)


@@ 68,20 68,20 @@ func (s *Slider) Layout(gtx *layout.Context, w layout.Widget) {
		op.InvalidateOp{}.Add(gtx.Ops)
	}

	var dims layout.Dimensions
	{
		prev := gtx.Ops
		if s.next == nil {
			s.next = new(op.Ops)
		}
		s.next.Reset()
		gtx := gtx
		gtx.Ops = s.next
		w()
		gtx.Ops = prev
		dims = w(gtx)
	}

	if s.offset == 0 {
		op.CallOp{Ops: s.next}.Add(gtx.Ops)
		return
		return dims
	}

	var stack op.StackOp


@@ 92,25 92,26 @@ func (s *Slider) Layout(gtx *layout.Context, w layout.Widget) {

	if s.offset > 0 {
		op.TransformOp{}.Offset(f32.Point{
			X: float32(gtx.Dimensions.Size.X) * (offset - 1),
			X: float32(dims.Size.X) * (offset - 1),
		}).Add(gtx.Ops)
		op.CallOp{Ops: s.last}.Add(gtx.Ops)

		op.TransformOp{}.Offset(f32.Point{
			X: float32(gtx.Dimensions.Size.X),
			X: float32(dims.Size.X),
		}).Add(gtx.Ops)
		op.CallOp{Ops: s.next}.Add(gtx.Ops)
	} else {
		op.TransformOp{}.Offset(f32.Point{
			X: float32(gtx.Dimensions.Size.X) * (offset + 1),
			X: float32(dims.Size.X) * (offset + 1),
		}).Add(gtx.Ops)
		op.CallOp{Ops: s.last}.Add(gtx.Ops)

		op.TransformOp{}.Offset(f32.Point{
			X: float32(-gtx.Dimensions.Size.X),
			X: float32(-dims.Size.X),
		}).Add(gtx.Ops)
		op.CallOp{Ops: s.next}.Add(gtx.Ops)
	}
	return dims
}

// smooth handles -1 to 1 with ease-in-out cubic easing func.

M example/tabs/tabs.go => example/tabs/tabs.go +32 -25
@@ 13,6 13,7 @@ import (
	"gioui.org/f32"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/paint"
	"gioui.org/unit"
	"gioui.org/widget"


@@ 34,14 35,14 @@ func main() {
func loop(w *app.Window) error {
	gofont.Register()
	th := material.NewTheme()
	gtx := new(layout.Context)
	var ops op.Ops
	for {
		e := <-w.Events()
		switch e := e.(type) {
		case system.DestroyEvent:
			return e.Err
		case system.FrameEvent:
			gtx.Reset(e.Queue, e.Config, e.Size)
			gtx := layout.NewContext(&ops, e.Queue, e.Config, e.Size)
			drawTabs(gtx, th)
			e.Frame(gtx.Ops)
		}


@@ 70,32 71,38 @@ func init() {
	}
}

func drawTabs(gtx *layout.Context, th *material.Theme) {
	layout.Flex{Axis: layout.Vertical}.Layout(gtx,
		layout.Rigid(func() {
			tabs.list.Layout(gtx, len(tabs.tabs), func(tabIdx int) {
type (
	C = layout.Context
	D = layout.Dimensions
)

func drawTabs(gtx layout.Context, th *material.Theme) layout.Dimensions {
	return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
		layout.Rigid(func(gtx C) D {
			return tabs.list.Layout(gtx, len(tabs.tabs), func(gtx C, tabIdx int) D {
				t := &tabs.tabs[tabIdx]
				if t.btn.Clicked(gtx) {
					if tabs.selected < tabIdx {
						slider.PushLeft(gtx)
						slider.PushLeft()
					} else if tabs.selected > tabIdx {
						slider.PushRight(gtx)
						slider.PushRight()
					}
					tabs.selected = tabIdx
				}
				var tabWidth int
				layout.Stack{Alignment: layout.S}.Layout(gtx,
					layout.Stacked(func() {
						material.Clickable(gtx, &t.btn, func() {
							layout.UniformInset(unit.Sp(12)).Layout(gtx, func() {
								material.H6(th, t.Title).Layout(gtx)
							})
				return layout.Stack{Alignment: layout.S}.Layout(gtx,
					layout.Stacked(func(gtx C) D {
						dims := material.Clickable(gtx, &t.btn, func(gtx C) D {
							return layout.UniformInset(unit.Sp(12)).Layout(gtx,
								material.H6(th, t.Title).Layout,
							)
						})
						tabWidth = gtx.Dimensions.Size.X
						tabWidth = dims.Size.X
						return dims
					}),
					layout.Stacked(func() {
					layout.Stacked(func(gtx C) D {
						if tabs.selected != tabIdx {
							return
							return layout.Dimensions{}
						}
						paint.ColorOp{Color: th.Color.Primary}.Add(gtx.Ops)
						tabHeight := gtx.Px(unit.Dp(4))


@@ 105,25 112,25 @@ func drawTabs(gtx *layout.Context, th *material.Theme) {
								Y: float32(tabHeight),
							},
						}}.Add(gtx.Ops)
						gtx.Dimensions = layout.Dimensions{
						return layout.Dimensions{
							Size: image.Point{X: tabWidth, Y: tabHeight},
						}
					}),
				)
			})
		}),
		layout.Flexed(1, func() {
			slider.Layout(gtx, func() {
		layout.Flexed(1, func(gtx C) D {
			return slider.Layout(gtx, func(gtx C) D {
				fill(gtx, dynamicColor(tabs.selected))
				layout.Center.Layout(gtx, func() {
					material.H1(th, fmt.Sprintf("Tab content #%d", tabs.selected+1)).Layout(gtx)
				})
				return layout.Center.Layout(gtx,
					material.H1(th, fmt.Sprintf("Tab content #%d", tabs.selected+1)).Layout,
				)
			})
		}),
	)
}

func bounds(gtx *layout.Context) f32.Rectangle {
func bounds(gtx layout.Context) f32.Rectangle {
	cs := gtx.Constraints
	d := cs.Min
	return f32.Rectangle{


@@ 131,7 138,7 @@ func bounds(gtx *layout.Context) f32.Rectangle {
	}
}

func fill(gtx *layout.Context, col color.RGBA) {
func fill(gtx layout.Context, col color.RGBA) {
	dr := bounds(gtx)
	paint.ColorOp{Color: col}.Add(gtx.Ops)
	paint.PaintOp{Rect: dr}.Add(gtx.Ops)