forked from tslocum/cview
179 lines
3.8 KiB
Go
179 lines
3.8 KiB
Go
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)
|
|
}
|