focus_manager.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package app
  2. import (
  3. "sync"
  4. "fyne.io/fyne/v2"
  5. "fyne.io/fyne/v2/internal/driver"
  6. )
  7. // FocusManager represents a standard manager of input focus for a canvas
  8. type FocusManager struct {
  9. sync.RWMutex
  10. content fyne.CanvasObject
  11. focused fyne.Focusable
  12. }
  13. // NewFocusManager returns a new instance of the standard focus manager for a canvas.
  14. func NewFocusManager(c fyne.CanvasObject) *FocusManager {
  15. return &FocusManager{content: c}
  16. }
  17. // Focus focuses the given obj.
  18. func (f *FocusManager) Focus(obj fyne.Focusable) bool {
  19. f.Lock()
  20. defer f.Unlock()
  21. if obj != nil {
  22. var hiddenAncestor fyne.CanvasObject
  23. hidden := false
  24. found := driver.WalkCompleteObjectTree(
  25. f.content,
  26. func(object fyne.CanvasObject, _, _ fyne.Position, _ fyne.Size) bool {
  27. if hiddenAncestor == nil && !object.Visible() {
  28. hiddenAncestor = object
  29. }
  30. if object == obj.(fyne.CanvasObject) {
  31. hidden = hiddenAncestor != nil
  32. return true
  33. }
  34. return false
  35. },
  36. func(object fyne.CanvasObject, pos fyne.Position, _ fyne.CanvasObject) {
  37. if hiddenAncestor == object {
  38. hiddenAncestor = nil
  39. }
  40. },
  41. )
  42. if !found {
  43. return false
  44. }
  45. if hidden {
  46. return true
  47. }
  48. if dis, ok := obj.(fyne.Disableable); ok && dis.Disabled() {
  49. type selectableText interface {
  50. SelectedText() string
  51. }
  52. if _, isSelectableText := obj.(selectableText); !isSelectableText || fyne.CurrentDevice().IsMobile() {
  53. return true
  54. }
  55. }
  56. }
  57. f.focus(obj)
  58. return true
  59. }
  60. // Focused returns the currently focused object or nil if none.
  61. func (f *FocusManager) Focused() fyne.Focusable {
  62. f.RLock()
  63. defer f.RUnlock()
  64. return f.focused
  65. }
  66. // FocusGained signals to the manager that its content got focus (due to window/overlay switch for instance).
  67. func (f *FocusManager) FocusGained() {
  68. if focused := f.Focused(); focused != nil {
  69. focused.FocusGained()
  70. }
  71. }
  72. // FocusLost signals to the manager that its content lost focus (due to window/overlay switch for instance).
  73. func (f *FocusManager) FocusLost() {
  74. if focused := f.Focused(); focused != nil {
  75. focused.FocusLost()
  76. }
  77. }
  78. // FocusNext will find the item after the current that can be focused and focus it.
  79. // If current is nil then the first focusable item in the canvas will be focused.
  80. func (f *FocusManager) FocusNext() {
  81. f.Lock()
  82. defer f.Unlock()
  83. f.focus(f.nextInChain(f.focused))
  84. }
  85. // FocusPrevious will find the item before the current that can be focused and focus it.
  86. // If current is nil then the last focusable item in the canvas will be focused.
  87. func (f *FocusManager) FocusPrevious() {
  88. f.Lock()
  89. defer f.Unlock()
  90. f.focus(f.previousInChain(f.focused))
  91. }
  92. func (f *FocusManager) focus(obj fyne.Focusable) {
  93. if f.focused == obj {
  94. return
  95. }
  96. if f.focused != nil {
  97. f.focused.FocusLost()
  98. }
  99. f.focused = obj
  100. if obj != nil {
  101. obj.FocusGained()
  102. }
  103. }
  104. func (f *FocusManager) nextInChain(current fyne.Focusable) fyne.Focusable {
  105. return f.nextWithWalker(current, driver.WalkVisibleObjectTree)
  106. }
  107. func (f *FocusManager) nextWithWalker(current fyne.Focusable, walker walkerFunc) fyne.Focusable {
  108. var next fyne.Focusable
  109. found := current == nil // if we have no starting point then pretend we matched already
  110. walker(f.content, func(obj fyne.CanvasObject, _ fyne.Position, _ fyne.Position, _ fyne.Size) bool {
  111. if w, ok := obj.(fyne.Disableable); ok && w.Disabled() {
  112. // disabled widget cannot receive focus
  113. return false
  114. }
  115. focus, ok := obj.(fyne.Focusable)
  116. if !ok {
  117. return false
  118. }
  119. if found {
  120. next = focus
  121. return true
  122. }
  123. if next == nil {
  124. next = focus
  125. }
  126. if obj == current.(fyne.CanvasObject) {
  127. found = true
  128. }
  129. return false
  130. }, nil)
  131. return next
  132. }
  133. func (f *FocusManager) previousInChain(current fyne.Focusable) fyne.Focusable {
  134. return f.nextWithWalker(current, driver.ReverseWalkVisibleObjectTree)
  135. }
  136. type walkerFunc func(
  137. fyne.CanvasObject,
  138. func(fyne.CanvasObject, fyne.Position, fyne.Position, fyne.Size) bool,
  139. func(fyne.CanvasObject, fyne.Position, fyne.CanvasObject),
  140. ) bool