tt/typer.go
2020-12-22 16:54:05 -05:00

232 lines
4.6 KiB
Go

package main
import (
"fmt"
"os"
"time"
"github.com/gdamore/tcell"
)
type typer struct {
Scr tcell.Screen
OnStart func()
currentWordStyle tcell.Style
nextWordStyle tcell.Style
incorrectSpaceStyle tcell.Style
incorrectStyle tcell.Style
correctStyle tcell.Style
backgroundStyle tcell.Style
}
func NewTyper(scr tcell.Screen, fgcol, bgcol, hicol, hicol2, hicol3, errcol tcell.Color) *typer {
def := tcell.StyleDefault.
Foreground(fgcol).
Background(bgcol)
return &typer{
Scr: scr,
backgroundStyle: def,
correctStyle: def.Foreground(hicol),
currentWordStyle: def.Foreground(hicol2),
nextWordStyle: def.Foreground(hicol3),
incorrectStyle: def.Foreground(errcol),
incorrectSpaceStyle: def.Background(errcol),
}
}
func (t *typer) highlight(text []cell, idx int, currentWordStyle, nextWorkStyle tcell.Style) {
for ; idx < len(text) && text[idx].c != ' '; idx++ {
text[idx].style = currentWordStyle
}
for ; idx < len(text) && text[idx].c == ' '; idx++ {
}
for ; idx < len(text) && text[idx].c != ' '; idx++ {
text[idx].style = nextWorkStyle
}
}
func (t *typer) Start(text []string) (nerrs, ncorrect int, tim time.Duration, complete bool) {
var startTime time.Time
for i, p := range text {
var fn func() = nil
if i == 0 {
fn = func() {
startTime = time.Now()
}
}
e, c, completed := t.start(p, fn)
nerrs += e
ncorrect += c
if !completed {
tim = time.Now().Sub(startTime)
complete = false
return
}
}
tim = time.Now().Sub(startTime)
complete = true
return
}
func (t *typer) start(s string, onStart func()) (int, int, bool) {
started := false
text := stringToCells(s)
for i, _ := range text {
text[i].style = t.backgroundStyle
}
fmt.Printf("\033[5 q")
//Assumes original cursor shape was a block (the one true cursor shape), there doesn't appear to be a
//good way to save/restore the shape if the user has changed it from the otcs.
defer fmt.Printf("\033[2 q")
t.Scr.SetStyle(t.backgroundStyle)
idx := 0
redraw := func() {
//Potentially inefficient, but seems to be good enough
drawCellsAtCenter(t.Scr, text, idx)
t.Scr.Show()
}
deleteWord := func() {
t.highlight(text, idx, t.backgroundStyle, t.backgroundStyle)
if idx == 0 {
return
}
idx--
for idx > 0 && (text[idx].c == ' ' || text[idx].c == '\n') {
text[idx].style = t.backgroundStyle
idx--
}
for idx > 0 && text[idx].c != ' ' && text[idx].c != '\n' {
text[idx].style = t.backgroundStyle
idx--
}
if text[idx].c == ' ' || text[idx].c == '\n' {
idx++
}
t.highlight(text, idx, t.currentWordStyle, t.nextWordStyle)
}
for {
t.highlight(text, idx, t.currentWordStyle, t.nextWordStyle)
redraw()
ev := t.Scr.PollEvent()
switch ev := ev.(type) {
case *tcell.EventResize:
t.Scr.Sync()
t.Scr.Clear()
case *tcell.EventKey:
if !started {
if onStart != nil {
onStart()
}
started = true
}
switch ev.Key() {
case tcell.KeyCtrlC:
fmt.Printf("\033[2 q")
t.Scr.Fini()
os.Exit(1)
case tcell.KeyEscape:
return -1, -1, false
case tcell.KeyCtrlL:
t.Scr.Sync()
case tcell.KeyCtrlH:
deleteWord()
case tcell.KeyBackspace2:
if ev.Modifiers() == tcell.ModAlt {
deleteWord()
} else {
t.highlight(text, idx, t.backgroundStyle, t.backgroundStyle)
if idx == 0 {
break
}
if idx < len(text) {
text[idx].style = t.backgroundStyle
}
idx--
for idx > 0 && text[idx].c == '\n' {
text[idx].style = t.backgroundStyle
idx--
}
t.highlight(text, idx, t.currentWordStyle, t.nextWordStyle)
}
case tcell.KeyRune:
if idx < len(text) {
switch {
case ev.Rune() == text[idx].c:
text[idx].style = t.correctStyle
idx++
case ev.Rune() == ' ':
for idx < len(text) && text[idx].c != ' ' && text[idx].c != '\n' {
text[idx].style = t.incorrectStyle
idx++
}
if idx < len(text) {
text[idx].style = t.incorrectSpaceStyle
idx++
}
case text[idx].c == ' ':
text[idx].style = t.incorrectSpaceStyle
idx++
default:
text[idx].style = t.incorrectStyle
idx++
}
for idx < len(text) && text[idx].c == '\n' {
idx++
}
t.highlight(text, idx, t.currentWordStyle, t.nextWordStyle)
}
if idx == len(text) {
nerrs := 0
for _, c := range text {
if c.style == t.incorrectStyle || c.style == t.incorrectSpaceStyle {
nerrs++
}
}
ncorrect := len(text) - nerrs
t.Scr.Clear()
return nerrs, ncorrect, true
}
}
}
}
}