bombadillo/cui/cui.go

190 lines
3.8 KiB
Go

package cui
import (
"bufio"
"bytes"
"fmt"
"os"
"os/exec"
"strings"
)
var Shapes = map[string]string{
"walll": "╎",
"wallr": " ",
"ceiling": " ",
"floor": " ",
"tl": "╎",
"tr": " ",
"bl": "╎",
"br": " ",
"awalll": "▌",
"awallr": "▐",
"aceiling": "▀",
"afloor": "▄",
"atl": "▞",
"atr": "▜",
"abl": "▚",
"abr": "▟",
}
func drawShape(shape string) {
if val, ok := Shapes[shape]; ok {
fmt.Printf("%s", val)
} else {
fmt.Print("x")
}
}
func moveThenDrawShape(r, c int, s string) {
MoveCursorTo(r, c)
drawShape(s)
}
func MoveCursorTo(row, col int) {
fmt.Printf("\033[%d;%dH", row, col)
}
func moveCursorToward(dir string, amount int) {
directions := map[string]string{
"up": "A",
"down": "B",
"left": "D",
"right": "C",
}
if val, ok := directions[dir]; ok {
fmt.Printf("\033[%d%s", amount, val)
}
}
func Exit() {
moveCursorToward("down", 500)
moveCursorToward("right", 500)
SetLineMode()
fmt.Print("\n")
fmt.Print("\033[?25h")
HandleAlternateScreen("rmcup")
os.Exit(0)
}
func Clear(dir string) {
directions := map[string]string{
"up": "\033[1J",
"down": "\033[0J",
"left": "\033[1K",
"right": "\033[0K",
"line": "\033[2K",
"screen": "\033[2J",
}
if val, ok := directions[dir]; ok {
fmt.Print(val)
}
}
// takes the document content (as a slice) and modifies any lines that are longer
// than the specified console width, splitting them over two lines. returns the
// amended document content as a slice.
func wrapLines(s []string, consolewidth int) []string {
// TODO redo this so that it returns a string and can hard and
// soft wrap. It will use parsing to go char by char. CUI should
// set tabs to 4 on screen init. Multispace chars should be detected
// tab spacing should be detected using % to see where the next tab stop
// is. Use a counter as building the sub buffer. len returns num of bytes
// be sure to get num of chars instead: len([]rune("a")).
indent := " " //11 spaces
out := []string{}
for _, ln := range s {
if len(ln) <= consolewidth {
out = append(out, ln)
} else {
words := strings.Split(ln, " ")
var subout bytes.Buffer
for i, wd := range words {
sublen := subout.Len()
if sublen+len(wd)+1 <= consolewidth {
//if line was indented, reinsert indent
if i == 11 && sublen == 0 {
subout.WriteString(indent)
}
if sublen > 0 {
subout.WriteString(" ")
}
subout.WriteString(wd)
if i == len(words)-1 {
out = append(out, subout.String())
}
} else {
out = append(out, subout.String())
subout.Reset()
subout.WriteString(indent + wd)
if i == len(words)-1 {
out = append(out, subout.String())
subout.Reset()
}
}
}
}
}
return out
}
func Getch() rune {
reader := bufio.NewReader(os.Stdin)
char, _, err := reader.ReadRune()
if err != nil {
return '@'
}
return char
}
func GetLine() (string, error) {
SetLineMode()
reader := bufio.NewReader(os.Stdin)
fmt.Print(": ")
text, err := reader.ReadString('\n')
if err != nil {
return "", err
}
SetCharMode()
return text[:len(text)-1], nil
}
func SetCharMode() {
cmd := exec.Command("stty", "cbreak", "-echo")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
panic(err)
}
fmt.Print("\033[?25l")
}
func SetLineMode() {
cmd := exec.Command("stty", "-cbreak", "echo")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
panic(err)
}
}
func HandleAlternateScreen(opt string) {
cmd := exec.Command("tput", opt)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
// explicitly ignoring the error here as
// the alternate screen is an optional feature
// that may not be available everywhere we expect
// to run
_ = cmd.Run()
}