move autocomplete into separate file and improve event forwarding
This commit is contained in:
parent
4c83b1448c
commit
61d36aebba
|
@ -831,71 +831,71 @@ func (t *TreeView) Draw(screen tcell.Screen) bool {
|
|||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (t *TreeView) InputHandler() InputHandlerFunc {
|
||||
return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) *tcell.EventKey {
|
||||
selectNode := func() {
|
||||
if t.currentNode != nil {
|
||||
if t.selected != nil {
|
||||
t.selected(t.currentNode)
|
||||
}
|
||||
if t.currentNode.selected != nil {
|
||||
t.currentNode.selected()
|
||||
return t.WrapInputHandler(t.DefaultInputHandler)
|
||||
}
|
||||
|
||||
// DefaultInputHandler handles basic navigation and interaction with the
|
||||
// TreeView.
|
||||
func (t *TreeView) DefaultInputHandler(event *tcell.EventKey, setFocus func(p Primitive)) *tcell.EventKey {
|
||||
// Because the tree is flattened into a list only at drawing time, we also
|
||||
// postpone the (selection) movement to drawing time.
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyTab, tcell.KeyDown, tcell.KeyRight:
|
||||
t.movement = treeDown
|
||||
case tcell.KeyBacktab, tcell.KeyUp, tcell.KeyLeft:
|
||||
t.movement = treeUp
|
||||
case tcell.KeyHome:
|
||||
t.movement = treeHome
|
||||
case tcell.KeyEnd:
|
||||
t.movement = treeEnd
|
||||
case tcell.KeyPgDn, tcell.KeyCtrlF:
|
||||
t.movement = treePageDown
|
||||
case tcell.KeyPgUp, tcell.KeyCtrlB:
|
||||
t.movement = treePageUp
|
||||
case tcell.KeyRune:
|
||||
if t.vimBindings {
|
||||
switch event.Rune() {
|
||||
case 'g':
|
||||
t.movement = treeHome
|
||||
case 'G':
|
||||
t.movement = treeEnd
|
||||
case 'j':
|
||||
t.movement = treeDown
|
||||
case 'k':
|
||||
t.movement = treeUp
|
||||
default:
|
||||
return event
|
||||
}
|
||||
} else if t.searchOnType {
|
||||
if time.Since(t.jumpTime) > (500 * time.Millisecond) {
|
||||
t.jumpBuffer = ""
|
||||
}
|
||||
|
||||
if event.Key() == tcell.KeyRune {
|
||||
t.jumpTime = time.Now()
|
||||
t.jumpBuffer += strings.ToLower(string(event.Rune()))
|
||||
|
||||
node := t.FindFirstSelectableNode(t.GetRoot(), t.jumpBuffer)
|
||||
if node != nil {
|
||||
t.SetCurrentNode(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Because the tree is flattened into a list only at drawing time, we also
|
||||
// postpone the (selection) movement to drawing time.
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyTab, tcell.KeyDown, tcell.KeyRight:
|
||||
t.movement = treeDown
|
||||
case tcell.KeyBacktab, tcell.KeyUp, tcell.KeyLeft:
|
||||
t.movement = treeUp
|
||||
case tcell.KeyHome:
|
||||
t.movement = treeHome
|
||||
case tcell.KeyEnd:
|
||||
t.movement = treeEnd
|
||||
case tcell.KeyPgDn, tcell.KeyCtrlF:
|
||||
t.movement = treePageDown
|
||||
case tcell.KeyPgUp, tcell.KeyCtrlB:
|
||||
t.movement = treePageUp
|
||||
case tcell.KeyRune:
|
||||
if t.vimBindings {
|
||||
switch event.Rune() {
|
||||
case 'g':
|
||||
t.movement = treeHome
|
||||
case 'G':
|
||||
t.movement = treeEnd
|
||||
case 'j':
|
||||
t.movement = treeDown
|
||||
case 'k':
|
||||
t.movement = treeUp
|
||||
default:
|
||||
return event
|
||||
}
|
||||
} else if t.searchOnType {
|
||||
if time.Since(t.jumpTime) > (500 * time.Millisecond) {
|
||||
t.jumpBuffer = ""
|
||||
}
|
||||
|
||||
if event.Key() == tcell.KeyRune {
|
||||
t.jumpTime = time.Now()
|
||||
t.jumpBuffer += strings.ToLower(string(event.Rune()))
|
||||
|
||||
node := t.FindFirstSelectableNode(t.GetRoot(), t.jumpBuffer)
|
||||
if node != nil {
|
||||
t.SetCurrentNode(node)
|
||||
}
|
||||
}
|
||||
case tcell.KeyEnter:
|
||||
if t.currentNode != nil {
|
||||
if t.selected != nil {
|
||||
t.selected(t.currentNode)
|
||||
}
|
||||
if t.currentNode.selected != nil {
|
||||
t.currentNode.selected()
|
||||
}
|
||||
case tcell.KeyEnter:
|
||||
selectNode()
|
||||
default:
|
||||
return event
|
||||
}
|
||||
default:
|
||||
return event
|
||||
}
|
||||
|
||||
t.process()
|
||||
return nil
|
||||
})
|
||||
t.process()
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindFirstSelectableNode iterates through the tree from top to bottom, trying
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"github.com/Bios-Marcel/cordless/tview"
|
||||
tcell "github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
// AutocompleteView is a simple treeview meant for displaying autocomplete
|
||||
// choices. The speccial part about this component is, that it can redirect
|
||||
// certain events to a different component, as only certain events are
|
||||
// treated directly.
|
||||
type AutocompleteView struct {
|
||||
*tview.TreeView
|
||||
}
|
||||
|
||||
// NewAutocompleteView creates a ready-to-use AutocompleteView.
|
||||
func NewAutocompleteView() *AutocompleteView {
|
||||
treeView := tview.NewTreeView()
|
||||
//Has to be disabled, as we need the events for the related editor.
|
||||
treeView.
|
||||
SetSearchOnTypeEnabled(false).
|
||||
SetVimBindingsEnabled(false).
|
||||
SetTopLevel(1).
|
||||
SetCycleSelection(true)
|
||||
|
||||
return &AutocompleteView{treeView}
|
||||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (a *AutocompleteView) InputHandler() tview.InputHandlerFunc {
|
||||
return a.WrapInputHandler(a.DefaultInputHandler)
|
||||
}
|
||||
|
||||
// WrapInputHandler unlike Box.WrapInputHandler calls the default handler
|
||||
// first, as all other shortcuts are meant to be forwarded. However, not
|
||||
// all events are handled by the TreeView, as some features aren't desired.
|
||||
func (a *AutocompleteView) WrapInputHandler(inputHandler tview.InputHandlerFunc) tview.InputHandlerFunc {
|
||||
return func(event *tcell.EventKey, setFocus func(p tview.Primitive)) *tcell.EventKey {
|
||||
switch key := event.Key(); key {
|
||||
//FIXME Maybe this should be made configurable as well
|
||||
case tcell.KeyDown, tcell.KeyUp, tcell.KeyPgDn, tcell.KeyPgUp,
|
||||
tcell.KeyEnter, tcell.KeyHome, tcell.KeyEnd:
|
||||
if inputHandler != nil {
|
||||
event = inputHandler(event, setFocus)
|
||||
}
|
||||
}
|
||||
|
||||
if event != nil {
|
||||
inputCapture := a.GetInputCapture()
|
||||
if inputCapture != nil {
|
||||
event = inputCapture(event)
|
||||
}
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
}
|
29
ui/window.go
29
ui/window.go
|
@ -133,13 +133,11 @@ func NewWindow(app *tview.Application, session *discordgo.Session, readyEvent *d
|
|||
guilds := readyEvent.Guilds
|
||||
|
||||
mentionWindowRootNode := tview.NewTreeNode("")
|
||||
autocompleteView := tview.NewTreeView().
|
||||
SetVimBindingsEnabled(false).
|
||||
autocompleteView := components.NewAutocompleteView()
|
||||
autocompleteView.
|
||||
SetRoot(mentionWindowRootNode).
|
||||
SetTopLevel(1).
|
||||
SetCycleSelection(true)
|
||||
autocompleteView.SetBorder(true)
|
||||
autocompleteView.SetBorderSides(false, true, false, true)
|
||||
SetBorder(true).
|
||||
SetBorderSides(false, true, false, true)
|
||||
|
||||
window.leftArea = tview.NewFlex().SetDirection(tview.FlexRow)
|
||||
|
||||
|
@ -423,7 +421,12 @@ func NewWindow(app *tview.Application, session *discordgo.Session, readyEvent *d
|
|||
autocompleteView.SetCurrentNode(rootNode)
|
||||
autocompleteView.SetVisible(true)
|
||||
window.app.SetFocus(autocompleteView)
|
||||
window.chatArea.ResizeItem(autocompleteView, maths.Min(10, len(values)), 0)
|
||||
_, _, _, height := window.app.GetRoot().GetRect()
|
||||
//The preferred height is limited to a certain to avoid the
|
||||
//autocomplete taking too much space in small windows, or
|
||||
//furthermore terminals with a big font size.
|
||||
prefHeight := maths.Min(maths.Min(10, height/4), len(values))
|
||||
window.chatArea.ResizeItem(autocompleteView, prefHeight, 0)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -881,14 +884,12 @@ func NewWindow(app *tview.Application, session *discordgo.Session, readyEvent *d
|
|||
window.updateUserList()
|
||||
|
||||
autocompleteView.SetVisible(false)
|
||||
//FIXME this probably needs to be adapted to custom bindings
|
||||
|
||||
//All uncaptured events, e.g. events not relevant for TreeViews, will be
|
||||
//forwarded to the message input, as both full typing capability, but also
|
||||
//autocomplete capability should work at the same time.
|
||||
autocompleteView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyRune, tcell.KeyDelete, tcell.KeyBackspace, tcell.KeyBackspace2, tcell.KeyLeft, tcell.KeyRight, tcell.KeyCtrlA, tcell.KeyCtrlV:
|
||||
window.messageInput.internalTextView.GetInputCapture()(event)
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
return window.messageInput.internalTextView.GetInputCapture()(event)
|
||||
})
|
||||
|
||||
window.chatArea.AddItem(window.messageContainer, 0, 1, false)
|
||||
|
|
Loading…
Reference in New Issue