diff --git a/widget/float.go b/widget/float.go index 64006338..04a7a055 100644 --- a/widget/float.go +++ b/widget/float.go @@ -9,59 +9,29 @@ import ( "gioui.org/io/pointer" "gioui.org/layout" "gioui.org/op/clip" + "gioui.org/unit" ) // Float is for selecting a value in a range. type Float struct { + // Value is the value of the Float, in the [0; 1] range. Value float32 - drag gesture.Drag - pos float32 // position normalized to [0, 1] - length float32 - changed bool + drag gesture.Drag + axis layout.Axis + length float32 } // Dragging returns whether the value is being interacted with. func (f *Float) Dragging() bool { return f.drag.Dragging() } -// 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, axis layout.Axis, min, max float32, invert bool, pointerMargin int) layout.Dimensions { +func (f *Float) Layout(gtx layout.Context, axis layout.Axis, pointerMargin unit.Dp) layout.Dimensions { + f.Update(gtx) size := gtx.Constraints.Min f.length = float32(axis.Convert(size).X) + f.axis = axis - var de *pointer.Event - for _, e := range f.drag.Events(gtx.Metric, gtx, gesture.Axis(axis)) { - if e.Kind == pointer.Press || e.Kind == pointer.Drag { - de = &e - } - } - - value := f.Value - if de != nil { - xy := de.Position.X - if axis == layout.Vertical { - xy = f.length - de.Position.Y - } - if invert { - xy = f.length - xy - } - f.pos = xy / f.length - value = min + (max-min)*f.pos - } else if min != max { - f.pos = (value - min) / (max - 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 - } - - margin := axis.Convert(image.Pt(pointerMargin, 0)) + margin := axis.Convert(image.Pt(gtx.Dp(pointerMargin), 0)) rect := image.Rectangle{ Min: margin.Mul(-1), Max: size.Add(margin), @@ -72,30 +42,26 @@ func (f *Float) Layout(gtx layout.Context, axis layout.Axis, min, max float32, i return layout.Dimensions{Size: size} } -func (f *Float) setValue(value, min, max float32) { - if min > max { - min, max = max, min +// Update the Value according to drag events along the f's main axis. +// The return value reports whether the value was changed. +// +// The range of f is set by the minimum constraints main axis value. +func (f *Float) Update(gtx layout.Context) bool { + changed := false + for _, e := range f.drag.Events(gtx.Metric, gtx, gesture.Axis(f.axis)) { + if f.length > 0 && (e.Kind == pointer.Press || e.Kind == pointer.Drag) { + pos := e.Position.X + if f.axis == layout.Vertical { + pos = f.length - e.Position.Y + } + f.Value = pos / f.length + if f.Value < 0 { + f.Value = 0 + } else if f.Value > 1 { + f.Value = 1 + } + changed = true + } } - 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 } diff --git a/widget/material/slider.go b/widget/material/slider.go index d5783f34..3f9ab73a 100644 --- a/widget/material/slider.go +++ b/widget/material/slider.go @@ -16,10 +16,8 @@ import ( ) // Slider is for selecting a value in a range. -func Slider(th *Theme, float *widget.Float, min, max float32) SliderStyle { +func Slider(th *Theme, float *widget.Float) SliderStyle { return SliderStyle{ - Min: min, - Max: max, Color: th.Palette.ContrastBg, Float: float, FingerSize: th.FingerSize, @@ -27,35 +25,34 @@ func Slider(th *Theme, float *widget.Float, min, max float32) SliderStyle { } type SliderStyle struct { - Axis layout.Axis - Min, Max float32 - Invert bool - Color color.NRGBA - Float *widget.Float + Axis layout.Axis + Color color.NRGBA + Float *widget.Float FingerSize unit.Dp } func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions { - thumbRadius := gtx.Dp(6) + const thumbRadius unit.Dp = 6 + tr := gtx.Dp(thumbRadius) trackWidth := gtx.Dp(2) axis := s.Axis // Keep a minimum length so that the track is always visible. - minLength := thumbRadius + 3*thumbRadius + thumbRadius + minLength := tr + 3*tr + tr // Try to expand to finger size, but only if the constraints // allow for it. touchSizePx := min(gtx.Dp(s.FingerSize), axis.Convert(gtx.Constraints.Max).Y) sizeMain := max(axis.Convert(gtx.Constraints.Min).X, minLength) - sizeCross := max(2*thumbRadius, touchSizePx) + sizeCross := max(2*tr, touchSizePx) size := axis.Convert(image.Pt(sizeMain, sizeCross)) - o := axis.Convert(image.Pt(thumbRadius, 0)) + o := axis.Convert(image.Pt(tr, 0)) trans := op.Offset(o).Push(gtx.Ops) - gtx.Constraints.Min = axis.Convert(image.Pt(sizeMain-2*thumbRadius, sizeCross)) - s.Float.Layout(gtx, axis, s.Min, s.Max, s.Invert, thumbRadius) + gtx.Constraints.Min = axis.Convert(image.Pt(sizeMain-2*tr, sizeCross)) + dims := s.Float.Layout(gtx, axis, thumbRadius) gtx.Constraints.Min = gtx.Constraints.Min.Add(axis.Convert(image.Pt(0, sizeCross))) - thumbPos := thumbRadius + int(s.Float.Pos()) + thumbPos := tr + int(s.Float.Value*float32(axis.Convert(dims.Size).X)) trans.Pop() color := s.Color @@ -65,7 +62,7 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions { rect := func(minx, miny, maxx, maxy int) image.Rectangle { r := image.Rect(minx, miny, maxx, maxy) - if s.Invert != (axis == layout.Vertical) { + if axis == layout.Vertical { r.Max.X, r.Min.X = sizeMain-r.Min.X, sizeMain-r.Max.X } r.Min = axis.Convert(r.Min) @@ -75,7 +72,7 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions { // Draw track before thumb. track := rect( - thumbRadius, sizeCross/2-trackWidth/2, + tr, sizeCross/2-trackWidth/2, thumbPos, sizeCross/2+trackWidth/2, ) paint.FillShape(gtx.Ops, color, clip.Rect(track).Op()) @@ -83,15 +80,15 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions { // Draw track after thumb. track = rect( thumbPos, axis.Convert(track.Min).Y, - sizeMain-thumbRadius, axis.Convert(track.Max).Y, + sizeMain-tr, axis.Convert(track.Max).Y, ) paint.FillShape(gtx.Ops, f32color.MulAlpha(color, 96), clip.Rect(track).Op()) // Draw thumb. pt := image.Pt(thumbPos, sizeCross/2) thumb := rect( - pt.X-thumbRadius, pt.Y-thumbRadius, - pt.X+thumbRadius, pt.Y+thumbRadius, + pt.X-tr, pt.Y-tr, + pt.X+tr, pt.Y+tr, ) paint.FillShape(gtx.Ops, color, clip.Ellipse(thumb).Op(gtx.Ops))