From 2782436ffc1348249baa41f54229b1826fed4e80 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 24 Sep 2019 21:10:35 +0200 Subject: [PATCH] ui/layout: add common state to Context Almost every layout and widget need the ui.Config for its environment, an ui.Ops to store operations. Stateful widgets need an input.Queue for events. Add all these common objects to Context, greatly simplifying the function signatures for Gio programs. Fixes gio#33 Signed-off-by: Elias Naur --- ui/layout/doc.go | 14 ++++---- ui/layout/flex.go | 14 ++++---- ui/layout/layout.go | 54 ++++++++++++++++++---------- ui/layout/layout_test.go | 76 +++++++++++++++++++--------------------- ui/layout/list.go | 38 ++++++++------------ ui/layout/stack.go | 14 ++++---- ui/text/editor.go | 75 +++++++++++++++++++-------------------- ui/text/label.go | 18 +++++----- ui/widget/image.go | 10 +++--- 9 files changed, 157 insertions(+), 156 deletions(-) diff --git a/ui/layout/doc.go b/ui/layout/doc.go index 287e1cd9..e32fc16b 100644 --- a/ui/layout/doc.go +++ b/ui/layout/doc.go @@ -13,17 +13,17 @@ in an implicit Context to keep the Widget declaration short. For example, to add space above a widget: - ctx := new(layout.Context) - ctx.Constraints = ... + c := &layout.Context{...} + c.Reset(...) // Configure a top inset. inset := layout.Inset{Top: ui.Dp(8), ...} // Use the inset to lay out a widget. - inset.Layout(..., ctx, func() { + inset.Layout(c, func() { // Lay out widget and determine its size given the constraints. ... dims := layout.Dimensions{...} - ctx.Dimensions = dims + c.Dimensions = dims }) Note that the example does not generate any garbage even though the @@ -37,10 +37,10 @@ be created from a few generic layouts. This example both aligns and insets a child: inset := layout.Inset{...} - inset.Layout(..., ctx, func() { + inset.Layout(c, func() { align := layout.Align(...) - align.Layout(..., ctx, func() { - widget.Layout(..., ctx) + align.Layout(c, func() { + widget.Layout(c, ...) }) }) diff --git a/ui/layout/flex.go b/ui/layout/flex.go index c33dd92a..a305060a 100644 --- a/ui/layout/flex.go +++ b/ui/layout/flex.go @@ -22,7 +22,6 @@ type Flex struct { ctx *Context macro ui.MacroOp - ops *ui.Ops mode flexMode size int rigidSize int @@ -69,13 +68,12 @@ const ( ) // Init must be called before Rigid or Flexible. -func (f *Flex) Init(ops *ui.Ops, ctx *Context) *Flex { +func (f *Flex) Init(c *Context) *Flex { if f.mode > modeBegun { panic("must End the current child before calling Init again") } f.mode = modeBegun - f.ops = ops - f.ctx = ctx + f.ctx = c f.size = 0 f.rigidSize = 0 f.maxCross = 0 @@ -91,7 +89,7 @@ func (f *Flex) begin(mode flexMode) { panic("must End before adding a child") } f.mode = mode - f.macro.Record(f.ops) + f.macro.Record(f.ctx.Ops) } // Rigid lays out a widget with the main axis constrained to the range @@ -192,9 +190,9 @@ func (f *Flex) Layout(children ...FlexChild) { } } var stack ui.StackOp - stack.Push(f.ops) - ui.TransformOp{}.Offset(toPointF(axisPoint(f.Axis, mainSize, cross))).Add(f.ops) - child.macro.Add(f.ops) + stack.Push(f.ctx.Ops) + ui.TransformOp{}.Offset(toPointF(axisPoint(f.Axis, mainSize, cross))).Add(f.ctx.Ops) + child.macro.Add(f.ctx.Ops) stack.Pop() mainSize += axisMain(f.Axis, dims.Size) if i < len(children)-1 { diff --git a/ui/layout/layout.go b/ui/layout/layout.go index a9b566b9..802e3853 100644 --- a/ui/layout/layout.go +++ b/ui/layout/layout.go @@ -6,6 +6,7 @@ import ( "image" "gioui.org/ui" + "gioui.org/ui/input" ) // Constraints represent a set of acceptable ranges for @@ -41,11 +42,18 @@ type Direction uint8 // computing dimensions for a user interface element. type Widget func() -// Context tracks the current constraints and dimensions during -// layout. +// Context carry the state needed by almost all layouts and widgets. type Context struct { + // Constraints track the constraints for the active widget or + // layout. Constraints Constraints - Dimensions Dimensions + // Dimensions track the result of the most recent layout + // operation. + Dimensions Dimensions + + ui.Config + input.Queue + *ui.Ops } const ( @@ -83,6 +91,16 @@ func (s *Context) Layout(cs Constraints, w Widget) Dimensions { return s.Dimensions } +// Reset the context. +func (c *Context) Reset(cfg ui.Config, cs Constraints) { + c.Constraints = cs + c.Config = cfg + if c.Ops == nil { + c.Ops = new(ui.Ops) + } + c.Ops.Reset() +} + // Constrain a value to the range [Min; Max]. func (c Constraint) Constrain(v int) int { if v < c.Min { @@ -116,12 +134,12 @@ type Inset struct { type Align Direction // Layout a widget. -func (in Inset) Layout(c ui.Config, ops *ui.Ops, ctx *Context, w Widget) { +func (in Inset) Layout(c *Context, w Widget) { top := c.Px(in.Top) right := c.Px(in.Right) bottom := c.Px(in.Bottom) left := c.Px(in.Left) - mcs := ctx.Constraints + mcs := c.Constraints mcs.Width.Min -= left + right mcs.Width.Max -= left + right if mcs.Width.Min < 0 { @@ -139,12 +157,12 @@ func (in Inset) Layout(c ui.Config, ops *ui.Ops, ctx *Context, w Widget) { mcs.Height.Max = mcs.Height.Min } var stack ui.StackOp - stack.Push(ops) - ui.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(ops) - dims := ctx.Layout(mcs, w) + stack.Push(c.Ops) + ui.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(c.Ops) + dims := c.Layout(mcs, w) stack.Pop() - ctx.Dimensions = Dimensions{ - Size: ctx.Constraints.Constrain(dims.Size.Add(image.Point{X: right + left, Y: top + bottom})), + c.Dimensions = Dimensions{ + Size: c.Constraints.Constrain(dims.Size.Add(image.Point{X: right + left, Y: top + bottom})), Baseline: dims.Baseline + top, } } @@ -156,14 +174,14 @@ func UniformInset(v ui.Value) Inset { } // Layout a widget. -func (a Align) Layout(ops *ui.Ops, st *Context, w Widget) { +func (a Align) Layout(c *Context, w Widget) { var macro ui.MacroOp - macro.Record(ops) - cs := st.Constraints + macro.Record(c.Ops) + cs := c.Constraints mcs := cs mcs.Width.Min = 0 mcs.Height.Min = 0 - dims := st.Layout(mcs, w) + dims := c.Layout(mcs, w) macro.Stop() sz := dims.Size if sz.X < cs.Width.Min { @@ -186,11 +204,11 @@ func (a Align) Layout(ops *ui.Ops, st *Context, w Widget) { p.Y = sz.Y - dims.Size.Y } var stack ui.StackOp - stack.Push(ops) - ui.TransformOp{}.Offset(toPointF(p)).Add(ops) - macro.Add(ops) + stack.Push(c.Ops) + ui.TransformOp{}.Offset(toPointF(p)).Add(c.Ops) + macro.Add(c.Ops) stack.Pop() - st.Dimensions = Dimensions{ + c.Dimensions = Dimensions{ Size: sz, Baseline: dims.Baseline, } diff --git a/ui/layout/layout_test.go b/ui/layout/layout_test.go index c0e1d766..8f9deb81 100644 --- a/ui/layout/layout_test.go +++ b/ui/layout/layout_test.go @@ -18,22 +18,22 @@ var q queue var cfg = new(config) func ExampleInset() { - ops := new(ui.Ops) - ctx := new(layout.Context) - + c := &layout.Context{Queue: q} // Loose constraints with no minimal size. - ctx.Constraints.Width.Max = 100 - ctx.Constraints.Height.Max = 100 + var cs layout.Constraints + cs.Width.Max = 100 + cs.Height.Max = 100 + c.Reset(cfg, cs) // Inset all edges by 10. inset := layout.UniformInset(ui.Dp(10)) - inset.Layout(cfg, ops, ctx, func() { + inset.Layout(c, func() { // Lay out a 50x50 sized widget. - layoutWidget(ctx, 50, 50) - fmt.Println(ctx.Dimensions.Size) + layoutWidget(c, 50, 50) + fmt.Println(c.Dimensions.Size) }) - fmt.Println(ctx.Dimensions.Size) + fmt.Println(c.Dimensions.Size) // Output: // (50,50) @@ -41,20 +41,19 @@ func ExampleInset() { } func ExampleAlign() { - ops := new(ui.Ops) - ctx := new(layout.Context) - + c := &layout.Context{Queue: q} // Rigid constraints with both minimum and maximum set. - ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100}) + cs := layout.RigidConstraints(image.Point{X: 100, Y: 100}) + c.Reset(cfg, cs) align := layout.Align(layout.Center) - align.Layout(ops, ctx, func() { + align.Layout(c, func() { // Lay out a 50x50 sized widget. - layoutWidget(ctx, 50, 50) - fmt.Println(ctx.Dimensions.Size) + layoutWidget(c, 50, 50) + fmt.Println(c.Dimensions.Size) }) - fmt.Println(ctx.Dimensions.Size) + fmt.Println(c.Dimensions.Size) // Output: // (50,50) @@ -62,24 +61,23 @@ func ExampleAlign() { } func ExampleFlex() { - ops := new(ui.Ops) - ctx := new(layout.Context) - - ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100}) + c := &layout.Context{Queue: q} + cs := layout.RigidConstraints(image.Point{X: 100, Y: 100}) + c.Reset(cfg, cs) flex := layout.Flex{} - flex.Init(ops, ctx) + flex.Init(c) // Rigid 10x10 widget. child1 := flex.Rigid(func() { - fmt.Printf("Rigid: %v\n", ctx.Constraints.Width) - layoutWidget(ctx, 10, 10) + fmt.Printf("Rigid: %v\n", c.Constraints.Width) + layoutWidget(c, 10, 10) }) // Child with 50% space allowance. child2 := flex.Flexible(0.5, func() { - fmt.Printf("50%%: %v\n", ctx.Constraints.Width) - layoutWidget(ctx, 10, 10) + fmt.Printf("50%%: %v\n", c.Constraints.Width) + layoutWidget(c, 10, 10) }) flex.Layout(child1, child2) @@ -90,23 +88,22 @@ func ExampleFlex() { } func ExampleStack() { - ops := new(ui.Ops) - ctx := new(layout.Context) - - ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100}) + c := &layout.Context{Queue: q} + cs := layout.RigidConstraints(image.Point{X: 100, Y: 100}) + c.Reset(cfg, cs) stack := layout.Stack{} - stack.Init(ops, ctx) + stack.Init(c) // Rigid 50x50 widget. child1 := stack.Rigid(func() { - layoutWidget(ctx, 50, 50) + layoutWidget(c, 50, 50) }) // Force widget to the same size as the first. child2 := stack.Expand(func() { - fmt.Printf("Expand: %v\n", ctx.Constraints) - layoutWidget(ctx, 10, 10) + fmt.Printf("Expand: %v\n", c.Constraints) + layoutWidget(c, 10, 10) }) stack.Layout(child1, child2) @@ -116,19 +113,18 @@ func ExampleStack() { } func ExampleList() { - ops := new(ui.Ops) - ctx := new(layout.Context) - - ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100}) + c := &layout.Context{Queue: q} + cs := layout.RigidConstraints(image.Point{X: 100, Y: 100}) + c.Reset(cfg, cs) // The list is 1e6 elements, but only 5 fit the constraints. const listLen = 1e6 var list layout.List count := 0 - list.Layout(cfg, q, ops, ctx, listLen, func(i int) { + list.Layout(c, listLen, func(i int) { count++ - layoutWidget(ctx, 20, 20) + layoutWidget(c, 20, 20) }) fmt.Println(count) diff --git a/ui/layout/list.go b/ui/layout/list.go index 6f8f5c0d..29f58140 100644 --- a/ui/layout/list.go +++ b/ui/layout/list.go @@ -7,7 +7,6 @@ import ( "gioui.org/ui" "gioui.org/ui/gesture" - "gioui.org/ui/input" "gioui.org/ui/paint" "gioui.org/ui/pointer" ) @@ -33,9 +32,7 @@ type List struct { // the very end. beforeEnd bool - config ui.Config - ops *ui.Ops - queue input.Queue + ctx *Context macro ui.MacroOp child ui.MacroOp scroll gesture.Scroll @@ -47,7 +44,6 @@ type List struct { // to the child with index first. offset int - cs Constraints len int // maxSize is the total size of visible children. @@ -71,18 +67,15 @@ const ( const inf = 1e6 // Init prepares the list for iterating through its children with Next. -func (l *List) init(cfg ui.Config, q input.Queue, ops *ui.Ops, cs Constraints, len int) { +func (l *List) init(c *Context, len int) { if l.more() { panic("unfinished child") } - l.config = cfg - l.queue = q - l.update() - l.ops = ops + l.ctx = c l.maxSize = 0 l.children = l.children[:0] - l.cs = cs l.len = len + l.update() if l.scrollToEnd() { l.offset = 0 l.first = len @@ -91,21 +84,20 @@ func (l *List) init(cfg ui.Config, q input.Queue, ops *ui.Ops, cs Constraints, l l.offset = 0 l.first = len } - l.macro.Record(ops) + l.macro.Record(c.Ops) l.next() } // Layout the List and return its dimensions. -func (l *List) Layout(c ui.Config, q input.Queue, ops *ui.Ops, ctx *Context, len int, w ListElement) { - cs := ctx.Constraints - for l.init(c, q, ops, cs, len); l.more(); l.next() { - cs := axisConstraints(l.Axis, Constraint{Max: inf}, axisCrossConstraint(l.Axis, l.cs)) +func (l *List) Layout(c *Context, len int, w ListElement) { + for l.init(c, len); l.more(); l.next() { + cs := axisConstraints(l.Axis, Constraint{Max: inf}, axisCrossConstraint(l.Axis, l.ctx.Constraints)) i := l.index() - l.end(ctx.Layout(cs, func() { + l.end(c.Layout(cs, func() { w(i) })) } - ctx.Dimensions = l.layout() + c.Dimensions = l.layout() } func (l *List) scrollToEnd() bool { @@ -118,7 +110,7 @@ func (l *List) Dragging() bool { } func (l *List) update() { - d := l.scroll.Scroll(l.config, l.queue, gesture.Axis(l.Axis)) + d := l.scroll.Scroll(l.ctx.Config, l.ctx.Queue, gesture.Axis(l.Axis)) l.scrollDelta = d l.offset += d } @@ -134,7 +126,7 @@ func (l *List) next() { l.dir = l.nextDir() } if l.more() { - l.child.Record(l.ops) + l.child.Record(l.ctx.Ops) } } @@ -156,7 +148,7 @@ func (l *List) more() bool { } func (l *List) nextDir() iterationDir { - vsize := axisMainConstraint(l.Axis, l.cs).Max + vsize := axisMainConstraint(l.Axis, l.ctx.Constraints).Max last := l.first + len(l.children) // Clamp offset. if l.maxSize-l.offset < vsize && last == l.len { @@ -200,7 +192,7 @@ func (l *List) layout() Dimensions { if l.more() { panic("unfinished child") } - mainc := axisMainConstraint(l.Axis, l.cs) + mainc := axisMainConstraint(l.Axis, l.ctx.Constraints) children := l.children // Skip invisible children for len(children) > 0 { @@ -226,7 +218,7 @@ func (l *List) layout() Dimensions { break } } - ops := l.ops + ops := l.ctx.Ops pos := -l.offset // ScrollToEnd lists lists are end aligned. if space := mainc.Max - size; l.ScrollToEnd && space > 0 { diff --git a/ui/layout/stack.go b/ui/layout/stack.go index e4f8accc..f83ec4f0 100644 --- a/ui/layout/stack.go +++ b/ui/layout/stack.go @@ -16,7 +16,6 @@ type Stack struct { Alignment Direction macro ui.MacroOp - ops *ui.Ops constrained bool ctx *Context maxSZ image.Point @@ -30,9 +29,8 @@ type StackChild struct { } // Init a stack before calling Rigid or Expand. -func (s *Stack) Init(ops *ui.Ops, ctx *Context) *Stack { - s.ops = ops - s.ctx = ctx +func (s *Stack) Init(c *Context) *Stack { + s.ctx = c s.constrained = true s.maxSZ = image.Point{} s.baseline = 0 @@ -43,7 +41,7 @@ func (s *Stack) begin() { if !s.constrained { panic("must Init before adding a child") } - s.macro.Record(s.ops) + s.macro.Record(s.ctx.Ops) } // Rigid lays out a widget with the same constraints that were @@ -102,9 +100,9 @@ func (s *Stack) Layout(children ...StackChild) { p.Y = s.maxSZ.Y - sz.Y } var stack ui.StackOp - stack.Push(s.ops) - ui.TransformOp{}.Offset(toPointF(p)).Add(s.ops) - ch.macro.Add(s.ops) + stack.Push(s.ctx.Ops) + ui.TransformOp{}.Offset(toPointF(p)).Add(s.ctx.Ops) + ch.macro.Add(s.ctx.Ops) stack.Pop() } b := s.baseline diff --git a/ui/text/editor.go b/ui/text/editor.go index 40a8d504..e11b1fad 100644 --- a/ui/text/editor.go +++ b/ui/text/editor.go @@ -11,7 +11,6 @@ import ( "gioui.org/ui" "gioui.org/ui/gesture" - "gioui.org/ui/input" "gioui.org/ui/key" "gioui.org/ui/layout" "gioui.org/ui/paint" @@ -82,9 +81,9 @@ const ( ) // Next returns the next available editor event, or false if none are available. -func (e *Editor) Next(cfg ui.Config, queue input.Queue) (EditorEvent, bool) { +func (e *Editor) Next(c *layout.Context) (EditorEvent, bool) { // Crude configuration change detection. - if scale := cfg.Px(ui.Sp(100)); scale != e.oldScale { + if scale := c.Px(ui.Sp(100)); scale != e.oldScale { e.invalidate() e.oldScale = scale } @@ -98,7 +97,7 @@ func (e *Editor) Next(cfg ui.Config, queue input.Queue) (EditorEvent, bool) { axis = gesture.Vertical smin, smax = sbounds.Min.Y, sbounds.Max.Y } - sdist := e.scroller.Scroll(cfg, queue, axis) + sdist := e.scroller.Scroll(c.Config, c.Queue, axis) var soff int if e.SingleLine { e.scrollOff.X += sdist @@ -107,26 +106,26 @@ func (e *Editor) Next(cfg ui.Config, queue input.Queue) (EditorEvent, bool) { e.scrollOff.Y += sdist soff = e.scrollOff.Y } - for evt, ok := e.clicker.Next(queue); ok; evt, ok = e.clicker.Next(queue) { + for evt, ok := e.clicker.Next(c.Queue); ok; evt, ok = e.clicker.Next(c.Queue) { switch { case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse, evt.Type == gesture.TypeClick && evt.Source == pointer.Touch: - e.blinkStart = cfg.Now() + e.blinkStart = c.Now() e.moveCoord(image.Point{ X: int(math.Round(float64(evt.Position.X))), Y: int(math.Round(float64(evt.Position.Y))), }) e.requestFocus = true if e.scroller.State() != gesture.StateFlinging { - e.scrollToCaret(cfg) + e.scrollToCaret(c) } } } if (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin) { e.scroller.Stop() } - for ke, ok := queue.Next(e); ok; ke, ok = queue.Next(e) { - e.blinkStart = cfg.Now() + for ke, ok := c.Queue.Next(e); ok; ke, ok = c.Queue.Next(e) { + e.blinkStart = c.Now() switch ke := ke.(type) { case key.FocusEvent: e.focused = ke.Focus @@ -140,11 +139,11 @@ func (e *Editor) Next(cfg ui.Config, queue input.Queue) (EditorEvent, bool) { } } if e.command(ke) { - e.scrollToCaret(cfg) + e.scrollToCaret(c.Config) e.scroller.Stop() } case key.EditEvent: - e.scrollToCaret(cfg) + e.scrollToCaret(c) e.scroller.Stop() e.append(ke.Text) } @@ -165,11 +164,11 @@ func (e *Editor) Focus() { e.requestFocus = true } -func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layout.Context) { - cs := ctx.Constraints - for _, ok := e.Next(cfg, queue); ok; _, ok = e.Next(cfg, queue) { +func (e *Editor) Layout(c *layout.Context) { + cs := c.Constraints + for _, ok := e.Next(c); ok; _, ok = e.Next(c) { } - twoDp := cfg.Px(ui.Dp(2)) + twoDp := c.Px(ui.Dp(2)) e.padLeft, e.padRight = twoDp, twoDp maxWidth := cs.Width.Max if e.SingleLine { @@ -197,7 +196,7 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layo Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: e.viewSize.X, Y: e.viewSize.Y}, } - key.InputOp{Key: e, Focus: e.requestFocus}.Add(ops) + key.InputOp{Key: e, Focus: e.requestFocus}.Add(c.Ops) e.requestFocus = false e.it = lineIterator{ Lines: lines, @@ -207,14 +206,14 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layo Offset: off, } var stack ui.StackOp - stack.Push(ops) + stack.Push(c.Ops) // Apply material. Set a default color in case the material is empty. if e.rr.len() > 0 { - paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(ops) - e.Material.Add(ops) + paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(c.Ops) + e.Material.Add(c.Ops) } else { - paint.ColorOp{Color: color.RGBA{A: 0xaa}}.Add(ops) - e.HintMaterial.Add(ops) + paint.ColorOp{Color: color.RGBA{A: 0xaa}}.Add(c.Ops) + e.HintMaterial.Add(c.Ops) } for { str, lineOff, ok := e.it.Next() @@ -222,21 +221,21 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layo break } var stack ui.StackOp - stack.Push(ops) - ui.TransformOp{}.Offset(lineOff).Add(ops) - e.Face.Path(str).Add(ops) - paint.PaintOp{Rect: toRectF(clip).Sub(lineOff)}.Add(ops) + stack.Push(c.Ops) + ui.TransformOp{}.Offset(lineOff).Add(c.Ops) + e.Face.Path(str).Add(c.Ops) + paint.PaintOp{Rect: toRectF(clip).Sub(lineOff)}.Add(c.Ops) stack.Pop() } if e.focused { - now := cfg.Now() + now := c.Now() dt := now.Sub(e.blinkStart) blinking := dt < maxBlinkDuration const timePerBlink = time.Second / blinksPerSecond nextBlink := now.Add(timePerBlink/2 - dt%(timePerBlink/2)) on := !blinking || dt%timePerBlink < timePerBlink/2 if on { - carWidth := e.caretWidth(cfg) + carWidth := e.caretWidth(c) carX -= carWidth / 2 carAsc, carDesc := -lines[carLine].Bounds.Min.Y, lines[carLine].Bounds.Max.Y carRect := image.Rectangle{ @@ -249,29 +248,29 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layo }) carRect = clip.Intersect(carRect) if !carRect.Empty() { - paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(ops) - e.Material.Add(ops) - paint.PaintOp{Rect: toRectF(carRect)}.Add(ops) + paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(c.Ops) + e.Material.Add(c.Ops) + paint.PaintOp{Rect: toRectF(carRect)}.Add(c.Ops) } } if blinking { redraw := ui.InvalidateOp{At: nextBlink} - redraw.Add(ops) + redraw.Add(c.Ops) } } stack.Pop() baseline := e.padTop + e.dims.Baseline - pointerPadding := cfg.Px(ui.Dp(4)) + pointerPadding := c.Px(ui.Dp(4)) r := image.Rectangle{Max: e.viewSize} r.Min.X -= pointerPadding r.Min.Y -= pointerPadding r.Max.X += pointerPadding r.Max.X += pointerPadding - pointer.RectAreaOp{Rect: r}.Add(ops) - e.scroller.Add(ops) - e.clicker.Add(ops) - ctx.Dimensions = layout.Dimensions{Size: e.viewSize, Baseline: baseline} + pointer.RectAreaOp{Rect: r}.Add(c.Ops) + e.scroller.Add(c.Ops) + e.clicker.Add(c.Ops) + c.Dimensions = layout.Dimensions{Size: e.viewSize, Baseline: baseline} } // Text returns the contents of the editor. @@ -550,8 +549,8 @@ func (e *Editor) moveEnd() { e.carXOff = l.Width + a - x } -func (e *Editor) scrollToCaret(cfg ui.Config) { - carWidth := e.caretWidth(cfg) +func (e *Editor) scrollToCaret(c ui.Config) { + carWidth := e.caretWidth(c) carLine, _, x, y := e.layoutCaret() l := e.lines[carLine] if e.SingleLine { diff --git a/ui/text/label.go b/ui/text/label.go index 79a94bd0..ebbd57d3 100644 --- a/ui/text/label.go +++ b/ui/text/label.go @@ -92,8 +92,8 @@ func (l *lineIterator) Next() (String, f32.Point, bool) { return String{}, f32.Point{}, false } -func (l Label) Layout(ops *ui.Ops, ctx *layout.Context) { - cs := ctx.Constraints +func (l Label) Layout(c *layout.Context) { + cs := c.Constraints textLayout := l.Face.Layout(l.Text, LayoutOptions{MaxWidth: cs.Width.Max}) lines := textLayout.Lines if max := l.MaxLines; max > 0 && len(lines) > max { @@ -119,16 +119,16 @@ func (l Label) Layout(ops *ui.Ops, ctx *layout.Context) { } lclip := toRectF(clip).Sub(off) var stack ui.StackOp - stack.Push(ops) - ui.TransformOp{}.Offset(off).Add(ops) - l.Face.Path(str).Add(ops) + stack.Push(c.Ops) + ui.TransformOp{}.Offset(off).Add(c.Ops) + l.Face.Path(str).Add(c.Ops) // Set a default color in case the material is empty. - paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(ops) - l.Material.Add(ops) - paint.PaintOp{Rect: lclip}.Add(ops) + paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(c.Ops) + l.Material.Add(c.Ops) + paint.PaintOp{Rect: lclip}.Add(c.Ops) stack.Pop() } - ctx.Dimensions = dims + c.Dimensions = dims } func toRectF(r image.Rectangle) f32.Rectangle { diff --git a/ui/widget/image.go b/ui/widget/image.go index 0a33c6ee..f9cfdd9c 100644 --- a/ui/widget/image.go +++ b/ui/widget/image.go @@ -25,7 +25,7 @@ type Image struct { Scale float32 } -func (im Image) Layout(c ui.Config, ops *ui.Ops, ctx *layout.Context) { +func (im Image) Layout(c *layout.Context) { size := im.Src.Bounds() wf, hf := float32(size.Dx()), float32(size.Dy()) var w, h int @@ -35,7 +35,7 @@ func (im Image) Layout(c ui.Config, ops *ui.Ops, ctx *layout.Context) { } else { w, h = int(wf*im.Scale+.5), int(hf*im.Scale+.5) } - cs := ctx.Constraints + cs := c.Constraints d := image.Point{X: cs.Width.Constrain(w), Y: cs.Height.Constrain(h)} aspect := float32(w) / float32(h) dw, dh := float32(d.X), float32(d.Y) @@ -48,7 +48,7 @@ func (im Image) Layout(c ui.Config, ops *ui.Ops, ctx *layout.Context) { dr := f32.Rectangle{ Max: f32.Point{X: float32(d.X), Y: float32(d.Y)}, } - paint.ImageOp{Src: im.Src, Rect: im.Rect}.Add(ops) - paint.PaintOp{Rect: dr}.Add(ops) - ctx.Dimensions = layout.Dimensions{Size: d, Baseline: d.Y} + paint.ImageOp{Src: im.Src, Rect: im.Rect}.Add(c.Ops) + paint.PaintOp{Rect: dr}.Add(c.Ops) + c.Dimensions = layout.Dimensions{Size: d, Baseline: d.Y} }