widget.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Package widget defines the UI widgets within the Fyne toolkit.
  2. package widget // import "fyne.io/fyne/v2/widget"
  3. import (
  4. "sync"
  5. "fyne.io/fyne/v2"
  6. "fyne.io/fyne/v2/canvas"
  7. "fyne.io/fyne/v2/internal/cache"
  8. internalWidget "fyne.io/fyne/v2/internal/widget"
  9. )
  10. // BaseWidget provides a helper that handles basic widget behaviours.
  11. type BaseWidget struct {
  12. size fyne.Size
  13. position fyne.Position
  14. Hidden bool
  15. impl fyne.Widget
  16. propertyLock sync.RWMutex
  17. }
  18. // ExtendBaseWidget is used by an extending widget to make use of BaseWidget functionality.
  19. func (w *BaseWidget) ExtendBaseWidget(wid fyne.Widget) {
  20. impl := w.super()
  21. if impl != nil {
  22. return
  23. }
  24. w.propertyLock.Lock()
  25. defer w.propertyLock.Unlock()
  26. w.impl = wid
  27. }
  28. // Size gets the current size of this widget.
  29. func (w *BaseWidget) Size() fyne.Size {
  30. w.propertyLock.RLock()
  31. defer w.propertyLock.RUnlock()
  32. return w.size
  33. }
  34. // Resize sets a new size for a widget.
  35. // Note this should not be used if the widget is being managed by a Layout within a Container.
  36. func (w *BaseWidget) Resize(size fyne.Size) {
  37. w.propertyLock.RLock()
  38. baseSize := w.size
  39. impl := w.impl
  40. w.propertyLock.RUnlock()
  41. if baseSize == size {
  42. return
  43. }
  44. w.propertyLock.Lock()
  45. w.size = size
  46. w.propertyLock.Unlock()
  47. if impl == nil {
  48. return
  49. }
  50. cache.Renderer(impl).Layout(size)
  51. }
  52. // Position gets the current position of this widget, relative to its parent.
  53. func (w *BaseWidget) Position() fyne.Position {
  54. w.propertyLock.RLock()
  55. defer w.propertyLock.RUnlock()
  56. return w.position
  57. }
  58. // Move the widget to a new position, relative to its parent.
  59. // Note this should not be used if the widget is being managed by a Layout within a Container.
  60. func (w *BaseWidget) Move(pos fyne.Position) {
  61. w.propertyLock.Lock()
  62. w.position = pos
  63. w.propertyLock.Unlock()
  64. internalWidget.Repaint(w.super())
  65. }
  66. // MinSize for the widget - it should never be resized below this value.
  67. func (w *BaseWidget) MinSize() fyne.Size {
  68. impl := w.super()
  69. r := cache.Renderer(impl)
  70. if r == nil {
  71. return fyne.NewSize(0, 0)
  72. }
  73. return r.MinSize()
  74. }
  75. // Visible returns whether or not this widget should be visible.
  76. // Note that this may not mean it is currently visible if a parent has been hidden.
  77. func (w *BaseWidget) Visible() bool {
  78. w.propertyLock.RLock()
  79. defer w.propertyLock.RUnlock()
  80. return !w.Hidden
  81. }
  82. // Show this widget so it becomes visible
  83. func (w *BaseWidget) Show() {
  84. if w.Visible() {
  85. return
  86. }
  87. w.setFieldsAndRefresh(func() {
  88. w.Hidden = false
  89. })
  90. }
  91. // Hide this widget so it is no longer visible
  92. func (w *BaseWidget) Hide() {
  93. if !w.Visible() {
  94. return
  95. }
  96. w.propertyLock.Lock()
  97. w.Hidden = true
  98. impl := w.impl
  99. w.propertyLock.Unlock()
  100. if impl == nil {
  101. return
  102. }
  103. canvas.Refresh(impl)
  104. }
  105. // Refresh causes this widget to be redrawn in it's current state
  106. func (w *BaseWidget) Refresh() {
  107. impl := w.super()
  108. if impl == nil {
  109. return
  110. }
  111. render := cache.Renderer(impl)
  112. render.Refresh()
  113. }
  114. // setFieldsAndRefresh helps to make changes to a widget that should be followed by a refresh.
  115. // This method is a guaranteed thread-safe way of directly manipulating widget fields.
  116. func (w *BaseWidget) setFieldsAndRefresh(f func()) {
  117. w.propertyLock.Lock()
  118. f()
  119. impl := w.impl
  120. w.propertyLock.Unlock()
  121. if impl == nil {
  122. return
  123. }
  124. impl.Refresh()
  125. }
  126. // super will return the actual object that this represents.
  127. // If extended then this is the extending widget, otherwise it is nil.
  128. func (w *BaseWidget) super() fyne.Widget {
  129. w.propertyLock.RLock()
  130. impl := w.impl
  131. w.propertyLock.RUnlock()
  132. return impl
  133. }
  134. // DisableableWidget describes an extension to BaseWidget which can be disabled.
  135. // Disabled widgets should have a visually distinct style when disabled, normally using theme.DisabledButtonColor.
  136. type DisableableWidget struct {
  137. BaseWidget
  138. disabled bool
  139. }
  140. // Enable this widget, updating any style or features appropriately.
  141. func (w *DisableableWidget) Enable() {
  142. if !w.Disabled() {
  143. return
  144. }
  145. w.setFieldsAndRefresh(func() {
  146. w.disabled = false
  147. })
  148. }
  149. // Disable this widget so that it cannot be interacted with, updating any style appropriately.
  150. func (w *DisableableWidget) Disable() {
  151. if w.Disabled() {
  152. return
  153. }
  154. w.setFieldsAndRefresh(func() {
  155. w.disabled = true
  156. })
  157. }
  158. // Disabled returns true if this widget is currently disabled or false if it can currently be interacted with.
  159. func (w *DisableableWidget) Disabled() bool {
  160. w.propertyLock.RLock()
  161. defer w.propertyLock.RUnlock()
  162. return w.disabled
  163. }
  164. // NewSimpleRenderer creates a new SimpleRenderer to render a widget using a
  165. // single fyne.CanvasObject.
  166. //
  167. // Since: 2.1
  168. func NewSimpleRenderer(object fyne.CanvasObject) fyne.WidgetRenderer {
  169. return internalWidget.NewSimpleRenderer(object)
  170. }