progressbar.go raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  package material
   4  
   5  import (
   6  	"image"
   7  	"image/color"
   8  
   9  	"github.com/p9c/p9/pkg/gel/gio/f32"
  10  	"github.com/p9c/p9/pkg/gel/gio/internal/f32color"
  11  	"github.com/p9c/p9/pkg/gel/gio/layout"
  12  	"github.com/p9c/p9/pkg/gel/gio/op/clip"
  13  	"github.com/p9c/p9/pkg/gel/gio/op/paint"
  14  	"github.com/p9c/p9/pkg/gel/gio/unit"
  15  )
  16  
  17  type ProgressBarStyle struct {
  18  	Color      color.NRGBA
  19  	TrackColor color.NRGBA
  20  	Progress   float32
  21  }
  22  
  23  func ProgressBar(th *Theme, progress float32) ProgressBarStyle {
  24  	return ProgressBarStyle{
  25  		Progress:   progress,
  26  		Color:      th.Palette.ContrastBg,
  27  		TrackColor: f32color.MulAlpha(th.Palette.Fg, 0x88),
  28  	}
  29  }
  30  
  31  func (p ProgressBarStyle) Layout(gtx layout.Context) layout.Dimensions {
  32  	shader := func(width float32, color color.NRGBA) layout.Dimensions {
  33  		maxHeight := unit.Dp(4)
  34  		rr := float32(gtx.Px(unit.Dp(2)))
  35  
  36  		d := image.Point{X: int(width), Y: gtx.Px(maxHeight)}
  37  
  38  		height := float32(gtx.Px(maxHeight))
  39  		clip.UniformRRect(f32.Rectangle{Max: f32.Pt(width, height)}, rr).Add(gtx.Ops)
  40  		paint.ColorOp{Color: color}.Add(gtx.Ops)
  41  		paint.PaintOp{}.Add(gtx.Ops)
  42  
  43  		return layout.Dimensions{Size: d}
  44  	}
  45  
  46  	progressBarWidth := float32(gtx.Constraints.Max.X)
  47  	return layout.Stack{Alignment: layout.W}.Layout(gtx,
  48  		layout.Stacked(func(gtx layout.Context) layout.Dimensions {
  49  			return shader(progressBarWidth, p.TrackColor)
  50  		}),
  51  		layout.Stacked(func(gtx layout.Context) layout.Dimensions {
  52  			fillWidth := progressBarWidth * clamp1(p.Progress)
  53  			fillColor := p.Color
  54  			if gtx.Queue == nil {
  55  				fillColor = f32color.Disabled(fillColor)
  56  			}
  57  			return shader(fillWidth, fillColor)
  58  		}),
  59  	)
  60  }
  61  
  62  // clamp1 limits v to range [0..1].
  63  func clamp1(v float32) float32 {
  64  	if v >= 1 {
  65  		return 1
  66  	} else if v <= 0 {
  67  		return 0
  68  	} else {
  69  		return v
  70  	}
  71  }
  72