mirror of https://github.com/rivo/tview.git
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:
parent
f61c66bb82
commit
626453b2a6
|
@ -62,6 +62,9 @@ Add your issue here on GitHub. Feel free to get in touch if you have any questio
|
|||
|
||||
## 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)
|
||||
- `TextView` now has word wrapping and text alignment
|
||||
- v0.4 (2018-01-12)
|
||||
|
|
|
@ -26,55 +26,28 @@ type Application struct {
|
|||
// Whether or not the application resizes the root primitive.
|
||||
rootAutoSize bool
|
||||
|
||||
// Key overrides.
|
||||
keyOverrides map[tcell.Key]func(p Primitive) bool
|
||||
|
||||
// Rune overrides.
|
||||
runeOverrides map[rune]func(p Primitive) bool
|
||||
// An optional capture function which receives a key event and returns the
|
||||
// event to be forwarded to the default input handler (nil if nothing should
|
||||
// be forwarded).
|
||||
inputCapture func(event *tcell.EventKey) *tcell.EventKey
|
||||
}
|
||||
|
||||
// NewApplication creates and returns a new application.
|
||||
func NewApplication() *Application {
|
||||
return &Application{
|
||||
keyOverrides: make(map[tcell.Key]func(p Primitive) bool),
|
||||
runeOverrides: make(map[rune]func(p Primitive) bool),
|
||||
}
|
||||
return &Application{}
|
||||
}
|
||||
|
||||
// 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. Draw() is called implicitly if the event is not forwarded.
|
||||
// SetInputCapture sets a function which captures all key events before they are
|
||||
// forwarded to the key event handler of the primitive which currently has
|
||||
// focus. This function can then choose to forward that key event (or a
|
||||
// different one) by returning it or stop the key event processing by returning
|
||||
// nil.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note that this also affects the default event handling of the application
|
||||
// itself: Such a handler can intercept the Ctrl-C event which closes the
|
||||
// applicaton.
|
||||
func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
|
||||
a.inputCapture = capture
|
||||
return a
|
||||
}
|
||||
|
||||
|
@ -131,23 +104,10 @@ func (a *Application) Run() error {
|
|||
a.RUnlock()
|
||||
|
||||
// Intercept keys.
|
||||
if event.Key() == tcell.KeyRune {
|
||||
if handler, ok := a.runeOverrides[event.Rune()]; ok {
|
||||
if !handler(p) {
|
||||
a.Draw()
|
||||
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
|
||||
}
|
||||
if a.inputCapture != nil {
|
||||
event = a.inputCapture(event)
|
||||
if event == nil {
|
||||
break // Don't forward event.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
34
box.go
34
box.go
|
@ -9,6 +9,9 @@ import (
|
|||
// border and a title. Most subclasses keep their content contained in the box
|
||||
// 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.
|
||||
type Box struct {
|
||||
// The position of the rect.
|
||||
|
@ -42,6 +45,11 @@ type Box struct {
|
|||
|
||||
// Whether or not this box has focus.
|
||||
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.
|
||||
|
@ -94,9 +102,33 @@ func (b *Box) SetRect(x, y, width, height int) {
|
|||
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.
|
||||
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.
|
||||
|
|
|
@ -122,7 +122,7 @@ func (b *Button) Draw(screen tcell.Screen) {
|
|||
|
||||
// InputHandler returns the handler for this 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.
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyEnter: // Selected.
|
||||
|
@ -134,5 +134,5 @@ func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Prim
|
|||
b.blur(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ func (c *Checkbox) Draw(screen tcell.Screen) {
|
|||
|
||||
// InputHandler returns the handler for this 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.
|
||||
switch key := event.Key(); key {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -77,12 +77,13 @@ func main() {
|
|||
AddItem(info, 1, 1, false)
|
||||
|
||||
// Shortcuts to navigate the slides.
|
||||
app.SetKeyCapture(tcell.KeyCtrlN, 0, func(p tview.Primitive) bool {
|
||||
nextSlide()
|
||||
return false
|
||||
}).SetKeyCapture(tcell.KeyCtrlP, 0, func(p tview.Primitive) bool {
|
||||
previousSlide()
|
||||
return false
|
||||
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyCtrlN {
|
||||
nextSlide()
|
||||
} else if event.Key() == tcell.KeyCtrlP {
|
||||
previousSlide()
|
||||
}
|
||||
return event
|
||||
})
|
||||
|
||||
// Start the application.
|
||||
|
|
|
@ -249,7 +249,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
|
|||
|
||||
// InputHandler returns the handler for this 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.
|
||||
switch key := event.Key(); key {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Focus is called by the application when the primitive receives focus.
|
||||
|
|
|
@ -236,7 +236,7 @@ func (i *InputField) setCursor(screen tcell.Screen) {
|
|||
|
||||
// InputHandler returns the handler for this 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.
|
||||
currentText := i.text
|
||||
defer func() {
|
||||
|
@ -271,5 +271,5 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
i.done(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
4
list.go
4
list.go
|
@ -232,7 +232,7 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
|
||||
// InputHandler returns the handler for this 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
|
||||
|
||||
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]
|
||||
l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@ type Primitive interface {
|
|||
//
|
||||
// The Application's Draw() function will be called automatically after the
|
||||
// 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))
|
||||
|
||||
// Focus is called by the application when the primitive receives focus.
|
||||
|
|
4
table.go
4
table.go
|
@ -717,7 +717,7 @@ ColumnLoop:
|
|||
|
||||
// InputHandler returns the handler for this 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()
|
||||
|
||||
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.selectionChanged(t.selectedRow, t.selectedColumn)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -836,7 +836,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
|||
|
||||
// InputHandler returns the handler for this 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?
|
||||
if t.capture != nil {
|
||||
if !t.capture(event) {
|
||||
|
@ -899,5 +899,5 @@ func (t *TextView) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
|||
t.trackEnd = false
|
||||
t.lineOffset -= t.pageSize
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue