cchat-gtk/internal/ui/primitives/completion/completer.go

103 lines
1.7 KiB
Go
Raw Normal View History

2020-07-01 01:09:22 +00:00
package completion
import (
"github.com/diamondburned/cchat/utils/split"
"github.com/gotk3/gotk3/gtk"
)
type Completeable interface {
Update([]string, int) []gtk.IWidget
Word(i int) string
}
type Completer struct {
ctrl Completeable
Input *gtk.TextView
List *gtk.ListBox
Popover *gtk.Popover
Words []string
Index int
Cursor int
}
func WrapCompleter(input *gtk.TextView, ctrl Completeable) {
NewCompleter(input, ctrl)
}
func NewCompleter(input *gtk.TextView, ctrl Completeable) *Completer {
l, _ := gtk.ListBoxNew()
l.Show()
p := NewPopover(input)
p.Add(l)
c := &Completer{
Input: input,
List: l,
Popover: p,
ctrl: ctrl,
}
input.Connect("key-press-event", KeyDownHandler(l, input.GrabFocus))
ibuf, _ := input.GetBuffer()
ibuf.Connect("changed", func() {
t, v := State(ibuf)
c.Cursor = v
c.Words, c.Index = split.SpaceIndexed(t, v)
c.complete()
})
l.Connect("row-activated", func(l *gtk.ListBox, r *gtk.ListBoxRow) {
SwapWord(ibuf, ctrl.Word(r.GetIndex()), c.Cursor)
c.clear()
c.Popover.Popdown()
input.GrabFocus()
})
return c
}
func (c *Completer) clear() {
var children = c.List.GetChildren()
if children.Length() == 0 {
return
}
children.Foreach(func(i interface{}) {
w := i.(gtk.IWidget).ToWidget()
c.List.Remove(w)
w.Destroy()
})
}
func (c *Completer) complete() {
c.clear()
var widgets []gtk.IWidget
if len(c.Words) > 0 {
widgets = c.ctrl.Update(c.Words, c.Index)
}
if len(widgets) > 0 {
c.Popover.SetPointingTo(CursorRect(c.Input))
c.Popover.Popup()
} else {
c.Popover.Popdown()
}
for i, widget := range widgets {
r, _ := gtk.ListBoxRowNew()
r.Add(widget)
r.Show()
c.List.Add(r)
if i == 0 {
c.List.SelectRow(r)
}
}
}