|
- package uiterm
-
- import (
- "errors"
- "sync/atomic"
-
- "github.com/nsf/termbox-go"
- )
-
- type KeyListener func(ui *Ui, key Key)
-
- type UiManager interface {
- OnUiInitialize(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
- }
-
- 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),
- }
- 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)
- }
- ui.Refresh()
- }
-
- func (ui *Ui) Run() 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)
- width, height := termbox.Size()
- ui.manager.OnUiResize(ui, width, height)
- ui.Refresh()
-
- for {
- select {
- case <-ui.close:
- return nil
- case event := <-events:
- switch event.Type {
- case termbox.EventResize:
- ui.manager.OnUiResize(ui, event.Width, event.Height)
- ui.Refresh()
- case termbox.EventKey:
- if event.Ch != 0 {
- ui.onCharacterEvent(event.Ch)
- } else {
- ui.onKeyEvent(Modifier(event.Mod), Key(event.Key))
- }
- }
- }
- }
- }
-
- func (ui *Ui) onCharacterEvent(ch rune) {
- if ui.activeElement != nil {
- ui.activeElement.View.uiCharacterEvent(ch)
- }
- }
-
- func (ui *Ui) onKeyEvent(mod Modifier, key Key) {
- if ui.keyListeners[key] != nil {
- for _, listener := range ui.keyListeners[key] {
- listener(ui, key)
- }
- }
- if ui.activeElement != nil {
- ui.activeElement.View.uiKeyEvent(mod, 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) {
- ui.keyListeners[key] = append(ui.keyListeners[key], listener)
- }
|