forked from tslocum/cview
Add FocusManager
parent
ffd8b57dec
commit
597052a7e7
@ -0,0 +1,82 @@
|
||||
// Demo code for the FocusManager utility.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"gitlab.com/tslocum/cbind"
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cview.NewApplication()
|
||||
app.EnableMouse(true)
|
||||
|
||||
input1 := cview.NewInputField()
|
||||
input1.SetLabel("InputField 1")
|
||||
|
||||
input2 := cview.NewInputField()
|
||||
input2.SetLabel("InputField 2")
|
||||
|
||||
input3 := cview.NewInputField()
|
||||
input3.SetLabel("InputField 3")
|
||||
|
||||
input4 := cview.NewInputField()
|
||||
input4.SetLabel("InputField 4")
|
||||
|
||||
grid := cview.NewGrid()
|
||||
grid.SetBorder(true)
|
||||
grid.SetTitle(" Press Tab to advance focus ")
|
||||
grid.AddItem(input1, 0, 0, 1, 1, 0, 0, true)
|
||||
grid.AddItem(input2, 0, 1, 1, 1, 0, 0, false)
|
||||
grid.AddItem(input3, 1, 1, 1, 1, 0, 0, false)
|
||||
grid.AddItem(input4, 1, 0, 1, 1, 0, 0, false)
|
||||
|
||||
focusManager := cview.NewFocusManager(app.SetFocus)
|
||||
focusManager.SetWrapAround(true)
|
||||
focusManager.Add(input1, input2, input3, input4)
|
||||
|
||||
inputHandler := cbind.NewConfiguration()
|
||||
for _, key := range cview.Keys.MovePreviousField {
|
||||
mod, key, ch, err := cbind.Decode(key)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if key == tcell.KeyRune {
|
||||
inputHandler.SetRune(mod, ch, func(ev *tcell.EventKey) *tcell.EventKey {
|
||||
focusManager.FocusPrevious()
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
inputHandler.SetKey(mod, key, func(ev *tcell.EventKey) *tcell.EventKey {
|
||||
focusManager.FocusPrevious()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, key := range cview.Keys.MoveNextField {
|
||||
mod, key, ch, err := cbind.Decode(key)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if key == tcell.KeyRune {
|
||||
inputHandler.SetRune(mod, ch, func(ev *tcell.EventKey) *tcell.EventKey {
|
||||
focusManager.FocusNext()
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
inputHandler.SetKey(mod, key, func(ev *tcell.EventKey) *tcell.EventKey {
|
||||
focusManager.FocusNext()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
app.SetInputCapture(inputHandler.Capture)
|
||||
|
||||
app.SetRoot(grid, true)
|
||||
if err := app.Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -0,0 +1,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)
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package cview
|
||||
|
||||
// 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
|
||||
}
|
Loading…
Reference in New Issue