From b4bf8f6c5a77c409f1532057768b767103aa4c66 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Thu, 2 Nov 2017 16:45:10 -0600 Subject: [PATCH] basic box drawing and moving in gim --- gim/main.go | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 gim/main.go diff --git a/gim/main.go b/gim/main.go new file mode 100644 index 0000000..6b61378 --- /dev/null +++ b/gim/main.go @@ -0,0 +1,280 @@ +package main + +import ( + "fmt" + "math/rand" + "os" + "os/signal" + "strings" + "time" + + "github.com/buger/goterm" +) + +const ( + // Reset all custom styles + ansiReset = "\033[0m" + + // Reset to default color + ansiResetColor = "\033[32m" + + // Return curor to start of line and clean it + ansiResetLine = "\r\033[K" +) + +// List of possible colors +const ( + black = iota + red + green + yellow + blue + magenta + cyan + white +) + +func getFgColor(code int) string { + return fmt.Sprintf("\033[3%dm", code) +} + +func getBgColor(code int) string { + return fmt.Sprintf("\033[4%dm", code) +} + +func fgColor(str string, color int) string { + return fmt.Sprintf("%s%s%s", getFgColor(color), str, ansiReset) +} + +func bgColor(str string, color int) string { + return fmt.Sprintf("%s%s%s", getBgColor(color), str, ansiReset) +} + +type xy [2]int + +func (p xy) x() int { + return p[0] +} + +func (p xy) y() int { + return p[1] +} + +func (p xy) add(p2 xy) xy { + p[0] += p2[0] + p[1] += p2[1] + return p +} + +//////////////////////////////////////////////////////////////////////////////// + +type terminal struct { + cursorPos xy +} + +func (t *terminal) moveAbs(to xy) { + t.cursorPos = to + goterm.MoveCursor(to.x()+1, to.y()+1) +} + +func (t *terminal) size() xy { + return xy{goterm.Width(), goterm.Height()} +} + +//////////////////////////////////////////////////////////////////////////////// + +const ( + boxBorderHoriz = iota + boxBorderVert + boxBorderTL + boxBorderTR + boxBorderBL + boxBorderBR +) + +var boxDefault = []string{ + "─", + "│", + "┌", + "┐", + "└", + "┘", +} + +type box struct { + pos xy + size xy // if unset, auto-determined + body string + + transparent bool +} + +func (b box) lines() []string { + lines := strings.Split(b.body, "\n") + // if the last line is empty don't include it, it means there was a trailing + // newline (or the whole string is empty) + if lines[len(lines)-1] == "" { + lines = lines[:len(lines)-1] + } + return lines +} + +func (b box) getSize() xy { + if b.size != (xy{}) { + return b.size + } + var size xy + for _, line := range b.lines() { + size[1]++ + if l := len(line); l > size[0] { + size[0] = l + } + } + return size +} + +func (b box) draw(term *terminal) { + chars := boxDefault + pos := b.pos + size := b.getSize() + w, h := size.x(), size.y() + + // draw top line + term.moveAbs(pos) + goterm.Print(chars[boxBorderTL]) + for i := 0; i < w; i++ { + goterm.Print(chars[boxBorderHoriz]) + } + goterm.Print(chars[boxBorderTR]) + + drawLine := func(line string) { + pos[1]++ + term.moveAbs(pos) + goterm.Print(chars[boxBorderVert]) + if len(line) > w { + line = line[:w] + } + goterm.Print(line) + if b.transparent { + term.moveAbs(pos.add(xy{w + 1, 0})) + } else { + goterm.Print(strings.Repeat(" ", w-len(line))) + } + goterm.Print(chars[boxBorderVert]) + } + + // truncate lines if necessary + lines := b.lines() + if len(lines) > h { + lines = lines[:h] + } + + // draw body + for _, line := range lines { + drawLine(line) + } + + // draw empty lines + for i := 0; i < h-len(lines); i++ { + drawLine("") + } + + // draw bottom line + pos[1]++ + term.moveAbs(pos) + goterm.Print(chars[boxBorderBL]) + for i := 0; i < w; i++ { + goterm.Print(chars[boxBorderHoriz]) + } + goterm.Print(chars[boxBorderBR]) +} + +//////////////////////////////////////////////////////////////////////////////// + +const ( + framerate = 30 + frameperiod = time.Second / time.Duration(framerate) +) + +func debugf(str string, args ...interface{}) { + fmt.Fprintf(os.Stderr, str, args...) +} + +func main() { + rand.Seed(time.Now().UnixNano()) + { // exit signal handling, cause ctrl-c doesn't work with goterm otherwise + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + go func() { + <-c + goterm.Clear() + goterm.Flush() + os.Stdout.Sync() + os.Exit(0) + }() + } + + term := new(terminal) + + type movingBox struct { + box + xRight bool + yDown bool + } + + randBox := func() movingBox { + tsize := term.size() + return movingBox{ + box: box{ + pos: xy{rand.Intn(tsize[0]), rand.Intn(tsize[1])}, + }, + xRight: rand.Intn(1) == 0, + yDown: rand.Intn(1) == 0, + } + } + + boxes := []movingBox{ + randBox(), + randBox(), + randBox(), + randBox(), + randBox(), + } + + for range time.Tick(frameperiod) { + goterm.Clear() + + termSize := term.size() + now := time.Now() + for i := range boxes { + b := &boxes[i] + b.body = fmt.Sprintf("%d\n%s", now.Unix(), now.String()) + + size := b.getSize() + if b.pos[0] <= 0 { + b.xRight = true + } else if b.pos[0]+size[0]+2 > termSize[0] { + b.xRight = false + } + if b.pos[1] <= 0 { + b.yDown = true + } else if b.pos[1]+size[1]+2 > termSize[1] { + b.yDown = false + } + + if b.xRight { + b.pos[0] += 3 + } else { + b.pos[0] -= 3 + } + if b.yDown { + b.pos[1]++ + } else { + b.pos[1]-- + } + + b.draw(term) + } + goterm.Flush() + } +}