Keyboard events are now propagated down the hierarchy, allowing users to intercept them. Closes #421

pull/487/head
Oliver 4 years ago
parent c65badfc3d
commit deb54e1422

@ -305,17 +305,19 @@ EventLoop:
switch event := event.(type) {
case *tcell.EventKey:
a.RLock()
p := a.focus
root := a.root
inputCapture := a.inputCapture
a.RUnlock()
// Intercept keys.
var draw bool
if inputCapture != nil {
event = inputCapture(event)
if event == nil {
a.draw()
continue // Don't forward event.
}
draw = true
}
// Ctrl-C closes the application.
@ -323,15 +325,20 @@ EventLoop:
a.Stop()
}
// Pass other key events to the currently focused primitive.
if p != nil {
if handler := p.InputHandler(); handler != nil {
// Pass other key events to the root primitive.
if root != nil && root.GetFocusable().HasFocus() {
if handler := root.InputHandler(); handler != nil {
handler(event, func(p Primitive) {
a.SetFocus(p)
})
a.draw()
draw = true
}
}
// Redraw.
if draw {
a.draw()
}
case *tcell.EventResize:
if time.Since(lastRedraw) < redrawPause {
if redrawTimer != nil {

@ -9,25 +9,24 @@ import (
func Flex(nextSlide func()) (title string, content tview.Primitive) {
modalShown := false
pages := tview.NewPages()
textView := tview.NewTextView().
SetDoneFunc(func(key tcell.Key) {
if modalShown {
nextSlide()
modalShown = false
} else {
pages.ShowPage("modal")
modalShown = true
}
})
textView.SetBorder(true).SetTitle("Flexible width, twice of middle column")
flex := tview.NewFlex().
AddItem(textView, 0, 2, true).
AddItem(tview.NewBox().SetBorder(true).SetTitle("Flexible width, twice of middle column"), 0, 2, true).
AddItem(tview.NewFlex().
SetDirection(tview.FlexRow).
AddItem(tview.NewBox().SetBorder(true).SetTitle("Flexible width"), 0, 1, false).
AddItem(tview.NewBox().SetBorder(true).SetTitle("Fixed height"), 15, 1, false).
AddItem(tview.NewBox().SetBorder(true).SetTitle("Flexible height"), 0, 1, false), 0, 1, false).
AddItem(tview.NewBox().SetBorder(true).SetTitle("Fixed width"), 30, 1, false)
flex.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if modalShown {
nextSlide()
modalShown = false
} else {
pages.ShowPage("modal")
modalShown = true
}
return event
})
modal := tview.NewModal().
SetText("Resize the window to see the effect of the flexbox parameters").
AddButtons([]string{"Ok"}).SetDoneFunc(func(buttonIndex int, buttonLabel string) {

@ -11,18 +11,9 @@ func Grid(nextSlide func()) (title string, content tview.Primitive) {
pages := tview.NewPages()
newPrimitive := func(text string) tview.Primitive {
return tview.NewTextView().
SetTextAlign(tview.AlignCenter).
SetText(text).
SetDoneFunc(func(key tcell.Key) {
if modalShown {
nextSlide()
modalShown = false
} else {
pages.ShowPage("modal")
modalShown = true
}
})
return tview.NewFrame(nil).
SetBorders(0, 0, 0, 0, 0, 0).
AddText(text, true, tview.AlignCenter, tcell.ColorWhite)
}
menu := newPrimitive("Menu")
@ -35,6 +26,16 @@ func Grid(nextSlide func()) (title string, content tview.Primitive) {
SetBorders(true).
AddItem(newPrimitive("Header"), 0, 0, 1, 3, 0, 0, true).
AddItem(newPrimitive("Footer"), 2, 0, 1, 3, 0, 0, false)
grid.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if modalShown {
nextSlide()
modalShown = false
} else {
pages.ShowPage("modal")
modalShown = true
}
return event
})
// Layout for screens narrower than 100 cells (menu and side bar are hidden).
grid.AddItem(menu, 0, 0, 0, 0, 0, 0, false).

@ -88,8 +88,10 @@ func main() {
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyCtrlN {
nextSlide()
return nil
} else if event.Key() == tcell.KeyCtrlP {
previousSlide()
return nil
}
return event
})

@ -396,6 +396,14 @@ 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 d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
// If the list has focus, let it process its own key events.
if d.list.GetFocusable().HasFocus() {
if handler := d.list.InputHandler(); handler != nil {
handler(event, setFocus)
}
return
}
// Process key event.
switch key := event.Key(); key {
case tcell.KeyEnter, tcell.KeyRune, tcell.KeyDown:

@ -223,3 +223,17 @@ func (f *Flex) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
return
})
}
// InputHandler returns the handler for this primitive.
func (f *Flex) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
for _, item := range f.items {
if item != nil && item.Item.GetFocusable().HasFocus() {
if handler := item.Item.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
}
})
}

@ -661,3 +661,26 @@ func (f *Form) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
return
})
}
// InputHandler returns the handler for this primitive.
func (f *Form) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
for _, item := range f.items {
if item != nil && item.GetFocusable().HasFocus() {
if handler := item.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
}
for _, button := range f.buttons {
if button.GetFocusable().HasFocus() {
if handler := button.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
}
})
}

