diff options
author | Michael Muré <batolettre@gmail.com> | 2018-07-30 18:22:52 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-07-30 18:22:52 +0200 |
commit | 12b0fecd9b9bce3f4a23754a5c0121598ed2f38a (patch) | |
tree | 4e2a1c92300a6e58bff635dc03e0505bccf26215 | |
parent | bb9168f98a9dd50a7215652ab77a1c46615064cd (diff) | |
download | git-bug-12b0fecd9b9bce3f4a23754a5c0121598ed2f38a.tar.gz |
vendor gocui on the master branch because of no release in a while
-rw-r--r-- | Gopkg.lock | 6 | ||||
-rw-r--r-- | Gopkg.toml | 10 | ||||
-rw-r--r-- | termui/termui.go | 6 | ||||
-rw-r--r-- | vendor/github.com/jroimartin/gocui/AUTHORS | 6 | ||||
-rw-r--r-- | vendor/github.com/jroimartin/gocui/README.md | 31 | ||||
-rw-r--r-- | vendor/github.com/jroimartin/gocui/doc.go | 32 | ||||
-rw-r--r-- | vendor/github.com/jroimartin/gocui/edit.go | 73 | ||||
-rw-r--r-- | vendor/github.com/jroimartin/gocui/escape.go | 167 | ||||
-rw-r--r-- | vendor/github.com/jroimartin/gocui/gui.go | 372 | ||||
-rw-r--r-- | vendor/github.com/jroimartin/gocui/keybinding.go | 92 | ||||
-rw-r--r-- | vendor/github.com/jroimartin/gocui/view.go | 57 |
11 files changed, 484 insertions, 368 deletions
@@ -50,10 +50,10 @@ version = "v1.0" [[projects]] + branch = "master" name = "github.com/jroimartin/gocui" packages = ["."] - revision = "4e9ce9a8e26f2ef33dfe297dbdfca148733b6b9b" - version = "v0.3.0" + revision = "c055c87ae801372cd74a0839b972db4f7697ae5f" [[projects]] name = "github.com/mattn/go-colorable" @@ -154,6 +154,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "793e41ab4418f9961ea3328a0233107af12a470e5237888891a7d52bd125c001" + inputs-digest = "b6a56afc16a3bcbab693a8ca7ddd3112c489f2ed6b7528e7d7369fa657d188f6" solver-name = "gps-cdcl" solver-version = 1 @@ -37,12 +37,12 @@ version = "1.0.2" [[constraint]] - branch = "master" name = "github.com/shurcooL/vfsgen" + branch = "master" [[constraint]] - branch = "master" name = "github.com/skratchdot/open-golang" + branch = "master" [[constraint]] name = "github.com/fatih/color" @@ -53,9 +53,13 @@ version = "v0.0.3" [[constraint]] - branch = "master" name = "github.com/dustin/go-humanize" + branch = "master" [[constraint]] name = "github.com/vektah/gqlgen" version = "0.3.0" + +[[constraint]] + name = "github.com/jroimartin/gocui" + branch = "master"
\ No newline at end of file diff --git a/termui/termui.go b/termui/termui.go index 41212d26..8c4cd80e 100644 --- a/termui/termui.go +++ b/termui/termui.go @@ -84,12 +84,18 @@ func keybindings(g *gocui.Gui) error { if err := g.SetKeybinding("bugTable", gocui.KeyArrowLeft, gocui.ModNone, previousPage); err != nil { return err } + if err := g.SetKeybinding("bugTable", gocui.KeyPgup, gocui.ModNone, previousPage); err != nil { + return err + } if err := g.SetKeybinding("bugTable", 'l', gocui.ModNone, nextPage); err != nil { return err } if err := g.SetKeybinding("bugTable", gocui.KeyArrowRight, gocui.ModNone, nextPage); err != nil { return err } + if err := g.SetKeybinding("bugTable", gocui.KeyPgup, gocui.ModNone, nextPage); err != nil { + return err + } //err = g.SetKeybinding("bugTable", 'p', gocui.ModNone, playSelected) //err = g.SetKeybinding("bugTable", gocui.KeyEnter, gocui.ModNone, playSelectedAndExit) diff --git a/vendor/github.com/jroimartin/gocui/AUTHORS b/vendor/github.com/jroimartin/gocui/AUTHORS index acf97f47..43ec4cec 100644 --- a/vendor/github.com/jroimartin/gocui/AUTHORS +++ b/vendor/github.com/jroimartin/gocui/AUTHORS @@ -22,3 +22,9 @@ Danny Tylman <dtylman@gmail.com> Frederik Deweerdt <frederik.deweerdt@gmail.com> Colored fonts + +Henri Koski <henri.t.koski@gmail.com> + Custom current view color + +Dustin Willis Webber <dustin.webber@gmail.com> + 256-colors output mode support diff --git a/vendor/github.com/jroimartin/gocui/README.md b/vendor/github.com/jroimartin/gocui/README.md index d2e5065f..d7b55a3b 100644 --- a/vendor/github.com/jroimartin/gocui/README.md +++ b/vendor/github.com/jroimartin/gocui/README.md @@ -14,6 +14,7 @@ Minimalist Go package aimed at creating Console User Interfaces. * Mouse support. * Colored text. * Customizable edition mode. +* Easy to build reusable widgets, complex layouts... ## Installation @@ -47,13 +48,13 @@ import ( ) func main() { - g := gocui.NewGui() - if err := g.Init(); err != nil { + g, err := gocui.NewGui(gocui.OutputNormal) + if err != nil { log.Panicln(err) } defer g.Close() - g.SetLayout(layout) + g.SetManagerFunc(layout) if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) @@ -82,10 +83,28 @@ func quit(g *gocui.Gui, v *gocui.View) error { ## Screenshots -_examples/demo.go: +![r2cui](https://cloud.githubusercontent.com/assets/1223476/19418932/63645052-93ce-11e6-867c-da5e97e37237.png) ![_examples/demo.go](https://cloud.githubusercontent.com/assets/1223476/5992750/720b84f0-aa36-11e4-88ec-296fa3247b52.png) -_examples/dynamic.go: - ![_examples/dynamic.go](https://cloud.githubusercontent.com/assets/1223476/5992751/76ad5cc2-aa36-11e4-8204-6a90269db827.png) + +## Projects using gocui + +* [komanda-cli](https://github.com/mephux/komanda-cli): IRC Client For Developers. +* [vuls](https://github.com/future-architect/vuls): Agentless vulnerability scanner for Linux/FreeBSD. +* [wuzz](https://github.com/asciimoo/wuzz): Interactive cli tool for HTTP inspection. +* [httplab](https://github.com/gchaincl/httplab): Interactive web server. +* [domainr](https://github.com/MichaelThessel/domainr): Tool that checks the availability of domains based on keywords. +* [gotime](https://github.com/nanohard/gotime): Time tracker for projects and tasks. +* [claws](https://github.com/thehowl/claws): Interactive command line client for testing websockets. +* [terminews](http://github.com/antavelos/terminews): Terminal based RSS reader. +* [diagram](https://github.com/esimov/diagram): Tool to convert ascii arts into hand drawn diagrams. +* [pody](https://github.com/JulienBreux/pody): CLI app to manage Pods in a Kubernetes cluster. +* [kubexp](https://github.com/alitari/kubexp): Kubernetes client. +* [kcli](https://github.com/cswank/kcli): Tool for inspecting kafka topics/partitions/messages. +* [fac](https://github.com/mkchoi212/fac): git merge conflict resolver +* [jsonui](https://github.com/gulyasm/jsonui): Interactive JSON explorer for your terminal. +* [cointop](https://github.com/miguelmota/cointop): Interactive terminal based UI application for tracking cryptocurrencies. + +Note: if your project is not listed here, let us know! :) diff --git a/vendor/github.com/jroimartin/gocui/doc.go b/vendor/github.com/jroimartin/gocui/doc.go index 2c753c99..fe128afb 100644 --- a/vendor/github.com/jroimartin/gocui/doc.go +++ b/vendor/github.com/jroimartin/gocui/doc.go @@ -7,28 +7,29 @@ Package gocui allows to create console user interfaces. Create a new GUI: - g := gocui.NewGui() - if err := g.Init(); err != nil { + g, err := gocui.NewGui(gocui.OutputNormal) + if err != nil { // handle error } defer g.Close() - // Set layout and key bindings + // Set GUI managers and key bindings // ... if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { // handle error } -Set the layout function: +Set GUI managers: - g.SetLayout(fcn) + g.SetManager(mgr1, mgr2) -On each iteration of the GUI's main loop, the "layout function" is executed. -These layout functions can be used to set-up and update the application's main -views, being possible to freely switch between them. Also, it is important to -mention that a main loop iteration is executed on each reported event -(key-press, mouse event, window resize, etc). +Managers are in charge of GUI's layout and can be used to build widgets. On +each iteration of the GUI's main loop, the Layout function of each configured +manager is executed. Managers are used to set-up and update the application's +main views, being possible to freely change them during execution. Also, it is +important to mention that a main loop iteration is executed on each reported +event (key-press, mouse event, window resize, etc). GUIs are composed by Views, you can think of it as buffers. Views implement the io.ReadWriter interface, so you can just write to them if you want to modify @@ -68,11 +69,12 @@ Mouse events are handled like any other keybinding: } IMPORTANT: Views can only be created, destroyed or updated in three ways: from -layout functions, from keybinding callbacks or via *Gui.Execute(). The reason -for this is that it allows gocui to be conccurent-safe. So, if you want to -update your GUI from a goroutine, you must use *Gui.Execute(). For example: +the Layout function within managers, from keybinding callbacks or via +*Gui.Update(). The reason for this is that it allows gocui to be +concurrent-safe. So, if you want to update your GUI from a goroutine, you must +use *Gui.Update(). For example: - g.Execute(func(g *gocui.Gui) error { + g.Update(func(g *gocui.Gui) error { v, err := g.View("viewname") if err != nil { // handle error @@ -83,7 +85,7 @@ update your GUI from a goroutine, you must use *Gui.Execute(). For example: }) By default, gocui provides a basic edition mode. This mode can be extended -and customized creating a new Editor and assigning it to *Gui.Editor: +and customized creating a new Editor and assigning it to *View.Editor: type Editor interface { Edit(v *View, key Key, ch rune, mod Modifier) diff --git a/vendor/github.com/jroimartin/gocui/edit.go b/vendor/github.com/jroimartin/gocui/edit.go index 66d84c8d..e1b19c20 100644 --- a/vendor/github.com/jroimartin/gocui/edit.go +++ b/vendor/github.com/jroimartin/gocui/edit.go @@ -108,16 +108,9 @@ func (v *View) EditDelete(back bool) { // EditNewLine inserts a new line under the cursor. func (v *View) EditNewLine() { v.breakLine(v.cx, v.cy) - - y := v.oy + v.cy - if y >= len(v.viewLines) || (y >= 0 && y < len(v.viewLines) && - !(v.Wrap && v.cx == 0 && v.viewLines[y].linesX > 0)) { - // new line at the end of the buffer or - // cursor is not at the beginning of a wrapped line - v.ox = 0 - v.cx = 0 - v.MoveCursor(0, 1, true) - } + v.ox = 0 + v.cx = 0 + v.MoveCursor(0, 1, true) } // MoveCursor moves the cursor taking into account the width of the line/view, @@ -155,11 +148,13 @@ func (v *View) MoveCursor(dx, dy int, writeMode bool) { // adjust cursor's x position and view's x origin if x > curLineWidth { // move to next line if dx > 0 { // horizontal movement - if !v.Wrap { - v.ox = 0 - } - v.cx = 0 cy++ + if writeMode || v.oy+cy < len(v.viewLines) { + if !v.Wrap { + v.ox = 0 + } + v.cx = 0 + } } else { // vertical movement if curLineWidth > 0 { // move cursor to the EOL if v.Wrap { @@ -177,16 +172,20 @@ func (v *View) MoveCursor(dx, dy int, writeMode bool) { } } } else { - if !v.Wrap { - v.ox = 0 + if writeMode || v.oy+cy < len(v.viewLines) { + if !v.Wrap { + v.ox = 0 + } + v.cx = 0 } - v.cx = 0 } } } else if cx < 0 { if !v.Wrap && v.ox > 0 { // move origin to the left - v.ox-- + v.ox += cx + v.cx = 0 } else { // move to previous line + cy-- if prevLineWidth > 0 { if !v.Wrap { // set origin so the EOL is visible nox := prevLineWidth - maxX + 1 @@ -203,14 +202,14 @@ func (v *View) MoveCursor(dx, dy int, writeMode bool) { } v.cx = 0 } - cy-- } } else { // stay on the same line if v.Wrap { v.cx = cx } else { if cx >= maxX { - v.ox++ + v.ox += cx - maxX + 1 + v.cx = maxX } else { v.cx = cx } @@ -218,14 +217,16 @@ func (v *View) MoveCursor(dx, dy int, writeMode bool) { } // adjust cursor's y position and view's y origin - if cy >= maxY { - v.oy++ - } else if cy < 0 { + if cy < 0 { if v.oy > 0 { v.oy-- } - } else { - v.cy = cy + } else if writeMode || v.oy+cy < len(v.viewLines) { + if cy >= maxY { + v.oy++ + } else { + v.cy = cy + } } } @@ -251,22 +252,24 @@ func (v *View) writeRune(x, y int, ch rune) error { } olen := len(v.lines[y]) + + var s []cell if x >= len(v.lines[y]) { - s := make([]cell, x-len(v.lines[y])+1) - v.lines[y] = append(v.lines[y], s...) + s = make([]cell, x-len(v.lines[y])+1) + } else if !v.Overwrite { + s = make([]cell, 1) } + v.lines[y] = append(v.lines[y], s...) - c := cell{ - fgColor: v.FgColor, - bgColor: v.BgColor, - } if !v.Overwrite || (v.Overwrite && x >= olen-1) { - c.chr = '\x00' - v.lines[y] = append(v.lines[y], c) copy(v.lines[y][x+1:], v.lines[y][x:]) } - c.chr = ch - v.lines[y][x] = c + v.lines[y][x] = cell{ + fgColor: v.FgColor, + bgColor: v.BgColor, + chr: ch, + } + return nil } diff --git a/vendor/github.com/jroimartin/gocui/escape.go b/vendor/github.com/jroimartin/gocui/escape.go index 07871d52..ec31bbe0 100644 --- a/vendor/github.com/jroimartin/gocui/escape.go +++ b/vendor/github.com/jroimartin/gocui/escape.go @@ -14,6 +14,7 @@ type escapeInterpreter struct { curch rune csiParam []string curFgColor, curBgColor Attribute + mode OutputMode } type escapeState int @@ -27,7 +28,6 @@ const ( var ( errNotCSI = errors.New("Not a CSI escape sequence") - errCSINotANumber = errors.New("CSI escape sequence was expecting a number or a ;") errCSIParseError = errors.New("CSI escape sequence parsing error") errCSITooLong = errors.New("CSI escape sequence is too long") ) @@ -54,11 +54,12 @@ func (ei *escapeInterpreter) runes() []rune { // newEscapeInterpreter returns an escapeInterpreter that will be able to parse // terminal escape sequences. -func newEscapeInterpreter() *escapeInterpreter { +func newEscapeInterpreter(mode OutputMode) *escapeInterpreter { ei := &escapeInterpreter{ state: stateNone, curFgColor: ColorDefault, curBgColor: ColorDefault, + mode: mode, } return ei } @@ -71,41 +72,20 @@ func (ei *escapeInterpreter) reset() { ei.csiParam = nil } -// paramToColor returns an attribute given a terminfo coloring. -func paramToColor(p int) Attribute { - switch p { - case 0: - return ColorBlack - case 1: - return ColorRed - case 2: - return ColorGreen - case 3: - return ColorYellow - case 4: - return ColorBlue - case 5: - return ColorMagenta - case 6: - return ColorCyan - case 7: - return ColorWhite - } - return ColorDefault -} - // parseOne parses a rune. If isEscape is true, it means that the rune is part // of an escape sequence, and as such should not be printed verbatim. Otherwise, // it's not an escape sequence. func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) { - // Sanity checks to make sure we're not parsing something totally bogus. + // Sanity checks if len(ei.csiParam) > 20 { return false, errCSITooLong } if len(ei.csiParam) > 0 && len(ei.csiParam[len(ei.csiParam)-1]) > 255 { return false, errCSITooLong } + ei.curch = ch + switch ei.state { case stateNone: if ch == 0x1b { @@ -120,12 +100,16 @@ func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) { } return false, errNotCSI case stateCSI: - if ch >= '0' && ch <= '9' { - ei.state = stateParams - ei.csiParam = append(ei.csiParam, string(ch)) - return true, nil + switch { + case ch >= '0' && ch <= '9': + ei.csiParam = append(ei.csiParam, "") + case ch == 'm': + ei.csiParam = append(ei.csiParam, "0") + default: + return false, errCSIParseError } - return false, errCSINotANumber + ei.state = stateParams + fallthrough case stateParams: switch { case ch >= '0' && ch <= '9': @@ -135,34 +119,111 @@ func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) { ei.csiParam = append(ei.csiParam, "") return true, nil case ch == 'm': - if len(ei.csiParam) < 1 { - return false, errCSIParseError + var err error + switch ei.mode { + case OutputNormal: + err = ei.outputNormal() + case Output256: + err = ei.output256() } - for _, param := range ei.csiParam { - p, err := strconv.Atoi(param) - if err != nil { - return false, errCSIParseError - } - switch { - case p >= 30 && p <= 37: - ei.curFgColor = paramToColor(p - 30) - case p >= 40 && p <= 47: - ei.curBgColor = paramToColor(p - 40) - case p == 1: - ei.curFgColor |= AttrBold - case p == 4: - ei.curFgColor |= AttrUnderline - case p == 7: - ei.curFgColor |= AttrReverse - case p == 0 || p == 39: - ei.curFgColor = ColorDefault - ei.curBgColor = ColorDefault - } + if err != nil { + return false, errCSIParseError } + ei.state = stateNone ei.csiParam = nil return true, nil + default: + return false, errCSIParseError } } return false, nil } + +// outputNormal provides 8 different colors: +// black, red, green, yellow, blue, magenta, cyan, white +func (ei *escapeInterpreter) outputNormal() error { + for _, param := range ei.csiParam { + p, err := strconv.Atoi(param) + if err != nil { + return errCSIParseError + } + + switch { + case p >= 30 && p <= 37: + ei.curFgColor = Attribute(p - 30 + 1) + case p == 39: + ei.curFgColor = ColorDefault + case p >= 40 && p <= 47: + ei.curBgColor = Attribute(p - 40 + 1) + case p == 49: + ei.curBgColor = ColorDefault + case p == 1: + ei.curFgColor |= AttrBold + case p == 4: + ei.curFgColor |= AttrUnderline + case p == 7: + ei.curFgColor |= AttrReverse + case p == 0: + ei.curFgColor = ColorDefault + ei.curBgColor = ColorDefault + } + } + + return nil +} + +// output256 allows you to leverage the 256-colors terminal mode: +// 0x01 - 0x08: the 8 colors as in OutputNormal +// 0x09 - 0x10: Color* | AttrBold +// 0x11 - 0xe8: 216 different colors +// 0xe9 - 0x1ff: 24 different shades of grey +func (ei *escapeInterpreter) output256() error { + if len(ei.csiParam) < 3 { + return ei.outputNormal() + } + + mode, err := strconv.Atoi(ei.csiParam[1]) + if err != nil { + return errCSIParseError + } + if mode != 5 { + return ei.outputNormal() + } + + fgbg, err := strconv.Atoi(ei.csiParam[0]) + if err != nil { + return errCSIParseError + } + color, err := strconv.Atoi(ei.csiParam[2]) + if err != nil { + return errCSIParseError + } + + switch fgbg { + case 38: + ei.curFgColor = Attribute(color + 1) + + for _, param := range ei.csiParam[3:] { + p, err := strconv.Atoi(param) + if err != nil { + return errCSIParseError + } + + switch { + case p == 1: + ei.curFgColor |= AttrBold + case p == 4: + ei.curFgColor |= AttrUnderline + case p == 7: + ei.curFgColor |= AttrReverse + } + } + case 48: + ei.curBgColor = Attribute(color + 1) + default: + return errCSIParseError + } + + return nil +} diff --git a/vendor/github.com/jroimartin/gocui/gui.go b/vendor/github.com/jroimartin/gocui/gui.go index 39579dc4..9499d3c3 100644 --- a/vendor/github.com/jroimartin/gocui/gui.go +++ b/vendor/github.com/jroimartin/gocui/gui.go @@ -10,14 +10,6 @@ import ( "github.com/nsf/termbox-go" ) -// Handler represents a handler that can be used to update or modify the GUI. -type Handler func(*Gui) error - -// userEvent represents an event triggered by the user. -type userEvent struct { - h Handler -} - var ( // ErrQuit is used to decide if the MainLoop finished successfully. ErrQuit = errors.New("quit") @@ -26,6 +18,17 @@ var ( ErrUnknownView = errors.New("unknown view") ) +// OutputMode represents the terminal's output mode (8 or 256 colors). +type OutputMode termbox.OutputMode + +const ( + // OutputNormal provides 8-colors terminal mode. + OutputNormal = OutputMode(termbox.OutputNormal) + + // Output256 provides 256-colors terminal mode. + Output256 = OutputMode(termbox.Output256) +) + // Gui represents the whole User Interface, including the views, layouts // and keybindings. type Gui struct { @@ -33,18 +36,23 @@ type Gui struct { userEvents chan userEvent views []*View currentView *View - layout Handler + managers []Manager keybindings []*keybinding maxX, maxY int + outputMode OutputMode // BgColor and FgColor allow to configure the background and foreground // colors of the GUI. BgColor, FgColor Attribute - // SelBgColor and SelFgColor are used to configure the background and - // foreground colors of the selected line, when it is highlighted. + // SelBgColor and SelFgColor allow to configure the background and + // foreground colors of the frame of the current view. SelBgColor, SelFgColor Attribute + // If Highlight is true, Sel{Bg,Fg}Colors will be used to draw the + // frame of the current view. + Highlight bool + // If Cursor is true then the cursor is enabled. Cursor bool @@ -55,30 +63,31 @@ type Gui struct { // match any known sequence, ESC means KeyEsc. InputEsc bool - // Editor allows to define the editor that manages the edition mode, - // including keybindings or cursor behaviour. DefaultEditor is used by - // default. - Editor Editor + // If ASCII is true then use ASCII instead of unicode to draw the + // interface. Using ASCII is more portable. + ASCII bool } -// NewGui returns a new Gui object. -func NewGui() *Gui { - return &Gui{} -} - -// Init initializes the library. This function must be called before -// any other functions. -func (g *Gui) Init() error { +// NewGui returns a new Gui object with a given output mode. +func NewGui(mode OutputMode) (*Gui, error) { if err := termbox.Init(); err != nil { - return err + return nil, err } + + g := &Gui{} + + g.outputMode = mode + termbox.SetOutputMode(termbox.OutputMode(mode)) + g.tbEvents = make(chan termbox.Event, 20) g.userEvents = make(chan userEvent, 20) + g.maxX, g.maxY = termbox.Size() - g.BgColor = ColorBlack - g.FgColor = ColorWhite - g.Editor = DefaultEditor - return nil + + g.BgColor, g.FgColor = ColorDefault, ColorDefault + g.SelBgColor, g.SelFgColor = ColorDefault, ColorDefault + + return g, nil } // Close finalizes the library. It should be called after a successful @@ -94,12 +103,12 @@ func (g *Gui) Size() (x, y int) { // SetRune writes a rune at the given point, relative to the top-left // corner of the terminal. It checks if the position is valid and applies -// the gui's colors. -func (g *Gui) SetRune(x, y int, ch rune) error { +// the given colors. +func (g *Gui) SetRune(x, y int, ch rune, fgColor, bgColor Attribute) error { if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY { return errors.New("invalid point") } - termbox.SetCell(x, y, ch, termbox.Attribute(g.FgColor), termbox.Attribute(g.BgColor)) + termbox.SetCell(x, y, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor)) return nil } @@ -135,7 +144,7 @@ func (g *Gui) SetView(name string, x0, y0, x1, y1 int) (*View, error) { return v, nil } - v := newView(name, x0, y0, x1, y1) + v := newView(name, x0, y0, x1, y1, g.outputMode) v.BgColor, v.FgColor = g.BgColor, g.FgColor v.SelBgColor, v.SelFgColor = g.SelBgColor, g.SelFgColor g.views = append(g.views, v) @@ -154,6 +163,23 @@ func (g *Gui) SetViewOnTop(name string) (*View, error) { return nil, ErrUnknownView } +// SetViewOnBottom sets the given view on bottom of the existing ones. +func (g *Gui) SetViewOnBottom(name string) (*View, error) { + for i, v := range g.views { + if v.name == name { + s := append(g.views[:i], g.views[i+1:]...) + g.views = append([]*View{v}, s...) + return v, nil + } + } + return nil, ErrUnknownView +} + +// Views returns all the views in the GUI. +func (g *Gui) Views() []*View { + return g.views +} + // View returns a pointer to the view with the given name, or error // ErrUnknownView if a view with that name does not exist. func (g *Gui) View(name string) (*View, error) { @@ -168,7 +194,9 @@ func (g *Gui) View(name string) (*View, error) { // ViewByPosition returns a pointer to a view matching the given position, or // error ErrUnknownView if a view in that position does not exist. func (g *Gui) ViewByPosition(x, y int) (*View, error) { - for _, v := range g.views { + // traverse views in reverse order checking top views first + for i := len(g.views); i > 0; i-- { + v := g.views[i-1] if x > v.x0 && x < v.x1 && y > v.y0 && y < v.y1 { return v, nil } @@ -199,14 +227,14 @@ func (g *Gui) DeleteView(name string) error { } // SetCurrentView gives the focus to a given view. -func (g *Gui) SetCurrentView(name string) error { +func (g *Gui) SetCurrentView(name string) (*View, error) { for _, v := range g.views { if v.name == name { g.currentView = v - return nil + return v, nil } } - return ErrUnknownView + return nil, ErrUnknownView } // CurrentView returns the currently focused view, or nil if no view @@ -218,14 +246,14 @@ func (g *Gui) CurrentView() *View { // SetKeybinding creates a new keybinding. If viewname equals to "" // (empty string) then the keybinding will apply to all views. key must // be a rune or a Key. -func (g *Gui) SetKeybinding(viewname string, key interface{}, mod Modifier, h KeybindingHandler) error { +func (g *Gui) SetKeybinding(viewname string, key interface{}, mod Modifier, handler func(*Gui, *View) error) error { var kb *keybinding k, ch, err := getKey(key) if err != nil { return err } - kb = newKeybinding(viewname, k, ch, mod, h) + kb = newKeybinding(viewname, k, ch, mod, handler) g.keybindings = append(g.keybindings, kb) return nil } @@ -270,24 +298,54 @@ func getKey(key interface{}) (Key, rune, error) { } } -// Execute executes the given handler. This function can be called safely from -// a goroutine in order to update the GUI. It is important to note that it -// won't be executed immediately, instead it will be added to the user events -// queue. -func (g *Gui) Execute(h Handler) { - go func() { g.userEvents <- userEvent{h: h} }() +// userEvent represents an event triggered by the user. +type userEvent struct { + f func(*Gui) error } -// SetLayout sets the current layout. A layout is a function that -// will be called every time the gui is redrawn, it must contain -// the base views and its initializations. -func (g *Gui) SetLayout(layout Handler) { - g.layout = layout +// Update executes the passed function. This method can be called safely from a +// goroutine in order to update the GUI. It is important to note that the +// passed function won't be executed immediately, instead it will be added to +// the user events queue. Given that Update spawns a goroutine, the order in +// which the user events will be handled is not guaranteed. +func (g *Gui) Update(f func(*Gui) error) { + go func() { g.userEvents <- userEvent{f: f} }() +} + +// A Manager is in charge of GUI's layout and can be used to build widgets. +type Manager interface { + // Layout is called every time the GUI is redrawn, it must contain the + // base views and its initializations. + Layout(*Gui) error +} + +// The ManagerFunc type is an adapter to allow the use of ordinary functions as +// Managers. If f is a function with the appropriate signature, ManagerFunc(f) +// is an Manager object that calls f. +type ManagerFunc func(*Gui) error + +// Layout calls f(g) +func (f ManagerFunc) Layout(g *Gui) error { + return f(g) +} + +// SetManager sets the given GUI managers. It deletes all views and +// keybindings. +func (g *Gui) SetManager(managers ...Manager) { + g.managers = managers g.currentView = nil g.views = nil + g.keybindings = nil + go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }() } +// SetManagerFunc sets the given manager function. It deletes all views and +// keybindings. +func (g *Gui) SetManagerFunc(manager func(*Gui) error) { + g.SetManager(ManagerFunc(manager)) +} + // MainLoop runs the main loop until an error is returned. A successful // finish should return ErrQuit. func (g *Gui) MainLoop() error { @@ -316,7 +374,7 @@ func (g *Gui) MainLoop() error { return err } case ev := <-g.userEvents: - if err := ev.h(g); err != nil { + if err := ev.f(g); err != nil { return err } } @@ -338,7 +396,7 @@ func (g *Gui) consumeevents() error { return err } case ev := <-g.userEvents: - if err := ev.h(g); err != nil { + if err := ev.f(g); err != nil { return err } default: @@ -362,10 +420,6 @@ func (g *Gui) handleEvent(ev *termbox.Event) error { // flush updates the gui, re-drawing frames and buffers. func (g *Gui) flush() error { - if g.layout == nil { - return errors.New("Null layout") - } - termbox.Clear(termbox.Attribute(g.FgColor), termbox.Attribute(g.BgColor)) maxX, maxY := termbox.Size() @@ -377,49 +431,60 @@ func (g *Gui) flush() error { } g.maxX, g.maxY = maxX, maxY - if err := g.layout(g); err != nil { - return err + for _, m := range g.managers { + if err := m.Layout(g); err != nil { + return err + } } for _, v := range g.views { if v.Frame { - if err := g.drawFrame(v); err != nil { + var fgColor, bgColor Attribute + if g.Highlight && v == g.currentView { + fgColor = g.SelFgColor + bgColor = g.SelBgColor + } else { + fgColor = g.FgColor + bgColor = g.BgColor + } + + if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil { return err } - if err := g.drawCorners(v); err != nil { + if err := g.drawFrameCorners(v, fgColor, bgColor); err != nil { return err } if v.Title != "" { - if err := g.drawTitle(v); err != nil { + if err := g.drawTitle(v, fgColor, bgColor); err != nil { return err } } } - if err := g.draw(v); err != nil { return err } } - if err := g.drawIntersections(); err != nil { - return err - } termbox.Flush() return nil - } -// drawFrame draws the horizontal and vertical edges of a view. -func (g *Gui) drawFrame(v *View) error { +// drawFrameEdges draws the horizontal and vertical edges of a view. +func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error { + runeH, runeV := '─', '│' + if g.ASCII { + runeH, runeV = '-', '|' + } + for x := v.x0 + 1; x < v.x1 && x < g.maxX; x++ { if x < 0 { continue } if v.y0 > -1 && v.y0 < g.maxY { - if err := g.SetRune(x, v.y0, '─'); err != nil { + if err := g.SetRune(x, v.y0, runeH, fgColor, bgColor); err != nil { return err } } if v.y1 > -1 && v.y1 < g.maxY { - if err := g.SetRune(x, v.y1, '─'); err != nil { + if err := g.SetRune(x, v.y1, runeH, fgColor, bgColor); err != nil { return err } } @@ -429,12 +494,12 @@ func (g *Gui) drawFrame(v *View) error { continue } if v.x0 > -1 && v.x0 < g.maxX { - if err := g.SetRune(v.x0, y, '│'); err != nil { + if err := g.SetRune(v.x0, y, runeV, fgColor, bgColor); err != nil { return err } } if v.x1 > -1 && v.x1 < g.maxX { - if err := g.SetRune(v.x1, y, '│'); err != nil { + if err := g.SetRune(v.x1, y, runeV, fgColor, bgColor); err != nil { return err } } @@ -442,33 +507,30 @@ func (g *Gui) drawFrame(v *View) error { return nil } -// drawCorners draws the corners of the view. -func (g *Gui) drawCorners(v *View) error { - if v.x0 >= 0 && v.y0 >= 0 && v.x0 < g.maxX && v.y0 < g.maxY { - if err := g.SetRune(v.x0, v.y0, '┌'); err != nil { - return err - } +// drawFrameCorners draws the corners of the view. +func (g *Gui) drawFrameCorners(v *View, fgColor, bgColor Attribute) error { + runeTL, runeTR, runeBL, runeBR := '┌', '┐', '└', '┘' + if g.ASCII { + runeTL, runeTR, runeBL, runeBR = '+', '+', '+', '+' } - if v.x1 >= 0 && v.y0 >= 0 && v.x1 < g.maxX && v.y0 < g.maxY { - if err := g.SetRune(v.x1, v.y0, '┐'); err != nil { - return err - } - } - if v.x0 >= 0 && v.y1 >= 0 && v.x0 < g.maxX && v.y1 < g.maxY { - if err := g.SetRune(v.x0, v.y1, '└'); err != nil { - return err - } - } - if v.x1 >= 0 && v.y1 >= 0 && v.x1 < g.maxX && v.y1 < g.maxY { - if err := g.SetRune(v.x1, v.y1, '┘'); err != nil { - return err + + corners := []struct { + x, y int + ch rune + }{{v.x0, v.y0, runeTL}, {v.x1, v.y0, runeTR}, {v.x0, v.y1, runeBL}, {v.x1, v.y1, runeBR}} + + for _, c := range corners { + if c.x >= 0 && c.y >= 0 && c.x < g.maxX && c.y < g.maxY { + if err := g.SetRune(c.x, c.y, c.ch, fgColor, bgColor); err != nil { + return err + } } } return nil } // drawTitle draws the title of the view. -func (g *Gui) drawTitle(v *View) error { +func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error { if v.y0 < 0 || v.y0 >= g.maxY { return nil } @@ -480,7 +542,7 @@ func (g *Gui) drawTitle(v *View) error { } else if x > v.x1-2 || x >= g.maxX { break } - if err := g.SetRune(x, v.y0, ch); err != nil { + if err := g.SetRune(x, v.y0, ch, fgColor, bgColor); err != nil { return err } } @@ -490,21 +552,21 @@ func (g *Gui) drawTitle(v *View) error { // draw manages the cursor and calls the draw function of a view. func (g *Gui) draw(v *View) error { if g.Cursor { - if v := g.currentView; v != nil { - vMaxX, vMaxY := v.Size() - if v.cx < 0 { - v.cx = 0 - } else if v.cx >= vMaxX { - v.cx = vMaxX - 1 + if curview := g.currentView; curview != nil { + vMaxX, vMaxY := curview.Size() + if curview.cx < 0 { + curview.cx = 0 + } else if curview.cx >= vMaxX { + curview.cx = vMaxX - 1 } - if v.cy < 0 { - v.cy = 0 - } else if v.cy >= vMaxY { - v.cy = vMaxY - 1 + if curview.cy < 0 { + curview.cy = 0 + } else if curview.cy >= vMaxY { + curview.cy = vMaxY - 1 } gMaxX, gMaxY := g.Size() - cx, cy := v.x0+v.cx+1, v.y0+v.cy+1 + cx, cy := curview.x0+curview.cx+1, curview.y0+curview.cy+1 if cx >= 0 && cx < gMaxX && cy >= 0 && cy < gMaxY { termbox.SetCursor(cx, cy) } else { @@ -522,95 +584,21 @@ func (g *Gui) draw(v *View) error { return nil } -// drawIntersections draws the corners of each view, based on the type -// of the edges that converge at these points. -func (g *Gui) drawIntersections() error { - for _, v := range g.views { - if ch, ok := g.intersectionRune(v.x0, v.y0); ok { - if err := g.SetRune(v.x0, v.y0, ch); err != nil { - return err - } - } - if ch, ok := g.intersectionRune(v.x0, v.y1); ok { - if err := g.SetRune(v.x0, v.y1, ch); err != nil { - return err - } - } - if ch, ok := g.intersectionRune(v.x1, v.y0); ok { - if err := g.SetRune(v.x1, v.y0, ch); err != nil { - return err - } - } - if ch, ok := g.intersectionRune(v.x1, v.y1); ok { - if err := g.SetRune(v.x1, v.y1, ch); err != nil { - return err - } - } - } - return nil -} - -// intersectionRune returns the correct intersection rune at a given -// point. -func (g *Gui) intersectionRune(x, y int) (rune, bool) { - if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY { - return ' ', false - } - - chTop, _ := g.Rune(x, y-1) - top := verticalRune(chTop) - chBottom, _ := g.Rune(x, y+1) - bottom := verticalRune(chBottom) - chLeft, _ := g.Rune(x-1, y) - left := horizontalRune(chLeft) - chRight, _ := g.Rune(x+1, y) - right := horizontalRune(chRight) - - var ch rune - switch { - case top && bottom && left && right: - ch = '┼' - case top && bottom && !left && right: - ch = '├' - case top && bottom && left && !right: - ch = '┤' - case !top && bottom && left && right: - ch = '┬' - case top && !bottom && left && right: - ch = '┴' - default: - return ' ', false - } - return ch, true -} - -// verticalRune returns if the given character is a vertical rune. -func verticalRune(ch rune) bool { - if ch == '│' || ch == '┼' || ch == '├' || ch == '┤' { - return true - } - return false -} - -// verticalRune returns if the given character is a horizontal rune. -func horizontalRune(ch rune) bool { - if ch == '─' || ch == '┼' || ch == '┬' || ch == '┴' { - return true - } - return false -} - // onKey manages key-press events. A keybinding handler is called when // a key-press or mouse event satisfies a configured keybinding. Furthermore, // currentView's internal buffer is modified if currentView.Editable is true. func (g *Gui) onKey(ev *termbox.Event) error { switch ev.Type { case termbox.EventKey: - if err := g.execKeybindings(g.currentView, ev); err != nil { + matched, err := g.execKeybindings(g.currentView, ev) + if err != nil { return err } - if g.currentView != nil && g.currentView.Editable && g.Editor != nil { - g.Editor.Edit(g.currentView, Key(ev.Key), ev.Ch, Modifier(ev.Mod)) + if matched { + break + } + if g.currentView != nil && g.currentView.Editable && g.currentView.Editor != nil { + g.currentView.Editor.Edit(g.currentView, Key(ev.Key), ev.Ch, Modifier(ev.Mod)) } case termbox.EventMouse: mx, my := ev.MouseX, ev.MouseY @@ -621,7 +609,7 @@ func (g *Gui) onKey(ev *termbox.Event) error { if err := v.SetCursor(mx-v.x0-1, my-v.y0-1); err != nil { return err } - if err := g.execKeybindings(v, ev); err != nil { + if _, err := g.execKeybindings(v, ev); err != nil { return err } } @@ -630,17 +618,19 @@ func (g *Gui) onKey(ev *termbox.Event) error { } // execKeybindings executes the keybinding handlers that match the passed view -// and event. -func (g *Gui) execKeybindings(v *View, ev *termbox.Event) error { +// and event. The value of matched is true if there is a match and no errors. +func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err error) { + matched = false for _, kb := range g.keybindings { - if kb.h == nil { + if kb.handler == nil { continue } if kb.matchKeypress(Key(ev.Key), ev.Ch, Modifier(ev.Mod)) && kb.matchView(v) { - if err := kb.h(g, v); err != nil { - return err + if err := kb.handler(g, v); err != nil { + return false, err } + matched = true } } - return nil + return matched, nil } diff --git a/vendor/github.com/jroimartin/gocui/keybinding.go b/vendor/github.com/jroimartin/gocui/keybinding.go index 61516821..03fe677c 100644 --- a/vendor/github.com/jroimartin/gocui/keybinding.go +++ b/vendor/github.com/jroimartin/gocui/keybinding.go @@ -6,19 +6,42 @@ package gocui import "github.com/nsf/termbox-go" -type ( - // Key represents special keys or keys combinations. - Key termbox.Key +// Keybidings are used to link a given key-press event with a handler. +type keybinding struct { + viewName string + key Key + ch rune + mod Modifier + handler func(*Gui, *View) error +} - // Modifier allows to define special keys combinations. They can be used - // in combination with Keys or Runes when a new keybinding is defined. - Modifier termbox.Modifier +// newKeybinding returns a new Keybinding object. +func newKeybinding(viewname string, key Key, ch rune, mod Modifier, handler func(*Gui, *View) error) (kb *keybinding) { + kb = &keybinding{ + viewName: viewname, + key: key, + ch: ch, + mod: mod, + handler: handler, + } + return kb +} - // KeybindingHandler represents the handler linked to a specific - // keybindings. The handler is called when a key-press event satisfies a - // configured keybinding. - KeybindingHandler func(*Gui, *View) error -) +// matchKeypress returns if the keybinding matches the keypress. +func (kb *keybinding) matchKeypress(key Key, ch rune, mod Modifier) bool { + return kb.key == key && kb.ch == ch && kb.mod == mod +} + +// matchView returns if the keybinding matches the current view. +func (kb *keybinding) matchView(v *View) bool { + if kb.viewName == "" { + return true + } + return v != nil && kb.viewName == v.name +} + +// Key represents special keys or keys combinations. +type Key termbox.Key // Special keys. const ( @@ -45,9 +68,12 @@ const ( KeyArrowLeft = Key(termbox.KeyArrowLeft) KeyArrowRight = Key(termbox.KeyArrowRight) - MouseLeft = Key(termbox.MouseLeft) - MouseMiddle = Key(termbox.MouseMiddle) - MouseRight = Key(termbox.MouseRight) + MouseLeft = Key(termbox.MouseLeft) + MouseMiddle = Key(termbox.MouseMiddle) + MouseRight = Key(termbox.MouseRight) + MouseRelease = Key(termbox.MouseRelease) + MouseWheelUp = Key(termbox.MouseWheelUp) + MouseWheelDown = Key(termbox.MouseWheelDown) ) // Keys combinations. @@ -100,42 +126,12 @@ const ( KeyCtrl8 = Key(termbox.KeyCtrl8) ) +// Modifier allows to define special keys combinations. They can be used +// in combination with Keys or Runes when a new keybinding is defined. +type Modifier termbox.Modifier + // Modifiers. const ( ModNone Modifier = Modifier(0) ModAlt = Modifier(termbox.ModAlt) ) - -// Keybidings are used to link a given key-press event with a handler. -type keybinding struct { - viewName string - key Key - ch rune - mod Modifier - h KeybindingHandler -} - -// newKeybinding returns a new Keybinding object. -func newKeybinding(viewname string, key Key, ch rune, mod Modifier, h KeybindingHandler) (kb *keybinding) { - kb = &keybinding{ - viewName: viewname, - key: key, - ch: ch, - mod: mod, - h: h, - } - return kb -} - -// matchKeypress returns if the keybinding matches the keypress. -func (kb *keybinding) matchKeypress(key Key, ch rune, mod Modifier) bool { - return kb.key == key && kb.ch == ch && kb.mod == mod -} - -// matchView returns if the keybinding matches the current view. -func (kb *keybinding) matchView(v *View) bool { - if kb.viewName == "" { - return true - } - return v != nil && kb.viewName == v.name -} diff --git a/vendor/github.com/jroimartin/gocui/view.go b/vendor/github.com/jroimartin/gocui/view.go index f8d86940..42082f8c 100644 --- a/vendor/github.com/jroimartin/gocui/view.go +++ b/vendor/github.com/jroimartin/gocui/view.go @@ -41,6 +41,11 @@ type View struct { // buffer at the cursor position. Editable bool + // Editor allows to define the editor that manages the edition mode, + // including keybindings or cursor behaviour. DefaultEditor is used by + // default. + Editor Editor + // Overwrite enables or disables the overwrite mode of the view. Overwrite bool @@ -90,7 +95,7 @@ func (l lineType) String() string { } // newView returns a new View object. -func newView(name string, x0, y0, x1, y1 int) *View { +func newView(name string, x0, y0, x1, y1 int, mode OutputMode) *View { v := &View{ name: name, x0: x0, @@ -98,8 +103,9 @@ func newView(name string, x0, y0, x1, y1 int) *View { x1: x1, y1: y1, Frame: true, + Editor: DefaultEditor, tainted: true, - ei: newEscapeInterpreter(), + ei: newEscapeInterpreter(mode), } return v } @@ -292,22 +298,19 @@ func (v *View) draw() error { v.viewLines = nil for i, line := range v.lines { if v.Wrap { - if len(line) <= maxX { + if len(line) < maxX { vline := viewLine{linesX: 0, linesY: i, line: line} v.viewLines = append(v.viewLines, vline) continue } else { - vline := viewLine{linesX: 0, linesY: i, line: line[:maxX]} - v.viewLines = append(v.viewLines, vline) - } - // Append remaining lines - for n := maxX; n < len(line); n += maxX { - if len(line[n:]) <= maxX { - vline := viewLine{linesX: n, linesY: i, line: line[n:]} - v.viewLines = append(v.viewLines, vline) - } else { - vline := viewLine{linesX: n, linesY: i, line: line[n : n+maxX]} - v.viewLines = append(v.viewLines, vline) + for n := 0; n <= len(line); n += maxX { + if len(line[n:]) <= maxX { + vline := viewLine{linesX: n, linesY: i, line: line[n:]} + v.viewLines = append(v.viewLines, vline) + } else { + vline := viewLine{linesX: n, linesY: i, line: line[n : n+maxX]} + v.viewLines = append(v.viewLines, vline) + } } } } else { @@ -389,6 +392,8 @@ func (v *View) Clear() { v.tainted = true v.lines = nil + v.viewLines = nil + v.readOffset = 0 v.clearRunes() } @@ -403,6 +408,18 @@ func (v *View) clearRunes() { } } +// BufferLines returns the lines in the view's internal +// buffer. +func (v *View) BufferLines() []string { + lines := make([]string, len(v.lines)) + for i, l := range v.lines { + str := lineType(l).String() + str = strings.Replace(str, "\x00", " ", -1) + lines[i] = str + } + return lines +} + // Buffer returns a string with the contents of the view's internal // buffer. func (v *View) Buffer() string { @@ -413,6 +430,18 @@ func (v *View) Buffer() string { return strings.Replace(str, "\x00", " ", -1) } +// ViewBufferLines returns the lines in the view's internal +// buffer that is shown to the user. +func (v *View) ViewBufferLines() []string { + lines := make([]string, len(v.viewLines)) + for i, l := range v.viewLines { + str := lineType(l.line).String() + str = strings.Replace(str, "\x00", " ", -1) + lines[i] = str + } + return lines +} + // ViewBuffer returns a string with the contents of the view's buffer that is // shown to the user. func (v *View) ViewBuffer() string { |