|
|
|
@ -7,8 +7,12 @@ import (
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// Application represents the top node of an application.
|
|
|
|
|
//
|
|
|
|
|
// It is not strictly required to use this class as none of the other classes
|
|
|
|
|
// depend on it. However, it provides useful tools to set up an application and
|
|
|
|
|
// plays nicely with all widgets.
|
|
|
|
|
type Application struct { |
|
|
|
|
sync.Mutex |
|
|
|
|
sync.RWMutex |
|
|
|
|
|
|
|
|
|
// The application's screen.
|
|
|
|
|
screen tcell.Screen |
|
|
|
@ -18,11 +22,57 @@ type Application struct {
|
|
|
|
|
|
|
|
|
|
// The root primitive to be seen on the screen.
|
|
|
|
|
root Primitive |
|
|
|
|
|
|
|
|
|
// Key overrides.
|
|
|
|
|
keyOverrides map[tcell.Key]func(p Primitive) bool |
|
|
|
|
|
|
|
|
|
// Rune overrides.
|
|
|
|
|
runeOverrides map[rune]func(p Primitive) bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// NewApplication creates and returns a new application.
|
|
|
|
|
func NewApplication() *Application { |
|
|
|
|
return &Application{} |
|
|
|
|
return &Application{ |
|
|
|
|
keyOverrides: make(map[tcell.Key]func(p Primitive) bool), |
|
|
|
|
runeOverrides: make(map[rune]func(p Primitive) bool), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// SetKeyCapture installs a global capture function for the given key. It
|
|
|
|
|
// intercepts all events for the given key and routes them to the handler.
|
|
|
|
|
// The handler receives the Primitive to which the key is originally redirected,
|
|
|
|
|
// the one which has focus, or nil if it was not directed to a Primitive. The
|
|
|
|
|
// handler also returns whether or not the key event is then forwarded to that
|
|
|
|
|
// Primitive.
|
|
|
|
|
//
|
|
|
|
|
// Special keys (e.g. Escape, Enter, or Ctrl-A) are defined by the "key"
|
|
|
|
|
// argument. The "ch" rune is ignored. Other keys (e.g. "a", "h", or "5") are
|
|
|
|
|
// specified by their rune, with key set to tcell.KeyRune. See also
|
|
|
|
|
// https://godoc.org/github.com/gdamore/tcell#EventKey for more information.
|
|
|
|
|
//
|
|
|
|
|
// To remove a handler again, provide a nil handler for the same key.
|
|
|
|
|
//
|
|
|
|
|
// The application itself will exit when Ctrl-C is pressed. You can intercept
|
|
|
|
|
// this with this function as well.
|
|
|
|
|
func (a *Application) SetKeyCapture(key tcell.Key, ch rune, handler func(p Primitive) bool) *Application { |
|
|
|
|
if key == tcell.KeyRune { |
|
|
|
|
if handler != nil { |
|
|
|
|
a.runeOverrides[ch] = handler |
|
|
|
|
} else { |
|
|
|
|
if _, ok := a.runeOverrides[ch]; ok { |
|
|
|
|
delete(a.runeOverrides, ch) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if handler != nil { |
|
|
|
|
a.keyOverrides[key] = handler |
|
|
|
|
} else { |
|
|
|
|
if _, ok := a.keyOverrides[key]; ok { |
|
|
|
|
delete(a.keyOverrides, key) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return a |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Run starts the application and thus the event loop. This function returns
|
|
|
|
@ -58,21 +108,47 @@ func (a *Application) Run() error {
|
|
|
|
|
|
|
|
|
|
// Start event loop.
|
|
|
|
|
for { |
|
|
|
|
a.RLock() |
|
|
|
|
if a.screen == nil { |
|
|
|
|
a.RUnlock() |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
event := a.screen.PollEvent() |
|
|
|
|
a.RUnlock() |
|
|
|
|
if event == nil { |
|
|
|
|
break // The screen was finalized.
|
|
|
|
|
} |
|
|
|
|
switch event := event.(type) { |
|
|
|
|
case *tcell.EventKey: |
|
|
|
|
a.RLock() |
|
|
|
|
p := a.focus |
|
|
|
|
a.RUnlock() |
|
|
|
|
|
|
|
|
|
// Intercept keys.
|
|
|
|
|
if event.Key() == tcell.KeyRune { |
|
|
|
|
if handler, ok := a.runeOverrides[event.Rune()]; ok { |
|
|
|
|
if !handler(p) { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if handler, ok := a.keyOverrides[event.Key()]; ok { |
|
|
|
|
pr := p |
|
|
|
|
if event.Key() == tcell.KeyCtrlC { |
|
|
|
|
pr = nil |
|
|
|
|
} |
|
|
|
|
if !handler(pr) { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Ctrl-C closes the application.
|
|
|
|
|
if event.Key() == tcell.KeyCtrlC { |
|
|
|
|
a.Stop() // Ctrl-C closes the application.
|
|
|
|
|
a.Stop() |
|
|
|
|
} |
|
|
|
|
a.Lock() |
|
|
|
|
p := a.focus // Pass other key events to the currently focused primitive.
|
|
|
|
|
a.Unlock() |
|
|
|
|
|
|
|
|
|
// Pass other key events to the currently focused primitive.
|
|
|
|
|
if p != nil { |
|
|
|
|
if handler := p.InputHandler(); handler != nil { |
|
|
|
|
handler(event, func(p Primitive) { |
|
|
|
@ -91,6 +167,8 @@ func (a *Application) Run() error {
|
|
|
|
|
|
|
|
|
|
// Stop stops the application, causing Run() to return.
|
|
|
|
|
func (a *Application) Stop() { |
|
|
|
|
a.RLock() |
|
|
|
|
defer a.RUnlock() |
|
|
|
|
if a.screen == nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -101,8 +179,8 @@ func (a *Application) Stop() {
|
|
|
|
|
// Draw refreshes the screen. It calls the Draw() function of the application's
|
|
|
|
|
// root primitive and then syncs the screen buffer.
|
|
|
|
|
func (a *Application) Draw() *Application { |
|
|
|
|
a.Lock() |
|
|
|
|
defer a.Unlock() |
|
|
|
|
a.RLock() |
|
|
|
|
defer a.RUnlock() |
|
|
|
|
|
|
|
|
|
// Maybe we're not ready yet or not anymore.
|
|
|
|
|
if a.screen == nil || a.root == nil { |
|
|
|
@ -156,5 +234,7 @@ func (a *Application) SetFocus(p Primitive) *Application {
|
|
|
|
|
// GetFocus returns the primitive which has the current focus. If none has it,
|
|
|
|
|
// nil is returned.
|
|
|
|
|
func (a *Application) GetFocus() Primitive { |
|
|
|
|
a.RLock() |
|
|
|
|
defer a.RUnlock() |
|
|
|
|
return a.focus |
|
|
|
|
} |
|
|
|
|