widget/material: added support for Vertical axis to Slider

Adding an axis to the Float widget, allows positioning the Slider one not only horizontally but also vertically.
Also update the fill ops while there.

Signed-off-by: pierre <pierre.curto@gmail.com>
This commit is contained in:
pierre
2021-01-14 17:41:32 +01:00
committed by Elias Naur
parent f86703e4b0
commit 85c0a7d803
2 changed files with 62 additions and 63 deletions
+16 -7
View File
@@ -14,6 +14,7 @@ import (
// Float is for selecting a value in a range. // Float is for selecting a value in a range.
type Float struct { type Float struct {
Value float32 Value float32
Axis layout.Axis
drag gesture.Drag drag gesture.Drag
pos float32 // position normalized to [0, 1] pos float32 // position normalized to [0, 1]
@@ -24,13 +25,15 @@ type Float struct {
// Dragging returns whether the value is being interacted with. // Dragging returns whether the value is being interacted with.
func (f *Float) Dragging() bool { return f.drag.Dragging() } func (f *Float) Dragging() bool { return f.drag.Dragging() }
// Layout processes events. // Layout updates the value according to drag events along the f's main axis.
//
// The range of f is set by the minimum constraints main axis value.
func (f *Float) Layout(gtx layout.Context, pointerMargin int, min, max float32) layout.Dimensions { func (f *Float) Layout(gtx layout.Context, pointerMargin int, min, max float32) layout.Dimensions {
size := gtx.Constraints.Min size := gtx.Constraints.Min
f.length = float32(size.X) f.length = float32(f.Axis.Convert(size).X)
var de *pointer.Event var de *pointer.Event
for _, e := range f.drag.Events(gtx.Metric, gtx, gesture.Horizontal) { for _, e := range f.drag.Events(gtx.Metric, gtx, gesture.Axis(f.Axis)) {
if e.Type == pointer.Press || e.Type == pointer.Drag { if e.Type == pointer.Press || e.Type == pointer.Drag {
de = &e de = &e
} }
@@ -38,7 +41,11 @@ func (f *Float) Layout(gtx layout.Context, pointerMargin int, min, max float32)
value := f.Value value := f.Value
if de != nil { if de != nil {
f.pos = de.Position.X / f.length xy := de.Position.X
if f.Axis == layout.Vertical {
xy = de.Position.Y
}
f.pos = xy / f.length
value = min + (max-min)*f.pos value = min + (max-min)*f.pos
} else if min != max { } else if min != max {
f.pos = value/(max-min) - min f.pos = value/(max-min) - min
@@ -53,9 +60,11 @@ func (f *Float) Layout(gtx layout.Context, pointerMargin int, min, max float32)
} }
defer op.Save(gtx.Ops).Load() defer op.Save(gtx.Ops).Load()
rect := image.Rectangle{Max: size} margin := f.Axis.Convert(image.Pt(pointerMargin, 0))
rect.Min.X -= pointerMargin rect := image.Rectangle{
rect.Max.X += pointerMargin Min: margin.Mul(-1),
Max: size.Add(margin),
}
pointer.Rect(rect).Add(gtx.Ops) pointer.Rect(rect).Add(gtx.Ops)
f.drag.Add(gtx.Ops) f.drag.Add(gtx.Ops)
+46 -56
View File
@@ -6,7 +6,6 @@ import (
"image" "image"
"image/color" "image/color"
"gioui.org/f32"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
@@ -36,34 +35,26 @@ type SliderStyle struct {
} }
func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions { func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
thumbRadiusInt := gtx.Px(unit.Dp(6)) thumbRadius := gtx.Px(unit.Dp(6))
trackWidth := float32(gtx.Px(unit.Dp(2))) trackWidth := gtx.Px(unit.Dp(2))
thumbRadius := float32(thumbRadiusInt)
size := gtx.Constraints.Min axis := s.Float.Axis
// Keep a minimum length so that the track is always visible. // Keep a minimum length so that the track is always visible.
minLength := thumbRadiusInt + 3*thumbRadiusInt + thumbRadiusInt minLength := thumbRadius + 3*thumbRadius + thumbRadius
if size.X < minLength {
size.X = minLength
}
size.Y = 2 * thumbRadiusInt
// Try to expand to finger size, but only if the constraints // Try to expand to finger size, but only if the constraints
// allow for it. // allow for it.
touchSizePx := gtx.Px(s.FingerSize) touchSizePx := min(gtx.Px(s.FingerSize), axis.Convert(gtx.Constraints.Max).Y)
if touchSizePx > gtx.Constraints.Max.Y { sizeMain := max(axis.Convert(gtx.Constraints.Min).X, minLength)
touchSizePx = gtx.Constraints.Max.Y sizeCross := max(2*thumbRadius, touchSizePx)
} size := axis.Convert(image.Pt(sizeMain, sizeCross))
if size.Y < touchSizePx {
size.Y = 2 * (touchSizePx / 2)
}
st := op.Save(gtx.Ops) st := op.Save(gtx.Ops)
op.Offset(f32.Pt(thumbRadius, 0)).Add(gtx.Ops) o := axis.Convert(image.Pt(thumbRadius, 0))
gtx.Constraints.Min = image.Pt(size.X-2*thumbRadiusInt, size.Y) op.Offset(layout.FPt(o)).Add(gtx.Ops)
s.Float.Layout(gtx, thumbRadiusInt, s.Min, s.Max) gtx.Constraints.Min = axis.Convert(image.Pt(sizeMain-2*thumbRadius, sizeCross))
gtx.Constraints.Min.Y = size.Y s.Float.Layout(gtx, thumbRadius, s.Min, s.Max)
thumbPos := thumbRadius + s.Float.Pos() gtx.Constraints.Min = gtx.Constraints.Min.Add(axis.Convert(image.Pt(0, sizeCross)))
thumbPos := thumbRadius + int(s.Float.Pos())
st.Load() st.Load()
color := s.Color color := s.Color
@@ -73,50 +64,49 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
// Draw track before thumb. // Draw track before thumb.
st = op.Save(gtx.Ops) st = op.Save(gtx.Ops)
track := f32.Rectangle{ track := image.Rectangle{
Min: f32.Point{ Min: axis.Convert(image.Pt(thumbRadius, sizeCross/2-trackWidth/2)),
X: thumbRadius, Max: axis.Convert(image.Pt(thumbPos, sizeCross/2+trackWidth/2)),
Y: float32(size.Y/2) - trackWidth/2,
},
Max: f32.Point{
X: thumbPos,
Y: float32(size.Y/2) + trackWidth/2,
},
} }
clip.RRect{Rect: track}.Add(gtx.Ops) clip.Rect(track).Add(gtx.Ops)
paint.ColorOp{Color: color}.Add(gtx.Ops) paint.Fill(gtx.Ops, color)
paint.PaintOp{}.Add(gtx.Ops)
st.Load() st.Load()
// Draw track after thumb. // Draw track after thumb.
st = op.Save(gtx.Ops) st = op.Save(gtx.Ops)
track.Min.X = thumbPos track = image.Rectangle{
track.Max.X = float32(size.X) - thumbRadius Min: axis.Convert(image.Pt(thumbPos, axis.Convert(track.Min).Y)),
clip.RRect{Rect: track}.Add(gtx.Ops) Max: axis.Convert(image.Pt(sizeMain-thumbRadius, axis.Convert(track.Max).Y)),
paint.ColorOp{Color: f32color.MulAlpha(color, 96)}.Add(gtx.Ops) }
paint.PaintOp{}.Add(gtx.Ops) clip.Rect(track).Add(gtx.Ops)
paint.Fill(gtx.Ops, f32color.MulAlpha(color, 96))
st.Load() st.Load()
// Draw thumb. // Draw thumb.
st = op.Save(gtx.Ops) st = op.Save(gtx.Ops)
thumb := f32.Rectangle{ pt := axis.Convert(image.Pt(thumbPos, sizeCross/2))
Min: f32.Point{ rpt := image.Pt(thumbRadius, thumbRadius)
X: thumbPos - thumbRadius, thumb := image.Rectangle{
Y: float32(size.Y/2) - thumbRadius, Min: pt.Sub(rpt),
}, Max: pt.Add(rpt),
Max: f32.Point{
X: thumbPos + thumbRadius,
Y: float32(size.Y/2) + thumbRadius,
},
} }
rr := thumbRadius clip.UniformRRect(layout.FRect(thumb), float32(thumbRadius)).Add(gtx.Ops)
clip.RRect{ paint.Fill(gtx.Ops, color)
Rect: thumb,
NE: rr, NW: rr, SE: rr, SW: rr,
}.Add(gtx.Ops)
paint.ColorOp{Color: color}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops)
st.Load() st.Load()
return layout.Dimensions{Size: size} return layout.Dimensions{Size: size}
} }
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}