~eliasnaur/gio

85c0a7d803e8513622dacd2fb49ef2d440e9bb3b — pierre 5 months ago f86703e
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>
2 files changed, 62 insertions(+), 63 deletions(-)

M widget/float.go
M widget/material/slider.go
M widget/float.go => widget/float.go +16 -7
@@ 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)


M widget/material/slider.go => widget/material/slider.go +46 -56
@@ 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
}