widget,widget/material: add Float and Slider

Signed-off-by: Gordon Klaus <gordon.klaus@gmail.com>
This commit is contained in:
Gordon Klaus
2020-06-17 19:24:25 +02:00
committed by Elias Naur
parent 817e0fa9c3
commit 5368743478
3 changed files with 266 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: Unlicense OR MIT
package widget
import (
"image"
"gioui.org/gesture"
"gioui.org/io/pointer"
"gioui.org/layout"
"gioui.org/op"
)
// Float is for selecting a value in a range.
type Float struct {
Value float32
drag gesture.Drag
pos float32 // position normalized to [0, 1]
length float32
changed bool
}
// Layout processes events.
func (f *Float) Layout(gtx layout.Context, pointerMargin int, min, max float32) layout.Dimensions {
size := gtx.Constraints.Min
f.length = float32(size.X)
var de *pointer.Event
for _, e := range f.drag.Events(gtx.Metric, gtx, gesture.Horizontal) {
if e.Type == pointer.Press || e.Type == pointer.Drag {
de = &e
}
}
value := f.Value
if de != nil {
f.pos = de.Position.X / f.length
value = min + (max-min)*f.pos
} else if min != max {
f.pos = value/(max-min) - min
}
// Unconditionally call setValue in case min, max, or value changed.
f.setValue(value, min, max)
if f.pos < 0 {
f.pos = 0
} else if f.pos > 1 {
f.pos = 1
}
defer op.Push(gtx.Ops).Pop()
rect := image.Rectangle{Max: size}
rect.Min.X -= pointerMargin
rect.Max.X += pointerMargin
pointer.Rect(rect).Add(gtx.Ops)
f.drag.Add(gtx.Ops)
return layout.Dimensions{Size: size}
}
func (f *Float) setValue(value, min, max float32) {
if min > max {
min, max = max, min
}
if value < min {
value = min
} else if value > max {
value = max
}
if f.Value != value {
f.Value = value
f.changed = true
}
}
// Pos reports the selected position.
func (f *Float) Pos() float32 {
return f.pos * f.length
}
// Changed reports whether the value has changed since
// the last call to Changed.
func (f *Float) Changed() bool {
changed := f.changed
f.changed = false
return changed
}
+109
View File
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: Unlicense OR MIT
package material
import (
"image"
"image/color"
"gioui.org/f32"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
)
// Slider is for selecting a value in a range.
func Slider(th *Theme, float *widget.Float, min, max float32) SliderStyle {
return SliderStyle{
Min: min,
Max: max,
Color: th.Color.Primary,
Float: float,
}
}
type SliderStyle struct {
Min, Max float32
Color color.RGBA
Float *widget.Float
}
func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
thumbRadiusInt := gtx.Px(unit.Dp(6))
trackWidth := float32(gtx.Px(unit.Dp(2)))
thumbRadius := float32(thumbRadiusInt)
halfWidthInt := 2 * thumbRadiusInt
halfWidth := float32(halfWidthInt)
size := gtx.Constraints.Min
// Keep a minimum length so that the track is always visible.
minLength := halfWidthInt + 3*thumbRadiusInt + halfWidthInt
if size.X < minLength {
size.X = minLength
}
size.Y = 2 * halfWidthInt
st := op.Push(gtx.Ops)
op.Offset(f32.Pt(halfWidth, 0)).Add(gtx.Ops)
gtx.Constraints.Min = image.Pt(size.X-2*halfWidthInt, size.Y)
s.Float.Layout(gtx, halfWidthInt, s.Min, s.Max)
thumbPos := halfWidth + s.Float.Pos()
st.Pop()
color := s.Color
if gtx.Queue == nil {
color = mulAlpha(color, 150)
}
// Draw track before thumb.
st = op.Push(gtx.Ops)
track := f32.Rectangle{
Min: f32.Point{
X: halfWidth,
Y: halfWidth - trackWidth/2,
},
Max: f32.Point{
X: thumbPos,
Y: halfWidth + trackWidth/2,
},
}
clip.Rect{Rect: track}.Op(gtx.Ops).Add(gtx.Ops)
paint.ColorOp{Color: color}.Add(gtx.Ops)
paint.PaintOp{Rect: track}.Add(gtx.Ops)
st.Pop()
// Draw track after thumb.
st = op.Push(gtx.Ops)
track.Min.X = thumbPos
track.Max.X = float32(size.X) - halfWidth
clip.Rect{Rect: track}.Op(gtx.Ops).Add(gtx.Ops)
paint.ColorOp{Color: mulAlpha(color, 96)}.Add(gtx.Ops)
paint.PaintOp{Rect: track}.Add(gtx.Ops)
st.Pop()
// Draw thumb.
st = op.Push(gtx.Ops)
thumb := f32.Rectangle{
Min: f32.Point{
X: thumbPos - thumbRadius,
Y: halfWidth - thumbRadius,
},
Max: f32.Point{
X: thumbPos + thumbRadius,
Y: halfWidth + thumbRadius,
},
}
rr := thumbRadius
clip.Rect{
Rect: thumb,
NE: rr, NW: rr, SE: rr, SW: rr,
}.Op(gtx.Ops).Add(gtx.Ops)
paint.ColorOp{Color: color}.Add(gtx.Ops)
paint.PaintOp{Rect: thumb}.Add(gtx.Ops)
st.Pop()
return layout.Dimensions{Size: size}
}