forked from joejulian/gio
a63e0cb44a
op.Offset is a convenience function most often used by layouts. Layouts usually operate in integer coordinates, and the float32 version of op.Offset needlessly force conversions from int to float32. This change makes op.Offset take integer coordinates, to better match its intended use. Signed-off-by: Elias Naur <mail@eliasnaur.com>
136 lines
3.5 KiB
Go
136 lines
3.5 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package material
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
|
|
"gioui.org/f32"
|
|
"gioui.org/internal/f32color"
|
|
"gioui.org/io/semantic"
|
|
"gioui.org/layout"
|
|
"gioui.org/op"
|
|
"gioui.org/op/clip"
|
|
"gioui.org/op/paint"
|
|
"gioui.org/unit"
|
|
"gioui.org/widget"
|
|
)
|
|
|
|
type SwitchStyle struct {
|
|
Description string
|
|
Color struct {
|
|
Enabled color.NRGBA
|
|
Disabled color.NRGBA
|
|
Track color.NRGBA
|
|
}
|
|
Switch *widget.Bool
|
|
}
|
|
|
|
// Switch is for selecting a boolean value.
|
|
func Switch(th *Theme, swtch *widget.Bool, description string) SwitchStyle {
|
|
sw := SwitchStyle{
|
|
Switch: swtch,
|
|
Description: description,
|
|
}
|
|
sw.Color.Enabled = th.Palette.ContrastBg
|
|
sw.Color.Disabled = th.Palette.Bg
|
|
sw.Color.Track = f32color.MulAlpha(th.Palette.Fg, 0x88)
|
|
return sw
|
|
}
|
|
|
|
// Layout updates the switch and displays it.
|
|
func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
|
|
trackWidth := gtx.Px(unit.Dp(36))
|
|
trackHeight := gtx.Px(unit.Dp(16))
|
|
thumbSize := gtx.Px(unit.Dp(20))
|
|
trackOff := (thumbSize - trackHeight) / 2
|
|
|
|
// Draw track.
|
|
trackCorner := float32(trackHeight) / 2
|
|
trackRect := f32.Rectangle{Max: f32.Point{
|
|
X: float32(trackWidth),
|
|
Y: float32(trackHeight),
|
|
}}
|
|
col := s.Color.Disabled
|
|
if s.Switch.Value {
|
|
col = s.Color.Enabled
|
|
}
|
|
if gtx.Queue == nil {
|
|
col = f32color.Disabled(col)
|
|
}
|
|
trackColor := s.Color.Track
|
|
t := op.Offset(image.Point{Y: trackOff}).Push(gtx.Ops)
|
|
cl := clip.UniformRRect(trackRect, trackCorner).Push(gtx.Ops)
|
|
paint.ColorOp{Color: trackColor}.Add(gtx.Ops)
|
|
paint.PaintOp{}.Add(gtx.Ops)
|
|
cl.Pop()
|
|
t.Pop()
|
|
|
|
// Draw thumb ink.
|
|
inkSize := gtx.Px(unit.Dp(44))
|
|
rr := float32(inkSize) * .5
|
|
inkOff := image.Point{
|
|
X: trackWidth/2 - int(rr),
|
|
Y: -int(rr) + trackHeight/2 + trackOff,
|
|
}
|
|
t = op.Offset(inkOff).Push(gtx.Ops)
|
|
gtx.Constraints.Min = image.Pt(inkSize, inkSize)
|
|
cl = clip.UniformRRect(f32.Rectangle{Max: layout.FPt(gtx.Constraints.Min)}, rr).Push(gtx.Ops)
|
|
for _, p := range s.Switch.History() {
|
|
drawInk(gtx, p)
|
|
}
|
|
cl.Pop()
|
|
t.Pop()
|
|
|
|
// Compute thumb offset.
|
|
if s.Switch.Value {
|
|
xoff := trackWidth - thumbSize
|
|
defer op.Offset(image.Point{X: xoff}).Push(gtx.Ops).Pop()
|
|
}
|
|
|
|
thumbRadius := float32(thumbSize) / 2
|
|
|
|
circle := func(x, y, r float32) clip.Op {
|
|
b := f32.Rectangle{
|
|
Min: f32.Pt(x-r, y-r),
|
|
Max: f32.Pt(x+r, y+r),
|
|
}
|
|
return clip.Ellipse(b).Op(gtx.Ops)
|
|
}
|
|
// Draw hover.
|
|
if s.Switch.Hovered() || s.Switch.Focused() {
|
|
r := 1.7 * thumbRadius
|
|
background := f32color.MulAlpha(s.Color.Enabled, 70)
|
|
paint.FillShape(gtx.Ops, background, circle(thumbRadius, thumbRadius, r))
|
|
}
|
|
|
|
// Draw thumb shadow, a translucent disc slightly larger than the
|
|
// thumb itself.
|
|
// Center shadow horizontally and slightly adjust its Y.
|
|
paint.FillShape(gtx.Ops, argb(0x55000000), circle(thumbRadius, thumbRadius+.25, thumbRadius+1))
|
|
|
|
// Draw thumb.
|
|
paint.FillShape(gtx.Ops, col, circle(thumbRadius, thumbRadius, thumbRadius))
|
|
|
|
// Set up click area.
|
|
clickSize := gtx.Px(unit.Dp(40))
|
|
clickOff := image.Point{
|
|
X: (thumbSize - clickSize) / 2,
|
|
Y: (trackHeight-clickSize)/2 + trackOff,
|
|
}
|
|
defer op.Offset(clickOff).Push(gtx.Ops).Pop()
|
|
sz := image.Pt(clickSize, clickSize)
|
|
defer clip.Ellipse(f32.Rectangle{Max: layout.FPt(sz)}).Push(gtx.Ops).Pop()
|
|
s.Switch.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
|
if d := s.Description; d != "" {
|
|
semantic.DescriptionOp(d).Add(gtx.Ops)
|
|
}
|
|
semantic.Switch.Add(gtx.Ops)
|
|
return layout.Dimensions{Size: sz}
|
|
})
|
|
|
|
dims := image.Point{X: trackWidth, Y: thumbSize}
|
|
return layout.Dimensions{Size: dims}
|
|
}
|