// SPDX-License-Identifier: Unlicense OR MIT package material import ( "image" "image/color" "math" "time" "gioui.org/f32" "gioui.org/layout" "gioui.org/op" "gioui.org/op/clip" "gioui.org/op/paint" ) type LoaderStyle struct { Color color.NRGBA } func Loader(th *Theme) LoaderStyle { return LoaderStyle{ Color: th.Palette.ContrastBg, } } func (l LoaderStyle) Layout(gtx layout.Context) layout.Dimensions { diam := gtx.Constraints.Min.X if minY := gtx.Constraints.Min.Y; minY > diam { diam = minY } if diam == 0 { diam = gtx.Dp(24) } sz := gtx.Constraints.Constrain(image.Pt(diam, diam)) radius := sz.X / 2 defer op.Offset(image.Pt(radius, radius)).Push(gtx.Ops).Pop() dt := float32((time.Duration(gtx.Now.UnixNano()) % (time.Second)).Seconds()) startAngle := dt * math.Pi * 2 endAngle := startAngle + math.Pi*1.5 defer clipLoader(gtx.Ops, startAngle, endAngle, float32(radius)).Push(gtx.Ops).Pop() paint.ColorOp{ Color: l.Color, }.Add(gtx.Ops) defer op.Offset(image.Pt(-radius, -radius)).Push(gtx.Ops).Pop() paint.PaintOp{}.Add(gtx.Ops) gtx.Execute(op.InvalidateCmd{}) return layout.Dimensions{ Size: sz, } } func clipLoader(ops *op.Ops, startAngle, endAngle, radius float32) clip.Op { const thickness = .25 var ( width = radius * thickness delta = endAngle - startAngle vy, vx = math.Sincos(float64(startAngle)) inner = radius * (1. - thickness*.5) pen = f32.Pt(float32(vx), float32(vy)).Mul(inner) center = f32.Pt(0, 0).Sub(pen) p clip.Path ) p.Begin(ops) p.Move(pen) p.Arc(center, center, delta) return clip.Stroke{ Path: p.End(), Width: width, }.Op() }