383 lines
4.5 KiB
Go
383 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gdamore/tcell"
|
|
)
|
|
|
|
type cell struct {
|
|
c rune
|
|
style tcell.Style
|
|
}
|
|
|
|
func wordWrapBytes(s []byte, n int) {
|
|
sp := 0
|
|
sz := 0
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
sz++
|
|
|
|
if s[i] == '\n' {
|
|
s[i] = ' '
|
|
}
|
|
|
|
if s[i] == ' ' {
|
|
sp = i
|
|
}
|
|
|
|
if sz > n {
|
|
if sp != 0 {
|
|
s[sp] = '\n'
|
|
}
|
|
|
|
sz = i - sp
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func wordWrap(s string, n int) string {
|
|
r := []byte(s)
|
|
wordWrapBytes(r, n)
|
|
return string(r)
|
|
}
|
|
|
|
func init() {
|
|
rand.Seed(time.Now().Unix())
|
|
}
|
|
|
|
func randomText(n int, ncols int) string {
|
|
r := ""
|
|
|
|
words := []string{
|
|
"the",
|
|
"be",
|
|
"of",
|
|
"and",
|
|
"a",
|
|
"to",
|
|
"in",
|
|
"he",
|
|
"have",
|
|
"it",
|
|
"that",
|
|
"for",
|
|
"they",
|
|
"I",
|
|
"with",
|
|
"as",
|
|
"not",
|
|
"on",
|
|
"she",
|
|
"at",
|
|
"by",
|
|
"this",
|
|
"we",
|
|
"you",
|
|
"do",
|
|
"but",
|
|
"from",
|
|
"or",
|
|
"which",
|
|
"one",
|
|
"would",
|
|
"all",
|
|
"will",
|
|
"there",
|
|
"say",
|
|
"who",
|
|
"make",
|
|
"when",
|
|
"can",
|
|
"more",
|
|
"if",
|
|
"no",
|
|
"man",
|
|
"out",
|
|
"other",
|
|
"so",
|
|
"what",
|
|
"time",
|
|
"up",
|
|
"go",
|
|
"about",
|
|
"than",
|
|
"into",
|
|
"could",
|
|
"state",
|
|
"only",
|
|
"new",
|
|
"year",
|
|
"some",
|
|
"take",
|
|
"come",
|
|
"these",
|
|
"know",
|
|
"see",
|
|
"use",
|
|
"get",
|
|
"like",
|
|
"then",
|
|
"first",
|
|
"any",
|
|
"work",
|
|
"now",
|
|
"may",
|
|
"such",
|
|
"give",
|
|
"over",
|
|
"think",
|
|
"most",
|
|
"even",
|
|
"find",
|
|
"day",
|
|
"also",
|
|
"after",
|
|
"way",
|
|
"many",
|
|
"must",
|
|
"look",
|
|
"before",
|
|
"great",
|
|
"back",
|
|
"through",
|
|
"long",
|
|
"where",
|
|
"much",
|
|
"should",
|
|
"well",
|
|
"people",
|
|
"down",
|
|
"own",
|
|
"just",
|
|
"because",
|
|
"good",
|
|
"each",
|
|
"those",
|
|
"feel",
|
|
"seem",
|
|
"how",
|
|
"high",
|
|
"too",
|
|
"place",
|
|
"little",
|
|
"world",
|
|
"very",
|
|
"still",
|
|
"nation",
|
|
"hand",
|
|
"old",
|
|
"life",
|
|
"tell",
|
|
"write",
|
|
"become",
|
|
"here",
|
|
"show",
|
|
"house",
|
|
"both",
|
|
"between",
|
|
"need",
|
|
"mean",
|
|
"call",
|
|
"develop",
|
|
"under",
|
|
"last",
|
|
"right",
|
|
"move",
|
|
"thing",
|
|
"general",
|
|
"school",
|
|
"never",
|
|
"same",
|
|
"another",
|
|
"begin",
|
|
"while",
|
|
"number",
|
|
"part",
|
|
"turn",
|
|
"real",
|
|
"leave",
|
|
"might",
|
|
"want",
|
|
"point",
|
|
"form",
|
|
"off",
|
|
"child",
|
|
"few",
|
|
"small",
|
|
"since",
|
|
"against",
|
|
"ask",
|
|
"late",
|
|
"home",
|
|
"interest",
|
|
"large",
|
|
"person",
|
|
"end",
|
|
"open",
|
|
"public",
|
|
"follow",
|
|
"during",
|
|
"present",
|
|
"without",
|
|
"again",
|
|
"hold",
|
|
"govern",
|
|
"around",
|
|
"possible",
|
|
"head",
|
|
"consider",
|
|
"word",
|
|
"program",
|
|
"problem",
|
|
"however",
|
|
"lead",
|
|
"system",
|
|
"set",
|
|
"order",
|
|
"eye",
|
|
"plan",
|
|
"run",
|
|
"keep",
|
|
"face",
|
|
"fact",
|
|
"group",
|
|
"play",
|
|
"stand",
|
|
"increase",
|
|
"early",
|
|
"course",
|
|
"change",
|
|
"help",
|
|
"line",
|
|
}
|
|
|
|
for i := 0; i < n; i++ {
|
|
r += words[rand.Int()%len(words)]
|
|
if i != n-1 {
|
|
r += " "
|
|
}
|
|
}
|
|
|
|
return strings.Replace(wordWrap(r, ncols), "\n", " \n", -1)
|
|
}
|
|
|
|
func stringToCells(s string) []cell {
|
|
a := make([]cell, len(s))
|
|
s = strings.TrimRight(s, "\n ")
|
|
|
|
len := 0
|
|
for _, r := range s {
|
|
a[len].c = r
|
|
a[len].style = tcell.StyleDefault
|
|
len++
|
|
}
|
|
|
|
return a[:len]
|
|
}
|
|
|
|
func drawString(scr tcell.Screen, x, y int, s string, cursorIdx int, style tcell.Style) {
|
|
sx := x
|
|
|
|
for i, c := range s {
|
|
if c == '\n' {
|
|
y++
|
|
x = sx
|
|
} else {
|
|
scr.SetContent(x, y, c, nil, style)
|
|
if i == cursorIdx {
|
|
scr.ShowCursor(x, y)
|
|
}
|
|
|
|
x++
|
|
}
|
|
}
|
|
|
|
if cursorIdx == len(s) {
|
|
scr.ShowCursor(x, y)
|
|
}
|
|
}
|
|
|
|
func drawCells(scr tcell.Screen, x, y int, s []cell, cursorIdx int) {
|
|
sx := x
|
|
|
|
for i, c := range s {
|
|
if c.c == '\n' {
|
|
y++
|
|
x = sx
|
|
} else {
|
|
scr.SetContent(x, y, c.c, nil, c.style)
|
|
if i == cursorIdx {
|
|
scr.ShowCursor(x, y)
|
|
}
|
|
|
|
x++
|
|
}
|
|
}
|
|
|
|
if cursorIdx == len(s) {
|
|
scr.ShowCursor(x, y)
|
|
}
|
|
}
|
|
|
|
func drawStringAtCenter(scr tcell.Screen, s string, style tcell.Style) {
|
|
nc, nr := calcStringDimensions(s)
|
|
sw, sh := scr.Size()
|
|
|
|
x := (sw - nc) / 2
|
|
y := (sh - nr) / 2
|
|
|
|
drawString(scr, x, y, s, -1, style)
|
|
}
|
|
|
|
func calcStringDimensions(s string) (nc, nr int) {
|
|
c := 0
|
|
|
|
for _, x := range s {
|
|
if x == '\n' {
|
|
nr++
|
|
if c > nc {
|
|
nc = c
|
|
}
|
|
c = 0
|
|
} else {
|
|
c++
|
|
}
|
|
}
|
|
|
|
nr++
|
|
if c > nc {
|
|
nc = c
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func newTcellColor(s string) tcell.Color {
|
|
if len(s) != 7 || s[0] != '#' {
|
|
panic(fmt.Errorf("%s is not a valid hex color", s))
|
|
}
|
|
|
|
tonum := func(c byte) int32 {
|
|
if c > '9' {
|
|
if c >= 'a' {
|
|
return (int32)(c - 'a' + 10)
|
|
} else {
|
|
return (int32)(c - 'A' + 10)
|
|
}
|
|
} else {
|
|
return (int32)(c - '0')
|
|
}
|
|
}
|
|
|
|
r := tonum(s[1])<<4 | tonum(s[2])
|
|
g := tonum(s[3])<<4 | tonum(s[4])
|
|
b := tonum(s[5])<<4 | tonum(s[6])
|
|
|
|
return tcell.NewRGBColor(r, g, b)
|
|
}
|