summaryrefslogtreecommitdiffstats
path: root/pkg/gui/style
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2021-07-31 12:54:28 +1000
committerJesse Duffield <jessedduffield@gmail.com>2021-07-31 17:33:13 +1000
commit117c0bd4f7adea7afb845107623b1b3d9b4e96fa (patch)
tree96cfe200a0a2f7aaef6cefdeb56a01cfddb78f95 /pkg/gui/style
parent79848087bccd5c87af1dbb44a39753aad1346f8b (diff)
simplify code a bit
Diffstat (limited to 'pkg/gui/style')
-rw-r--r--pkg/gui/style/basic.go173
-rw-r--r--pkg/gui/style/color.go35
-rw-r--r--pkg/gui/style/decoration.go53
-rw-r--r--pkg/gui/style/rgb.go111
-rw-r--r--pkg/gui/style/style.go151
-rw-r--r--pkg/gui/style/style_test.go38
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{}