177 lines
3.4 KiB
Go
177 lines
3.4 KiB
Go
package widget
|
|
|
|
import (
|
|
"image/color"
|
|
"time"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/canvas"
|
|
"fyne.io/fyne/v2/theme"
|
|
)
|
|
|
|
var _ fyne.Widget = (*Activity)(nil)
|
|
|
|
// Activity is used to indicate that something is happening that should be waited for,
|
|
// or is in the background (depending on usage).
|
|
//
|
|
// Since: 2.5
|
|
type Activity struct {
|
|
BaseWidget
|
|
|
|
started bool
|
|
}
|
|
|
|
// NewActivity returns a widget for indicating activity
|
|
//
|
|
// Since: 2.5
|
|
func NewActivity() *Activity {
|
|
a := &Activity{}
|
|
a.ExtendBaseWidget(a)
|
|
return a
|
|
}
|
|
|
|
func (a *Activity) MinSize() fyne.Size {
|
|
a.ExtendBaseWidget(a)
|
|
return a.BaseWidget.MinSize()
|
|
}
|
|
|
|
// Start the activity indicator animation
|
|
func (a *Activity) Start() {
|
|
if a.started {
|
|
return // already started
|
|
}
|
|
|
|
a.started = true
|
|
|
|
a.Refresh()
|
|
}
|
|
|
|
// Stop the activity indicator animation
|
|
func (a *Activity) Stop() {
|
|
if !a.started {
|
|
return // already stopped
|
|
}
|
|
|
|
a.started = false
|
|
|
|
a.Refresh()
|
|
}
|
|
|
|
func (a *Activity) CreateRenderer() fyne.WidgetRenderer {
|
|
dots := make([]fyne.CanvasObject, 3)
|
|
v := fyne.CurrentApp().Settings().ThemeVariant()
|
|
for i := range dots {
|
|
dots[i] = canvas.NewCircle(a.Theme().Color(theme.ColorNameForeground, v))
|
|
}
|
|
r := &activityRenderer{dots: dots, parent: a}
|
|
r.anim = &fyne.Animation{
|
|
Duration: time.Second * 2,
|
|
RepeatCount: fyne.AnimationRepeatForever,
|
|
Tick: r.animate}
|
|
r.updateColor()
|
|
|
|
if a.started {
|
|
r.start()
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
var _ fyne.WidgetRenderer = (*activityRenderer)(nil)
|
|
|
|
type activityRenderer struct {
|
|
anim *fyne.Animation
|
|
dots []fyne.CanvasObject
|
|
parent *Activity
|
|
|
|
bound fyne.Size
|
|
maxCol color.NRGBA
|
|
maxRad float32
|
|
wasStarted bool
|
|
}
|
|
|
|
func (a *activityRenderer) Destroy() {
|
|
a.parent.started = false
|
|
a.stop()
|
|
}
|
|
|
|
func (a *activityRenderer) Layout(size fyne.Size) {
|
|
a.maxRad = fyne.Min(size.Width, size.Height) / 2
|
|
a.bound = size
|
|
}
|
|
|
|
func (a *activityRenderer) MinSize() fyne.Size {
|
|
return fyne.NewSquareSize(a.parent.Theme().Size(theme.SizeNameInlineIcon))
|
|
}
|
|
|
|
func (a *activityRenderer) Objects() []fyne.CanvasObject {
|
|
return a.dots
|
|
}
|
|
|
|
func (a *activityRenderer) Refresh() {
|
|
if a.parent.started {
|
|
if !a.wasStarted {
|
|
a.start()
|
|
}
|
|
} else if a.wasStarted {
|
|
a.stop()
|
|
}
|
|
|
|
a.updateColor()
|
|
}
|
|
|
|
func (a *activityRenderer) animate(done float32) {
|
|
off := done * 2
|
|
if off > 1 {
|
|
off = 2 - off
|
|
}
|
|
|
|
off1 := (done + 0.25) * 2
|
|
if done >= 0.75 {
|
|
off1 = (done - 0.75) * 2
|
|
}
|
|
if off1 > 1 {
|
|
off1 = 2 - off1
|
|
}
|
|
|
|
off2 := (done + 0.75) * 2
|
|
if done >= 0.25 {
|
|
off2 = (done - 0.25) * 2
|
|
}
|
|
if off2 > 1 {
|
|
off2 = 2 - off2
|
|
}
|
|
|
|
a.scaleDot(a.dots[0].(*canvas.Circle), off)
|
|
a.scaleDot(a.dots[1].(*canvas.Circle), off1)
|
|
a.scaleDot(a.dots[2].(*canvas.Circle), off2)
|
|
}
|
|
|
|
func (a *activityRenderer) scaleDot(dot *canvas.Circle, off float32) {
|
|
rad := a.maxRad - a.maxRad*off/1.2
|
|
mid := fyne.NewPos(a.bound.Width/2, a.bound.Height/2)
|
|
|
|
dot.Move(mid.Subtract(fyne.NewSquareOffsetPos(rad)))
|
|
dot.Resize(fyne.NewSquareSize(rad * 2))
|
|
|
|
alpha := uint8(0 + int(float32(a.maxCol.A)*off))
|
|
dot.FillColor = color.NRGBA{R: a.maxCol.R, G: a.maxCol.G, B: a.maxCol.B, A: alpha}
|
|
dot.Refresh()
|
|
}
|
|
|
|
func (a *activityRenderer) start() {
|
|
a.wasStarted = true
|
|
a.anim.Start()
|
|
}
|
|
|
|
func (a *activityRenderer) stop() {
|
|
a.wasStarted = false
|
|
a.anim.Stop()
|
|
}
|
|
|
|
func (a *activityRenderer) updateColor() {
|
|
v := fyne.CurrentApp().Settings().ThemeVariant()
|
|
rr, gg, bb, aa := a.parent.Theme().Color(theme.ColorNameForeground, v).RGBA()
|
|
a.maxCol = color.NRGBA{R: uint8(rr >> 8), G: uint8(gg >> 8), B: uint8(bb >> 8), A: uint8(aa >> 8)}
|
|
}
|