entry_cursor_anim.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package widget
  2. import (
  3. "image/color"
  4. "sync"
  5. "time"
  6. "fyne.io/fyne/v2"
  7. "fyne.io/fyne/v2/canvas"
  8. col "fyne.io/fyne/v2/internal/color"
  9. "fyne.io/fyne/v2/theme"
  10. )
  11. const cursorInterruptTime = 300 * time.Millisecond
  12. type entryCursorAnimation struct {
  13. mu *sync.RWMutex
  14. cursor *canvas.Rectangle
  15. anim *fyne.Animation
  16. lastInterruptTime time.Time
  17. timeNow func() time.Time // useful for testing
  18. }
  19. func newEntryCursorAnimation(cursor *canvas.Rectangle) *entryCursorAnimation {
  20. a := &entryCursorAnimation{mu: &sync.RWMutex{}, cursor: cursor, timeNow: time.Now}
  21. return a
  22. }
  23. // creates fyne animation
  24. func (a *entryCursorAnimation) createAnim(inverted bool) *fyne.Animation {
  25. cursorOpaque := theme.PrimaryColor()
  26. r, g, b, _ := col.ToNRGBA(theme.PrimaryColor())
  27. cursorDim := color.NRGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 0x16}
  28. start, end := color.Color(cursorDim), cursorOpaque
  29. if inverted {
  30. start, end = cursorOpaque, color.Color(cursorDim)
  31. }
  32. interrupted := false
  33. anim := canvas.NewColorRGBAAnimation(start, end, time.Second/2, func(c color.Color) {
  34. a.mu.RLock()
  35. shouldInterrupt := a.timeNow().Sub(a.lastInterruptTime) <= cursorInterruptTime
  36. a.mu.RUnlock()
  37. if shouldInterrupt {
  38. if !interrupted {
  39. a.cursor.FillColor = cursorOpaque
  40. a.cursor.Refresh()
  41. interrupted = true
  42. }
  43. return
  44. }
  45. if interrupted {
  46. a.mu.Lock()
  47. a.anim.Stop()
  48. if !inverted {
  49. a.anim = a.createAnim(true)
  50. }
  51. interrupted = false
  52. a.mu.Unlock()
  53. go func() {
  54. a.mu.RLock()
  55. canStart := a.anim != nil
  56. a.mu.RUnlock()
  57. if canStart {
  58. a.anim.Start()
  59. }
  60. }()
  61. return
  62. }
  63. a.cursor.FillColor = c
  64. a.cursor.Refresh()
  65. })
  66. anim.RepeatCount = fyne.AnimationRepeatForever
  67. anim.AutoReverse = true
  68. return anim
  69. }
  70. // starts cursor animation.
  71. func (a *entryCursorAnimation) start() {
  72. a.mu.Lock()
  73. isStopped := a.anim == nil
  74. if isStopped {
  75. a.anim = a.createAnim(false)
  76. }
  77. a.mu.Unlock()
  78. if isStopped {
  79. a.anim.Start()
  80. }
  81. }
  82. // temporarily stops the animation by "cursorInterruptTime".
  83. func (a *entryCursorAnimation) interrupt() {
  84. a.mu.Lock()
  85. a.lastInterruptTime = a.timeNow()
  86. a.mu.Unlock()
  87. }
  88. // stops cursor animation.
  89. func (a *entryCursorAnimation) stop() {
  90. a.mu.Lock()
  91. if a.anim != nil {
  92. a.anim.Stop()
  93. a.anim = nil
  94. }
  95. a.mu.Unlock()
  96. }