layout: change Widget to take explicit Context and return explicit Dimensions

Change the definition of Widget from the implicit

        type Widget func()

to the explicit functional

        type Widget func(gtx layout.Context) layout.Dimensions

The advantages are numerous:

- Clearer connection between the incoming context and the output dimensions.
- Returning the Dimensions are impossible to omit.
- Contexts passed by value, so its fields can be exported
and freely mutated by the program.

The only disadvantage is the longer function literals and the many "returns".
What tipped the scales in favour of the explicit Widget variant is that type
aliases can dramatically shorten the literals:

	type (
		C = layout.Context
		D = layout.Dimensions
	)

	widget := func(gtx C) D {
		...
	}

Note that the aliases are not part of the Gio API and it is up to each user
whether they want to use them.

Finally the Go proposal for lightweight function literals,
https://github.com/golang/go/issues/21498, may remove the disadvantage
completely in future.

Context becomes a plain struct with only public fields, and its Reset is
replaced by a NewContext convenience constructor.

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