diff --git a/cmd/go.mod b/cmd/go.mod index 6d1497e2..c583b72e 100644 --- a/cmd/go.mod +++ b/cmd/go.mod @@ -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 diff --git a/cmd/go.sum b/cmd/go.sum index f11d3be2..71d0e62d 100644 --- a/cmd/go.sum +++ b/cmd/go.sum @@ -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= diff --git a/cmd/gogio/testdata/red.go b/cmd/gogio/testdata/red.go index b71d02c5..67ef722b 100644 --- a/cmd/gogio/testdata/red.go +++ b/cmd/gogio/testdata/red.go @@ -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} } diff --git a/example/glfw/main.go b/example/glfw/main.go index fe45ad20..2946851e 100644 --- a/example/glfw/main.go +++ b/example/glfw/main.go @@ -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) { diff --git a/example/go.mod b/example/go.mod index cec3fa55..a4a42312 100644 --- a/example/go.mod +++ b/example/go.mod @@ -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 diff --git a/example/go.sum b/example/go.sum index 0fc3c5da..40d3f18a 100644 --- a/example/go.sum +++ b/example/go.sum @@ -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= diff --git a/example/gophers/main.go b/example/gophers/main.go index 73a925d3..af916f65 100644 --- a/example/gophers/main.go +++ b/example/gophers/main.go @@ -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) } diff --git a/example/gophers/main_test.go b/example/gophers/main_test.go index 5b7018d1..bc147f75 100644 --- a/example/gophers/main_test.go +++ b/example/gophers/main_test.go @@ -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) } } diff --git a/example/gophers/ui.go b/example/gophers/ui.go index 4890dba2..60903832 100644 --- a/example/gophers/ui.go +++ b/example/gophers/ui.go @@ -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 diff --git a/example/hello/hello.go b/example/hello/hello.go index 5bfc6f31..8cc34091 100644 --- a/example/hello/hello.go +++ b/example/hello/hello.go @@ -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 diff --git a/example/kitchen/kitchen.go b/example/kitchen/kitchen.go index 7b89fa05..1c60f29d 100644 --- a/example/kitchen/kitchen.go +++ b/example/kitchen/kitchen.go @@ -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]) }) } diff --git a/example/tabs/slider.go b/example/tabs/slider.go index 08f21c90..ba192fda 100644 --- a/example/tabs/slider.go +++ b/example/tabs/slider.go @@ -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. diff --git a/example/tabs/tabs.go b/example/tabs/tabs.go index 50e65273..5c20ddd0 100644 --- a/example/tabs/tabs.go +++ b/example/tabs/tabs.go @@ -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)