diff options
author | Michael Muré <batolettre@gmail.com> | 2018-08-01 02:15:40 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-08-01 02:17:06 +0200 |
commit | c875d40e631f83507b602807480be96dae05fc85 (patch) | |
tree | a81b0909eda009acb395d936d1e2bf09e03cff3a /termui | |
parent | 2f88c28c59ce0480e64dfba6d820dc6f7589a6cf (diff) | |
download | git-bug-c875d40e631f83507b602807480be96dae05fc85.tar.gz |
termui: add a view to display a bug
Diffstat (limited to 'termui')
-rw-r--r-- | termui/bug_table.go | 81 | ||||
-rw-r--r-- | termui/error_popup.go | 3 | ||||
-rw-r--r-- | termui/show_bug.go | 158 | ||||
-rw-r--r-- | termui/termui.go | 24 |
4 files changed, 238 insertions, 28 deletions
diff --git a/termui/bug_table.go b/termui/bug_table.go index a6eb569d..ae67c289 100644 --- a/termui/bug_table.go +++ b/termui/bug_table.go @@ -9,18 +9,23 @@ import ( ) const bugTableView = "bugTableView" +const bugTableHeaderView = "bugTableHeaderView" +const bugTableFooterView = "bugTableFooterView" +const bugTableInstructionView = "bugTableInstructionView" type bugTable struct { - cache cache.RepoCacher - allIds []string - bugs []*bug.Snapshot - cursor int + cache cache.RepoCacher + allIds []string + bugs []*bug.Snapshot + pageCursor int + selectCursor int } func newBugTable(cache cache.RepoCacher) *bugTable { return &bugTable{ - cache: cache, - cursor: 0, + cache: cache, + pageCursor: 0, + selectCursor: 0, } } @@ -32,7 +37,7 @@ func (bt *bugTable) layout(g *gocui.Gui) error { return nil } - v, err := g.SetView("header", -1, -1, maxX, 3) + v, err := g.SetView(bugTableHeaderView, -1, -1, maxX, 3) if err != nil { if err != gocui.ErrUnknownView { @@ -56,6 +61,10 @@ func (bt *bugTable) layout(g *gocui.Gui) error { v.Highlight = true v.SelBgColor = gocui.ColorWhite v.SelFgColor = gocui.ColorBlack + + // restore the cursor + // window is too small to set the cursor properly, ignoring the error + _ = v.SetCursor(0, bt.selectCursor) } _, viewHeight := v.Size() @@ -72,7 +81,7 @@ func (bt *bugTable) layout(g *gocui.Gui) error { v.Clear() bt.render(v, maxX) - v, err = g.SetView("footer", -1, maxY-4, maxX, maxY) + v, err = g.SetView(bugTableFooterView, -1, maxY-4, maxX, maxY) if err != nil { if err != gocui.ErrUnknownView { @@ -85,7 +94,7 @@ func (bt *bugTable) layout(g *gocui.Gui) error { v.Clear() bt.renderFooter(v, maxX) - v, err = g.SetView("instructions", -1, maxY-2, maxX, maxY) + v, err = g.SetView(bugTableInstructionView, -1, maxY-2, maxX, maxY) if err != nil { if err != gocui.ErrUnknownView { @@ -99,12 +108,7 @@ func (bt *bugTable) layout(g *gocui.Gui) error { } _, err = g.SetCurrentView(bugTableView) - - if err != nil { - return err - } - - return nil + return err } func (bt *bugTable) keybindings(g *gocui.Gui) error { @@ -165,6 +169,28 @@ func (bt *bugTable) keybindings(g *gocui.Gui) error { return err } + // Open bug + if err := g.SetKeybinding(bugTableView, gocui.KeyEnter, gocui.ModNone, + bt.openBug); err != nil { + return err + } + + return nil +} + +func (bt *bugTable) disable(g *gocui.Gui) error { + if err := g.DeleteView(bugTableView); err != nil { + return err + } + if err := g.DeleteView(bugTableHeaderView); err != nil { + return err + } + if err := g.DeleteView(bugTableFooterView); err != nil { + return err + } + if err := g.DeleteView(bugTableInstructionView); err != nil { + return err + } return nil } @@ -181,10 +207,10 @@ func (bt *bugTable) paginate(max int) error { func (bt *bugTable) doPaginate(allIds []string, max int) error { // clamp the cursor - bt.cursor = maxInt(bt.cursor, 0) - bt.cursor = minInt(bt.cursor, len(allIds)-1) + bt.pageCursor = maxInt(bt.pageCursor, 0) + bt.pageCursor = minInt(bt.pageCursor, len(allIds)-1) - nb := minInt(len(allIds)-bt.cursor, max) + nb := minInt(len(allIds)-bt.pageCursor, max) if nb < 0 { bt.bugs = []*bug.Snapshot{} @@ -192,7 +218,7 @@ func (bt *bugTable) doPaginate(allIds []string, max int) error { } // slice the data - ids := allIds[bt.cursor : bt.cursor+nb] + ids := allIds[bt.pageCursor : bt.pageCursor+nb] bt.bugs = make([]*bug.Snapshot, len(ids)) @@ -217,7 +243,7 @@ func (bt *bugTable) getColumnWidths(maxX int) map[string]int { m["id"] = 10 m["status"] = 8 - left := maxX - m["id"] - m["status"] + left := maxX - 4 - m["id"] - m["status"] m["summary"] = maxInt(30, left/3) left -= m["summary"] @@ -272,6 +298,7 @@ func (bt *bugTable) cursorDown(g *gocui.Gui, v *gocui.View) error { // window is too small to set the cursor properly, ignoring the error _ = v.SetCursor(0, y) + bt.selectCursor = y return nil } @@ -282,6 +309,7 @@ func (bt *bugTable) cursorUp(g *gocui.Gui, v *gocui.View) error { // window is too small to set the cursor properly, ignoring the error _ = v.SetCursor(0, y) + bt.selectCursor = y return nil } @@ -294,6 +322,7 @@ func (bt *bugTable) cursorClamp(v *gocui.View) error { // window is too small to set the cursor properly, ignoring the error _ = v.SetCursor(0, y) + bt.selectCursor = y return nil } @@ -308,11 +337,11 @@ func (bt *bugTable) nextPage(g *gocui.Gui, v *gocui.View) error { bt.allIds = allIds - if bt.cursor+max >= len(allIds) { + if bt.pageCursor+max >= len(allIds) { return nil } - bt.cursor += max + bt.pageCursor += max return bt.doPaginate(allIds, max) } @@ -326,7 +355,13 @@ func (bt *bugTable) previousPage(g *gocui.Gui, v *gocui.View) error { bt.allIds = allIds - bt.cursor = maxInt(0, bt.cursor-max) + bt.pageCursor = maxInt(0, bt.pageCursor-max) return bt.doPaginate(allIds, max) } + +func (bt *bugTable) openBug(g *gocui.Gui, v *gocui.View) error { + _, y := v.Cursor() + ui.showBug.bug = bt.bugs[bt.pageCursor+y] + return ui.activateWindow(ui.showBug) +} diff --git a/termui/error_popup.go b/termui/error_popup.go index f0af5a93..7ec6b783 100644 --- a/termui/error_popup.go +++ b/termui/error_popup.go @@ -62,8 +62,7 @@ func (ep *errorPopup) layout(g *gocui.Gui) error { func (ep *errorPopup) close(g *gocui.Gui, v *gocui.View) error { ep.err = "" - g.DeleteView(errorPopupView) - return nil + return g.DeleteView(errorPopupView) } func (ep *errorPopup) isActive() bool { diff --git a/termui/show_bug.go b/termui/show_bug.go new file mode 100644 index 00000000..9d1f5b76 --- /dev/null +++ b/termui/show_bug.go @@ -0,0 +1,158 @@ +package termui + +import ( + "fmt" + "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/bug/operations" + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/util" + "github.com/jroimartin/gocui" +) + +const showBugView = "showBugView" +const showBugSidebarView = "showBugSidebarView" +const showBugInstructionView = "showBugInstructionView" + +const timeLayout = "Jan _2 2006" + +type showBug struct { + cache cache.RepoCacher + bug *bug.Snapshot +} + +func newShowBug(cache cache.RepoCacher) *showBug { + return &showBug{ + cache: cache, + } +} + +func (sb *showBug) layout(g *gocui.Gui) error { + maxX, maxY := g.Size() + + v, err := g.SetView(showBugView, 0, 0, maxX*2/3, maxY-2) + + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + + v.Frame = false + } + + v.Clear() + sb.renderMain(v) + + v, err = g.SetView(showBugSidebarView, maxX*2/3+1, 0, maxX-1, maxY-2) + + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + + v.Frame = false + } + + v.Clear() + sb.renderSidebar(v) + + v, err = g.SetView(showBugInstructionView, -1, maxY-2, maxX, maxY) + + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + + v.Frame = false + v.BgColor = gocui.ColorBlue + + fmt.Fprintf(v, "[q] Return") + } + + _, err = g.SetCurrentView(showBugView) + return err +} + +func (sb *showBug) keybindings(g *gocui.Gui) error { + // Return + if err := g.SetKeybinding(showBugView, 'q', gocui.ModNone, sb.back); err != nil { + return err + } + + if err := g.SetKeybinding(showBugView, gocui.KeyPgup, gocui.ModNone, + sb.scrollUp); err != nil { + return err + } + if err := g.SetKeybinding(showBugView, gocui.KeyPgdn, gocui.ModNone, + sb.scrollDown); err != nil { + return err + } + + return nil +} + +func (sb *showBug) disable(g *gocui.Gui) error { + if err := g.DeleteView(showBugView); err != nil { + return err + } + if err := g.DeleteView(showBugSidebarView); err != nil { + return err + } + if err := g.DeleteView(showBugInstructionView); err != nil { + return err + } + return nil +} + +func (sb *showBug) renderMain(v *gocui.View) { + maxX, _ := v.Size() + + header1 := fmt.Sprintf("[%s] %s", sb.bug.HumanId(), sb.bug.Title) + fmt.Fprintf(v, util.LeftPaddedString(header1, maxX, 2)+"\n\n") + + header2 := fmt.Sprintf("[%s] %s opened this bug on %s", + sb.bug.Status, sb.bug.Author.Name, sb.bug.CreatedAt.Format(timeLayout)) + fmt.Fprintf(v, util.LeftPaddedString(header2, maxX, 2)+"\n\n") + + for _, op := range sb.bug.Operations { + switch op.(type) { + + case operations.CreateOperation: + create := op.(operations.CreateOperation) + fmt.Fprintf(v, util.LeftPaddedString(create.Message, maxX, 6)+"\n\n\n") + + case operations.AddCommentOperation: + comment := op.(operations.AddCommentOperation) + header := fmt.Sprintf("%s commented on %s", + comment.Author.Name, comment.Time().Format(timeLayout)) + fmt.Fprintf(v, util.LeftPaddedString(header, maxX, 6)+"\n\n") + fmt.Fprintf(v, util.LeftPaddedString(comment.Message, maxX, 6)+"\n\n\n") + } + } + +} + +func (sb *showBug) renderSidebar(v *gocui.View) { + maxX, _ := v.Size() + + title := util.LeftPaddedString("LABEL", maxX, 2) + fmt.Fprintf(v, title+"\n\n") + + for _, label := range sb.bug.Labels { + fmt.Fprintf(v, util.LeftPaddedString(label.String(), maxX, 2)) + fmt.Fprintln(v) + } +} + +func (sb *showBug) back(g *gocui.Gui, v *gocui.View) error { + sb.bug = nil + ui.activateWindow(ui.bugTable) + return nil +} + +func (sb *showBug) scrollUp(g *gocui.Gui, v *gocui.View) error { + return nil +} + +func (sb *showBug) scrollDown(g *gocui.Gui, v *gocui.View) error { + return nil +} diff --git a/termui/termui.go b/termui/termui.go index f4a4fb26..727b9f3d 100644 --- a/termui/termui.go +++ b/termui/termui.go @@ -11,20 +11,33 @@ import ( var errTerminateMainloop = errors.New("terminate gocui mainloop") type termUI struct { - g *gocui.Gui - gError chan error - cache cache.RepoCacher + g *gocui.Gui + gError chan error + cache cache.RepoCacher + activeWindow window bugTable *bugTable + showBug *showBug errorPopup *errorPopup } +func (tui *termUI) activateWindow(window window) error { + if err := tui.activeWindow.disable(tui.g); err != nil { + return err + } + + tui.activeWindow = window + + return nil +} + var ui *termUI type window interface { keybindings(g *gocui.Gui) error layout(g *gocui.Gui) error + disable(g *gocui.Gui) error } func Run(repo repository.Repo) error { @@ -34,6 +47,7 @@ func Run(repo repository.Repo) error { gError: make(chan error, 1), cache: c, bugTable: newBugTable(c), + showBug: newShowBug(c), errorPopup: newErrorPopup(), } @@ -107,6 +121,10 @@ func keybindings(g *gocui.Gui) error { return err } + if err := ui.showBug.keybindings(g); err != nil { + return err + } + if err := ui.errorPopup.keybindings(g); err != nil { return err } |