From 53a3d5e11355ec6bed2e27bc26459a50e435b643 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 12 Aug 2018 02:42:03 +0200 Subject: termui: add and remove labels --- termui/error_popup.go | 5 +-- termui/input_popup.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ termui/show_bug.go | 75 +++++++++++++++++++++++++++++++++++++++- termui/termui.go | 16 +++++++-- 4 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 termui/input_popup.go diff --git a/termui/error_popup.go b/termui/error_popup.go index 855bd05d..f8f53feb 100644 --- a/termui/error_popup.go +++ b/termui/error_popup.go @@ -2,6 +2,7 @@ package termui import ( "fmt" + "github.com/MichaelMure/git-bug/util" "github.com/jroimartin/gocui" ) @@ -38,7 +39,7 @@ func (ep *errorPopup) layout(g *gocui.Gui) error { width := minInt(30, maxX) wrapped, nblines := util.WordWrap(ep.message, width-2) - height := minInt(nblines+2, maxY) + height := minInt(nblines+1, maxY) x0 := (maxX - width) / 2 y0 := (maxY - height) / 2 @@ -66,6 +67,6 @@ func (ep *errorPopup) close(g *gocui.Gui, v *gocui.View) error { return g.DeleteView(errorPopupView) } -func (ep *errorPopup) activate(message string) { +func (ep *errorPopup) Activate(message string) { ep.message = message } diff --git a/termui/input_popup.go b/termui/input_popup.go new file mode 100644 index 00000000..00e602e5 --- /dev/null +++ b/termui/input_popup.go @@ -0,0 +1,96 @@ +package termui + +import ( + "io/ioutil" + + "github.com/jroimartin/gocui" +) + +const inputPopupView = "inputPopupView" + +type inputPopup struct { + active bool + title string + c chan string +} + +func newInputPopup() *inputPopup { + return &inputPopup{} +} + +func (ip *inputPopup) keybindings(g *gocui.Gui) error { + // Close + if err := g.SetKeybinding(inputPopupView, gocui.KeyEsc, gocui.ModNone, ip.close); err != nil { + return err + } + + // Validate + if err := g.SetKeybinding(inputPopupView, gocui.KeyEnter, gocui.ModNone, ip.validate); err != nil { + return err + } + + return nil +} + +func (ip *inputPopup) layout(g *gocui.Gui) error { + if !ip.active { + return nil + } + + maxX, maxY := g.Size() + + width := minInt(30, maxX) + height := 2 + x0 := (maxX - width) / 2 + y0 := (maxY - height) / 2 + + v, err := g.SetView(inputPopupView, x0, y0, x0+width, y0+height) + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + + v.Frame = true + v.Title = ip.title + v.Editable = true + } + + if _, err := g.SetCurrentView(inputPopupView); err != nil { + return err + } + + return nil +} + +func (ip *inputPopup) close(g *gocui.Gui, v *gocui.View) error { + ip.title = "" + ip.active = false + return g.DeleteView(inputPopupView) +} + +func (ip *inputPopup) validate(g *gocui.Gui, v *gocui.View) error { + ip.title = "" + + content, err := ioutil.ReadAll(v) + if err != nil { + return err + } + + ip.title = "" + ip.active = false + err = g.DeleteView(inputPopupView) + if err != nil { + return err + } + + ip.c <- string(content) + + return nil +} + +func (ip *inputPopup) Activate(title string) <-chan string { + ip.title = title + ip.active = true + ip.c = make(chan string) + return ip.c +} diff --git a/termui/show_bug.go b/termui/show_bug.go index 040a7b5f..ab0fd472 100644 --- a/termui/show_bug.go +++ b/termui/show_bug.go @@ -90,8 +90,15 @@ func (sb *showBug) layout(g *gocui.Gui) error { sb.childViews = append(sb.childViews, showBugInstructionView) v.Frame = false v.BgColor = gocui.ColorBlue + } + + v.Clear() + fmt.Fprintf(v, "[q] Save and return [←,h] Left [↓,j] Down [↑,k] Up [→,l] Right ") - fmt.Fprintf(v, "[q] Save and return [c] Comment [t] Change title [↓,j] Down [↑,k] Up") + if sb.isOnSide { + fmt.Fprint(v, "[a] Add label [r] Remove label") + } else { + fmt.Fprint(v, "[c] Comment [t] Change title") } _, err = g.SetViewOnTop(showBugInstructionView) @@ -170,6 +177,14 @@ func (sb *showBug) keybindings(g *gocui.Gui) error { } // Labels + if err := g.SetKeybinding(showBugView, 'a', gocui.ModNone, + sb.addLabel); err != nil { + return err + } + if err := g.SetKeybinding(showBugView, 'r', gocui.ModNone, + sb.removeLabel); err != nil { + return err + } return nil } @@ -563,3 +578,61 @@ func (sb *showBug) comment(g *gocui.Gui, v *gocui.View) error { func (sb *showBug) setTitle(g *gocui.Gui, v *gocui.View) error { return setTitleWithEditor(sb.bug) } + +func (sb *showBug) addLabel(g *gocui.Gui, v *gocui.View) error { + c := ui.inputPopup.Activate("Add labels") + + go func() { + input := <-c + + labels := strings.FieldsFunc(input, func(r rune) bool { + return r == ' ' || r == ',' + }) + + err := sb.bug.ChangeLabels(trimLabels(labels), nil) + if err != nil { + ui.errorPopup.Activate(err.Error()) + } + + g.Update(func(gui *gocui.Gui) error { + return nil + }) + }() + + return nil +} + +func (sb *showBug) removeLabel(g *gocui.Gui, v *gocui.View) error { + c := ui.inputPopup.Activate("Remove labels") + + go func() { + input := <-c + + labels := strings.FieldsFunc(input, func(r rune) bool { + return r == ' ' || r == ',' + }) + + err := sb.bug.ChangeLabels(nil, trimLabels(labels)) + if err != nil { + ui.errorPopup.Activate(err.Error()) + } + + g.Update(func(gui *gocui.Gui) error { + return nil + }) + }() + + return nil +} + +func trimLabels(labels []string) []string { + var result []string + + for _, label := range labels { + trimmed := strings.TrimSpace(label) + if len(trimmed) > 0 { + result = append(result, trimmed) + } + } + return result +} diff --git a/termui/termui.go b/termui/termui.go index d2585c4b..535842e4 100644 --- a/termui/termui.go +++ b/termui/termui.go @@ -20,6 +20,7 @@ type termUI struct { bugTable *bugTable showBug *showBug errorPopup *errorPopup + inputPopup *inputPopup } func (tui *termUI) activateWindow(window window) error { @@ -49,6 +50,7 @@ func Run(repo repository.Repo) error { bugTable: newBugTable(c), showBug: newShowBug(c), errorPopup: newErrorPopup(), + inputPopup: newInputPopup(), } ui.activeWindow = ui.bugTable @@ -108,6 +110,10 @@ func layout(g *gocui.Gui) error { return err } + if err := ui.inputPopup.layout(g); err != nil { + return err + } + return nil } @@ -129,6 +135,10 @@ func keybindings(g *gocui.Gui) error { return err } + if err := ui.inputPopup.keybindings(g); err != nil { + return err + } + return nil } @@ -156,7 +166,7 @@ func newBugWithEditor(repo cache.RepoCacher) error { } if err == input.ErrEmptyTitle { - ui.errorPopup.activate("Empty title, aborting.") + ui.errorPopup.Activate("Empty title, aborting.") } else { _, err := repo.NewBug(title, message) if err != nil { @@ -189,7 +199,7 @@ func addCommentWithEditor(bug cache.BugCacher) error { } if err == input.ErrEmptyMessage { - ui.errorPopup.activate("Empty message, aborting.") + ui.errorPopup.Activate("Empty message, aborting.") } else { err := bug.AddComment(message) if err != nil { @@ -222,7 +232,7 @@ func setTitleWithEditor(bug cache.BugCacher) error { } if err == input.ErrEmptyTitle { - ui.errorPopup.activate("Empty title, aborting.") + ui.errorPopup.Activate("Empty title, aborting.") } else { err := bug.SetTitle(title) if err != nil { -- cgit