1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00
wtf/app/focus_tracker.go
Chris Cummer 200dbcc03c
WTF-730 Fix missing color key config error (#738)
* WTF-730 Fix missing color key config error

Signed-off-by: Chris Cummer <chriscummer@me.com>

* WTF-730 Add Subheading color formatting to modules

Users can now set a `subheading` color in their config to change the
color of subheadings in widget display.

Defaults to `red`.

Signed-off-by: Chris Cummer <chriscummer@me.com>

* WTF-730 Fix oustanding color issues

Clean up missing color config changes not addressed in earlier commits.

Signed-off-by: Chris Cummer <chriscummer@me.com>

* Remove unused dependency

Signed-off-by: Chris Cummer <chriscummer@me.com>

* WTF-730 Base cleanup

Signed-off-by: Chris Cummer <chriscummer@me.com>

* WTF-730 Fix a few bugs related to color config changes

Signed-off-by: Chris Cummer <chriscummer@me.com>

* WTF-730 Fix issues with PagerDuty subheading display

Signed-off-by: Chris Cummer <chriscummer@me.com>

* WTF-730 Fix bug with Todo list colour rendering

Signed-off-by: Chris Cummer <chriscummer@me.com>
2019-11-09 11:21:45 -08:00

252 lines
4.9 KiB
Go

package app
import (
"sort"
"github.com/olebedev/config"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/wtf"
)
type FocusState int
const (
widgetFocused FocusState = iota
appBoardFocused
neverFocused
)
// FocusTracker is used by the app to track which onscreen widget currently has focus,
// and to move focus between widgets.
type FocusTracker struct {
App *tview.Application
Idx int
IsFocused bool
Widgets []wtf.Wtfable
config *config.Config
}
func NewFocusTracker(app *tview.Application, widgets []wtf.Wtfable, config *config.Config) FocusTracker {
focusTracker := FocusTracker{
App: app,
Idx: -1,
IsFocused: false,
Widgets: widgets,
config: config,
}
focusTracker.assignHotKeys()
return focusTracker
}
/* -------------------- Exported Functions -------------------- */
func (tracker *FocusTracker) FocusOn(char string) bool {
if !tracker.useNavShortcuts() {
return false
}
if tracker.focusState() == appBoardFocused {
return false
}
hasFocusable := false
for idx, focusable := range tracker.focusables() {
if focusable.FocusChar() == char {
tracker.blur(tracker.Idx)
tracker.Idx = idx
tracker.focus(tracker.Idx)
hasFocusable = true
tracker.IsFocused = true
break
}
}
return hasFocusable
}
// Next sets the focus on the next widget in the widget list. If the current widget is
// the last widget, sets focus on the first widget.
func (tracker *FocusTracker) Next() {
if tracker.focusState() == appBoardFocused {
return
}
tracker.blur(tracker.Idx)
tracker.increment()
tracker.focus(tracker.Idx)
tracker.IsFocused = true
}
// None removes focus from the currently-focused widget.
func (tracker *FocusTracker) None() {
if tracker.focusState() == appBoardFocused {
return
}
tracker.blur(tracker.Idx)
}
// Prev sets the focus on the previous widget in the widget list. If the current widget is
// the last widget, sets focus on the last widget.
func (tracker *FocusTracker) Prev() {
if tracker.focusState() == appBoardFocused {
return
}
tracker.blur(tracker.Idx)
tracker.decrement()
tracker.focus(tracker.Idx)
tracker.IsFocused = true
}
func (tracker *FocusTracker) Refocus() {
tracker.focus(tracker.Idx)
}
/* -------------------- Unexported Functions -------------------- */
// AssignHotKeys assigns an alphabetic keyboard character to each focusable
// widget so that the widget can be brought into focus by pressing that keyboard key
func (tracker *FocusTracker) assignHotKeys() {
if !tracker.useNavShortcuts() {
return
}
usedKeys := make(map[string]bool)
focusables := tracker.focusables()
i := 1
for _, focusable := range focusables {
if focusable.FocusChar() != "" {
usedKeys[focusable.FocusChar()] = true
}
}
for _, focusable := range focusables {
if focusable.FocusChar() != "" {
continue
}
if _, foundKey := usedKeys[string('0'+i)]; foundKey {
for ; foundKey; _, foundKey = usedKeys[string('0'+i)] {
i++
}
}
// Don't have nav characters > "9"
if i >= 10 {
break
}
focusable.SetFocusChar(string('0' + i))
i++
}
}
func (tracker *FocusTracker) blur(idx int) {
widget := tracker.focusableAt(idx)
if widget == nil {
return
}
view := widget.TextView()
view.Blur()
view.SetBorderColor(
wtf.ColorFor(
widget.BorderColor(),
),
)
tracker.IsFocused = false
}
func (tracker *FocusTracker) decrement() {
tracker.Idx--
if tracker.Idx < 0 {
tracker.Idx = len(tracker.focusables()) - 1
}
}
func (tracker *FocusTracker) focus(idx int) {
widget := tracker.focusableAt(idx)
if widget == nil {
return
}
view := widget.TextView()
view.SetBorderColor(
wtf.ColorFor(
widget.CommonSettings().Colors.BorderTheme.Focused,
),
)
tracker.App.SetFocus(view)
}
func (tracker *FocusTracker) focusables() []wtf.Wtfable {
focusable := []wtf.Wtfable{}
for _, widget := range tracker.Widgets {
if widget.Focusable() {
focusable = append(focusable, widget)
}
}
// Sort for deterministic ordering
sort.SliceStable(focusable[:], func(i, j int) bool {
iTop := focusable[i].CommonSettings().Top
jTop := focusable[j].CommonSettings().Top
if iTop < jTop {
return true
}
if iTop == jTop {
return focusable[i].CommonSettings().Left < focusable[j].CommonSettings().Left
}
return false
})
return focusable
}
func (tracker *FocusTracker) focusableAt(idx int) wtf.Wtfable {
if idx < 0 || idx >= len(tracker.focusables()) {
return nil
}
return tracker.focusables()[idx]
}
func (tracker *FocusTracker) focusState() FocusState {
if tracker.Idx < 0 {
return neverFocused
}
for _, widget := range tracker.Widgets {
if widget.TextView() == tracker.App.GetFocus() {
return widgetFocused
}
}
return appBoardFocused
}
func (tracker *FocusTracker) increment() {
tracker.Idx++
if tracker.Idx == len(tracker.focusables()) {
tracker.Idx = 0
}
}
func (tracker *FocusTracker) useNavShortcuts() bool {
return tracker.config.UBool("wtf.navigation.shortcuts", true)
}