All primitives now offer a way to intercept all key events sent to them. Also made the global key event intercept handler more general/consistent. Resolves #22

This commit is contained in:
Oliver 2018-01-14 21:29:34 +01:00
parent f61c66bb82
commit 626453b2a6
12 changed files with 80 additions and 80 deletions

View File

@ -62,6 +62,9 @@ Add your issue here on GitHub. Feel free to get in touch if you have any questio
## Releases ## Releases
- v0.6 (2018-01-14)
- All primitives can now intercept all key events when they have focus.
- Key events can also be intercepted globally (changed to a more general, consistent handling)
- v0.5 (2018-01-13) - v0.5 (2018-01-13)
- `TextView` now has word wrapping and text alignment - `TextView` now has word wrapping and text alignment
- v0.4 (2018-01-12) - v0.4 (2018-01-12)

View File

@ -26,55 +26,28 @@ type Application struct {
// Whether or not the application resizes the root primitive. // Whether or not the application resizes the root primitive.
rootAutoSize bool rootAutoSize bool
// Key overrides. // An optional capture function which receives a key event and returns the
keyOverrides map[tcell.Key]func(p Primitive) bool // event to be forwarded to the default input handler (nil if nothing should
// be forwarded).
// Rune overrides. inputCapture func(event *tcell.EventKey) *tcell.EventKey
runeOverrides map[rune]func(p Primitive) bool
} }
// NewApplication creates and returns a new application. // NewApplication creates and returns a new application.
func NewApplication() *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 // SetInputCapture sets a function which captures all key events before they are
// intercepts all events for the given key and routes them to the handler. // forwarded to the key event handler of the primitive which currently has
// The handler receives the Primitive to which the key is originally redirected, // focus. This function can then choose to forward that key event (or a
// the one which has focus, or nil if it was not directed to a Primitive. The // different one) by returning it or stop the key event processing by returning
// handler also returns whether or not the key event is then forwarded to that // nil.
// Primitive. Draw() is called implicitly if the event is not forwarded.
// //
// Special keys (e.g. Escape, Enter, or Ctrl-A) are defined by the "key" // Note that this also affects the default event handling of the application
// argument. The "ch" rune is ignored. Other keys (e.g. "a", "h", or "5") are // itself: Such a handler can intercept the Ctrl-C event which closes the
// specified by their rune, with key set to tcell.KeyRune. See also // applicaton.
// https://godoc.org/github.com/gdamore/tcell#EventKey for more information. func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
// a.inputCapture = capture
// 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 return a
} }
@ -131,23 +104,10 @@ func (a *Application) Run() error {
a.RUnlock() a.RUnlock()
// Intercept keys. // Intercept keys.
if event.Key() == tcell.KeyRune { if a.inputCapture != nil {
if handler, ok := a.runeOverrides[event.Rune()]; ok { event = a.inputCapture(event)
if !handler(p) { if event == nil {
a.Draw() break // Don't forward event.
break
}
}
} else {
if handler, ok := a.keyOverrides[event.Key()]; ok {
pr := p
if event.Key() == tcell.KeyCtrlC {
pr = nil
}
if !handler(pr) {
a.Draw()
break
}
} }
} }

34
box.go
View File

@ -9,6 +9,9 @@ import (
// border and a title. Most subclasses keep their content contained in the box // border and a title. Most subclasses keep their content contained in the box
// but don't necessarily have to. // but don't necessarily have to.
// //
// Note that all classes which subclass from Box will also have access to its
// functions.
//
// See https://github.com/rivo/tview/wiki/Box for an example. // See https://github.com/rivo/tview/wiki/Box for an example.
type Box struct { type Box struct {
// The position of the rect. // The position of the rect.
@ -42,6 +45,11 @@ type Box struct {
// Whether or not this box has focus. // Whether or not this box has focus.
hasFocus bool hasFocus bool
// An optional capture function which receives a key event and returns the
// event to be forwarded to the primitive's default input handler (nil if
// nothing should be forwarded).
inputCapture func(event *tcell.EventKey) *tcell.EventKey
} }
// NewBox returns a Box without a border. // NewBox returns a Box without a border.
@ -94,9 +102,33 @@ func (b *Box) SetRect(x, y, width, height int) {
b.height = height b.height = height
} }
// wrapInputHandler wraps an input handler (see InputHandler()) with the
// functionality to capture input (see SetInputCapture()) before passing it
// on to the provided (default) input handler.
func (b *Box) wrapInputHandler(inputHandler func(*tcell.EventKey, func(p Primitive))) func(*tcell.EventKey, func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) {
if b.inputCapture != nil {
event = b.inputCapture(event)
}
if event != nil && inputHandler != nil {
inputHandler(event, setFocus)
}
}
}
// InputHandler returns nil. // InputHandler returns nil.
func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return nil return b.wrapInputHandler(nil)
}
// SetInputCapture sets a function which captures key events before they are
// forwarded to the primitive's default key event handler. This function can
// then choose to forward that key event (or a different one) to the default
// handler by returning it. If nil is returned, the default handler will not
// be called.
func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Box {
b.inputCapture = capture
return b
} }
// SetBackgroundColor sets the box's background color. // SetBackgroundColor sets the box's background color.

