You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cointop/cointop/colorscheme.go

390 lines
9.8 KiB
Go

package cointop
import (
"fmt"
"strings"
"sync"
fcolor "github.com/fatih/color"
"github.com/gdamore/tcell/v2"
)
// TODO: fix hex color support
// ColorschemeColors is a map of color string names to Attribute types
type ColorschemeColors map[string]interface{}
// ISprintf is a sprintf interface
type ISprintf func(...interface{}) string
// ColorCache is a map of color string names to sprintf functions
type ColorCache map[string]ISprintf
// Colorscheme is the struct for colorscheme
type Colorscheme struct {
colors ColorschemeColors
cache ColorCache
cacheMutex sync.RWMutex
}
var FgColorschemeColorsMap = map[string]fcolor.Attribute{
"black": fcolor.FgBlack,
"blue": fcolor.FgBlue,
"cyan": fcolor.FgCyan,
"green": fcolor.FgGreen,
"magenta": fcolor.FgMagenta,
"red": fcolor.FgRed,
"white": fcolor.FgWhite,
"yellow": fcolor.FgYellow,
}
var BgColorschemeColorsMap = map[string]fcolor.Attribute{
"black": fcolor.BgBlack,
"blue": fcolor.BgBlue,
"cyan": fcolor.BgCyan,
"green": fcolor.BgGreen,
"magenta": fcolor.BgMagenta,
"red": fcolor.BgRed,
"white": fcolor.BgWhite,
"yellow": fcolor.BgYellow,
}
// See more: vendor/github.com/mattn/go-colorable/colorable_windows.go:905
// any new color for the below mapping should be compatible with this above list
// TcellColorschemeColorsMap map colorscheme names to tcell colors
var TcellColorschemeColorsMap = map[string]tcell.Color{
"black": tcell.ColorBlack,
"blue": tcell.ColorNavy,
"cyan": tcell.ColorTeal,
"green": tcell.ColorGreen,
"magenta": tcell.ColorPurple,
"red": tcell.ColorMaroon,
"white": tcell.ColorSilver,
"yellow": tcell.ColorOlive,
}
// NewColorscheme ...
func NewColorscheme(colors ColorschemeColors) *Colorscheme {
// Build lookup table for defined values, then replace references to these
const prefix = "define_"
const reference = "$"
defines := ColorschemeColors{}
for k, v := range colors {
if strings.HasPrefix(k, prefix) {
defines[k[len(prefix):]] = v
}
}
for k, v := range colors {
if vs, ok := v.(string); ok {
if strings.HasPrefix(vs, reference) {
colors[k] = defines[vs[len(reference):]]
}
}
}
return &Colorscheme{
colors: colors,
cache: make(ColorCache),
cacheMutex: sync.RWMutex{},
}
}
func (c *Colorscheme) BaseStyle() tcell.Style {
return c.Style("base")
}
// Chart ...
func (c *Colorscheme) Chart(a ...interface{}) string {
return c.Color("chart", a...)
}
// Marketbar ...
func (c *Colorscheme) Marketbar(a ...interface{}) string {
return c.Color("marketbar", a...)
}
// MarketbarSprintf ...
func (c *Colorscheme) MarketbarSprintf() ISprintf {
return c.ToSprintf("marketbar")
}
// MarketbarChangeSprintf ...
func (c *Colorscheme) MarketbarChangeSprintf() ISprintf {
// NOTE: reusing table styles
return c.ToSprintf("table_column_change")
}
// MarketbarChangeDownSprintf ...
func (c *Colorscheme) MarketbarChangeDownSprintf() ISprintf {
// NOTE: reusing table styles
return c.ToSprintf("table_column_change_down")
}
// MarketbarChangeUpSprintf ...
func (c *Colorscheme) MarketbarChangeUpSprintf() ISprintf {
// NOTE: reusing table styles
return c.ToSprintf("table_column_change_up")
}
// MarketBarLabelActive ...
func (c *Colorscheme) MarketBarLabelActive(a ...interface{}) string {
return c.Color("marketbar_label_active", a...)
}
// Menu ...
func (c *Colorscheme) Menu(a ...interface{}) string {
return c.Color("menu", a...)
}
// MenuHeader ...
func (c *Colorscheme) MenuHeader(a ...interface{}) string {
return c.Color("menu_header", a...)
}
// MenuLabel ...
func (c *Colorscheme) MenuLabel(a ...interface{}) string {
return c.Color("menu_label", a...)
}
// MenuLabelActive ...
func (c *Colorscheme) MenuLabelActive(a ...interface{}) string {
return c.Color("menu_label_active", a...)
}
// Searchbar ...
func (c *Colorscheme) Searchbar(a ...interface{}) string {
return c.Color("searchbar", a...)
}
// Statusbar ...
func (c *Colorscheme) Statusbar(a ...interface{}) string {
return c.Color("statusbar", a...)
}
// TableColumnPrice ...
func (c *Colorscheme) TableColumnPrice(a ...interface{}) string {
return c.Color("table_column_price", a...)
}
// TableColumnPriceSprintf ...
func (c *Colorscheme) TableColumnPriceSprintf() ISprintf {
return c.ToSprintf("table_column_price")
}
// TableColumnChange ...
func (c *Colorscheme) TableColumnChange(a ...interface{}) string {
return c.Color("table_column_change", a...)
}
// TableColumnChangeSprintf ...
func (c *Colorscheme) TableColumnChangeSprintf() ISprintf {
return c.ToSprintf("table_column_change")
}
// TableColumnChangeDown ...
func (c *Colorscheme) TableColumnChangeDown(a ...interface{}) string {
return c.Color("table_column_change_down", a...)
}
// TableColumnChangeDownSprintf ...
func (c *Colorscheme) TableColumnChangeDownSprintf() ISprintf {
return c.ToSprintf("table_column_change_down")
}
// TableColumnChangeUp ...
func (c *Colorscheme) TableColumnChangeUp(a ...interface{}) string {
return c.Color("table_column_change_up", a...)
}
// TableColumnChangeUpSprintf ...
func (c *Colorscheme) TableColumnChangeUpSprintf() ISprintf {
return c.ToSprintf("table_column_change_up")
}
// TableHeader ...
func (c *Colorscheme) TableHeader(a ...interface{}) string {
return c.Color("table_header", a...)
}
// TableHeaderSprintf ...
func (c *Colorscheme) TableHeaderSprintf() ISprintf {
return c.ToSprintf("table_header")
}
// TableHeaderColumnActive ...
func (c *Colorscheme) TableHeaderColumnActive(a ...interface{}) string {
return c.Color("table_header_column_active", a...)
}
// TableHeaderColumnActiveSprintf ...
func (c *Colorscheme) TableHeaderColumnActiveSprintf() ISprintf {
return c.ToSprintf("table_header_column_active")
}
// TableRow ...
func (c *Colorscheme) TableRow(a ...interface{}) string {
return c.Color("table_row", a...)
}
// TableRowSprintf ...
func (c *Colorscheme) TableRowSprintf() ISprintf {
return c.ToSprintf("table_row")
}
// TableRowActive ...
func (c *Colorscheme) TableRowActive(a ...interface{}) string {
return c.Color("table_row_active", a...)
}
// TableRowFavorite ...
func (c *Colorscheme) TableRowFavorite(a ...interface{}) string {
return c.Color("table_row_favorite", a...)
}
// TableRowFavoriteSprintf ...
func (c *Colorscheme) TableRowFavoriteSprintf() ISprintf {
return c.ToSprintf("table_row_favorite")
}
// Default ...
func (c *Colorscheme) Default(a ...interface{}) string {
return fmt.Sprintf(a[0].(string), a[1:]...)
}
func (c *Colorscheme) ToSprintf(name string) ISprintf {
c.cacheMutex.Lock()
defer c.cacheMutex.Unlock()
if cached, ok := c.cache[name]; ok {
return cached
}
// TODO: use c.Style(name)?
var attrs []fcolor.Attribute
if v, ok := c.colors[name+"_fg"].(string); ok {
if fg, ok := c.ToFgAttr(v); ok {
attrs = append(attrs, fg)
} else {
color := tcell.GetColor(v)
if color != tcell.ColorDefault {
// 24-bit foreground 38;2;⟨r⟩;⟨g⟩;⟨b⟩
r, g, b := color.RGB()
attrs = append(attrs, 38)
attrs = append(attrs, 2)
attrs = append(attrs, fcolor.Attribute(r))
attrs = append(attrs, fcolor.Attribute(g))
attrs = append(attrs, fcolor.Attribute(b))
}
}
}
if v, ok := c.colors[name+"_bg"].(string); ok {
if bg, ok := c.ToBgAttr(v); ok {
attrs = append(attrs, bg)
} else {
color := tcell.GetColor(v)
if color != tcell.ColorDefault {
// 24-bit background 48;2;⟨r⟩;⟨g⟩;⟨b⟩
r, g, b := color.RGB()
attrs = append(attrs, 48)
attrs = append(attrs, 2)
attrs = append(attrs, fcolor.Attribute(r))
attrs = append(attrs, fcolor.Attribute(g))
attrs = append(attrs, fcolor.Attribute(b))
}
}
}
if v, ok := c.colors[name+"_bold"].(bool); ok {
if bold, ok := c.ToBoldAttr(v); ok {
attrs = append(attrs, bold)
}
}
if v, ok := c.colors[name+"_underline"].(bool); ok {
if underline, ok := c.ToUnderlineAttr(v); ok {
attrs = append(attrs, underline)
}
}
c.cache[name] = fcolor.New(attrs...).SprintFunc()
return c.cache[name]
}
func (c *Colorscheme) Color(name string, a ...interface{}) string {
return c.ToSprintf(name)(a...)
}
func (c *Colorscheme) Style(name string) tcell.Style {
st := tcell.StyleDefault
st = st.Foreground(c.tcellColor(name + "_fg"))
st = st.Background(c.tcellColor(name + "_bg"))
if v, ok := c.colors[name+"_bold"].(bool); ok {
st = st.Bold(v)
}
if v, ok := c.colors[name+"_underline"].(bool); ok {
st = st.Underline(v)
}
// TODO: Blink Dim Italic Reverse Strikethrough
return st
}
// tcellColor can supply for types of color name: specific mapped name, tcell color name, hex
// Examples: black, honeydew, #000000
func (c *Colorscheme) tcellColor(name string) tcell.Color {
v, ok := c.colors[name].(string)
if !ok {
return tcell.ColorDefault
}
if color, found := TcellColorschemeColorsMap[v]; found {
return color
}
color := tcell.GetColor(v)
if color != tcell.ColorDefault {
return color
}
// find closest X11 color to RGB
// if code, ok := HexToAnsi(v); ok {
// return tcell.PaletteColor(int(code) & 0xff)
// }
return color
}
func (c *Colorscheme) ToFgAttr(v string) (fcolor.Attribute, bool) {
if attr, ok := FgColorschemeColorsMap[v]; ok {
return attr, true
}
// find closest X11 color to RGB
// if code, ok := HexToAnsi(v); ok {
// return fcolor.Attribute(code), true
// }
return 0, false
}
func (c *Colorscheme) ToBgAttr(v string) (fcolor.Attribute, bool) {
if attr, ok := BgColorschemeColorsMap[v]; ok {
return attr, true
}
// find closest X11 color to RGB
// if code, ok := HexToAnsi(v); ok {
// return fcolor.Attribute(code), true
// }
return 0, false
}
// ToBoldAttr converts a boolean to an Attribute type
func (c *Colorscheme) ToBoldAttr(v bool) (fcolor.Attribute, bool) {
return fcolor.Bold, v
}
// ToUnderlineAttr converts a boolean to an Attribute type
func (c *Colorscheme) ToUnderlineAttr(v bool) (fcolor.Attribute, bool) {
return fcolor.Underline, v
}