forked from tslocum/cview
8 changed files with 274 additions and 24 deletions
@ -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