github.com/utopiagio/gio@v0.0.8/widget/material/slider.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package material
     4  
     5  import (
     6  	"image"
     7  	"image/color"
     8  
     9  	"github.com/utopiagio/gio/internal/f32color"
    10  	"github.com/utopiagio/gio/layout"
    11  	"github.com/utopiagio/gio/op"
    12  	"github.com/utopiagio/gio/op/clip"
    13  	"github.com/utopiagio/gio/op/paint"
    14  	"github.com/utopiagio/gio/unit"
    15  	"github.com/utopiagio/gio/widget"
    16  )
    17  
    18  // Slider is for selecting a value in a range.
    19  func Slider(th *Theme, float *widget.Float) SliderStyle {
    20  	return SliderStyle{
    21  		Color:      th.Palette.ContrastBg,
    22  		Float:      float,
    23  		FingerSize: th.FingerSize,
    24  	}
    25  }
    26  
    27  type SliderStyle struct {
    28  	Axis  layout.Axis
    29  	Color color.NRGBA
    30  	Float *widget.Float
    31  
    32  	FingerSize unit.Dp
    33  }
    34  
    35  func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
    36  	const thumbRadius unit.Dp = 6
    37  	tr := gtx.Dp(thumbRadius)
    38  	trackWidth := gtx.Dp(2)
    39  
    40  	axis := s.Axis
    41  	// Keep a minimum length so that the track is always visible.
    42  	minLength := tr + 3*tr + tr
    43  	// Try to expand to finger size, but only if the constraints
    44  	// allow for it.
    45  	touchSizePx := min(gtx.Dp(s.FingerSize), axis.Convert(gtx.Constraints.Max).Y)
    46  	sizeMain := max(axis.Convert(gtx.Constraints.Min).X, minLength)
    47  	sizeCross := max(2*tr, touchSizePx)
    48  	size := axis.Convert(image.Pt(sizeMain, sizeCross))
    49  
    50  	o := axis.Convert(image.Pt(tr, 0))
    51  	trans := op.Offset(o).Push(gtx.Ops)
    52  	gtx.Constraints.Min = axis.Convert(image.Pt(sizeMain-2*tr, sizeCross))
    53  	dims := s.Float.Layout(gtx, axis, thumbRadius)
    54  	gtx.Constraints.Min = gtx.Constraints.Min.Add(axis.Convert(image.Pt(0, sizeCross)))
    55  	thumbPos := tr + int(s.Float.Value*float32(axis.Convert(dims.Size).X))
    56  	trans.Pop()
    57  
    58  	color := s.Color
    59  	if !gtx.Enabled() {
    60  		color = f32color.Disabled(color)
    61  	}
    62  
    63  	rect := func(minx, miny, maxx, maxy int) image.Rectangle {
    64  		r := image.Rect(minx, miny, maxx, maxy)
    65  		if axis == layout.Vertical {
    66  			r.Max.X, r.Min.X = sizeMain-r.Min.X, sizeMain-r.Max.X
    67  		}
    68  		r.Min = axis.Convert(r.Min)
    69  		r.Max = axis.Convert(r.Max)
    70  		return r
    71  	}
    72  
    73  	// Draw track before thumb.
    74  	track := rect(
    75  		tr, sizeCross/2-trackWidth/2,
    76  		thumbPos, sizeCross/2+trackWidth/2,
    77  	)
    78  	paint.FillShape(gtx.Ops, color, clip.Rect(track).Op())
    79  
    80  	// Draw track after thumb.
    81  	track = rect(
    82  		thumbPos, axis.Convert(track.Min).Y,
    83  		sizeMain-tr, axis.Convert(track.Max).Y,
    84  	)
    85  	paint.FillShape(gtx.Ops, f32color.MulAlpha(color, 96), clip.Rect(track).Op())
    86  
    87  	// Draw thumb.
    88  	pt := image.Pt(thumbPos, sizeCross/2)
    89  	thumb := rect(
    90  		pt.X-tr, pt.Y-tr,
    91  		pt.X+tr, pt.Y+tr,
    92  	)
    93  	paint.FillShape(gtx.Ops, color, clip.Ellipse(thumb).Op(gtx.Ops))
    94  
    95  	return layout.Dimensions{Size: size}
    96  }
    97  
    98  func max(a, b int) int {
    99  	if a > b {
   100  		return a
   101  	}
   102  	return b
   103  }
   104  
   105  func min(a, b int) int {
   106  	if a < b {
   107  		return a
   108  	}
   109  	return b
   110  }