...

Source file src/code.rocketnine.space/tslocum/cview/focus.go

Documentation: code.rocketnine.space/tslocum/cview

     1  package cview
     2  
     3  import "sync"
     4  
     5  // Focusable provides a method which determines if a primitive has focus.
     6  // Composed primitives may be focused based on the focused state of their
     7  // contained primitives.
     8  type Focusable interface {
     9  	HasFocus() bool
    10  }
    11  
    12  type focusElement struct {
    13  	primitive Primitive
    14  	disabled  bool
    15  }
    16  
    17  // FocusManager manages application focus.
    18  type FocusManager struct {
    19  	elements []*focusElement
    20  
    21  	focused    int
    22  	wrapAround bool
    23  
    24  	setFocus func(p Primitive)
    25  
    26  	sync.RWMutex
    27  }
    28  
    29  // NewFocusManager returns a new FocusManager object.
    30  func NewFocusManager(setFocus func(p Primitive)) *FocusManager {
    31  	return &FocusManager{setFocus: setFocus}
    32  }
    33  
    34  // SetWrapAround sets the flag that determines whether navigation will wrap
    35  // around. That is, navigating forwards on the last field will move the
    36  // selection to the first field (similarly in the other direction). If set to
    37  // false, the focus won't change when navigating forwards on the last element
    38  // or navigating backwards on the first element.
    39  func (f *FocusManager) SetWrapAround(wrapAround bool) {
    40  	f.Lock()
    41  	defer f.Unlock()
    42  
    43  	f.wrapAround = wrapAround
    44  }
    45  
    46  // Add adds an element to the focus handler.
    47  func (f *FocusManager) Add(p ...Primitive) {
    48  	f.Lock()
    49  	defer f.Unlock()
    50  
    51  	for _, primitive := range p {
    52  		f.elements = append(f.elements, &focusElement{primitive: primitive})
    53  	}
    54  }
    55  
    56  // AddAt adds an element to the focus handler at the specified index.
    57  func (f *FocusManager) AddAt(index int, p Primitive) {
    58  	f.Lock()
    59  	defer f.Unlock()
    60  
    61  	if index < 0 || index > len(f.elements) {
    62  		panic("index out of range")
    63  	}
    64  
    65  	element := &focusElement{primitive: p}
    66  
    67  	if index == len(f.elements) {
    68  		f.elements = append(f.elements, element)
    69  		return
    70  	}
    71  	f.elements = append(f.elements[:index+1], f.elements[index:]...)
    72  	f.elements[index] = element
    73  }
    74  
    75  // Focus focuses the provided element.
    76  func (f *FocusManager) Focus(p Primitive) {
    77  	f.Lock()
    78  	defer f.Unlock()
    79  
    80  	for i, element := range f.elements {
    81  		if p == element.primitive && !element.disabled {
    82  			f.focused = i
    83  			break
    84  		}
    85  	}
    86  	f.setFocus(f.elements[f.focused].primitive)
    87  }
    88  
    89  // FocusPrevious focuses the previous element.
    90  func (f *FocusManager) FocusPrevious() {
    91  	f.Lock()
    92  	defer f.Unlock()
    93  
    94  	f.focused--
    95  	f.updateFocusIndex(true)
    96  	f.setFocus(f.elements[f.focused].primitive)
    97  }
    98  
    99  // FocusNext focuses the next element.
   100  func (f *FocusManager) FocusNext() {
   101  	f.Lock()
   102  	defer f.Unlock()
   103  
   104  	f.focused++
   105  	f.updateFocusIndex(false)
   106  	f.setFocus(f.elements[f.focused].primitive)
   107  }
   108  
   109  // FocusAt focuses the element at the provided index.
   110  func (f *FocusManager) FocusAt(index int) {
   111  	f.Lock()
   112  	defer f.Unlock()
   113  
   114  	f.focused = index
   115  	f.setFocus(f.elements[f.focused].primitive)
   116  }
   117  
   118  // GetFocusIndex returns the index of the currently focused element.
   119  func (f *FocusManager) GetFocusIndex() int {
   120  	f.Lock()
   121  	defer f.Unlock()
   122  
   123  	return f.focused
   124  }
   125  
   126  // GetFocusedPrimitive returns the currently focused primitive.
   127  func (f *FocusManager) GetFocusedPrimitive() Primitive {
   128  	f.Lock()
   129  	defer f.Unlock()
   130  
   131  	return f.elements[f.focused].primitive
   132  }
   133  
   134  func (f *FocusManager) updateFocusIndex(decreasing bool) {
   135  	for i := 0; i < len(f.elements); i++ {
   136  		if f.focused < 0 {
   137  			if f.wrapAround {
   138  				f.focused = len(f.elements) - 1
   139  			} else {
   140  				f.focused = 0
   141  			}
   142  		} else if f.focused >= len(f.elements) {
   143  			if f.wrapAround {
   144  				f.focused = 0
   145  			} else {
   146  				f.focused = len(f.elements) - 1
   147  			}
   148  		}
   149  
   150  		item := f.elements[f.focused]
   151  		if !item.disabled {
   152  			break
   153  		}
   154  
   155  		if decreasing {
   156  			f.focused--
   157  		} else {
   158  			f.focused++
   159  		}
   160  	}
   161  }
   162  
   163  // Transform modifies the current focus.
   164  func (f *FocusManager) Transform(tr Transformation) {
   165  	var decreasing bool
   166  	switch tr {
   167  	case TransformFirstItem:
   168  		f.focused = 0
   169  		decreasing = true
   170  	case TransformLastItem:
   171  		f.focused = len(f.elements) - 1
   172  	case TransformPreviousItem:
   173  		f.focused--
   174  		decreasing = true
   175  	case TransformNextItem:
   176  		f.focused++
   177  	}
   178  	f.updateFocusIndex(decreasing)
   179  }
   180  

View as plain text