Files
gio/widget/material/slider.go
T
Elias Naur d42dae73f0 widget: [API] separate Float state update; remove min, max, invert parameters
This change allows users of Float to determine its state before Layout
by calling Update.

While here, remove the value transformation represented by the min, max,
invert parameters; they're too many arguments for a computation that
may as well be done by the user.

Remove Float.Pos; it is better to compute its value from the dimensions
returned by Float.Layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 20:04:32 -05:00

111 lines
2.5 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package material
import (
"image"
"image/color"
"gioui.org/internal/f32color"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
)
// Slider is for selecting a value in a range.
func Slider(th *Theme, float *widget.Float) SliderStyle {
return SliderStyle{
Color: th.Palette.ContrastBg,
Float: float,
FingerSize: th.FingerSize,
}
}
type SliderStyle struct {
Axis layout.Axis
Color color.NRGBA
Float *widget.Float
FingerSize unit.Dp
}
func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
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 := 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*tr, touchSizePx)
size := axis.Convert(image.Pt(sizeMain, sizeCross))
o := axis.Convert(image.Pt(tr, 0))
trans := op.Offset(o).Push(gtx.Ops)
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 := tr + int(s.Float.Value*float32(axis.Convert(dims.Size).X))
trans.Pop()
color := s.Color
if gtx.Queue == nil {
color = f32color.Disabled(color)
}
rect := func(minx, miny, maxx, maxy int) image.Rectangle {
r := image.Rect(minx, miny, maxx, maxy)
if axis == layout.Vertical {
r.Max.X, r.Min.X = sizeMain-r.Min.X, sizeMain-r.Max.X
}
r.Min = axis.Convert(r.Min)
r.Max = axis.Convert(r.Max)
return r
}
// Draw track before thumb.
track := rect(
tr, sizeCross/2-trackWidth/2,
thumbPos, sizeCross/2+trackWidth/2,
)
paint.FillShape(gtx.Ops, color, clip.Rect(track).Op())
// Draw track after thumb.
track = rect(
thumbPos, axis.Convert(track.Min).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-tr, pt.Y-tr,
pt.X+tr, pt.Y+tr,
)
paint.FillShape(gtx.Ops, color, clip.Ellipse(thumb).Op(gtx.Ops))
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
}