barnard/uiterm/ui.go

219 lines
4.3 KiB
Go

package uiterm
import (
"errors"
"strings"
"sync/atomic"
"github.com/nsf/termbox-go"
)
type KeyListener func(ui *Ui, key Key)
type CommandListener func(ui *Ui, cmd string)
type UiManager interface {
OnUiInitialize(ui *Ui)
OnUiDoneInitialize(ui *Ui)
OnUiResize(ui *Ui, width, height int)
}
type Ui struct {
Fg, Bg Attribute
close chan bool
manager UiManager
drawCount int32
elements map[string]*uiElement
activeElement *uiElement
keyListeners map[Key][]KeyListener
commandListeners map[string][]CommandListener
}
type uiElement struct {
Name string
X0, Y0, X1, Y1 int
View View
}
func New(manager UiManager) *Ui {
ui := &Ui{
close: make(chan bool, 10),
elements: make(map[string]*uiElement),
manager: manager,
keyListeners: make(map[Key][]KeyListener),
commandListeners: make(map[string][]CommandListener),
}
return ui
}
func (ui *Ui) Close() {
if termbox.IsInit {
ui.close <- true
}
}
func (ui *Ui) Refresh() {
if termbox.IsInit {
ui.beginDraw()
defer ui.endDraw()
termbox.Clear(termbox.Attribute(ui.Fg), termbox.Attribute(ui.Bg))
termbox.HideCursor()
for _, element := range ui.elements {
element.View.uiDraw()
}
}
}
func (ui *Ui) beginDraw() {
atomic.AddInt32(&ui.drawCount, 1)
}
func (ui *Ui) endDraw() {
if count := atomic.AddInt32(&ui.drawCount, -1); count == 0 {
termbox.Flush()
}
}
func (ui *Ui) Active() string {
return ui.activeElement.Name
}
func (ui *Ui) SetActive(name string) {
element, _ := ui.elements[name]
if ui.activeElement != nil {
ui.activeElement.View.uiSetActive(false)
}
ui.activeElement = element
if element != nil {
element.View.uiSetActive(true)
}
}
func (ui *Ui) Run(cmds chan string) error {
if termbox.IsInit {
return nil
}
if err := termbox.Init(); err != nil {
return nil
}
defer termbox.Close()
termbox.SetInputMode(termbox.InputAlt)
events := make(chan termbox.Event)
go func() {
for {
events <- termbox.PollEvent()
}
}()
ui.manager.OnUiInitialize(ui)
ui.manager.OnUiDoneInitialize(ui)
width, height := termbox.Size()
ui.manager.OnUiResize(ui, width, height)
ui.Refresh()
for {
select {
case <-ui.close:
return nil
case cmd := <-cmds:
ui.onCommandEvent(cmd)
case event := <-events:
switch event.Type {
case termbox.EventResize:
ui.manager.OnUiResize(ui, event.Width, event.Height)
ui.Refresh()
case termbox.EventKey:
var k = uint32(event.Key)
if event.Ch != 0 && event.Mod != 0 {
k = uint32(event.Ch)
}
if event.Ch != 0 && event.Mod == 0 {
ui.onCharacterEvent(event.Ch)
} else {
if event.Mod > 0 {
k = k + (uint32(event.Mod) << 16)
}
ui.onKeyEvent(Key(k))
}
}
}
}
}
func (ui *Ui) onCharacterEvent(ch rune) {
if ui.activeElement != nil {
ui.activeElement.View.uiCharacterEvent(ch)
}
}
func (ui *Ui) onKeyEvent(key Key) {
if ui.keyListeners[key] != nil {
for _, listener := range ui.keyListeners[key] {
listener(ui, key)
}
}
if ui.activeElement != nil {
ui.activeElement.View.uiKeyEvent(key)
}
}
func (ui *Ui) onCommandEvent(cmd string) {
ta := strings.SplitN(cmd, " ", 2)
t := ta[0]
rest := ""
if len(ta) == 2 {
rest = ta[1]
}
if ui.commandListeners[t] != nil {
for _, listener := range ui.commandListeners[t] {
listener(ui, rest)
}
}
if ui.commandListeners["*"] != nil {
for _, listener := range ui.commandListeners["*"] {
listener(ui, cmd)
}
}
// if ui.activeElement != nil {
// ui.activeElement.View.uiKeyEvent(key)
// }
}
func (ui *Ui) Add(name string, view View) error {
if _, ok := ui.elements[name]; ok {
return errors.New("view already exists")
}
ui.elements[name] = &uiElement{
Name: name,
View: view,
}
view.uiInitialize(ui)
return nil
}
func (ui *Ui) SetBounds(name string, x0, y0, x1, y1 int) error {
element, ok := ui.elements[name]
if !ok {
return errors.New("view does not exist")
}
element.X0, element.Y0, element.X1, element.Y1 = x0, y0, x1, y1
element.View.uiSetBounds(x0, y0, x1, y1)
return nil
}
func (ui *Ui) AddKeyListener(listener KeyListener, key *Key) {
if key != nil {
ui.keyListeners[*key] = append(ui.keyListeners[*key], listener)
}
}
func (ui *Ui) AddCommandListener(listener CommandListener, cmd string) {
//if cmd!=nil {
ui.commandListeners[cmd] = append(ui.commandListeners[cmd], listener)
//}
}