termui.go

  1package termui
  2
  3import (
  4	"github.com/MichaelMure/git-bug/cache"
  5	"github.com/MichaelMure/git-bug/input"
  6	"github.com/MichaelMure/git-bug/repository"
  7	"github.com/jroimartin/gocui"
  8	"github.com/pkg/errors"
  9)
 10
 11var errTerminateMainloop = errors.New("terminate gocui mainloop")
 12
 13type termUI struct {
 14	g            *gocui.Gui
 15	gError       chan error
 16	cache        cache.RepoCacher
 17	activeWindow window
 18
 19	bugTable *bugTable
 20}
 21
 22var ui *termUI
 23
 24type window interface {
 25	keybindings(g *gocui.Gui) error
 26	layout(g *gocui.Gui) error
 27}
 28
 29func Run(repo repository.Repo) error {
 30	c := cache.NewRepoCache(repo)
 31
 32	ui = &termUI{
 33		gError:   make(chan error, 1),
 34		cache:    c,
 35		bugTable: newBugTable(c),
 36	}
 37
 38	ui.activeWindow = ui.bugTable
 39
 40	initGui()
 41
 42	err := <-ui.gError
 43
 44	if err != nil && err != gocui.ErrQuit {
 45		return err
 46	}
 47
 48	return nil
 49}
 50
 51func initGui() {
 52	g, err := gocui.NewGui(gocui.OutputNormal)
 53
 54	if err != nil {
 55		ui.gError <- err
 56		return
 57	}
 58
 59	ui.g = g
 60
 61	ui.g.SetManagerFunc(layout)
 62
 63	err = keybindings(ui.g)
 64
 65	if err != nil {
 66		ui.g.Close()
 67		ui.gError <- err
 68		return
 69	}
 70
 71	err = g.MainLoop()
 72
 73	if err != nil && err != errTerminateMainloop {
 74		ui.g.Close()
 75		ui.gError <- err
 76	}
 77
 78	return
 79}
 80
 81func layout(g *gocui.Gui) error {
 82	//maxX, maxY := g.Size()
 83
 84	g.Cursor = false
 85
 86	if err := ui.activeWindow.layout(g); err != nil {
 87		return err
 88	}
 89
 90	return nil
 91}
 92
 93func keybindings(g *gocui.Gui) error {
 94	// Quit
 95	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
 96		return err
 97	}
 98
 99	if err := ui.bugTable.keybindings(g); err != nil {
100		return err
101	}
102
103	return nil
104}
105
106func quit(g *gocui.Gui, v *gocui.View) error {
107	return gocui.ErrQuit
108}
109
110func newBugWithEditor(g *gocui.Gui, v *gocui.View) error {
111	// This is somewhat hacky.
112	// As there is no way to pause gocui, run the editor, restart gocui,
113	// we have to stop it entirely and start a new one later.
114	//
115	// - an error channel is used to route the returned error of this new
116	// 		instance into the original launch function
117	// - a custom error (errTerminateMainloop) is used to terminate the original
118	//		instance's mainLoop. This error is then filtered.
119
120	ui.g.Close()
121
122	title, message, err := input.BugCreateEditorInput(ui.cache.Repository(), "", "")
123
124	if err == input.ErrEmptyTitle {
125		// TODO: display proper error
126		return err
127	}
128	if err != nil {
129		return err
130	}
131
132	_, err = ui.cache.NewBug(title, message)
133	if err != nil {
134		return err
135	}
136
137	initGui()
138
139	return errTerminateMainloop
140}
141
142func maxInt(a, b int) int {
143	if a > b {
144		return a
145	}
146	return b
147}
148
149func minInt(a, b int) int {
150	if a > b {
151		return b
152	}
153	return a
154}