You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
3.8 KiB
179 lines
3.8 KiB
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) |
|
}
|
|
|