mirror of
https://github.com/rivo/tview.git
synced 2025-04-24 13:48:56 +08:00
Keyboard events are now propagated down the hierarchy, allowing users to intercept them. Closes #421
This commit is contained in:
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:
|
||||
|
14
flex.go
14
flex.go
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
23
form.go
23
form.go
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
63
frame.go
63
frame.go
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -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
|
||||
)
|
||||
|
8
go.sum
8
go.sum
@ -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=
|
||||
|
14
grid.go
14
grid.go
@ -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() {
|
||||
|
12
modal.go
12
modal.go
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
14
pages.go
14
pages.go
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
2
table.go
2
table.go
@ -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…
x
Reference in New Issue
Block a user