diff --git a/layout/context.go b/layout/context.go index 33e0e691..dc35e2ff 100644 --- a/layout/context.go +++ b/layout/context.go @@ -20,63 +20,54 @@ type Context struct { // Constraints track the constraints for the active widget or // layout. Constraints Constraints - // Dimensions track the result of the most recent layout - // operation. - Dimensions Dimensions - cfg system.Config - queue event.Queue + Config system.Config + Queue event.Queue *op.Ops } -// layout a widget with a set of constraints and return its -// dimensions. The widget dimensions are constrained and the previous -// constraints are restored after layout. -func ctxLayout(gtx *Context, cs Constraints, w Widget) Dimensions { - saved := gtx.Constraints - gtx.Constraints = cs - gtx.Dimensions = Dimensions{} - w() - gtx.Dimensions.Size = cs.Constrain(gtx.Dimensions.Size) - gtx.Constraints = saved - return gtx.Dimensions -} - -// Reset the context. The constraints' minimum and maximum values are -// set to the size. -func (c *Context) Reset(q event.Queue, cfg system.Config, size image.Point) { - c.Constraints = Constraints{Min: size, Max: size} - c.Dimensions = Dimensions{} - c.cfg = cfg - c.queue = q - if c.Ops == nil { - c.Ops = new(op.Ops) +// NewContext is a shorthand for +// +// Context{ +// Ops: ops, +// Queue: q, +// Config: cfg, +// Constraints: Exact(size), +// } +// +// NewContext calls ops.Reset. +func NewContext(ops *op.Ops, q event.Queue, cfg system.Config, size image.Point) Context { + ops.Reset() + return Context{ + Ops: ops, + Queue: q, + Config: cfg, + Constraints: Exact(size), } - c.Ops.Reset() } // Now returns the configuration time or the zero time. -func (c *Context) Now() time.Time { - if c.cfg == nil { +func (c Context) Now() time.Time { + if c.Config == nil { return time.Time{} } - return c.cfg.Now() + return c.Config.Now() } // Px maps the value to pixels. If no configuration is set, // Px returns the rounded value of v. -func (c *Context) Px(v unit.Value) int { - if c.cfg == nil { +func (c Context) Px(v unit.Value) int { + if c.Config == nil { return int(math.Round(float64(v.V))) } - return c.cfg.Px(v) + return c.Config.Px(v) } // Events returns the events available for the key. If no // queue is configured, Events returns nil. -func (c *Context) Events(k event.Tag) []event.Event { - if c.queue == nil { +func (c Context) Events(k event.Tag) []event.Event { + if c.Queue == nil { return nil } - return c.queue.Events(k) + return c.Queue.Events(k) } diff --git a/layout/doc.go b/layout/doc.go index 4fe8b4a7..d318765b 100644 --- a/layout/doc.go +++ b/layout/doc.go @@ -13,17 +13,16 @@ in an implicit Context to keep the Widget declaration short. For example, to add space above a widget: - gtx := new(layout.Context) - gtx.Reset(...) + var gtx layout.Context // Configure a top inset. inset := layout.Inset{Top: unit.Dp(8), ...} // Use the inset to lay out a widget. inset.Layout(gtx, func() { - // Lay out widget and determine its size given the constraints. + // Lay out widget and determine its size given the constraints + // in gtx.Constraints. ... - dims := layout.Dimensions{...} - gtx.Dimensions = dims + return layout.Dimensions{...} }) Note that the example does not generate any garbage even though the @@ -37,10 +36,10 @@ be created from a few generic layouts. This example both aligns and insets a child: inset := layout.Inset{...} - inset.Layout(gtx, func() { + inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { align := layout.Align(...) - align.Layout(gtx, func() { - widget.Layout(gtx, ...) + return align.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return widget.Layout(gtx, ...) }) }) diff --git a/layout/example_test.go b/layout/example_test.go index a2710d28..a7ad1c21 100644 --- a/layout/example_test.go +++ b/layout/example_test.go @@ -5,24 +5,29 @@ import ( "image" "gioui.org/layout" + "gioui.org/op" "gioui.org/unit" ) func ExampleInset() { - gtx := new(layout.Context) - gtx.Reset(nil, nil, image.Point{X: 100, Y: 100}) - // Loose constraints with no minimal size. - gtx.Constraints.Min = image.Point{} + gtx := layout.Context{ + Ops: new(op.Ops), + // Loose constraints with no minimal size. + Constraints: layout.Constraints{ + Max: image.Point{X: 100, Y: 100}, + }, + } // Inset all edges by 10. inset := layout.UniformInset(unit.Dp(10)) - inset.Layout(gtx, func() { + dims := inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { // Lay out a 50x50 sized widget. - layoutWidget(gtx, 50, 50) - fmt.Println(gtx.Dimensions.Size) + dims := layoutWidget(gtx, 50, 50) + fmt.Println(dims.Size) + return dims }) - fmt.Println(gtx.Dimensions.Size) + fmt.Println(dims.Size) // Output: // (50,50) @@ -30,17 +35,20 @@ func ExampleInset() { } func ExampleDirection() { - gtx := new(layout.Context) - // Rigid constraints with both minimum and maximum set. - gtx.Reset(nil, nil, image.Point{X: 100, Y: 100}) + gtx := layout.Context{ + Ops: new(op.Ops), + // Rigid constraints with both minimum and maximum set. + Constraints: layout.Exact(image.Point{X: 100, Y: 100}), + } - layout.Center.Layout(gtx, func() { + dims := layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions { // Lay out a 50x50 sized widget. - layoutWidget(gtx, 50, 50) - fmt.Println(gtx.Dimensions.Size) + dims := layoutWidget(gtx, 50, 50) + fmt.Println(dims.Size) + return dims }) - fmt.Println(gtx.Dimensions.Size) + fmt.Println(dims.Size) // Output: // (50,50) @@ -48,19 +56,22 @@ func ExampleDirection() { } func ExampleFlex() { - gtx := new(layout.Context) - gtx.Reset(nil, nil, image.Point{X: 100, Y: 100}) + gtx := layout.Context{ + Ops: new(op.Ops), + // Rigid constraints with both minimum and maximum set. + Constraints: layout.Exact(image.Point{X: 100, Y: 100}), + } layout.Flex{}.Layout(gtx, // Rigid 10x10 widget. - layout.Rigid(func() { + layout.Rigid(func(gtx layout.Context) layout.Dimensions { fmt.Printf("Rigid: %v\n", gtx.Constraints) - layoutWidget(gtx, 10, 10) + return layoutWidget(gtx, 10, 10) }), // Child with 50% space allowance. - layout.Flexed(0.5, func() { + layout.Flexed(0.5, func(gtx layout.Context) layout.Dimensions { fmt.Printf("50%%: %v\n", gtx.Constraints) - layoutWidget(gtx, 10, 10) + return layoutWidget(gtx, 10, 10) }), ) @@ -70,19 +81,22 @@ func ExampleFlex() { } func ExampleStack() { - gtx := new(layout.Context) - gtx.Reset(nil, nil, image.Point{X: 100, Y: 100}) - gtx.Constraints.Min = image.Point{} + gtx := layout.Context{ + Ops: new(op.Ops), + Constraints: layout.Constraints{ + Max: image.Point{X: 100, Y: 100}, + }, + } layout.Stack{}.Layout(gtx, // Force widget to the same size as the second. - layout.Expanded(func() { + layout.Expanded(func(gtx layout.Context) layout.Dimensions { fmt.Printf("Expand: %v\n", gtx.Constraints) - layoutWidget(gtx, 10, 10) + return layoutWidget(gtx, 10, 10) }), // Rigid 50x50 widget. - layout.Stacked(func() { - layoutWidget(gtx, 50, 50) + layout.Stacked(func(gtx layout.Context) layout.Dimensions { + return layoutWidget(gtx, 50, 50) }), ) @@ -91,17 +105,20 @@ func ExampleStack() { } func ExampleList() { - gtx := new(layout.Context) - gtx.Reset(nil, nil, image.Point{X: 100, Y: 100}) + gtx := layout.Context{ + Ops: new(op.Ops), + // Rigid constraints with both minimum and maximum set. + Constraints: layout.Exact(image.Point{X: 100, Y: 100}), + } // The list is 1e6 elements, but only 5 fit the constraints. const listLen = 1e6 var list layout.List count := 0 - list.Layout(gtx, listLen, func(i int) { + list.Layout(gtx, listLen, func(gtx layout.Context, i int) layout.Dimensions { count++ - layoutWidget(gtx, 20, 20) + return layoutWidget(gtx, 20, 20) }) fmt.Println(count) @@ -110,8 +127,8 @@ func ExampleList() { // 5 } -func layoutWidget(ctx *layout.Context, width, height int) { - ctx.Dimensions = layout.Dimensions{ +func layoutWidget(ctx layout.Context, width, height int) layout.Dimensions { + return layout.Dimensions{ Size: image.Point{ X: width, Y: height, diff --git a/layout/flex.go b/layout/flex.go index 39f5b70c..a31077e0 100644 --- a/layout/flex.go +++ b/layout/flex.go @@ -74,7 +74,7 @@ func Flexed(weight float32, widget Widget) FlexChild { // Layout a list of children. The position of the children are // determined by the specified order, but Rigid children are laid out // before Flexed children. -func (f Flex) Layout(gtx *Context, children ...FlexChild) { +func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { size := 0 // Lay out Rigid children. for i, child := range children { @@ -91,7 +91,9 @@ func (f Flex) Layout(gtx *Context, children ...FlexChild) { cs = axisConstraints(f.Axis, 0, mainMax, crossMin, crossMax) var m op.MacroOp m.Record(gtx.Ops) - dims := ctxLayout(gtx, cs, child.widget) + gtx := gtx + gtx.Constraints = cs + dims := child.widget(gtx) m.Stop() sz := axisMain(f.Axis, dims.Size) size += sz @@ -124,7 +126,9 @@ func (f Flex) Layout(gtx *Context, children ...FlexChild) { cs = axisConstraints(f.Axis, flexSize, flexSize, crossMin, crossMax) var m op.MacroOp m.Record(gtx.Ops) - dims := ctxLayout(gtx, cs, child.widget) + gtx := gtx + gtx.Constraints = cs + dims := child.widget(gtx) m.Stop() sz := axisMain(f.Axis, dims.Size) size += sz @@ -200,7 +204,7 @@ func (f Flex) Layout(gtx *Context, children ...FlexChild) { mainSize += space / (len(children) * 2) } sz := axisPoint(f.Axis, mainSize, maxCross) - gtx.Dimensions = Dimensions{Size: sz, Baseline: sz.Y - maxBaseline} + return Dimensions{Size: sz, Baseline: sz.Y - maxBaseline} } func axisPoint(a Axis, main, cross int) image.Point { diff --git a/layout/layout.go b/layout/layout.go index 6094a213..a92df55b 100644 --- a/layout/layout.go +++ b/layout/layout.go @@ -40,7 +40,7 @@ type Direction uint8 // Widget is a function scope for drawing, processing events and // computing dimensions for a user interface element. -type Widget func() +type Widget func(gtx Context) Dimensions const ( Start Alignment = iota @@ -111,7 +111,7 @@ type Inset struct { } // Layout a widget. -func (in Inset) Layout(gtx *Context, w Widget) { +func (in Inset) Layout(gtx Context, w Widget) Dimensions { top := gtx.Px(in.Top) right := gtx.Px(in.Right) bottom := gtx.Px(in.Bottom) @@ -138,9 +138,10 @@ func (in Inset) Layout(gtx *Context, w Widget) { var stack op.StackOp stack.Push(gtx.Ops) op.TransformOp{}.Offset(FPt(image.Point{X: left, Y: top})).Add(gtx.Ops) - dims := ctxLayout(gtx, mcs, w) + gtx.Constraints = mcs + dims := w(gtx) stack.Pop() - gtx.Dimensions = Dimensions{ + return Dimensions{ Size: dims.Size.Add(image.Point{X: right + left, Y: top + bottom}), Baseline: dims.Baseline + bottom, } @@ -153,13 +154,12 @@ func UniformInset(v unit.Value) Inset { } // Layout a widget according to the direction. -func (a Direction) Layout(gtx *Context, w Widget) { +func (a Direction) Layout(gtx Context, w Widget) Dimensions { var macro op.MacroOp macro.Record(gtx.Ops) cs := gtx.Constraints - mcs := cs - mcs.Min = image.Point{} - dims := ctxLayout(gtx, mcs, w) + gtx.Constraints.Min = image.Point{} + dims := w(gtx) macro.Stop() sz := dims.Size if sz.X < cs.Min.X { @@ -186,7 +186,7 @@ func (a Direction) Layout(gtx *Context, w Widget) { op.TransformOp{}.Offset(FPt(p)).Add(gtx.Ops) macro.Add() stack.Pop() - gtx.Dimensions = Dimensions{ + return Dimensions{ Size: sz, Baseline: dims.Baseline + sz.Y - dims.Size.Y - p.Y, } diff --git a/layout/list.go b/layout/list.go index cd09d79e..a0022b21 100644 --- a/layout/list.go +++ b/layout/list.go @@ -29,7 +29,7 @@ type List struct { // Alignment is the cross axis alignment of list elements. Alignment Alignment - ctx *Context + ctx Context macro op.MacroOp child op.MacroOp scroll gesture.Scroll @@ -51,7 +51,7 @@ type List struct { // ListElement is a function that computes the dimensions of // a list element. -type ListElement func(index int) +type ListElement func(gtx Context, index int) Dimensions type iterationDir uint8 @@ -82,7 +82,7 @@ const ( const inf = 1e6 // init prepares the list for iterating through its children with next. -func (l *List) init(gtx *Context, len int) { +func (l *List) init(gtx Context, len int) { if l.more() { panic("unfinished child") } @@ -100,16 +100,15 @@ func (l *List) init(gtx *Context, len int) { } // Layout the List. -func (l *List) Layout(gtx *Context, len int, w ListElement) { +func (l *List) Layout(gtx Context, len int, w ListElement) Dimensions { for l.init(gtx, len); l.more(); l.next() { crossMin, crossMax := axisCrossConstraint(l.Axis, l.ctx.Constraints) cs := axisConstraints(l.Axis, 0, inf, crossMin, crossMax) i := l.index() - l.end(ctxLayout(gtx, cs, func() { - w(i) - })) + gtx.Constraints = cs + l.end(w(gtx, i)) } - gtx.Dimensions = l.layout() + return l.layout() } func (l *List) scrollToEnd() bool { diff --git a/layout/stack.go b/layout/stack.go index a083b7fc..447547b2 100644 --- a/layout/stack.go +++ b/layout/stack.go @@ -47,18 +47,18 @@ func Expanded(w Widget) StackChild { // Layout a stack of children. The position of the children are // determined by the specified order, but Stacked children are laid out // before Expanded children. -func (s Stack) Layout(gtx *Context, children ...StackChild) { +func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions { var maxSZ image.Point // First lay out Stacked children. for i, w := range children { if w.expanded { continue } - cs := gtx.Constraints - cs.Min = image.Pt(0, 0) var m op.MacroOp m.Record(gtx.Ops) - dims := ctxLayout(gtx, cs, w.widget) + gtx := gtx + gtx.Constraints.Min = image.Pt(0, 0) + dims := w.widget(gtx) m.Stop() if w := dims.Size.X; w > maxSZ.X { maxSZ.X = w @@ -76,10 +76,11 @@ func (s Stack) Layout(gtx *Context, children ...StackChild) { } var m op.MacroOp m.Record(gtx.Ops) - cs := Constraints{ + gtx := gtx + gtx.Constraints = Constraints{ Min: maxSZ, Max: gtx.Constraints.Max, } - dims := ctxLayout(gtx, cs, w.widget) + dims := w.widget(gtx) m.Stop() if w := dims.Size.X; w > maxSZ.X { maxSZ.X = w @@ -119,7 +120,7 @@ func (s Stack) Layout(gtx *Context, children ...StackChild) { } } } - gtx.Dimensions = Dimensions{ + return Dimensions{ Size: maxSZ, Baseline: baseline, } diff --git a/layout/stack_test.go b/layout/stack_test.go index 1fc619f5..f46fbfcd 100644 --- a/layout/stack_test.go +++ b/layout/stack_test.go @@ -5,22 +5,27 @@ package layout import ( "image" "testing" + + "gioui.org/op" ) func TestStack(t *testing.T) { - var gtx Context - gtx.Reset(nil, nil, image.Point{X: 100, Y: 100}) - gtx.Constraints.Min = image.Point{} + gtx := Context{ + Ops: new(op.Ops), + Constraints: Constraints{ + Max: image.Pt(100, 100), + }, + } exp := image.Point{X: 60, Y: 70} - Stack{Alignment: Center}.Layout(>x, - Expanded(func() { - gtx.Dimensions.Size = exp + dims := Stack{Alignment: Center}.Layout(gtx, + Expanded(func(gtx Context) Dimensions { + return Dimensions{Size: exp} }), - Stacked(func() { - gtx.Dimensions.Size = image.Point{X: 50, Y: 50} + Stacked(func(gtx Context) Dimensions { + return Dimensions{Size: image.Point{X: 50, Y: 50}} }), ) - if got := gtx.Dimensions.Size; got != exp { + if got := dims.Size; got != exp { t.Errorf("Stack ignored Expanded size, got %v expected %v", got, exp) } } diff --git a/widget/bool.go b/widget/bool.go index ec459ac3..fde8c323 100644 --- a/widget/bool.go +++ b/widget/bool.go @@ -16,7 +16,7 @@ type Bool struct { // Update the checked state according to incoming events, // and reports whether Value changed. -func (b *Bool) Update(gtx *layout.Context) bool { +func (b *Bool) Update(gtx layout.Context) bool { was := b.Value for _, e := range b.gesture.Events(gtx) { switch e.Type { @@ -31,6 +31,6 @@ func (b *Bool) Update(gtx *layout.Context) bool { return b.Value != was } -func (b *Bool) Layout(gtx *layout.Context) { +func (b *Bool) Layout(gtx layout.Context) { b.gesture.Add(gtx.Ops) } diff --git a/widget/button.go b/widget/button.go index c43e5732..2f4b73f9 100644 --- a/widget/button.go +++ b/widget/button.go @@ -30,7 +30,7 @@ type Click struct { // Clicked calls Update and reports whether the button was // clicked since the last call. Multiple clicks result in Clicked // returning true once per click. -func (b *Clickable) Clicked(gtx *layout.Context) bool { +func (b *Clickable) Clicked(gtx layout.Context) bool { b.Update(gtx) if b.clicks > 0 { b.clicks-- @@ -49,7 +49,7 @@ func (b *Clickable) History() []Click { return b.history } -func (b *Clickable) Layout(gtx *layout.Context) { +func (b *Clickable) Layout(gtx layout.Context) layout.Dimensions { // Flush clicks from before the previous frame. b.Update(gtx) var st op.StackOp @@ -65,11 +65,12 @@ func (b *Clickable) Layout(gtx *layout.Context) { n := copy(b.history, b.history[1:]) b.history = b.history[:n] } + return layout.Dimensions{Size: gtx.Constraints.Min} } // Update the button state by processing events. The underlying // gesture events are returned for use beyond what Clicked offers. -func (b *Clickable) Update(gtx *layout.Context) []gesture.ClickEvent { +func (b *Clickable) Update(gtx layout.Context) []gesture.ClickEvent { evts := b.click.Events(gtx) for _, e := range evts { switch e.Type { diff --git a/widget/editor.go b/widget/editor.go index fbe2f4c7..ebab3919 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -89,7 +89,7 @@ const ( ) // Events returns available editor events. -func (e *Editor) Events(gtx *layout.Context) []EditorEvent { +func (e *Editor) Events(gtx layout.Context) []EditorEvent { e.processEvents(gtx) events := e.events e.events = nil @@ -97,7 +97,7 @@ func (e *Editor) Events(gtx *layout.Context) []EditorEvent { return events } -func (e *Editor) processEvents(gtx *layout.Context) { +func (e *Editor) processEvents(gtx layout.Context) { if e.shaper == nil { // Can't process events without a shaper. return @@ -114,7 +114,7 @@ func (e *Editor) makeValid() { } } -func (e *Editor) processPointer(gtx *layout.Context) { +func (e *Editor) processPointer(gtx layout.Context) { sbounds := e.scrollBounds() var smin, smax int var axis gesture.Axis @@ -154,7 +154,7 @@ func (e *Editor) processPointer(gtx *layout.Context) { } } -func (e *Editor) processKey(gtx *layout.Context) { +func (e *Editor) processKey(gtx layout.Context) { if e.rr.Changed() { e.events = append(e.events, ChangeEvent{}) } @@ -233,7 +233,7 @@ func (e *Editor) Focused() bool { } // Layout lays out the editor. -func (e *Editor) Layout(gtx *layout.Context, sh text.Shaper, font text.Font, size unit.Value) { +func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size unit.Value) layout.Dimensions { // Flush events from before the previous frame. copy(e.events, e.events[e.prevEvents:]) e.events = e.events[:len(e.events)-e.prevEvents] @@ -258,10 +258,10 @@ func (e *Editor) Layout(gtx *layout.Context, sh text.Shaper, font text.Font, siz } e.processEvents(gtx) - e.layout(gtx) + return e.layout(gtx) } -func (e *Editor) layout(gtx *layout.Context) { +func (e *Editor) layout(gtx layout.Context) layout.Dimensions { e.makeValid() e.viewSize = gtx.Constraints.Constrain(e.dims.Size) @@ -321,10 +321,10 @@ func (e *Editor) layout(gtx *layout.Context) { e.caretOn = e.focused && (!blinking || dt%timePerBlink < timePerBlink/2) } - gtx.Dimensions = layout.Dimensions{Size: e.viewSize, Baseline: e.dims.Baseline} + return layout.Dimensions{Size: e.viewSize, Baseline: e.dims.Baseline} } -func (e *Editor) PaintText(gtx *layout.Context) { +func (e *Editor) PaintText(gtx layout.Context) { clip := textPadding(e.lines) clip.Max = clip.Max.Add(e.viewSize) for _, shape := range e.shapes { @@ -337,7 +337,7 @@ func (e *Editor) PaintText(gtx *layout.Context) { } } -func (e *Editor) PaintCaret(gtx *layout.Context) { +func (e *Editor) PaintCaret(gtx layout.Context) { if !e.caretOn { return } diff --git a/widget/enum.go b/widget/enum.go index 6068740d..7b38931d 100644 --- a/widget/enum.go +++ b/widget/enum.go @@ -23,7 +23,7 @@ func index(vs []string, t string) int { // Update the Value according to incoming events, and // reports whether Value changed. -func (e *Enum) Update(gtx *layout.Context) bool { +func (e *Enum) Update(gtx layout.Context) bool { was := e.Value for i := range e.clicks { for _, ev := range e.clicks[i].Events(gtx) { @@ -37,7 +37,7 @@ func (e *Enum) Update(gtx *layout.Context) bool { } // Layout adds the event handler for key. -func (e *Enum) Layout(gtx *layout.Context, key string) { +func (e *Enum) Layout(gtx layout.Context, key string) { if index(e.values, key) == -1 { e.values = append(e.values, key) e.clicks = append(e.clicks, gesture.Click{}) diff --git a/widget/icon.go b/widget/icon.go index da703bd0..ea3a4094 100644 --- a/widget/icon.go +++ b/widget/icon.go @@ -32,7 +32,7 @@ func NewIcon(data []byte) (*Icon, error) { return &Icon{src: data, Color: color.RGBA{A: 0xff}}, nil } -func (ic *Icon) Layout(gtx *layout.Context, sz unit.Value) { +func (ic *Icon) Layout(gtx layout.Context, sz unit.Value) layout.Dimensions { ico := ic.image(gtx.Px(sz)) ico.Add(gtx.Ops) paint.PaintOp{ @@ -40,7 +40,7 @@ func (ic *Icon) Layout(gtx *layout.Context, sz unit.Value) { Max: layout.FPt(ico.Size()), }, }.Add(gtx.Ops) - gtx.Dimensions = layout.Dimensions{ + return layout.Dimensions{ Size: ico.Size(), } } diff --git a/widget/image.go b/widget/image.go index 1c226162..15a3ca83 100644 --- a/widget/image.go +++ b/widget/image.go @@ -23,7 +23,7 @@ type Image struct { Scale float32 } -func (im Image) Layout(gtx *layout.Context) { +func (im Image) Layout(gtx layout.Context) layout.Dimensions { scale := im.Scale if scale == 0 { scale = 160.0 / 72.0 @@ -39,5 +39,5 @@ func (im Image) Layout(gtx *layout.Context) { im.Src.Add(gtx.Ops) paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: float32(w), Y: float32(h)}}}.Add(gtx.Ops) s.Pop() - gtx.Dimensions = layout.Dimensions{Size: d} + return layout.Dimensions{Size: d} } diff --git a/widget/label.go b/widget/label.go index f424b092..cab5aeec 100644 --- a/widget/label.go +++ b/widget/label.go @@ -84,7 +84,7 @@ func (l *lineIterator) Next() (int, int, []text.Glyph, f32.Point, bool) { return 0, 0, nil, f32.Point{}, false } -func (l Label) Layout(gtx *layout.Context, s text.Shaper, font text.Font, size unit.Value, txt string) { +func (l Label) Layout(gtx layout.Context, s text.Shaper, font text.Font, size unit.Value, txt string) layout.Dimensions { cs := gtx.Constraints textSize := fixed.I(gtx.Px(size)) lines := s.LayoutString(font, textSize, cs.Max.X, txt) @@ -115,7 +115,7 @@ func (l Label) Layout(gtx *layout.Context, s text.Shaper, font text.Font, size u paint.PaintOp{Rect: lclip}.Add(gtx.Ops) stack.Pop() } - gtx.Dimensions = dims + return dims } func textPadding(lines []text.Line) (padding image.Rectangle) { diff --git a/widget/material/button.go b/widget/material/button.go index 2cf2e536..610e6571 100644 --- a/widget/material/button.go +++ b/widget/material/button.go @@ -80,12 +80,10 @@ func IconButton(th *Theme, icon *widget.Icon) IconButtonStyle { // Clickable lays out a rectangular clickable widget without further // decoration. -func Clickable(gtx *layout.Context, button *widget.Clickable, w layout.Widget) { - layout.Stack{}.Layout(gtx, - layout.Expanded(func() { - button.Layout(gtx) - }), - layout.Expanded(func() { +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 { clip.Rect{ Rect: f32.Rectangle{Max: f32.Point{ X: float32(gtx.Constraints.Min.X), @@ -95,26 +93,27 @@ func Clickable(gtx *layout.Context, button *widget.Clickable, w layout.Widget) { 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, button *widget.Clickable) { - ButtonLayoutStyle{ +func (b ButtonStyle) Layout(gtx layout.Context, button *widget.Clickable) layout.Dimensions { + return ButtonLayoutStyle{ Background: b.Background, CornerRadius: b.CornerRadius, Inset: b.Inset, - }.Layout(gtx, button, func() { + }.Layout(gtx, button, func(gtx layout.Context) layout.Dimensions { paint.ColorOp{Color: b.Color}.Add(gtx.Ops) - widget.Label{Alignment: text.Middle}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text) + return widget.Label{Alignment: text.Middle}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text) }) } -func (b ButtonLayoutStyle) Layout(gtx *layout.Context, button *widget.Clickable, w layout.Widget) { +func (b ButtonLayoutStyle) Layout(gtx layout.Context, button *widget.Clickable, w layout.Widget) layout.Dimensions { min := gtx.Constraints.Min - layout.Stack{Alignment: layout.Center}.Layout(gtx, - layout.Expanded(func() { + return layout.Stack{Alignment: layout.Center}.Layout(gtx, + layout.Expanded(func(gtx layout.Context) layout.Dimensions { rr := float32(gtx.Px(b.CornerRadius)) clip.Rect{ Rect: f32.Rectangle{Max: f32.Point{ @@ -123,28 +122,25 @@ func (b ButtonLayoutStyle) Layout(gtx *layout.Context, button *widget.Clickable, }}, NE: rr, NW: rr, SE: rr, SW: rr, }.Op(gtx.Ops).Add(gtx.Ops) - fill(gtx, b.Background) + dims := fill(gtx, b.Background) for _, c := range button.History() { drawInk(gtx, c) } + return dims }), - layout.Stacked(func() { + layout.Stacked(func(gtx layout.Context) layout.Dimensions { gtx.Constraints.Min = min - layout.Center.Layout(gtx, func() { - b.Inset.Layout(gtx, func() { - w() - }) + return layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return b.Inset.Layout(gtx, w) }) }), - layout.Expanded(func() { - button.Layout(gtx) - }), + layout.Expanded(button.Layout), ) } -func (b IconButtonStyle) Layout(gtx *layout.Context, button *widget.Clickable) { - layout.Stack{Alignment: layout.Center}.Layout(gtx, - layout.Expanded(func() { +func (b IconButtonStyle) Layout(gtx layout.Context, button *widget.Clickable) layout.Dimensions { + return layout.Stack{Alignment: layout.Center}.Layout(gtx, + layout.Expanded(func(gtx layout.Context) layout.Dimensions { size := gtx.Constraints.Min.X sizef := float32(size) rr := sizef * .5 @@ -152,31 +148,32 @@ func (b IconButtonStyle) Layout(gtx *layout.Context, button *widget.Clickable) { Rect: f32.Rectangle{Max: f32.Point{X: sizef, Y: sizef}}, NE: rr, NW: rr, SE: rr, SW: rr, }.Op(gtx.Ops).Add(gtx.Ops) - fill(gtx, b.Background) + dims := fill(gtx, b.Background) for _, c := range button.History() { drawInk(gtx, c) } + return dims }), - layout.Stacked(func() { - b.Inset.Layout(gtx, func() { + 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 { b.Icon.Color = b.Color b.Icon.Layout(gtx, unit.Px(float32(size))) } - gtx.Dimensions = layout.Dimensions{ + return layout.Dimensions{ Size: image.Point{X: size, Y: size}, } }) }), - layout.Expanded(func() { + layout.Expanded(func(gtx layout.Context) layout.Dimensions { pointer.Ellipse(image.Rectangle{Max: gtx.Constraints.Min}).Add(gtx.Ops) - button.Layout(gtx) + return button.Layout(gtx) }), ) } -func drawInk(gtx *layout.Context, c widget.Click) { +func drawInk(gtx layout.Context, c widget.Click) { d := gtx.Now().Sub(c.Time) t := float32(d.Seconds()) const duration = 0.5 diff --git a/widget/material/checkable.go b/widget/material/checkable.go index 495b6da5..24febe2a 100644 --- a/widget/material/checkable.go +++ b/widget/material/checkable.go @@ -26,7 +26,7 @@ type checkable struct { uncheckedStateIcon *widget.Icon } -func (c *checkable) layout(gtx *layout.Context, checked bool) { +func (c *checkable) layout(gtx layout.Context, checked bool) layout.Dimensions { var icon *widget.Icon if checked { icon = c.checkedStateIcon @@ -35,29 +35,30 @@ func (c *checkable) layout(gtx *layout.Context, checked bool) { } min := gtx.Constraints.Min - layout.Flex{Alignment: layout.Middle}.Layout(gtx, - layout.Rigid(func() { - layout.Center.Layout(gtx, func() { - layout.UniformInset(unit.Dp(2)).Layout(gtx, func() { + dims := layout.Flex{Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.UniformInset(unit.Dp(2)).Layout(gtx, func(gtx layout.Context) layout.Dimensions { size := gtx.Px(c.Size) icon.Color = c.IconColor icon.Layout(gtx, unit.Px(float32(size))) - gtx.Dimensions = layout.Dimensions{ + return layout.Dimensions{ Size: image.Point{X: size, Y: size}, } }) }) }), - layout.Rigid(func() { + layout.Rigid(func(gtx layout.Context) layout.Dimensions { gtx.Constraints.Min = min - layout.W.Layout(gtx, func() { - layout.UniformInset(unit.Dp(2)).Layout(gtx, func() { + return layout.W.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.UniformInset(unit.Dp(2)).Layout(gtx, func(gtx layout.Context) layout.Dimensions { paint.ColorOp{Color: c.Color}.Add(gtx.Ops) - widget.Label{}.Layout(gtx, c.shaper, c.Font, c.TextSize, c.Label) + return widget.Label{}.Layout(gtx, c.shaper, c.Font, c.TextSize, c.Label) }) }) }), ) - pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops) + pointer.Rect(image.Rectangle{Max: dims.Size}).Add(gtx.Ops) + return dims } diff --git a/widget/material/checkbox.go b/widget/material/checkbox.go index 552f06cf..0d23f825 100644 --- a/widget/material/checkbox.go +++ b/widget/material/checkbox.go @@ -28,8 +28,9 @@ func CheckBox(th *Theme, label string) CheckBoxStyle { } // Layout updates the checkBox and displays it. -func (c CheckBoxStyle) Layout(gtx *layout.Context, checkBox *widget.Bool) { +func (c CheckBoxStyle) Layout(gtx layout.Context, checkBox *widget.Bool) layout.Dimensions { checkBox.Update(gtx) - c.layout(gtx, checkBox.Value) + dims := c.layout(gtx, checkBox.Value) checkBox.Layout(gtx) + return dims } diff --git a/widget/material/doc.go b/widget/material/doc.go index 0431886d..d7679d58 100644 --- a/widget/material/doc.go +++ b/widget/material/doc.go @@ -11,7 +11,7 @@ // // This snippet defines a button that prints a message when clicked: // -// var gtx *layout.Context +// var gtx layout.Context // button := new(widget.Clickable) // // for button.Clicked(gtx) { @@ -43,7 +43,7 @@ // // btn := material.Button(theme, "Click me!") // btn.Font.Style = text.Italic -// btn.Layout(gtx) +// btn.Layout(gtx, button) // // Widget variants: A widget can have several distinct representations even // though the underlying state is the same. A widget.Clickable can be drawn as a diff --git a/widget/material/editor.go b/widget/material/editor.go index c2bb7e1f..6887a258 100644 --- a/widget/material/editor.go +++ b/widget/material/editor.go @@ -36,22 +36,22 @@ func Editor(th *Theme, hint string) EditorStyle { } } -func (e EditorStyle) Layout(gtx *layout.Context, editor *widget.Editor) { +func (e EditorStyle) Layout(gtx layout.Context, editor *widget.Editor) layout.Dimensions { var stack op.StackOp stack.Push(gtx.Ops) var macro op.MacroOp macro.Record(gtx.Ops) paint.ColorOp{Color: e.HintColor}.Add(gtx.Ops) tl := widget.Label{Alignment: editor.Alignment} - tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint) + dims := tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint) macro.Stop() - if w := gtx.Dimensions.Size.X; gtx.Constraints.Min.X < w { + if w := dims.Size.X; gtx.Constraints.Min.X < w { gtx.Constraints.Min.X = w } - if h := gtx.Dimensions.Size.Y; gtx.Constraints.Min.Y < h { + if h := dims.Size.Y; gtx.Constraints.Min.Y < h { gtx.Constraints.Min.Y = h } - editor.Layout(gtx, e.shaper, e.Font, e.TextSize) + dims = editor.Layout(gtx, e.shaper, e.Font, e.TextSize) if editor.Len() > 0 { paint.ColorOp{Color: e.Color}.Add(gtx.Ops) editor.PaintText(gtx) @@ -61,4 +61,5 @@ func (e EditorStyle) Layout(gtx *layout.Context, editor *widget.Editor) { paint.ColorOp{Color: e.Color}.Add(gtx.Ops) editor.PaintCaret(gtx) stack.Pop() + return dims } diff --git a/widget/material/label.go b/widget/material/label.go index 96830be5..c475c31d 100644 --- a/widget/material/label.go +++ b/widget/material/label.go @@ -72,8 +72,8 @@ func Label(th *Theme, size unit.Value, txt string) LabelStyle { } } -func (l LabelStyle) Layout(gtx *layout.Context) { +func (l LabelStyle) Layout(gtx layout.Context) layout.Dimensions { paint.ColorOp{Color: l.Color}.Add(gtx.Ops) tl := widget.Label{Alignment: l.Alignment, MaxLines: l.MaxLines} - tl.Layout(gtx, l.shaper, l.Font, l.TextSize, l.Text) + return tl.Layout(gtx, l.shaper, l.Font, l.TextSize, l.Text) } diff --git a/widget/material/progressbar.go b/widget/material/progressbar.go index 33e8c974..391f80e9 100644 --- a/widget/material/progressbar.go +++ b/widget/material/progressbar.go @@ -23,8 +23,8 @@ func ProgressBar(th *Theme) ProgressBarStyle { } } -func (b ProgressBarStyle) Layout(gtx *layout.Context, progress int) { - shader := func(width float32, color color.RGBA) { +func (p ProgressBarStyle) Layout(gtx layout.Context, progress int) layout.Dimensions { + shader := func(width float32, color color.RGBA) layout.Dimensions { maxHeight := unit.Dp(4) rr := float32(gtx.Px(unit.Dp(2))) @@ -41,7 +41,7 @@ func (b ProgressBarStyle) Layout(gtx *layout.Context, progress int) { paint.ColorOp{Color: color}.Add(gtx.Ops) paint.PaintOp{Rect: dr}.Add(gtx.Ops) - gtx.Dimensions = layout.Dimensions{Size: d} + return layout.Dimensions{Size: d} } if progress > 100 { @@ -52,16 +52,16 @@ func (b ProgressBarStyle) Layout(gtx *layout.Context, progress int) { progressBarWidth := float32(gtx.Constraints.Max.X) - layout.Stack{Alignment: layout.W}.Layout(gtx, - layout.Stacked(func() { + return layout.Stack{Alignment: layout.W}.Layout(gtx, + layout.Stacked(func(gtx layout.Context) layout.Dimensions { // Use a transparent equivalent of progress color. - bgCol := mulAlpha(b.Color, 150) + bgCol := mulAlpha(p.Color, 150) - shader(progressBarWidth, bgCol) + return shader(progressBarWidth, bgCol) }), - layout.Stacked(func() { + layout.Stacked(func(gtx layout.Context) layout.Dimensions { fillWidth := (progressBarWidth / 100) * float32(progress) - shader(fillWidth, b.Color) + return shader(fillWidth, p.Color) }), ) } diff --git a/widget/material/radiobutton.go b/widget/material/radiobutton.go index b44d7a9e..1a9d2006 100644 --- a/widget/material/radiobutton.go +++ b/widget/material/radiobutton.go @@ -33,8 +33,9 @@ func RadioButton(th *Theme, key, label string) RadioButtonStyle { } // Layout updates enum and displays the radio button. -func (r RadioButtonStyle) Layout(gtx *layout.Context, enum *widget.Enum) { +func (r RadioButtonStyle) Layout(gtx layout.Context, enum *widget.Enum) layout.Dimensions { enum.Update(gtx) - r.layout(gtx, enum.Value == r.Key) + dims := r.layout(gtx, enum.Value == r.Key) enum.Layout(gtx, r.Key) + return dims } diff --git a/widget/material/switch.go b/widget/material/switch.go index 7b1fd6c8..302c449a 100644 --- a/widget/material/switch.go +++ b/widget/material/switch.go @@ -27,7 +27,7 @@ func Switch(th *Theme) SwitchStyle { } // Layout updates the checkBox and displays it. -func (s SwitchStyle) Layout(gtx *layout.Context, swtch *widget.Bool) { +func (s SwitchStyle) Layout(gtx layout.Context, swtch *widget.Bool) layout.Dimensions { swtch.Update(gtx) trackWidth := gtx.Px(unit.Dp(36)) @@ -112,7 +112,7 @@ func (s SwitchStyle) Layout(gtx *layout.Context, swtch *widget.Bool) { swtch.Layout(gtx) stack.Pop() - gtx.Dimensions = layout.Dimensions{ + return layout.Dimensions{ Size: image.Point{X: trackWidth, Y: trackHeight}, } } diff --git a/widget/material/theme.go b/widget/material/theme.go index 27d5237c..160dfbd4 100644 --- a/widget/material/theme.go +++ b/widget/material/theme.go @@ -63,7 +63,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 fill(gtx *layout.Context, col color.RGBA) { +func fill(gtx layout.Context, col color.RGBA) layout.Dimensions { cs := gtx.Constraints d := cs.Min dr := f32.Rectangle{ @@ -71,5 +71,5 @@ func fill(gtx *layout.Context, col color.RGBA) { } paint.ColorOp{Color: col}.Add(gtx.Ops) paint.PaintOp{Rect: dr}.Add(gtx.Ops) - gtx.Dimensions = layout.Dimensions{Size: d} + return layout.Dimensions{Size: d} }