base.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package widget
  2. import (
  3. "sync"
  4. "fyne.io/fyne/v2"
  5. "fyne.io/fyne/v2/canvas"
  6. "fyne.io/fyne/v2/internal/cache"
  7. )
  8. // Base provides a helper that handles basic widget behaviours.
  9. type Base struct {
  10. hidden bool
  11. position fyne.Position
  12. size fyne.Size
  13. impl fyne.Widget
  14. propertyLock sync.RWMutex
  15. }
  16. // ExtendBaseWidget is used by an extending widget to make use of BaseWidget functionality.
  17. func (w *Base) ExtendBaseWidget(wid fyne.Widget) {
  18. impl := w.super()
  19. if impl != nil {
  20. return
  21. }
  22. w.propertyLock.Lock()
  23. defer w.propertyLock.Unlock()
  24. w.impl = wid
  25. }
  26. // Size gets the current size of this widget.
  27. func (w *Base) Size() fyne.Size {
  28. w.propertyLock.RLock()
  29. defer w.propertyLock.RUnlock()
  30. return w.size
  31. }
  32. // Resize sets a new size for a widget.
  33. // Note this should not be used if the widget is being managed by a Layout within a Container.
  34. func (w *Base) Resize(size fyne.Size) {
  35. w.propertyLock.RLock()
  36. baseSize := w.size
  37. impl := w.impl
  38. w.propertyLock.RUnlock()
  39. if baseSize == size {
  40. return
  41. }
  42. w.propertyLock.Lock()
  43. w.size = size
  44. w.propertyLock.Unlock()
  45. if impl == nil {
  46. return
  47. }
  48. cache.Renderer(impl).Layout(size)
  49. }
  50. // Position gets the current position of this widget, relative to its parent.
  51. func (w *Base) Position() fyne.Position {
  52. w.propertyLock.RLock()
  53. defer w.propertyLock.RUnlock()
  54. return w.position
  55. }
  56. // Move the widget to a new position, relative to its parent.
  57. // Note this should not be used if the widget is being managed by a Layout within a Container.
  58. func (w *Base) Move(pos fyne.Position) {
  59. w.propertyLock.Lock()
  60. w.position = pos
  61. w.propertyLock.Unlock()
  62. Repaint(w.super())
  63. }
  64. // MinSize for the widget - it should never be resized below this value.
  65. func (w *Base) MinSize() fyne.Size {
  66. impl := w.super()
  67. r := cache.Renderer(impl)
  68. if r == nil {
  69. return fyne.NewSize(0, 0)
  70. }
  71. return r.MinSize()
  72. }
  73. // Visible returns whether or not this widget should be visible.
  74. // Note that this may not mean it is currently visible if a parent has been hidden.
  75. func (w *Base) Visible() bool {
  76. w.propertyLock.RLock()
  77. defer w.propertyLock.RUnlock()
  78. return !w.hidden
  79. }
  80. // Show this widget so it becomes visible
  81. func (w *Base) Show() {
  82. if w.Visible() {
  83. return
  84. }
  85. w.setFieldsAndRefresh(func() {
  86. w.hidden = false
  87. })
  88. }
  89. // Hide this widget so it is no longer visible
  90. func (w *Base) Hide() {
  91. if !w.Visible() {
  92. return
  93. }
  94. w.propertyLock.Lock()
  95. w.hidden = true
  96. impl := w.impl
  97. w.propertyLock.Unlock()
  98. if impl == nil {
  99. return
  100. }
  101. canvas.Refresh(impl)
  102. }
  103. // Refresh causes this widget to be redrawn in it's current state
  104. func (w *Base) Refresh() {
  105. impl := w.super()
  106. if impl == nil {
  107. return
  108. }
  109. render := cache.Renderer(impl)
  110. render.Refresh()
  111. }
  112. // setFieldsAndRefresh helps to make changes to a widget that should be followed by a refresh.
  113. // This method is a guaranteed thread-safe way of directly manipulating widget fields.
  114. func (w *Base) setFieldsAndRefresh(f func()) {
  115. w.propertyLock.Lock()
  116. f()
  117. impl := w.impl
  118. w.propertyLock.Unlock()
  119. if impl == nil {
  120. return
  121. }
  122. impl.Refresh()
  123. }
  124. // super will return the actual object that this represents.
  125. // If extended then this is the extending widget, otherwise it is nil.
  126. func (w *Base) super() fyne.Widget {
  127. w.propertyLock.RLock()
  128. impl := w.impl
  129. w.propertyLock.RUnlock()
  130. return impl
  131. }
  132. // Repaint instructs the containing canvas to redraw, even if nothing changed.
  133. // This method is a duplicate of what is in `canvas/canvas.go` to avoid a dependency loop or public API.
  134. func Repaint(obj fyne.CanvasObject) {
  135. if fyne.CurrentApp() == nil || fyne.CurrentApp().Driver() == nil {
  136. return
  137. }
  138. c := fyne.CurrentApp().Driver().CanvasForObject(obj)
  139. if c != nil {
  140. if paint, ok := c.(interface{ SetDirty() }); ok {
  141. paint.SetDirty()
  142. }
  143. }
  144. }