@ -19,7 +19,7 @@ type frameText struct {
type Frame struct {
*Box
// The contained primitive.
// The contained primitive. May be nil.
primitive Primitive
// The lines of text to be displayed.
@ -30,7 +30,8 @@ type Frame struct {
}
// NewFrame returns a new frame around the given primitive. The primitive's
// size will be changed to fit within this frame.
// size will be changed to fit within this frame. The primitive may be nil, in
// which case no other primitive is embedded in the frame.
func NewFrame(primitive Primitive) *Frame {
box := NewBox()
@ -127,33 +128,42 @@ func (f *Frame) Draw(screen tcell.Screen) {
}
// Set the size of the contained primitive.
if topMax > top {
top = topMax + f.header
}
if bottomMin < bottom {
bottom = bottomMin - f.footer
}
if top > bottom {
return // No space for the primitive.
}
f.primitive.SetRect(x, top, width, bottom+1-top)
if f.primitive != nil {
if topMax > top {
top = topMax + f.header
}
if bottomMin < bottom {
bottom = bottomMin - f.footer
}
if top > bottom {
return // No space for the primitive.
}
f.primitive.SetRect(x, top, width, bottom+1-top)
// Finally, draw the contained primitive.
f.primitive.Draw(screen)
// Finally, draw the contained primitive.
f.primitive.Draw(screen)
}
}
// Focus is called when this primitive receives focus.
func (f *Frame) Focus(delegate func(p Primitive)) {
delegate(f.primitive)
if f.primitive != nil {
delegate(f.primitive)
} else {
f.hasFocus = true
}
}
// HasFocus returns whether or not this primitive has focus.
func (f *Frame) HasFocus() bool {
if f.primitive == nil {
return f.hasFocus
}
focusable, ok := f.primitive.(Focusable)
if ok {
return focusable.HasFocus()
}
return false
return f.hasFocus
}
// MouseHandler returns the mouse handler for this primitive.
@ -164,6 +174,25 @@ func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
}
// Pass mouse events on to contained primitive.
return f.primitive.MouseHandler()(action, event, setFocus)
if f.primitive != nil {
return f.primitive.MouseHandler()(action, event, setFocus)
}
return false, nil
})
}
// InputHandler returns the handler for this primitive.
func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
if f.primitive == nil {
return
}
if f.primitive.GetFocusable().HasFocus() {
if handler := f.primitive.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
})
}

@ -5,8 +5,8 @@ go 1.12
require (
github.com/gdamore/tcell v1.3.0
github.com/lucasb-eyer/go-colorful v1.0.3
github.com/mattn/go-runewidth v0.0.8
github.com/mattn/go-runewidth v0.0.9
github.com/rivo/uniseg v0.1.0
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect
golang.org/x/sys v0.0.0-20200817155316-9781c653f443 // indirect
golang.org/x/text v0.3.2 // indirect
)

@ -10,14 +10,14 @@ github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tW
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200817155316-9781c653f443 h1:X18bCaipMcoJGm27Nv7zr4XYPKGUy92GtqboKC2Hxaw=
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=

@ -271,6 +271,20 @@ func (g *Grid) HasFocus() bool {
// InputHandler returns the handler for this primitive.
func (g *Grid) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return g.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
if !g.hasFocus {
// Pass event on to child primitive.
for _, item := range g.items {
if item != nil && item.Item.GetFocusable().HasFocus() {
if handler := item.Item.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
}
return
}
// Process our own key events if we have direct focus.
switch event.Key() {
case tcell.KeyRune:
switch event.Rune() {

@ -188,3 +188,15 @@ func (m *Modal) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
return
})
}
// InputHandler returns the handler for this primitive.
func (m *Modal) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return m.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
if m.frame.GetFocusable().HasFocus() {
if handler := m.frame.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
})
}

@ -300,3 +300,17 @@ func (p *Pages) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
return
})
}
// InputHandler returns the handler for this primitive.
func (p *Pages) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return p.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
for _, page := range p.pages {
if page.Item.GetFocusable().HasFocus() {
if handler := page.Item.InputHandler(); handler != nil {
handler(event, setFocus)
return
}
}
}
})
}

@ -1250,8 +1250,8 @@ func (t *Table) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
if t.rowsSelectable || t.columnsSelectable {
t.Select(t.cellAt(x, y))
}
consumed = true
setFocus(t)
consumed = true
case MouseScrollUp:
t.trackEnd = false
t.rowOffset--

@ -1173,8 +1173,8 @@ func (t *TextView) MouseHandler() func(action MouseAction, event *tcell.EventMou
break
}
}
consumed = true
setFocus(t)
consumed = true
case MouseScrollUp:
t.trackEnd = false
t.lineOffset--

Loading…
Cancel
Save