erm/app/darktile/termutil/theme.go

258 lines
5.1 KiB
Go
Raw Permalink Normal View History

2021-07-30 22:29:20 +00:00
package termutil
import (
"fmt"
"image/color"
"strconv"
2024-02-07 23:43:18 +00:00
"tuxpa.in/t/erm/app/darktile/config"
2021-07-30 22:29:20 +00:00
)
type Colour uint8
// See https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
const (
ColourBlack Colour = iota
ColourRed
ColourGreen
ColourYellow
ColourBlue
ColourMagenta
ColourCyan
ColourWhite
ColourBrightBlack
ColourBrightRed
ColourBrightGreen
ColourBrightYellow
ColourBrightBlue
ColourBrightMagenta
ColourBrightCyan
ColourBrightWhite
ColourBackground
ColourForeground
ColourSelectionBackground
ColourSelectionForeground
ColourCursorForeground
ColourCursorBackground
)
type Theme struct {
colourMap map[Colour]color.Color
}
var (
2024-02-07 23:43:18 +00:00
// https://www.ditig.com/publications/256-colors-cheat-sheet
xtermColors = map[string]Colour{
"color0": ColourBlack,
"color1": ColourRed,
"color2": ColourGreen,
"color3": ColourYellow,
"color4": ColourBlue,
"color5": ColourMagenta,
"color6": ColourCyan,
"color7": ColourWhite,
"color8": ColourBrightBlack,
"color9": ColourBrightRed,
"color10": ColourBrightGreen,
"color11": ColourBrightYellow,
"color12": ColourBrightBlue,
"color13": ColourBrightMagenta,
"color14": ColourBrightCyan,
"color15": ColourBrightWhite,
"foreground": ColourForeground,
"background": ColourBackground,
"cursorColor": ColourCursorBackground,
"cursorColor2": ColourCursorForeground,
"highlightColor": ColourSelectionBackground,
"highlightTextColor": ColourSelectionForeground,
}
2021-07-30 22:29:20 +00:00
map4Bit = map[uint8]Colour{
30: ColourBlack,
31: ColourRed,
32: ColourGreen,
33: ColourYellow,
34: ColourBlue,
35: ColourMagenta,
36: ColourCyan,
37: ColourWhite,
90: ColourBrightBlack,
91: ColourBrightRed,
92: ColourBrightGreen,
93: ColourBrightYellow,
94: ColourBrightBlue,
95: ColourBrightMagenta,
96: ColourBrightCyan,
97: ColourBrightWhite,
40: ColourBlack,
41: ColourRed,
42: ColourGreen,
43: ColourYellow,
44: ColourBlue,
45: ColourMagenta,
46: ColourCyan,
47: ColourWhite,
100: ColourBrightBlack,
101: ColourBrightRed,
102: ColourBrightGreen,
103: ColourBrightYellow,
104: ColourBrightBlue,
105: ColourBrightMagenta,
106: ColourBrightCyan,
107: ColourBrightWhite,
}
)
func (t *Theme) ColourFrom4Bit(code uint8) color.Color {
colour, ok := map4Bit[code]
if !ok {
return color.Black
}
return t.colourMap[colour]
}
func (t *Theme) DefaultBackground() color.Color {
c, ok := t.colourMap[ColourBackground]
if !ok {
return color.RGBA{0, 0, 0, 0xff}
2021-07-30 22:29:20 +00:00
}
return c
}
func (t *Theme) DefaultForeground() color.Color {
c, ok := t.colourMap[ColourForeground]
if !ok {
return color.RGBA{255, 255, 255, 0xff}
2021-07-30 22:29:20 +00:00
}
return c
}
func (t *Theme) SelectionBackground() color.Color {
c, ok := t.colourMap[ColourSelectionBackground]
if !ok {
return color.RGBA{0, 0, 0, 0xff}
2021-07-30 22:29:20 +00:00
}
return c
}
func (t *Theme) SelectionForeground() color.Color {
c, ok := t.colourMap[ColourSelectionForeground]
if !ok {
return color.RGBA{255, 255, 255, 0xff}
2021-07-30 22:29:20 +00:00
}
return c
}
func (t *Theme) CursorBackground() color.Color {
c, ok := t.colourMap[ColourCursorBackground]
if !ok {
return color.RGBA{255, 255, 255, 0xff}
2021-07-30 22:29:20 +00:00
}
return c
}
func (t *Theme) CursorForeground() color.Color {
c, ok := t.colourMap[ColourCursorForeground]
if !ok {
return color.RGBA{0, 0, 0, 0xff}
2021-07-30 22:29:20 +00:00
}
return c
}
func (t *Theme) ColourFrom8Bit(n string) (color.Color, error) {
index, err := strconv.Atoi(n)
if err != nil {
return nil, err
}
if index < 16 {
return t.colourMap[Colour(index)], nil
}
if index >= 232 {
c := ((index - 232) * 0xff) / 0x18
return color.RGBA{
R: byte(c),
G: byte(c),
B: byte(c),
A: 0xff,
2021-07-30 22:29:20 +00:00
}, nil
}
var colour color.RGBA
colour.A = 0xff
2021-07-30 22:29:20 +00:00
indexR := ((index - 16) / 36)
if indexR > 0 {
colour.R = uint8(55 + indexR*40)
}
indexG := (((index - 16) % 36) / 6)
if indexG > 0 {
colour.G = uint8(55 + indexG*40)
}
indexB := ((index - 16) % 6)
if indexB > 0 {
colour.B = uint8(55 + indexB*40)
}
return colour, nil
}
func (t *Theme) ColourFrom24Bit(r, g, b string) (color.Color, error) {
ri, err := strconv.Atoi(r)
if err != nil {
return nil, err
}
gi, err := strconv.Atoi(g)
if err != nil {
return nil, err
}
bi, err := strconv.Atoi(b)
if err != nil {
return nil, err
}
return color.RGBA{
R: byte(ri),
G: byte(gi),
B: byte(bi),
A: 0xff,
2021-07-30 22:29:20 +00:00
}, nil
}
func (t *Theme) ColourFromAnsi(ansi []string, bg bool) (color.Color, error) {
if len(ansi) == 0 {
return nil, fmt.Errorf("invalid ansi colour code")
}
switch ansi[0] {
case "2":
if len(ansi) != 4 {
return nil, fmt.Errorf("invalid 24-bit ansi colour code")
}
return t.ColourFrom24Bit(ansi[1], ansi[2], ansi[3])
case "5":
if len(ansi) != 2 {
return nil, fmt.Errorf("invalid 8-bit ansi colour code")
}
return t.ColourFrom8Bit(ansi[1])
default:
return nil, fmt.Errorf("invalid ansi colour code")
}
}
2024-02-07 23:43:18 +00:00
func ThemeFromLark(c *config.Lark) *Theme {
tf := NewThemeFactory()
for k, v := range xtermColors {
val, err := c.Color("colors", k)
if err == nil {
tf = tf.WithColour(v, val)
}
}
theme := tf.Build()
return theme
}