-
Notifications
You must be signed in to change notification settings - Fork 0
/
focus.go
179 lines (147 loc) · 3.83 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
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
170
171
172
173
174
175
176
177
178
179
package cview
import "sync"
// Focusable provides a method which determines if a primitive has focus.
// Composed primitives may be focused based on the focused state of their
// contained primitives.
type Focusable interface {
HasFocus() bool
}
type focusElement struct {
primitive Primitive
disabled bool
}
// FocusManager manages application focus.
type FocusManager struct {
elements []*focusElement
focused int
wrapAround bool
setFocus func(p Primitive)
sync.RWMutex
}
// NewFocusManager returns a new FocusManager object.
func NewFocusManager(setFocus func(p Primitive)) *FocusManager {
return &FocusManager{setFocus: setFocus}
}
// SetWrapAround sets the flag that determines whether navigation will wrap
// around. That is, navigating forwards on the last field will move the
// selection to the first field (similarly in the other direction). If set to
// false, the focus won't change when navigating forwards on the last element
// or navigating backwards on the first element.
func (f *FocusManager) SetWrapAround(wrapAround bool) {
f.Lock()
defer f.Unlock()
f.wrapAround = wrapAround
}
// Add adds an element to the focus handler.
func (f *FocusManager) Add(p ...Primitive) {
f.Lock()
defer f.Unlock()
for _, primitive := range p {
f.elements = append(f.elements, &focusElement{primitive: primitive})
}
}
// AddAt adds an element to the focus handler at the specified index.
func (f *FocusManager) AddAt(index int, p Primitive) {
f.Lock()
defer f.Unlock()
if index < 0 || index > len(f.elements) {
panic("index out of range")
}
element := &focusElement{primitive: p}
if index == len(f.elements) {
f.elements = append(f.elements, element)
return
}
f.elements = append(f.elements[:index+1], f.elements[index:]...)
f.elements[index] = element
}
// Focus focuses the provided element.
func (f *FocusManager) Focus(p Primitive) {
f.Lock()
defer f.Unlock()
for i, element := range f.elements {
if p == element.primitive && !element.disabled {
f.focused = i
break
}
}
f.setFocus(f.elements[f.focused].primitive)
}
// FocusPrevious focuses the previous element.
func (f *FocusManager) FocusPrevious() {
f.Lock()
defer f.Unlock()
f.focused--
f.updateFocusIndex(true)
f.setFocus(f.elements[f.focused].primitive)
}
// FocusNext focuses the next element.
func (f *FocusManager) FocusNext() {
f.Lock()
defer f.Unlock()
f.focused++
f.updateFocusIndex(false)
f.setFocus(f.elements[f.focused].primitive)
}
// FocusAt focuses the element at the provided index.
func (f *FocusManager) FocusAt(index int) {
f.Lock()
defer f.Unlock()
f.focused = index
f.setFocus(f.elements[f.focused].primitive)
}
// GetFocusIndex returns the index of the currently focused element.
func (f *FocusManager) GetFocusIndex() int {
f.Lock()
defer f.Unlock()
return f.focused
}
// GetFocusedPrimitive returns the currently focused primitive.
func (f *FocusManager) GetFocusedPrimitive() Primitive {
f.Lock()
defer f.Unlock()
return f.elements[f.focused].primitive
}
func (f *FocusManager) updateFocusIndex(decreasing bool) {
for i := 0; i < len(f.elements); i++ {
if f.focused < 0 {
if f.wrapAround {
f.focused = len(f.elements) - 1
} else {
f.focused = 0
}
} else if f.focused >= len(f.elements) {
if f.wrapAround {
f.focused = 0
} else {
f.focused = len(f.elements) - 1
}
}
item := f.elements[f.focused]
if !item.disabled {
break
}
if decreasing {
f.focused--
} else {
f.focused++
}
}
}
// Transform modifies the current focus.
func (f *FocusManager) Transform(tr Transformation) {
var decreasing bool
switch tr {
case TransformFirstItem:
f.focused = 0
decreasing = true
case TransformLastItem:
f.focused = len(f.elements) - 1
case TransformPreviousItem:
f.focused--
decreasing = true
case TransformNextItem:
f.focused++
}
f.updateFocusIndex(decreasing)
}