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} }