indefinite.go raw

   1  package gel
   2  
   3  import (
   4  	"image"
   5  	"image/color"
   6  	"math"
   7  	"time"
   8  	
   9  	"github.com/p9c/gio/f32"
  10  	l "github.com/p9c/gio/layout"
  11  	"github.com/p9c/gio/op"
  12  	"github.com/p9c/gio/op/clip"
  13  	"github.com/p9c/gio/op/paint"
  14  )
  15  
  16  type Indefinite struct {
  17  	*Window
  18  	color color.NRGBA
  19  	scale float32
  20  }
  21  
  22  // Indefinite creates an indefinite loading animation icon
  23  func (w *Window) Indefinite() *Indefinite {
  24  	return &Indefinite{
  25  		Window: w,
  26  		color:  w.Colors.GetNRGBAFromName("Primary"),
  27  	}
  28  }
  29  
  30  // Scale sets the size of the spinner
  31  func (lo *Indefinite) Scale(scale float32) *Indefinite {
  32  	lo.scale = scale
  33  	return lo
  34  }
  35  
  36  // Color sets the color of the spinner
  37  func (lo *Indefinite) Color(color string) *Indefinite {
  38  	lo.color = lo.Theme.Colors.GetNRGBAFromName(color)
  39  	return lo
  40  }
  41  
  42  // Fn renders the loader
  43  func (lo *Indefinite) Fn(gtx l.Context) l.Dimensions {
  44  	diam := gtx.Constraints.Min.X
  45  	if minY := gtx.Constraints.Min.Y; minY > diam {
  46  		diam = minY
  47  	}
  48  	if diam == 0 {
  49  		diam = gtx.Px(lo.Theme.TextSize.Scale(lo.scale))
  50  	}
  51  	sz := gtx.Constraints.Constrain(image.Pt(diam, diam))
  52  	radius := float64(sz.X) * .5
  53  	defer op.Save(gtx.Ops).Load()
  54  	op.Offset(f32.Pt(float32(radius), float32(radius))).Add(gtx.Ops)
  55  	dt := (time.Duration(gtx.Now.UnixNano()) % (time.Second)).Seconds()
  56  	startAngle := dt * math.Pi * 2
  57  	endAngle := startAngle + math.Pi*1.5
  58  	clipLoader(gtx.Ops, startAngle, endAngle, radius)
  59  	paint.ColorOp{
  60  		Color: lo.color,
  61  	}.Add(gtx.Ops)
  62  	op.Offset(f32.Pt(-float32(radius), -float32(radius))).Add(gtx.Ops)
  63  	paint.PaintOp{
  64  		// Rect: f32.Rectangle{Max: l.FPt(sz)},
  65  	}.Add(gtx.Ops)
  66  	op.InvalidateOp{}.Add(gtx.Ops)
  67  	return l.Dimensions{
  68  		Size: sz,
  69  	}
  70  }
  71  
  72  func clipLoader(ops *op.Ops, startAngle, endAngle, radius float64) {
  73  	const thickness = .25
  74  	var (
  75  		width = float32(radius * thickness)
  76  		delta = float32(endAngle - startAngle)
  77  		
  78  		vy, vx = math.Sincos(startAngle)
  79  		
  80  		pen    = f32.Pt(float32(vx), float32(vy)).Mul(float32(radius))
  81  		center = f32.Pt(0, 0).Sub(pen)
  82  		
  83  		p clip.Path
  84  	)
  85  	p.Begin(ops)
  86  	p.Move(pen)
  87  	p.Arc(center, center, delta)
  88  	clip.Stroke{
  89  		Path: p.End(),
  90  		Style: clip.StrokeStyle{
  91  			Width: width,
  92  			Cap:   clip.FlatCap,
  93  		},
  94  	}.Op().Add(ops)
  95  }
  96