sshpong/internal/ansii/ansii.go

136 lines
3.2 KiB
Go

package ansii
import (
"fmt"
"os"
"strings"
"golang.org/x/term"
)
type ANSI string
const (
reset ANSI = "\033[0m"
plain ANSI = ""
bold ANSI = "\033[1m"
underline ANSI = "\033[4m"
black ANSI = "\033[30m"
red ANSI = "\033[31m"
green ANSI = "\033[32m"
yellow ANSI = "\033[33m"
blue ANSI = "\033[34m"
purple ANSI = "\033[35m"
cyan ANSI = "\033[36m"
white ANSI = "\033[37m"
blackBg ANSI = "\033[40m"
redBg ANSI = "\033[41m"
greenBg ANSI = "\033[42m"
yellowBg ANSI = "\033[43m"
blueBg ANSI = "\033[44m"
purpleBg ANSI = "\033[45m"
cyanBg ANSI = "\033[46m"
whiteBg ANSI = "\033[47m"
clearScreen ANSI = "\033[2J"
hideCursor ANSI = "\033[?25l"
showCursor ANSI = "\033[?25h"
)
type Offset struct {
X int
Y int
}
type style struct {
Reset ANSI
Plain ANSI
Bold ANSI
Underline ANSI
}
type color struct {
Black ANSI
Red ANSI
Green ANSI
Yellow ANSI
Blue ANSI
Purple ANSI
Cyan ANSI
White ANSI
}
type screen struct {
ClearScreen ANSI
HideCursor ANSI
ShowCursor ANSI
}
type ascii struct {
Block string
}
func GetTermSize() (width int, height int) {
var fd int = int(os.Stdout.Fd())
width, height, err := term.GetSize(fd)
if err != nil {
fmt.Println(string(Screen.ClearScreen) + "Fatal: error getting terminal size.")
os.Exit(1)
}
return width, height
}
func MakeTermRaw() (*term.State, error) {
var fd int = int(os.Stdout.Fd())
return term.MakeRaw(fd)
}
func RestoreTerm(prev *term.State) error {
var fd int = int(os.Stdout.Fd())
return term.Restore(fd, prev)
}
func (s screen) PlaceCursor(offset Offset) ANSI {
return ANSI(fmt.Sprintf("\033[%d;%dH", offset.Y, offset.X))
}
var (
Styles = style{Bold: bold, Underline: underline, Reset: reset, Plain: plain}
Colors = color{Red: red, Green: green, Yellow: yellow, Blue: blue, Purple: purple, Cyan: cyan, White: white}
Screen = screen{ClearScreen: clearScreen, HideCursor: hideCursor, ShowCursor: showCursor}
Blocks = ascii{Block: "█"}
)
// Draws a box of dimensions `height` and `width` at `offset`.
// The `offset` is the top left cell of the square.
// Blocks that would be placed off screen are clipped.
func DrawBox(builder *strings.Builder, offset Offset, height int, width int, style ANSI) {
builder.WriteString(string(style))
for hIdx := 0; hIdx < height; hIdx++ {
if hIdx == 0 || hIdx == height-1 {
for wIdx := 0; wIdx < width; wIdx++ {
DrawPixel(builder, Offset{X: offset.X + wIdx, Y: offset.Y + hIdx})
}
} else {
DrawPixel(builder, Offset{X: offset.X, Y: offset.Y + hIdx})
DrawPixel(builder, Offset{X: offset.X + width - 1, Y: offset.Y + hIdx})
}
}
builder.WriteString(string(Styles.Reset))
return
}
func DrawPixel(builder *strings.Builder, offset Offset) {
termWidth, termHeight := GetTermSize()
if offset.X > termWidth || offset.Y > termHeight || offset.X < 0 || offset.Y < 0 {
return
}
builder.WriteString(string(Screen.PlaceCursor(offset) + ANSI(Blocks.Block)))
}
func DrawPixelStyle(builder *strings.Builder, offset Offset, style ANSI) {
builder.WriteString(string(style))
DrawPixel(builder, offset)
builder.WriteString(string(Styles.Reset))
}