View File

@ -122,7 +122,7 @@ func (b *Button) Draw(screen tcell.Screen) {
// InputHandler returns the handler for this primitive. // InputHandler returns the handler for this primitive.
func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) { return b.wrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
// Process key event. // Process key event.
switch key := event.Key(); key { switch key := event.Key(); key {
case tcell.KeyEnter: // Selected. case tcell.KeyEnter: // Selected.
@ -134,5 +134,5 @@ func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Prim
b.blur(key) b.blur(key)
} }
} }
} })
} }

View File

@ -150,7 +150,7 @@ func (c *Checkbox) Draw(screen tcell.Screen) {
// InputHandler returns the handler for this primitive. // InputHandler returns the handler for this primitive.
func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) { return c.wrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
// Process key event. // Process key event.
switch key := event.Key(); key { switch key := event.Key(); key {
case tcell.KeyRune, tcell.KeyEnter: // Check. case tcell.KeyRune, tcell.KeyEnter: // Check.
@ -166,5 +166,5 @@ func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
c.done(key) c.done(key)
} }
} }
} })
} }

View File

@ -77,12 +77,13 @@ func main() {
AddItem(info, 1, 1, false) AddItem(info, 1, 1, false)
// Shortcuts to navigate the slides. // Shortcuts to navigate the slides.
app.SetKeyCapture(tcell.KeyCtrlN, 0, func(p tview.Primitive) bool { app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyCtrlN {
nextSlide() nextSlide()
return false } else if event.Key() == tcell.KeyCtrlP {
}).SetKeyCapture(tcell.KeyCtrlP, 0, func(p tview.Primitive) bool {
previousSlide() previousSlide()
return false }
return event
}) })
// Start the application. // Start the application.

View File

@ -249,7 +249,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
// InputHandler returns the handler for this primitive. // InputHandler returns the handler for this primitive.
func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) { return d.wrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
// Process key event. // Process key event.
switch key := event.Key(); key { switch key := event.Key(); key {
case tcell.KeyEnter, tcell.KeyRune, tcell.KeyDown: case tcell.KeyEnter, tcell.KeyRune, tcell.KeyDown:
@ -274,7 +274,7 @@ func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
d.done(key) d.done(key)
} }
} }
} })
} }
// Focus is called by the application when the primitive receives focus. // Focus is called by the application when the primitive receives focus.

View File

@ -236,7 +236,7 @@ func (i *InputField) setCursor(screen tcell.Screen) {
// InputHandler returns the handler for this primitive. // InputHandler returns the handler for this primitive.
func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) { return i.wrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
// Trigger changed events. // Trigger changed events.
currentText := i.text currentText := i.text
defer func() { defer func() {
@ -271,5 +271,5 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
i.done(key) i.done(key)
} }
} }
} })
} }

View File

@ -232,7 +232,7 @@ func (l *List) Draw(screen tcell.Screen) {
// InputHandler returns the handler for this primitive. // InputHandler returns the handler for this primitive.
func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) { return l.wrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
previousItem := l.currentItem previousItem := l.currentItem
switch key := event.Key(); key { switch key := event.Key(); key {
@ -296,5 +296,5 @@ func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
item := l.items[l.currentItem] item := l.items[l.currentItem]
l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
} }
} })
} }

View File

@ -28,6 +28,10 @@ type Primitive interface {
// //
// The Application's Draw() function will be called automatically after the // The Application's Draw() function will be called automatically after the
// handler returns. // handler returns.
//
// The Box class provides functionality to intercept keyboard input. If you
// subclass from Box, it is recommended that you wrap your handler using
// Box.wrapInputHandler() so you inherit that functionality.
InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive))
// Focus is called by the application when the primitive receives focus. // Focus is called by the application when the primitive receives focus.

View File

@ -717,7 +717,7 @@ ColumnLoop:
// InputHandler returns the handler for this primitive. // InputHandler returns the handler for this primitive.
func (t *Table) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (t *Table) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) { return t.wrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
key := event.Key() key := event.Key()
if (!t.rowsSelectable && !t.columnsSelectable && key == tcell.KeyEnter) || if (!t.rowsSelectable && !t.columnsSelectable && key == tcell.KeyEnter) ||
@ -920,5 +920,5 @@ func (t *Table) InputHandler() func(event *tcell.EventKey, setFocus func(p Primi
t.columnsSelectable && previouslySelectedColumn != t.selectedColumn) { t.columnsSelectable && previouslySelectedColumn != t.selectedColumn) {
t.selectionChanged(t.selectedRow, t.selectedColumn) t.selectionChanged(t.selectedRow, t.selectedColumn)
} }
} })
} }

View File

@ -836,7 +836,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
// InputHandler returns the handler for this primitive. // InputHandler returns the handler for this primitive.
func (t *TextView) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { func (t *TextView) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) { return t.wrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
// Do we pass this event on? // Do we pass this event on?
if t.capture != nil { if t.capture != nil {
if !t.capture(event) { if !t.capture(event) {
@ -899,5 +899,5 @@ func (t *TextView) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
t.trackEnd = false t.trackEnd = false
t.lineOffset -= t.pageSize t.lineOffset -= t.pageSize
} }
} })
} }