1
0
This repository has been archived on 2023-12-27. You can view files and clone it, but cannot push or open issues or pull requests.
beginning-go/gui.go
Tony Grosinger dc4dc2b967 Added unit tests
Code changes improve the testability of the code, breaking out
functionality into pure functions wherever possible.
2017-11-11 10:34:05 -08:00

235 lines
4.5 KiB
Go

package main
import (
"fmt"
"os"
"strings"
"github.com/jroimartin/gocui"
)
//
// This file does not need to be edited!
//
// Please feel free to dig through this file if you are curious, however the
// contents are fully implemented, so no edits are required to arrive at a
// functional chat client.
//
var (
gui *gocui.Gui
logsVisible = false
)
func runGUI(cl ClientList) {
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
fmt.Println("Fatal GUI error: ", err)
os.Exit(1)
}
defer gui.Close()
gui = g
// Set GUI managers and key bindings
gui.Cursor = true
gui.SetManagerFunc(layout)
err = gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit)
if err != nil {
fmt.Println("Fatal GUI error: ", err)
os.Exit(1)
}
err = gui.SetKeybinding("", gocui.KeyCtrlL, gocui.ModNone, toggleLogs)
if err != nil {
fmt.Println("Fatal GUI error: ", err)
os.Exit(1)
}
err = gui.SetKeybinding("enter-text", gocui.KeyEnter, gocui.ModNone, readGuiMsg)
if err != nil {
fmt.Println("Fatal GUI error: ", err)
os.Exit(1)
}
// We will update the client list after the GUI is initialized because we
// need to print the name of the initial client we connected to when
// creating Smudge.
// If this is skipped, we will not see the initial node connected until
// another node is added or removed.
printClientList(cl)
if err := gui.MainLoop(); err != nil && err != gocui.ErrQuit {
fmt.Println("Fatal GUI error: ", err)
os.Exit(1)
}
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
helpY := maxY - 2
chatX := 25
if maxX < 25 {
// Support for small terminals
chatX = 15
}
chatMaxY := maxY - 6
if v, err := g.SetView("logs", 3, 2, maxX-3, chatMaxY-2); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Logs"
v.Autoscroll = true
v.Wrap = true
_, err = g.SetViewOnBottom("logs")
if err != nil {
return err
}
}
if v, err := g.SetView("help", 0, helpY, maxX, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Frame = false
fmt.Fprintf(v, "%s %s %s %s %s %s",
frameText("Ctrl-L"), "Toggle Logs",
frameText("Ctrl-C"), "Quit",
frameText("Enter"), "Send Message")
}
if v, err := g.SetView("clients", 0, 0, chatX-1, helpY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Clients"
}
if v, err := g.SetView("messages", chatX, 0, maxX-1, chatMaxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Autoscroll = true
v.Wrap = true
v.Title = "Message-History"
}
if v, err := g.SetView("enter-text", chatX, chatMaxY+1, maxX-1, helpY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
if _, err := g.SetCurrentView("enter-text"); err != nil {
return err
}
v.Title = "Send:"
v.Editable = true
v.Wrap = true
}
return nil
}
func readGuiMsg(g *gocui.Gui, v *gocui.View) error {
msgText := v.Buffer()
v.Clear()
if err := v.SetCursor(0, 0); err != nil {
return err
}
return SendMessage(msgText)
}
func printChatMessage(msg, sender string) {
gui.Update(func(g *gocui.Gui) error {
v, err := g.View("messages")
if err != nil {
return err
}
fmt.Fprintf(v, "%s: %s\n", sender, msg)
return nil
})
}
// printClientList takes a ClientList and prints the username or NodeAddress for
// each entry into the clients section of the UI.
func printClientList(cl ClientList) {
if gui == nil {
return
}
gui.Update(func(g *gocui.Gui) error {
v, err := g.View("clients")
if err != nil {
return err
}
v.Clear()
err = v.SetCursor(0, 0)
if err != nil {
return err
}
for _, client := range cl {
fmt.Fprintln(v, client.GetName())
}
return nil
})
}
func printLogs(msg string) {
if gui == nil {
fmt.Println(msg)
} else {
gui.Update(func(g *gocui.Gui) error {
v, err := g.View("logs")
if err != nil {
return err
}
fmt.Fprintln(v, msg)
return nil
})
}
}
func toggleLogs(g *gocui.Gui, v *gocui.View) error {
if logsVisible {
_, err := g.SetViewOnBottom("logs")
if err != nil {
return err
}
} else {
_, err := g.SetViewOnTop("logs")
if err != nil {
return err
}
}
logsVisible = !logsVisible
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
func stringFormatBoth(fg, bg int, str string, args []string) string {
return fmt.Sprintf("\x1b[48;5;%dm\x1b[38;5;%d;%sm%s\x1b[0m", bg, fg, strings.Join(args, ";"), str)
}
// Frame text with colors
func frameText(text string) string {
return stringFormatBoth(15, 0, text, []string{"1"})
}