Last active
April 4, 2017 09:30
-
-
Save christophberger/08c26260a10525c90168beb8c9cb0f49 to your computer and use it in GitHub Desktop.
Code from github.com/appliedgo/tui
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
// Under normal circumstances, importing two UI libraries at the | |
// same time is probably not a good idea, as each has its own event | |
// loop. This demo code takes care of using only one of these | |
// libraries at a time. | |
import ( | |
"fmt" | |
"log" | |
"os" | |
// Both TUI packages are abbreviated to avoid making the code | |
// overly verbose. | |
t "github.com/gizak/termui" | |
c "github.com/jroimartin/gocui" | |
"github.com/pkg/errors" | |
) | |
const ( | |
// List box width. | |
lw = 20 | |
// Input box height. | |
ih = 3 | |
) | |
// Items to fill the list with. | |
var listItems = []string{ | |
"Line 1", | |
"Line 2", | |
"Line 3", | |
"Line 4", | |
"Line 5", | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func runTermui() { | |
// Initialize termui. | |
err := t.Init() | |
if err != nil { | |
log.Fatalln("Cannot initialize termui") | |
} | |
// `termui` needs some cleanup when terminating. | |
defer t.Close() | |
// Get the height of the terminal. | |
th := t.TermHeight() | |
// The list block | |
lb := t.NewList() | |
lb.Height = th | |
lb.BorderLabel = "List" | |
lb.BorderLabelFg = t.ColorGreen | |
lb.BorderFg = t.ColorGreen | |
lb.ItemFgColor = t.ColorWhite | |
lb.Items = listItems | |
// The input block. termui has no edit box yet, but at the time of | |
// this writing, there is an open [pull request](https://github.com/gizak/termui/pull/129) for adding | |
// a text input widget. | |
ib := t.NewPar("") | |
ib.Height = ih | |
ib.BorderLabel = "Input" | |
ib.BorderLabelFg = t.ColorYellow | |
ib.BorderFg = t.ColorYellow | |
ib.TextFgColor = t.ColorWhite | |
// The Output block. | |
ob := t.NewPar("\nPress Ctrl-C to quit") | |
ob.Height = th - ih | |
ob.BorderLabel = "Output" | |
ob.BorderLabelFg = t.ColorCyan | |
ob.BorderFg = t.ColorCyan | |
ob.TextFgColor = t.ColorWhite | |
// Now we need to create the layout. The blocks have gotten a size | |
// but no position. A grid layout puts everything into place. | |
// t.Body is a pre-defined grid. We add one row that contains | |
// two columns. | |
// | |
// The grid uses a 12-column system, so we have to give a "span" | |
// parameter to each column that specifies how many grid column | |
// each column occupies. | |
t.Body.AddRows( | |
t.NewRow( | |
t.NewCol(3, 0, lb), | |
t.NewCol(9, 0, ob, ib))) | |
// Render the grid. | |
t.Body.Align() | |
t.Render(t.Body) | |
// When the window resizes, the grid must adopt to the new size. | |
// We use a hander func for this. | |
t.Handle("/sys/wnd/resize", func(t.Event) { | |
// Update the heights of list box and output box. | |
lb.Height = t.TermHeight() | |
ob.Height = t.TermHeight() - ih | |
t.Body.Width = t.TermWidth() | |
t.Body.Align() | |
t.Render(t.Body) | |
}) | |
// | |
// We need a way out. Ctrl-C shall stop the event loop. | |
t.Handle("/sys/kbd/C-c", func(t.Event) { | |
t.StopLoop() | |
}) | |
// start the event loop. | |
t.Loop() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func runGocui() { | |
// Create a new GUI. | |
g, err := c.NewGui(c.OutputNormal) | |
if err != nil { | |
log.Println("Failed to create a GUI:", err) | |
return | |
} | |
defer g.Close() | |
// Activate the cursor for the current view. | |
g.Cursor = true | |
// The GUI object wants to know how to manage the layout. | |
// Unlike `termui`, `gocui` does not use | |
// a grid layout. Instead, it relies on a custom layout handler function | |
// to manage the layout. | |
// Here we set the layout manager to a function named `layout` | |
// that is defined further down. | |
g.SetManagerFunc(layout) | |
// Bind the `quit` handler function (also defined further down) to Ctrl-C, | |
// so that we can leave the application at any time. | |
err = g.SetKeybinding("", c.KeyCtrlC, c.ModNone, quit) | |
if err != nil { | |
log.Println("Could not set key binding:", err) | |
return | |
} | |
// Now let's define the views. | |
// The terminal's width and height are needed for layout calculations. | |
tw, th := g.Size() | |
// First, create the list view. | |
lv, err := g.SetView("list", 0, 0, lw, th-1) | |
// ErrUnknownView is not a real error condition. | |
// It just says that the view did not exist before and needs initialization. | |
if err != nil && err != c.ErrUnknownView { | |
log.Println("Failed to create main view:", err) | |
return | |
} | |
lv.Title = "List" | |
lv.FgColor = c.ColorCyan | |
// Then the output view. | |
ov, err := g.SetView("output", lw+1, 0, tw-1, th-ih-1) | |
if err != nil && err != c.ErrUnknownView { | |
log.Println("Failed to create output view:", err) | |
return | |
} | |
ov.Title = "Output" | |
ov.FgColor = c.ColorGreen | |
// Let the view scroll if the output exceeds the visible area. | |
ov.Autoscroll = true | |
_, err = fmt.Fprintln(ov, "Press Ctrl-c to quit") | |
if err != nil { | |
log.Println("Failed to print into output view:", err) | |
} | |
// And finally the input view. | |
iv, err := g.SetView("input", lw+1, th-ih, tw-1, th-1) | |
if err != nil && err != c.ErrUnknownView { | |
log.Println("Failed to create input view:", err) | |
return | |
} | |
iv.Title = "Input" | |
iv.FgColor = c.ColorYellow | |
// The input view shall be editable. | |
iv.Editable = true | |
err = iv.SetCursor(0, 0) | |
if err != nil { | |
log.Println("Failed to set cursor:", err) | |
return | |
} | |
// Make the enter key copy the input to the output. | |
err = g.SetKeybinding("input", c.KeyEnter, c.ModNone, func(g *c.Gui, iv *c.View) error { | |
// We want to read the view's buffer from the beginning. | |
iv.Rewind() | |
// Get the output view via its name. | |
ov, e := g.View("output") | |
if e != nil { | |
log.Println("Cannot get output view:", e) | |
return e | |
} | |
// Thanks to views being an io.Writer, we can simply Fprint to a view. | |
_, e = fmt.Fprint(ov, iv.Buffer()) | |
if e != nil { | |
log.Println("Cannot print to output view:", e) | |
} | |
// Clear the input view | |
iv.Clear() | |
// Put the cursor back to the start. | |
e = iv.SetCursor(0, 0) | |
if e != nil { | |
log.Println("Failed to set cursor:", e) | |
} | |
return e | |
}) | |
if err != nil { | |
log.Println("Cannot bind the enter key:", err) | |
} | |
// Fill the list view. | |
for _, s := range listItems { | |
// Again, we can simply Fprint to a view. | |
_, err = fmt.Fprintln(lv, s) | |
if err != nil { | |
log.Println("Error writing to the list view:", err) | |
return | |
} | |
} | |
// Set the focus to the input view. | |
_, err = g.SetCurrentView("input") | |
if err != nil { | |
log.Println("Cannot set focus to input view:", err) | |
} | |
// Start the main loop. | |
err = g.MainLoop() | |
log.Println("Main loop has finished:", err) | |
} | |
// The layout handler calculates all sizes depending | |
// on the current terminal size. | |
func layout(g *c.Gui) error { | |
// Get the current terminal size. | |
tw, th := g.Size() | |
// Update the views according to the new terminal size. | |
_, err := g.SetView("list", 0, 0, lw, th-1) | |
if err != nil { | |
return errors.Wrap(err, "Cannot update list view") | |
} | |
_, err = g.SetView("output", lw+1, 0, tw-1, th-ih-1) | |
if err != nil { | |
return errors.Wrap(err, "Cannot update output view") | |
} | |
_, err = g.SetView("input", lw+1, th-ih, tw-1, th-1) | |
if err != nil { | |
return errors.Wrap(err, "Cannot update input view.") | |
} | |
return nil | |
} | |
// `quit` is a handler that gets bound to Ctrl-C. | |
// It signals the main loop to exit. | |
func quit(g *c.Gui, v *c.View) error { | |
return c.ErrQuit | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func main() { | |
if len(os.Args) <= 1 { | |
log.Println("Usage: go run tui.go [termui|gocui]") | |
return | |
} | |
if os.Args[1] == "termui" { | |
runTermui() | |
return | |
} | |
if os.Args[1] == "gocui" { | |
runGocui() | |
return | |
} | |
log.Println("No such option:", os.Args[0]) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment