diff --git a/widget/bool.go b/widget/bool.go index a15b50c1..1a52ece1 100644 --- a/widget/bool.go +++ b/widget/bool.go @@ -36,8 +36,8 @@ func (b *Bool) History() []Press { return b.clk.History() } -func (b *Bool) Layout(gtx layout.Context) layout.Dimensions { - dims := b.clk.Layout(gtx) +func (b *Bool) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions { + dims := b.clk.Layout(gtx, w) for b.clk.Clicked() { b.Value = !b.Value b.changed = true diff --git a/widget/button.go b/widget/button.go index 17e00a44..0775462e 100644 --- a/widget/button.go +++ b/widget/button.go @@ -10,6 +10,7 @@ import ( "gioui.org/gesture" "gioui.org/io/key" "gioui.org/layout" + "gioui.org/op" "gioui.org/op/clip" ) @@ -90,10 +91,14 @@ func (b *Clickable) History() []Press { } // Layout and update the button state -func (b *Clickable) Layout(gtx layout.Context) layout.Dimensions { +func (b *Clickable) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions { b.update(gtx) - defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Push(gtx.Ops).Pop() + m := op.Record(gtx.Ops) + dims := w(gtx) + c := m.Stop() + defer clip.Rect(image.Rectangle{Max: dims.Size}).Push(gtx.Ops).Pop() b.click.Add(gtx.Ops) + c.Add(gtx.Ops) for len(b.history) > 0 { c := b.history[0] if c.End.IsZero() || gtx.Now.Sub(c.End) < 1*time.Second { @@ -102,7 +107,7 @@ func (b *Clickable) Layout(gtx layout.Context) layout.Dimensions { n := copy(b.history, b.history[1:]) b.history = b.history[:n] } - return layout.Dimensions{Size: gtx.Constraints.Min} + return dims } // update the button state by processing events. diff --git a/widget/example_test.go b/widget/example_test.go index c4714061..f72c186c 100644 --- a/widget/example_test.go +++ b/widget/example_test.go @@ -28,11 +28,12 @@ func ExampleClickable_passthrough() { // widget lays out two buttons on top of each other. widget := func() { - button1.Layout(gtx) + content := func(gtx layout.Context) layout.Dimensions { return layout.Dimensions{Size: gtx.Constraints.Min} } + button1.Layout(gtx, content) // button2 completely covers button1, but pass-through allows pointer // events to pass through to button1. defer pointer.PassOp{}.Push(gtx.Ops).Pop() - button2.Layout(gtx) + button2.Layout(gtx, content) } // The first layout and call to Frame declare the Clickable handlers diff --git a/widget/material/button.go b/widget/material/button.go index 46965369..0136344b 100644 --- a/widget/material/button.go +++ b/widget/material/button.go @@ -86,17 +86,18 @@ func IconButton(th *Theme, button *widget.Clickable, icon *widget.Icon) IconButt // Clickable lays out a rectangular clickable widget without further // decoration. func Clickable(gtx layout.Context, button *widget.Clickable, w layout.Widget) layout.Dimensions { - return layout.Stack{}.Layout(gtx, - layout.Expanded(button.Layout), - layout.Expanded(func(gtx layout.Context) layout.Dimensions { - defer clip.Rect{Max: gtx.Constraints.Min}.Push(gtx.Ops).Pop() - for _, c := range button.History() { - drawInk(gtx, c) - } - return layout.Dimensions{Size: gtx.Constraints.Min} - }), - layout.Stacked(w), - ) + return button.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Stack{}.Layout(gtx, + layout.Expanded(func(gtx layout.Context) layout.Dimensions { + defer clip.Rect{Max: gtx.Constraints.Min}.Push(gtx.Ops).Pop() + for _, c := range button.History() { + drawInk(gtx, c) + } + return layout.Dimensions{Size: gtx.Constraints.Min} + }), + layout.Stacked(w), + ) + }) } func (b ButtonStyle) Layout(gtx layout.Context) layout.Dimensions { @@ -114,74 +115,78 @@ func (b ButtonStyle) Layout(gtx layout.Context) layout.Dimensions { func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions { min := gtx.Constraints.Min - return layout.Stack{Alignment: layout.Center}.Layout(gtx, - layout.Expanded(func(gtx layout.Context) layout.Dimensions { - rr := float32(gtx.Px(b.CornerRadius)) - defer clip.UniformRRect(f32.Rectangle{Max: f32.Point{ - X: float32(gtx.Constraints.Min.X), - Y: float32(gtx.Constraints.Min.Y), - }}, rr).Push(gtx.Ops).Pop() - background := b.Background - switch { - case gtx.Queue == nil: - background = f32color.Disabled(b.Background) - case b.Button.Hovered(): - background = f32color.Hovered(b.Background) - } - paint.Fill(gtx.Ops, background) - for _, c := range b.Button.History() { - drawInk(gtx, c) - } - return layout.Dimensions{Size: gtx.Constraints.Min} - }), - layout.Stacked(func(gtx layout.Context) layout.Dimensions { - gtx.Constraints.Min = min - return layout.Center.Layout(gtx, w) - }), - layout.Expanded(b.Button.Layout), - ) + return b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Stack{Alignment: layout.Center}.Layout(gtx, + layout.Expanded(func(gtx layout.Context) layout.Dimensions { + rr := float32(gtx.Px(b.CornerRadius)) + defer clip.UniformRRect(f32.Rectangle{Max: f32.Point{ + X: float32(gtx.Constraints.Min.X), + Y: float32(gtx.Constraints.Min.Y), + }}, rr).Push(gtx.Ops).Pop() + background := b.Background + switch { + case gtx.Queue == nil: + background = f32color.Disabled(b.Background) + case b.Button.Hovered(): + background = f32color.Hovered(b.Background) + } + paint.Fill(gtx.Ops, background) + for _, c := range b.Button.History() { + drawInk(gtx, c) + } + return layout.Dimensions{Size: gtx.Constraints.Min} + }), + layout.Stacked(func(gtx layout.Context) layout.Dimensions { + gtx.Constraints.Min = min + return layout.Center.Layout(gtx, w) + }), + ) + }) } func (b IconButtonStyle) Layout(gtx layout.Context) layout.Dimensions { - return layout.Stack{Alignment: layout.Center}.Layout(gtx, - layout.Expanded(func(gtx layout.Context) layout.Dimensions { - sizex, sizey := gtx.Constraints.Min.X, gtx.Constraints.Min.Y - sizexf, sizeyf := float32(sizex), float32(sizey) - rr := (sizexf + sizeyf) * .25 - defer clip.UniformRRect(f32.Rectangle{ - Max: f32.Point{X: sizexf, Y: sizeyf}, - }, rr).Push(gtx.Ops).Pop() - background := b.Background - switch { - case gtx.Queue == nil: - background = f32color.Disabled(b.Background) - case b.Button.Hovered(): - background = f32color.Hovered(b.Background) - } - paint.Fill(gtx.Ops, background) - for _, c := range b.Button.History() { - drawInk(gtx, c) - } - return layout.Dimensions{Size: gtx.Constraints.Min} - }), - layout.Stacked(func(gtx layout.Context) layout.Dimensions { - return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - size := gtx.Px(b.Size) - if b.Icon != nil { - gtx.Constraints.Min = image.Point{X: size} - b.Icon.Layout(gtx, b.Color) + m := op.Record(gtx.Ops) + dims := b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Stack{Alignment: layout.Center}.Layout(gtx, + layout.Expanded(func(gtx layout.Context) layout.Dimensions { + sizex, sizey := gtx.Constraints.Min.X, gtx.Constraints.Min.Y + sizexf, sizeyf := float32(sizex), float32(sizey) + rr := (sizexf + sizeyf) * .25 + defer clip.UniformRRect(f32.Rectangle{ + Max: f32.Point{X: sizexf, Y: sizeyf}, + }, rr).Push(gtx.Ops).Pop() + background := b.Background + switch { + case gtx.Queue == nil: + background = f32color.Disabled(b.Background) + case b.Button.Hovered(): + background = f32color.Hovered(b.Background) } - return layout.Dimensions{ - Size: image.Point{X: size, Y: size}, + paint.Fill(gtx.Ops, background) + for _, c := range b.Button.History() { + drawInk(gtx, c) } - }) - }), - layout.Expanded(func(gtx layout.Context) layout.Dimensions { - bounds := f32.Rectangle{Max: layout.FPt(gtx.Constraints.Min)} - defer clip.Ellipse(bounds).Push(gtx.Ops).Pop() - return b.Button.Layout(gtx) - }), - ) + return layout.Dimensions{Size: gtx.Constraints.Min} + }), + layout.Stacked(func(gtx layout.Context) layout.Dimensions { + return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + size := gtx.Px(b.Size) + if b.Icon != nil { + gtx.Constraints.Min = image.Point{X: size} + b.Icon.Layout(gtx, b.Color) + } + return layout.Dimensions{ + Size: image.Point{X: size, Y: size}, + } + }) + }), + ) + }) + c := m.Stop() + bounds := f32.Rectangle{Max: layout.FPt(dims.Size)} + defer clip.Ellipse(bounds).Push(gtx.Ops).Pop() + c.Add(gtx.Ops) + return dims } func drawInk(gtx layout.Context, c widget.Press) { diff --git a/widget/material/checkbox.go b/widget/material/checkbox.go index 96238459..505966ee 100644 --- a/widget/material/checkbox.go +++ b/widget/material/checkbox.go @@ -3,10 +3,7 @@ package material import ( - "image" - "gioui.org/layout" - "gioui.org/op/clip" "gioui.org/unit" "gioui.org/widget" ) @@ -34,9 +31,7 @@ func CheckBox(th *Theme, checkBox *widget.Bool, label string) CheckBoxStyle { // Layout updates the checkBox and displays it. func (c CheckBoxStyle) Layout(gtx layout.Context) layout.Dimensions { - dims := c.layout(gtx, c.CheckBox.Value, c.CheckBox.Hovered()) - defer clip.Rect(image.Rectangle{Max: dims.Size}).Push(gtx.Ops).Pop() - gtx.Constraints.Min = dims.Size - c.CheckBox.Layout(gtx) - return dims + return c.CheckBox.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return c.layout(gtx, c.CheckBox.Value, c.CheckBox.Hovered()) + }) } diff --git a/widget/material/switch.go b/widget/material/switch.go index 1ba182aa..0853b630 100644 --- a/widget/material/switch.go +++ b/widget/material/switch.go @@ -124,8 +124,9 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { defer op.Offset(clickOff).Push(gtx.Ops).Pop() sz := image.Pt(clickSize, clickSize) defer clip.Ellipse(f32.Rectangle{Max: layout.FPt(sz)}).Push(gtx.Ops).Pop() - gtx.Constraints.Min = sz - s.Switch.Layout(gtx) + s.Switch.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Dimensions{Size: sz} + }) dims := image.Point{X: trackWidth, Y: thumbSize} return layout.Dimensions{Size: dims}