-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
focus.go
136 lines (112 loc) · 2.95 KB
/
focus.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
package app
import (
"sync"
"fyne.io/fyne"
"fyne.io/fyne/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) {
f.Lock()
defer f.Unlock()
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
}
// 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 dis, ok := obj.(fyne.Disableable); ok && dis.Disabled() {
obj = nil
}
if f.focused != nil {
f.focused.FocusLost()
}
f.focused = obj
if obj != nil {
obj.FocusGained()
}
}
func (f *FocusManager) nextInChain(current fyne.Focusable) fyne.Focusable {
var first, next fyne.Focusable
found := current == nil // if we have no starting point then pretend we matched already
driver.WalkVisibleObjectTree(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 obj == current.(fyne.CanvasObject) {
found = true
}
if first == nil {
first = focus
}
return false
}, nil)
if next != nil {
return next
}
return first
}
func (f *FocusManager) previousInChain(current fyne.Focusable) fyne.Focusable {
var last, previous fyne.Focusable
found := false
driver.WalkVisibleObjectTree(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 current != nil && obj == current.(fyne.CanvasObject) {
found = true
}
last = focus
if !found {
previous = focus
}
return false // we cannot exit early - until we make a reverse tree walk...
}, nil)
if previous != nil {
return previous
}
return last
}