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