diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2021-07-31 12:54:28 +1000 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2021-07-31 17:33:13 +1000 |
commit | 117c0bd4f7adea7afb845107623b1b3d9b4e96fa (patch) | |
tree | 96cfe200a0a2f7aaef6cefdeb56a01cfddb78f95 /pkg/gui/style | |
parent | 79848087bccd5c87af1dbb44a39753aad1346f8b (diff) |
simplify code a bit
Diffstat (limited to 'pkg/gui/style')
-rw-r--r-- | pkg/gui/style/basic.go | 173 | ||||
-rw-r--r-- | pkg/gui/style/color.go | 35 | ||||
-rw-r--r-- | pkg/gui/style/decoration.go | 53 | ||||
-rw-r--r-- | pkg/gui/style/rgb.go | 111 | ||||
-rw-r--r-- | pkg/gui/style/style.go | 151 | ||||
-rw-r--r-- | pkg/gui/style/style_test.go | 38 |
6 files changed, 244 insertions, 317 deletions
diff --git a/pkg/gui/style/basic.go b/pkg/gui/style/basic.go index 3e2c7a067..5cb1ec8aa 100644 --- a/pkg/gui/style/basic.go +++ b/pkg/gui/style/basic.go @@ -1,147 +1,98 @@ package style import ( - "fmt" - "github.com/gookit/color" ) -type BasicTextStyle struct { - fg color.Color - bg color.Color - opts []color.Color - - style color.Style +type TextStyle struct { + fg *Color + bg *Color + decoration Decoration } -func (b BasicTextStyle) Sprint(a ...interface{}) string { - return b.style.Sprint(a...) +type Sprinter interface { + Sprint(a ...interface{}) string + Sprintf(format string, a ...interface{}) string } -func (b BasicTextStyle) Sprintf(format string, a ...interface{}) string { - return b.style.Sprintf(format, a...) +func (b TextStyle) Sprint(a ...interface{}) string { + return b.deriveStyle().Sprint(a...) } -func (b BasicTextStyle) deriveStyle() BasicTextStyle { - // b.style[:0] makes sure to use the same slice memory - if b.fg == 0 { - // Fg is most of the time defined so we reverse the check - b.style = b.style[:0] - } else { - b.style = append(b.style[:0], b.fg) - } - - if b.bg != 0 { - b.style = append(b.style, b.bg) - } +func (b TextStyle) Sprintf(format string, a ...interface{}) string { + return b.deriveStyle().Sprintf(format, a...) +} - b.style = append(b.style, b.opts...) +func (b TextStyle) SetBold() TextStyle { + b.decoration.SetBold() return b } -func (b BasicTextStyle) setOpt(opt color.Color, v bool, deriveIfChanged bool) BasicTextStyle { - if v { - // Add value - for _, listOpt := range b.opts { - if listOpt == opt { - // Option already added - return b - } - } - - b.opts = append(b.opts, opt) - } else { - // Remove value - for idx, listOpt := range b.opts { - if listOpt == opt { - b.opts = append(b.opts[:idx], b.opts[idx+1:]...) - - if deriveIfChanged { - return b.deriveStyle() - } - return b - } - } - } - - if deriveIfChanged { - return b.deriveStyle() - } +func (b TextStyle) SetUnderline() TextStyle { + b.decoration.SetUnderline() return b } -func (b BasicTextStyle) SetBold(v bool) TextStyle { - return b.setOpt(color.OpBold, v, true) +func (b TextStyle) SetReverse() TextStyle { + b.decoration.SetReverse() + return b } -func (b BasicTextStyle) SetReverse(v bool) TextStyle { - return b.setOpt(color.OpReverse, v, true) +func (b TextStyle) deriveStyle() Sprinter { + // TODO: consider caching + return deriveStyle(b.fg, b.bg, b.decoration) } -func (b BasicTextStyle) SetUnderline(v bool) TextStyle { - return b.setOpt(color.OpUnderscore, v, true) -} +func deriveStyle(fg *Color, bg *Color, decoration Decoration) Sprinter { + if fg == nil && bg == nil { + return color.Style(decoration.ToOpts()) + } -func (b BasicTextStyle) SetRGBColor(red, green, blue uint8, background bool) TextStyle { - return b.convertToRGB().SetRGBColor(red, green, blue, background) -} + isRgb := (fg != nil && fg.IsRGB()) || (bg != nil && bg.IsRGB()) + if isRgb { + s := &color.RGBStyle{} + if fg != nil { + s.SetFg(*fg.ToRGB().rgb) + } + if bg != nil { + s.SetBg(*bg.ToRGB().rgb) + } + s.SetOpts(decoration.ToOpts()) + return s + } + + style := make([]color.Color, 0, 5) -func (b BasicTextStyle) convertToRGB() RGBTextStyle { - res := RGBTextStyle{ - fg: b.fg.RGB(), - fgSet: b.fg != 0, - opts: b.opts, + if fg != nil { + style = append(style, *fg.basic) } - if b.bg != 0 { - // Need to convert bg to fg otherwise .RGB wont work - // for more info see https://github.com/gookit/color/issues/39 - rgbBg := (b.bg - 10).RGB() - rgbBg[3] = 1 - res.bg = &rgbBg - res.style = *color.NewRGBStyle(res.fg, rgbBg) - } else { - res.style = *color.NewRGBStyle(res.fg) + if bg != nil { + style = append(style, *bg.basic) } - res.style.SetOpts(b.opts) - return res -} + style = append(style, decoration.ToOpts()...) -func (b BasicTextStyle) SetColor(other TextStyle) TextStyle { - switch typedOther := other.(type) { - case BasicTextStyle: - if typedOther.fg != 0 { - b.fg = typedOther.fg - } - if typedOther.bg != 0 { - b.bg = typedOther.bg - } - for _, opt := range typedOther.opts { - b = b.setOpt(opt, true, false) - } - return b.deriveStyle() - case RGBTextStyle: - bAsRGB := b.convertToRGB() + return color.Style(style) +} - for _, opt := range typedOther.opts { - bAsRGB.setOpt(opt, true) - } +// // Need to convert bg to fg otherwise .RGB wont work +// // for more info see https://github.com/gookit/color/issues/39 +// rgbBg := (*b.bg - 10).RGB() +// rgbBg[3] = 1 +// *res.bg = rgbBg +// res.style = *color.NewRGBStyle(*res.fg, rgbBg) - if typedOther.fgSet { - bAsRGB.fg = typedOther.fg - bAsRGB.style.SetFg(typedOther.fg) - } +func (b TextStyle) MergeStyle(other TextStyle) TextStyle { + b.decoration = b.decoration.Merge(other.decoration) - if typedOther.bg != nil { - // Making sure to copy the value - bAsRGB.bg = &color.RGBColor{} - *bAsRGB.bg = *typedOther.bg - bAsRGB.style.SetBg(*typedOther.bg) - } + if other.fg != nil { + b.fg = other.fg + } - return bAsRGB - default: - panic(fmt.Sprintf("got %T but expected BasicTextStyle or RGBTextStyle", typedOther)) + if other.bg != nil { + b.bg = other.bg } + + return b } diff --git a/pkg/gui/style/color.go b/pkg/gui/style/color.go new file mode 100644 index 000000000..ddd8549aa --- /dev/null +++ b/pkg/gui/style/color.go @@ -0,0 +1,35 @@ +package style + +import "github.com/gookit/color" + +type Color struct { + rgb *color.RGBColor + basic *color.Color +} + +func NewRGBColor(cl color.RGBColor) Color { + c := Color{} + c.rgb = &cl + return c +} + +func NewBasicColor(cl color.Color) Color { + c := Color{} + c.basic = &cl + return c +} + +func (c *Color) IsRGB() bool { + return c.rgb != nil +} + +func (c *Color) ToRGB() Color { + if c.IsRGB() { + return *c + } + + rgb := c.basic.RGB() + c.rgb = &rgb + + return NewRGBColor(rgb) +} diff --git a/pkg/gui/style/decoration.go b/pkg/gui/style/decoration.go new file mode 100644 index 000000000..165a54084 --- /dev/null +++ b/pkg/gui/style/decoration.go @@ -0,0 +1,53 @@ +package style + +import "github.com/gookit/color" + +type Decoration struct { + bold bool + underline bool + reverse bool +} + +func (d Decoration) SetBold() { + d.bold = true +} + +func (d Decoration) SetUnderline() { + d.underline = true +} + +func (d Decoration) SetReverse() { + d.reverse = true +} + +func (d Decoration) ToOpts() color.Opts { + opts := make([]color.Color, 0, 3) + + if d.bold { + opts = append(opts, color.OpBold) + } + + if d.underline { + opts = append(opts, color.OpUnderscore) + } + + if d.reverse { + opts = append(opts, color.OpReverse) + } + + return opts +} + +func (d Decoration) Merge(other Decoration) Decoration { + if other.bold { + d.bold = true + } + if other.underline { + d.underline = true + } + if other.reverse { + d.reverse = true + } + + return d +} diff --git a/pkg/gui/style/rgb.go b/pkg/gui/style/rgb.go deleted file mode 100644 index bfda6a30b..000000000 --- a/pkg/gui/style/rgb.go +++ /dev/null @@ -1,111 +0,0 @@ -package style - -import ( - "fmt" - - "github.com/gookit/color" -) - -type RGBTextStyle struct { - opts color.Opts - fgSet bool - fg color.RGBColor - bg *color.RGBColor - style color.RGBStyle -} - -func (b RGBTextStyle) Sprint(a ...interface{}) string { - return b.style.Sprint(a...) -} - -func (b RGBTextStyle) Sprintf(format string, a ...interface{}) string { - return b.style.Sprintf(format, a...) -} - -func (b RGBTextStyle) setOpt(opt color.Color, v bool) RGBTextStyle { - if v { - // Add value - for _, listOpt := range b.opts { - if listOpt == opt { - return b - } - } - b.opts = append(b.opts, opt) - } else { - // Remove value - for idx, listOpt := range b.opts { - if listOpt == opt { - b.opts = append(b.opts[:idx], b.opts[idx+1:]...) - return b - } - } - } - return b -} - -func (b RGBTextStyle) SetBold(v bool) TextStyle { - b = b.setOpt(color.OpBold, v) - b.style.SetOpts(b.opts) - return b -} - -func (b RGBTextStyle) SetReverse(v bool) TextStyle { - b = b.setOpt(color.OpReverse, v) - b.style.SetOpts(b.opts) - return b -} - -func (b RGBTextStyle) SetUnderline(v bool) TextStyle { - b = b.setOpt(color.OpUnderscore, v) - b.style.SetOpts(b.opts) - return b -} - -func (b RGBTextStyle) SetColor(style TextStyle) TextStyle { - var rgbStyle RGBTextStyle - - switch typedStyle := style.(type) { - case BasicTextStyle: - rgbStyle = typedStyle.convertToRGB() - case RGBTextStyle: - rgbStyle = typedStyle - default: - panic(fmt.Sprintf("got %T but expected BasicTextStyle or RGBTextStyle", typedStyle)) - } - - for _, opt := range rgbStyle.GetOpts() { - b = b.setOpt(opt, true) - } - - if rgbStyle.fgSet { - b.fg = rgbStyle.fg - b.style.SetFg(rgbStyle.fg) - b.fgSet = true - } - - if rgbStyle.bg != nil { - // Making sure to copy value - b.bg = &color.RGBColor{} - *b.bg = *rgbStyle.bg - b.style.SetBg(*rgbStyle.bg) - } - - return b -} - -func (b RGBTextStyle) SetRGBColor(red, green, blue uint8, background bool) TextStyle { - parsedColor := color.Rgb(red, green, blue, background) - if background { - b.bg = &parsedColor - b.style.SetBg(parsedColor) - } else { - b.fg = parsedColor - b.style.SetFg(parsedColor) - b.fgSet = true - } - return b -} - -func (b RGBTextStyle) GetOpts() color.Opts { - return b.opts -} diff --git a/pkg/gui/style/style.go b/pkg/gui/style/style.go index 50ea7b523..5911546c7 100644 --- a/pkg/gui/style/style.go +++ b/pkg/gui/style/style.go @@ -5,91 +5,90 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) -type TextStyle interface { - Sprint(a ...interface{}) string - Sprintf(format string, a ...interface{}) string - SetBold(v bool) TextStyle - SetReverse(v bool) TextStyle - SetUnderline(v bool) TextStyle - SetColor(style TextStyle) TextStyle - SetRGBColor(r, g, b uint8, background bool) TextStyle -} - var ( - FgWhite = New(color.FgWhite, 0) - FgLightWhite = New(color.FgLightWhite, 0) - FgBlack = New(color.FgBlack, 0) - FgBlackLighter = New(color.FgBlack.Light(), 0) - FgCyan = New(color.FgCyan, 0) - FgRed = New(color.FgRed, 0) - FgGreen = New(color.FgGreen, 0) - FgBlue = New(color.FgBlue, 0) - FgYellow = New(color.FgYellow, 0) - FgMagenta = New(color.FgMagenta, 0) + // FgWhite = New(pointerTo(color.FgWhite), nil) + FgWhite = FromBasicFg(color.FgWhite) + FgLightWhite = FromBasicFg(color.FgLightWhite) + FgBlack = FromBasicFg(color.FgBlack) + FgBlackLighter = FromBasicFg(color.FgBlack.Light()) + FgCyan = FromBasicFg(color.FgCyan) + FgRed = FromBasicFg(color.FgRed) + FgGreen = FromBasicFg(color.FgGreen) + FgBlue = FromBasicFg(color.FgBlue) + FgYellow = FromBasicFg(color.FgYellow) + FgMagenta = FromBasicFg(color.FgMagenta) - BgWhite = New(0, color.BgWhite) - BgBlack = New(0, color.BgBlack) - BgRed = New(0, color.BgRed) - BgGreen = New(0, color.BgGreen) - BgYellow = New(0, color.BgYellow) - BgBlue = New(0, color.BgBlue) - BgMagenta = New(0, color.BgMagenta) - BgCyan = New(0, color.BgCyan) + BgWhite = FromBasicBg(color.BgWhite) + BgBlack = FromBasicBg(color.BgBlack) + BgRed = FromBasicBg(color.BgRed) + BgGreen = FromBasicBg(color.BgGreen) + BgYellow = FromBasicBg(color.BgYellow) + BgBlue = FromBasicBg(color.BgBlue) + BgMagenta = FromBasicBg(color.BgMagenta) + BgCyan = FromBasicBg(color.BgCyan) - AttrUnderline = New(0, 0).SetUnderline(true) - AttrBold = New(0, 0).SetUnderline(true) + AttrUnderline = New().SetUnderline() + AttrBold = New().SetBold() ) -func New(fg color.Color, bg color.Color, opts ...color.Color) TextStyle { - return BasicTextStyle{ - fg: fg, - bg: bg, - opts: opts, - style: color.Style{}, - }.deriveStyle() +func New() TextStyle { + return TextStyle{} } -func SetConfigStyles(s TextStyle, keys []string, background bool) TextStyle { - for _, key := range keys { - colorMap := map[string]struct { - forground TextStyle - background TextStyle - }{ - "default": {FgWhite, BgBlack}, - "black": {FgBlack, BgBlack}, - "red": {FgRed, BgRed}, - "green": {FgGreen, BgGreen}, - "yellow": {FgYellow, BgYellow}, - "blue": {FgBlue, BgBlue}, - "magenta": {FgMagenta, BgMagenta}, - "cyan": {FgCyan, BgCyan}, - "white": {FgWhite, BgWhite}, - } - value, present := colorMap[key] - if present { - if background { - s = s.SetColor(value.background) - } else { - s = s.SetColor(value.forground) - } - continue - } +func FromBasicFg(fg color.Color) TextStyle { + s := New() + c := NewBasicColor(fg) + s.fg = &c + return s +} - if key == "bold" { - s = s.SetBold(true) - continue - } else if key == "reverse" { - s = s.SetReverse(true) - continue - } else if key == "underline" { - s = s.SetUnderline(true) - continue - } +func FromBasicBg(bg color.Color) TextStyle { + s := New() + c := NewBasicColor(bg) + s.bg = &c + return s +} + +var colorMap = map[string]struct { + forground TextStyle + background TextStyle +}{ + "default": {FgWhite, BgBlack}, + "black": {FgBlack, BgBlack}, + "red": {FgRed, BgRed}, + "green": {FgGreen, BgGreen}, + "yellow": {FgYellow, BgYellow}, + "blue": {FgBlue, BgBlue}, + "magenta": {FgMagenta, BgMagenta}, + "cyan": {FgCyan, BgCyan}, + "white": {FgWhite, BgWhite}, +} + +func SetConfigStyles(keys []string, background bool) TextStyle { + s := New() - r, g, b, validHexColor := utils.GetHexColorValues(key) - if validHexColor { - s = s.SetRGBColor(r, g, b, background) - continue + for _, key := range keys { + switch key { + case "bold": + s = s.SetBold() + case "reverse": + s = s.SetReverse() + case "underline": + s = s.SetUnderline() + default: + value, present := colorMap[key] + if present { + var c TextStyle + if background { + c = value.background + } else { + c = value.forground + } + s = s.MergeStyle(c) + } else if utils.IsValidHexValue(key) { + c := NewRGBColor(color.HEX(key, background)) + s.bg = &c + } } } diff --git a/pkg/gui/style/style_test.go b/pkg/gui/style/style_test.go index ecb3efa70..669bd4db5 100644 --- a/pkg/gui/style/style_test.go +++ b/pkg/gui/style/style_test.go @@ -53,39 +53,39 @@ func TestNewStyle(t *testing.T) { func TestBasicSetColor(t *testing.T) { type scenario struct { name string - colorToSet BasicTextStyle - expect BasicTextStyle + colorToSet TextStyle + expect TextStyle } scenarios := []scenario{ { "empty color", - BasicTextStyle{}, - BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}}}, + TextStyle{}, + TextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}}}, { "set new fg color", - BasicTextStyle{fg: color.FgCyan}, - BasicTextStyle{fg: color.FgCyan, bg: color.BgBlue, opts: []color.Color{color.OpBold}}, + TextStyle{fg: color.FgCyan}, + TextStyle{fg: color.FgCyan, bg: color.BgBlue, opts: []color.Color{color.OpBold}}, }, { "set new bg color", - BasicTextStyle{bg: color.BgGray}, - BasicTextStyle{fg: color.FgRed, bg: color.BgGray, opts: []color.Color{color.OpBold}}, + TextStyle{bg: color.BgGray}, + TextStyle{fg: color.FgRed, bg: color.BgGray, opts: []color.Color{color.OpBold}}, }, { "set new fg and bg color", - BasicTextStyle{fg: color.FgCyan, bg: color.BgGray}, - BasicTextStyle{fg: color.FgCyan, bg: color.BgGray, opts: []color.Color{color.OpBold}}, + TextStyle{fg: color.FgCyan, bg: color.BgGray}, + TextStyle{fg: color.FgCyan, bg: color.BgGray, opts: []color.Color{color.OpBold}}, }, { "add options", - BasicTextStyle{opts: []color.Color{color.OpUnderscore}}, - BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold, color.OpUnderscore}}, + TextStyle{opts: []color.Color{color.OpUnderscore}}, + TextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold, color.OpUnderscore}}, }, { "add options that already exists", - BasicTextStyle{opts: []color.Color{color.OpBold}}, - BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}}, + TextStyle{opts: []color.Color{color.OpBold}}, + TextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}}, }, } @@ -127,12 +127,12 @@ func TestRGBSetColor(t *testing.T) { }, { "empty BasicTextStyle input", - BasicTextStyle{}, + TextStyle{}, RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold}}, }, { "set fg and bg color using BasicTextStyle", - BasicTextStyle{fg: color.FgCyan, bg: color.BgGray}, + TextStyle{fg: color.FgCyan, bg: color.BgGray}, RGBTextStyle{fgSet: true, fg: cyan, bg: toBg(gray), opts: []color.Color{color.OpBold}}, }, { @@ -147,7 +147,7 @@ func TestRGBSetColor(t *testing.T) { }, { "add options using BasicTextStyle", - BasicTextStyle{opts: []color.Color{color.OpUnderscore}}, + TextStyle{opts: []color.Color{color.OpUnderscore}}, RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold, color.OpUnderscore}}, }, { @@ -159,10 +159,10 @@ func TestRGBSetColor(t *testing.T) { for _, s := range scenarios { t.Run(s.name, func(t *testing.T) { - style, ok := New(color.FgRed, color.BgBlue).SetBold(true).(BasicTextStyle) + style, ok := New(color.FgRed, color.BgBlue).SetBold().(BasicTextStyle) assert.True(t, ok, "SetBold should return a interface of type BasicTextStyle") - rgbStyle, ok := style.convertToRGB().SetColor(s.colorToSet).(RGBTextStyle) + rgbStyle, ok := style.convertToRGB().MergeStyle(s.colorToSet).(RGBTextStyle) assert.True(t, ok, "SetColor should return a interface of type RGBTextColor") rgbStyle.style = color.RGBStyle{} |