bubbles/viewport/renderer.go
2020-06-15 21:21:08 -04:00

99 lines
2.0 KiB
Go

package viewport
import (
"bytes"
"fmt"
"io"
)
type renderer struct {
Out io.Writer
Y int
Height int
TerminalWidth int
TerminalHeight int
}
// sync paints the whole area.
func (r *renderer) sync(content string) {
r.clear()
moveTo(r.Out, r.Y, 0)
r.write(content)
}
// clear clears the viewport region.
func (r *renderer) clear() {
b := new(bytes.Buffer)
moveTo(b, r.Y, 0)
for i := 0; i < r.Height; i++ {
clearLine(b)
cursorDown(b, 1)
}
r.Out.Write(b.Bytes())
}
// write writes to the set writer.
func (r *renderer) write(s string) {
if len(s) == 0 {
return
}
buf := new(bytes.Buffer)
for _, r := range []rune(s) {
if r == '\n' {
buf.WriteString("\r\n")
continue
}
buf.WriteRune(r)
}
r.Out.Write(buf.Bytes())
}
// Effectively scroll up. That is, insert a line at the top, scrolling
// everything else down. This is roughly how ncurses does it.
func (r *renderer) insertTop(line string) {
changeScrollingRegion(r.Out, r.Y, r.Y+r.Height)
moveTo(r.Out, r.Y, 0)
insertLine(r.Out, 1)
io.WriteString(r.Out, line)
changeScrollingRegion(r.Out, r.TerminalWidth, r.TerminalHeight)
}
// Effectively scroll down. That is, insert a line at the bottom, scrolling
// everything else up. This is roughly how ncurses does it.
func (r *renderer) insertBottom(line string) {
changeScrollingRegion(r.Out, r.Y, r.Y+r.Height)
moveTo(r.Out, r.Y+r.Height, 0)
io.WriteString(r.Out, "\n"+line)
changeScrollingRegion(r.Out, r.TerminalWidth, r.TerminalHeight)
}
// Screen command
const csi = "\x1b["
func changeScrollingRegion(w io.Writer, top, bottom int) {
fmt.Fprintf(w, csi+"%d;%dr", top, bottom)
}
func moveTo(w io.Writer, row, col int) {
fmt.Fprintf(w, csi+"%d;%dH", row, col)
}
func cursorDown(w io.Writer, numLines int) {
fmt.Fprintf(w, csi+"%dB", numLines)
}
func cursorDownString(numLines int) string {
return fmt.Sprintf(csi+"%dB", numLines)
}
func clearLine(w io.Writer) {
fmt.Fprint(w, csi+"2K")
}
func insertLine(w io.Writer, numLines int) {
fmt.Fprintf(w, csi+"%dL", numLines)
}