From 85c0a7d803e8513622dacd2fb49ef2d440e9bb3b Mon Sep 17 00:00:00 2001 From: pierre Date: Thu, 14 Jan 2021 17:41:32 +0100 Subject: [PATCH] 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 --- widget/float.go | 23 ++++++--- widget/material/slider.go | 102 +++++++++++++++++--------------------- 2 files changed, 62 insertions(+), 63 deletions(-) diff --git a/widget/float.go b/widget/float.go index 3c59a26a..8d9c9145 100644 --- a/widget/float.go +++ b/widget/float.go @@ -14,6 +14,7 @@ import ( // Float is for selecting a value in a range. type Float struct { Value float32 + Axis layout.Axis drag gesture.Drag pos float32 // position normalized to [0, 1] @@ -24,13 +25,15 @@ type Float struct { // Dragging returns whether the value is being interacted with. 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 { size := gtx.Constraints.Min - f.length = float32(size.X) + f.length = float32(f.Axis.Convert(size).X) 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 { de = &e } @@ -38,7 +41,11 @@ func (f *Float) Layout(gtx layout.Context, pointerMargin int, min, max float32) value := f.Value 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 } else if min != max { 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() - rect := image.Rectangle{Max: size} - rect.Min.X -= pointerMargin - rect.Max.X += pointerMargin + margin := f.Axis.Convert(image.Pt(pointerMargin, 0)) + rect := image.Rectangle{ + Min: margin.Mul(-1), + Max: size.Add(margin), + } pointer.Rect(rect).Add(gtx.Ops) f.drag.Add(gtx.Ops) diff --git a/widget/material/slider.go b/widget/material/slider.go index 1ce95ffa..6617fbca 100644 --- a/widget/material/slider.go +++ b/widget/material/slider.go @@ -6,7 +6,6 @@ import ( "image" "image/color" - "gioui.org/f32" "gioui.org/internal/f32color" "gioui.org/layout" "gioui.org/op" @@ -36,34 +35,26 @@ type SliderStyle struct { } 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) + thumbRadius := gtx.Px(unit.Dp(6)) + trackWidth := gtx.Px(unit.Dp(2)) - size := gtx.Constraints.Min + axis := s.Float.Axis // Keep a minimum length so that the track is always visible. - minLength := thumbRadiusInt + 3*thumbRadiusInt + thumbRadiusInt - if size.X < minLength { - size.X = minLength - } - size.Y = 2 * thumbRadiusInt - + minLength := thumbRadius + 3*thumbRadius + thumbRadius // Try to expand to finger size, but only if the constraints // allow for it. - touchSizePx := gtx.Px(s.FingerSize) - if touchSizePx > gtx.Constraints.Max.Y { - touchSizePx = gtx.Constraints.Max.Y - } - if size.Y < touchSizePx { - size.Y = 2 * (touchSizePx / 2) - } + touchSizePx := min(gtx.Px(s.FingerSize), axis.Convert(gtx.Constraints.Max).Y) + sizeMain := max(axis.Convert(gtx.Constraints.Min).X, minLength) + sizeCross := max(2*thumbRadius, touchSizePx) + size := axis.Convert(image.Pt(sizeMain, sizeCross)) st := op.Save(gtx.Ops) - op.Offset(f32.Pt(thumbRadius, 0)).Add(gtx.Ops) - gtx.Constraints.Min = image.Pt(size.X-2*thumbRadiusInt, size.Y) - s.Float.Layout(gtx, thumbRadiusInt, s.Min, s.Max) - gtx.Constraints.Min.Y = size.Y - thumbPos := thumbRadius + s.Float.Pos() + o := axis.Convert(image.Pt(thumbRadius, 0)) + op.Offset(layout.FPt(o)).Add(gtx.Ops) + gtx.Constraints.Min = axis.Convert(image.Pt(sizeMain-2*thumbRadius, sizeCross)) + s.Float.Layout(gtx, thumbRadius, s.Min, s.Max) + gtx.Constraints.Min = gtx.Constraints.Min.Add(axis.Convert(image.Pt(0, sizeCross))) + thumbPos := thumbRadius + int(s.Float.Pos()) st.Load() color := s.Color @@ -73,50 +64,49 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions { // Draw track before thumb. st = op.Save(gtx.Ops) - track := f32.Rectangle{ - Min: f32.Point{ - X: thumbRadius, - Y: float32(size.Y/2) - trackWidth/2, - }, - Max: f32.Point{ - X: thumbPos, - Y: float32(size.Y/2) + trackWidth/2, - }, + track := image.Rectangle{ + Min: axis.Convert(image.Pt(thumbRadius, sizeCross/2-trackWidth/2)), + Max: axis.Convert(image.Pt(thumbPos, sizeCross/2+trackWidth/2)), } - clip.RRect{Rect: track}.Add(gtx.Ops) - paint.ColorOp{Color: color}.Add(gtx.Ops) - paint.PaintOp{}.Add(gtx.Ops) + clip.Rect(track).Add(gtx.Ops) + paint.Fill(gtx.Ops, color) st.Load() // Draw track after thumb. st = op.Save(gtx.Ops) - track.Min.X = thumbPos - track.Max.X = float32(size.X) - thumbRadius - clip.RRect{Rect: track}.Add(gtx.Ops) - paint.ColorOp{Color: f32color.MulAlpha(color, 96)}.Add(gtx.Ops) - paint.PaintOp{}.Add(gtx.Ops) + track = image.Rectangle{ + Min: axis.Convert(image.Pt(thumbPos, axis.Convert(track.Min).Y)), + Max: axis.Convert(image.Pt(sizeMain-thumbRadius, axis.Convert(track.Max).Y)), + } + clip.Rect(track).Add(gtx.Ops) + paint.Fill(gtx.Ops, f32color.MulAlpha(color, 96)) st.Load() // Draw thumb. st = op.Save(gtx.Ops) - thumb := f32.Rectangle{ - Min: f32.Point{ - X: thumbPos - thumbRadius, - Y: float32(size.Y/2) - thumbRadius, - }, - Max: f32.Point{ - X: thumbPos + thumbRadius, - Y: float32(size.Y/2) + thumbRadius, - }, + pt := axis.Convert(image.Pt(thumbPos, sizeCross/2)) + rpt := image.Pt(thumbRadius, thumbRadius) + thumb := image.Rectangle{ + Min: pt.Sub(rpt), + Max: pt.Add(rpt), } - rr := thumbRadius - clip.RRect{ - 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) + clip.UniformRRect(layout.FRect(thumb), float32(thumbRadius)).Add(gtx.Ops) + paint.Fill(gtx.Ops, color) st.Load() 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 +}