bind_helper.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package widget
  2. import (
  3. "sync"
  4. "sync/atomic"
  5. "fyne.io/fyne/v2/data/binding"
  6. )
  7. // basicBinder stores a DataItem and a function to be called when it changes.
  8. // It provides a convenient way to replace data and callback independently.
  9. type basicBinder struct {
  10. callback atomic.Value // func(binding.DataItem)
  11. dataListenerPairLock sync.RWMutex
  12. dataListenerPair annotatedListener // access guarded by dataListenerPairLock
  13. }
  14. // Bind replaces the data item whose changes are tracked by the callback function.
  15. func (binder *basicBinder) Bind(data binding.DataItem) {
  16. listener := binding.NewDataListener(func() { // NB: listener captures `data` but always calls the up-to-date callback
  17. f := binder.callback.Load()
  18. if fn, ok := f.(func(binding.DataItem)); ok && fn != nil {
  19. fn(data)
  20. }
  21. })
  22. data.AddListener(listener)
  23. listenerInfo := annotatedListener{
  24. data: data,
  25. listener: listener,
  26. }
  27. binder.dataListenerPairLock.Lock()
  28. binder.unbindLocked()
  29. binder.dataListenerPair = listenerInfo
  30. binder.dataListenerPairLock.Unlock()
  31. }
  32. // CallWithData passes the currently bound data item as an argument to the
  33. // provided function.
  34. func (binder *basicBinder) CallWithData(f func(data binding.DataItem)) {
  35. binder.dataListenerPairLock.RLock()
  36. data := binder.dataListenerPair.data
  37. binder.dataListenerPairLock.RUnlock()
  38. f(data)
  39. }
  40. // SetCallback replaces the function to be called when the data changes.
  41. func (binder *basicBinder) SetCallback(f func(data binding.DataItem)) {
  42. binder.callback.Store(f)
  43. }
  44. // Unbind requests the callback to be no longer called when the previously bound
  45. // data item changes.
  46. func (binder *basicBinder) Unbind() {
  47. binder.dataListenerPairLock.Lock()
  48. binder.unbindLocked()
  49. binder.dataListenerPairLock.Unlock()
  50. }
  51. // unbindLocked expects the caller to hold dataListenerPairLock.
  52. func (binder *basicBinder) unbindLocked() {
  53. previousListener := binder.dataListenerPair
  54. binder.dataListenerPair = annotatedListener{nil, nil}
  55. if previousListener.listener == nil || previousListener.data == nil {
  56. return
  57. }
  58. previousListener.data.RemoveListener(previousListener.listener)
  59. }
  60. type annotatedListener struct {
  61. data binding.DataItem
  62. listener binding.DataListener
  63. }