Files
gio/widget/material/switch.go
T
Elias Naur 3af01a3f43 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>
2020-05-23 22:28:49 +02:00

133 lines
3.1 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package material
import (
"image"
"image/color"
"gioui.org/f32"
"gioui.org/io/pointer"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
)
type SwitchStyle struct {
Color color.RGBA
}
func Switch(th *Theme) SwitchStyle {
return SwitchStyle{
Color: th.Color.Primary,
}
}
// Layout updates the checkBox and displays it.
func (s SwitchStyle) Layout(gtx layout.Context, swtch *widget.Bool) layout.Dimensions {
swtch.Update(gtx)
trackWidth := gtx.Px(unit.Dp(36))
trackHeight := gtx.Px(unit.Dp(16))
thumbSize := gtx.Px(unit.Dp(20))
trackOff := float32(thumbSize-trackHeight) * .5
// Draw track.
var stack op.StackOp
stack.Push(gtx.Ops)
trackCorner := float32(trackHeight) / 2
trackRect := f32.Rectangle{Max: f32.Point{
X: float32(trackWidth),
Y: float32(trackHeight),
}}
op.TransformOp{}.Offset(f32.Point{Y: trackOff}).Add(gtx.Ops)
clip.Rect{
Rect: trackRect,
NE: trackCorner, NW: trackCorner, SE: trackCorner, SW: trackCorner,
}.Op(gtx.Ops).Add(gtx.Ops)
paint.ColorOp{Color: rgb(0x9b9b9b)}.Add(gtx.Ops)
paint.PaintOp{Rect: trackRect}.Add(gtx.Ops)
stack.Pop()
// Compute thumb offset and color.
stack.Push(gtx.Ops)
col := rgb(0xffffff)
if swtch.Value {
off := trackWidth - thumbSize
op.TransformOp{}.Offset(f32.Point{X: float32(off)}).Add(gtx.Ops)
col = s.Color
}
// Draw thumb shadow, a translucent disc slightly larger than the
// thumb itself.
var shadowStack op.StackOp
shadowStack.Push(gtx.Ops)
shadowSize := float32(2)
// Center shadow horizontally and slightly adjust its Y.
op.TransformOp{}.Offset(f32.Point{X: -shadowSize / 2, Y: -.75}).Add(gtx.Ops)
drawDisc(gtx.Ops, float32(thumbSize)+shadowSize, argb(0x55000000))
shadowStack.Pop()
// Draw thumb.
drawDisc(gtx.Ops, float32(thumbSize), col)
stack.Pop()
// Draw thumb ink.
stack.Push(gtx.Ops)
inkSize := float32(gtx.Px(unit.Dp(44)))
rr := inkSize * .5
inkOff := f32.Point{
X: float32(trackWidth)*.5 - rr,
Y: -rr + float32(trackHeight)*.5 + trackOff,
}
op.TransformOp{}.Offset(inkOff).Add(gtx.Ops)
clip.Rect{
Rect: f32.Rectangle{
Max: f32.Point{
X: inkSize,
Y: inkSize,
},
},
NE: rr, NW: rr, SE: rr, SW: rr,
}.Op(gtx.Ops).Add(gtx.Ops)
drawInk(gtx, swtch.Last)
stack.Pop()
// Set up click area.
stack.Push(gtx.Ops)
clickSize := gtx.Px(unit.Dp(40))
clickOff := f32.Point{
X: (float32(trackWidth) - float32(clickSize)) * .5,
Y: (float32(trackHeight)-float32(clickSize))*.5 + trackOff,
}
op.TransformOp{}.Offset(clickOff).Add(gtx.Ops)
pointer.Ellipse(image.Rectangle{
Max: image.Point{
X: clickSize, Y: clickSize,
},
}).Add(gtx.Ops)
swtch.Layout(gtx)
stack.Pop()
return layout.Dimensions{
Size: image.Point{X: trackWidth, Y: trackHeight},
}
}
func drawDisc(ops *op.Ops, sz float32, col color.RGBA) {
var stack op.StackOp
stack.Push(ops)
rr := sz / 2
r := f32.Rectangle{Max: f32.Point{X: sz, Y: sz}}
clip.Rect{
Rect: r,
NE: rr, NW: rr, SE: rr, SW: rr,
}.Op(ops).Add(ops)
paint.ColorOp{Color: col}.Add(ops)
paint.PaintOp{Rect: r}.Add(ops)
stack.Pop()
}