gocui/gui.go

271 lines
5.1 KiB
Go
Raw Normal View History

2013-12-27 21:36:26 +01:00
package gocui
import (
"errors"
2013-12-27 22:02:56 +01:00
2013-12-27 21:36:26 +01:00
"github.com/jroimartin/termbox-go"
)
var ErrorQuit = errors.New("quit")
type Gui struct {
events chan termbox.Event
views []*View
currentView *View
BgColor, FgColor termbox.Attribute
2013-12-27 21:36:26 +01:00
}
func NewGui() (g *Gui) {
return &Gui{}
}
func (g *Gui) Init() (err error) {
g.events = make(chan termbox.Event, 20)
g.BgColor = termbox.ColorWhite
g.FgColor = termbox.ColorBlack
2013-12-27 21:36:26 +01:00
return termbox.Init()
}
func (g *Gui) Close() {
termbox.Close()
}
func (g *Gui) Size() (x, y int) {
return termbox.Size()
}
func (g *Gui) AddView(name string, x0, y0, x1, y1 int) (v *View, err error) {
2013-12-27 21:36:26 +01:00
maxX, maxY := termbox.Size()
if x0 < -1 || y0 < -1 || x1 < -1 || y1 < -1 ||
2013-12-27 22:02:56 +01:00
x0 > maxX || y0 > maxY || x1 > maxX || y1 > maxY ||
x0 >= x1 || y0 >= y1 {
return nil, errors.New("invalid points")
2013-12-27 21:36:26 +01:00
}
for _, v := range g.views {
if name == v.Name {
return nil, errors.New("invalid name")
}
}
v = NewView(name, x0, y0, x1, y1)
2013-12-27 21:36:26 +01:00
g.views = append(g.views, v)
return v, nil
}
func (g *Gui) MainLoop() (err error) {
go func() {
for {
g.events <- termbox.PollEvent()
}
}()
if err := g.resize(); err != nil {
return err
}
if err := g.draw(); err != nil {
return err
}
// TODO: Set initial cursor position
//termbox.SetCursor(10, 10)
termbox.Flush()
for {
select {
case ev := <-g.events:
if err := g.handleEvent(&ev); err != nil {
return err
}
if err := g.consumeevents(); err != nil {
return err
}
if err := g.draw(); err != nil {
return err
}
termbox.Flush()
}
}
return nil
}
2013-12-30 14:19:22 +01:00
func (g *Gui) SetCell(x, y int, ch rune) (err error) {
maxX, maxY := termbox.Size()
if x < 0 || y < 0 || x >= maxX || y >= maxY {
return errors.New("invalid point")
}
termbox.SetCell(x, y, ch, g.FgColor, g.BgColor)
2013-12-30 14:19:22 +01:00
return nil
}
func (g *Gui) GetCell(x, y int) (ch rune, err error) {
maxX, maxY := termbox.Size()
if x < 0 || y < 0 || x >= maxX || y >= maxY {
return 0, errors.New("invalid point")
}
c := termbox.CellBuffer()[y*maxX+x]
return c.Ch, nil
}
2013-12-27 21:36:26 +01:00
func (g *Gui) consumeevents() (err error) {
for {
select {
case ev := <-g.events:
if err := g.handleEvent(&ev); err != nil {
return err
}
default:
return nil
}
}
}
func (g *Gui) handleEvent(ev *termbox.Event) (err error) {
switch ev.Type {
case termbox.EventKey:
return g.onKey(ev)
case termbox.EventResize:
return g.resize()
case termbox.EventError:
return ev.Err
default:
return nil
}
}
func (g *Gui) draw() (err error) {
for _, v := range g.views {
if err := g.drawView(v); err != nil {
2013-12-27 21:36:26 +01:00
return err
}
}
return nil
}
2013-12-27 21:36:26 +01:00
func (g *Gui) drawView(v *View) (err error) {
return nil
2013-12-27 21:36:26 +01:00
}
func (g *Gui) resize() (err error) {
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
2013-12-30 14:19:22 +01:00
if err := g.resizeViews(); err != nil {
return err
}
if err := g.drawFrames(); err != nil {
return err
}
2013-12-30 14:19:22 +01:00
if err := g.drawIntersections(); err != nil {
return err
}
return nil
}
func (g *Gui) drawFrames() (err error) {
maxX, maxY := termbox.Size()
2013-12-27 21:36:26 +01:00
for _, v := range g.views {
for x := v.X0 + 1; x < v.X1; x++ {
if v.Y0 != -1 {
2013-12-30 14:45:02 +01:00
g.SetCell(x, v.Y0, '─')
}
if v.Y1 != maxY {
2013-12-30 14:45:02 +01:00
g.SetCell(x, v.Y1, '─')
}
}
for y := v.Y0 + 1; y < v.Y1; y++ {
if v.X0 != -1 {
2013-12-30 14:45:02 +01:00
g.SetCell(v.X0, y, '│')
}
if v.X1 != maxX {
2013-12-30 14:45:02 +01:00
g.SetCell(v.X1, y, '│')
}
2013-12-27 21:36:26 +01:00
}
}
return nil
}
2013-12-30 14:19:22 +01:00
func (g *Gui) drawIntersections() (err error) {
for _, v := range g.views {
2013-12-30 14:19:22 +01:00
if ch, ok := g.getIntersectionRune(v.X0, v.Y0); ok {
g.SetCell(v.X0, v.Y0, ch)
}
2013-12-30 14:19:22 +01:00
if ch, ok := g.getIntersectionRune(v.X0, v.Y1); ok {
g.SetCell(v.X0, v.Y1, ch)
}
2013-12-30 14:19:22 +01:00
if ch, ok := g.getIntersectionRune(v.X1, v.Y0); ok {
g.SetCell(v.X1, v.Y0, ch)
}
2013-12-30 14:19:22 +01:00
if ch, ok := g.getIntersectionRune(v.X1, v.Y1); ok {
g.SetCell(v.X1, v.Y1, ch)
}
}
return nil
}
2013-12-30 14:19:22 +01:00
func (g *Gui) getIntersectionRune(x, y int) (ch rune, ok bool) {
maxX, maxY := termbox.Size()
if x < 0 || y < 0 || x >= maxX || y >= maxY {
return 0, false
}
2013-12-31 01:37:34 +01:00
chTop, _ := g.GetCell(x, y-1)
top := verticalRune(chTop)
chBottom, _ := g.GetCell(x, y+1)
bottom := verticalRune(chBottom)
chLeft, _ := g.GetCell(x-1, y)
left := horizontalRune(chLeft)
chRight, _ := g.GetCell(x+1, y)
right := horizontalRune(chRight)
2013-12-30 14:19:22 +01:00
switch {
2013-12-31 01:37:34 +01:00
case !top && bottom && !left && right:
2013-12-30 14:45:02 +01:00
ch = '┌'
2013-12-31 01:37:34 +01:00
case !top && bottom && left && !right:
2013-12-30 14:45:02 +01:00
ch = '┐'
2013-12-31 01:37:34 +01:00
case top && !bottom && !left && right:
2013-12-30 14:45:02 +01:00
ch = '└'
2013-12-31 01:37:34 +01:00
case top && !bottom && left && !right:
2013-12-30 14:45:02 +01:00
ch = '┘'
2013-12-31 01:37:34 +01:00
case top && bottom && left && right:
2013-12-30 14:45:02 +01:00
ch = '┼'
2013-12-31 01:37:34 +01:00
case top && bottom && !left && right:
2013-12-30 14:45:02 +01:00
ch = '├'
2013-12-31 01:37:34 +01:00
case top && bottom && left && !right:
2013-12-30 14:45:02 +01:00
ch = '┤'
2013-12-31 01:37:34 +01:00
case !top && bottom && left && right:
2013-12-30 14:45:02 +01:00
ch = '┬'
2013-12-31 01:37:34 +01:00
case top && !bottom && left && right:
2013-12-30 14:45:02 +01:00
ch = '┴'
2013-12-30 14:19:22 +01:00
default:
return 0, false
}
return ch, true
}
2013-12-30 14:37:27 +01:00
func verticalRune(ch rune) bool {
2013-12-30 14:45:02 +01:00
if ch == '│' || ch == '┼' || ch == '├' || ch == '┤' {
2013-12-30 14:37:27 +01:00
return true
}
return false
}
func horizontalRune(ch rune) bool {
2013-12-30 14:45:02 +01:00
if ch == '─' || ch == '┼' || ch == '┬' || ch == '┴' {
2013-12-30 14:37:27 +01:00
return true
}
return false
}
2013-12-30 14:19:22 +01:00
func (g *Gui) resizeViews() (err error) {
return nil
2013-12-27 21:36:26 +01:00
}
func (g *Gui) onKey(ev *termbox.Event) (err error) {
switch ev.Key {
case termbox.KeyCtrlC:
return ErrorQuit
default:
return nil
}
}