package uiterm import ( "github.com/nsf/termbox-go" ) type LayoutFunc func(ui *Ui, width, height int) type KeyListener func(ui *Ui, key Key) type UiManager interface { OnUiInitialize(ui *Ui) OnUiResize(ui *Ui, width, height int) } type Ui struct { close chan bool manager UiManager elements map[string]*uiElement activeElement *uiElement keyListeners map[Key][]KeyListener fg Attribute bg Attribute } type uiElement struct { 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 { termbox.Clear(termbox.Attribute(ui.fg), termbox.Attribute(ui.bg)) termbox.HideCursor() for _, element := range ui.elements { element.View.Draw(ui) } termbox.Flush() } } func (ui *Ui) Active() View { return ui.activeElement.View } func (ui *Ui) SetActive(name string) { element, _ := ui.elements[name] if ui.activeElement != nil { ui.activeElement.View.SetActive(ui, false) } ui.activeElement = element if element != nil { element.View.SetActive(ui, true) } ui.Refresh() } func (ui *Ui) SetClear(fg, bg Attribute) { ui.fg = fg ui.bg = bg } 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.CharacterEvent(ui, 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.KeyEvent(ui, mod, key) } } func (ui *Ui) SetView(name string, x0, y0, x1, y1 int, view View) { if element, ok := ui.elements[name]; ok { element.X0 = x0 element.Y0 = y0 element.X1 = x1 element.Y1 = y1 view = element.View } else { ui.elements[name] = &uiElement{ X0: x0, Y0: y0, X1: x1, Y1: y1, View: view, } } view.SetBounds(ui, x0, y0, x1, y1) } func (ui *Ui) View(name string) View { if element, ok := ui.elements[name]; !ok { return nil } else { return element.View } } func (ui *Ui) AddKeyListener(listener KeyListener, key Key) { ui.keyListeners[key] = append(ui.keyListeners[key], listener) }