slider.go 2.5 KB

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