summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2021-07-31 15:34:45 +1000
committerJesse Duffield <jessedduffield@gmail.com>2021-07-31 17:33:20 +1000
commit0bc0e4ac88f430976e4f2e69285ad6c56d94c137 (patch)
treea317e1f10ccdc111b3e2d71ca87ad47555b6118c
parent117c0bd4f7adea7afb845107623b1b3d9b4e96fa (diff)
more efficient
-rw-r--r--pkg/gui/style/basic.go136
-rw-r--r--pkg/gui/style/color.go11
-rw-r--r--pkg/gui/style/decoration.go2
-rw-r--r--pkg/gui/style/style.go66
-rw-r--r--pkg/theme/theme.go68
5 files changed, 164 insertions, 119 deletions
diff --git a/pkg/gui/style/basic.go b/pkg/gui/style/basic.go
index 5cb1ec8aa..45652097e 100644
--- a/pkg/gui/style/basic.go
+++ b/pkg/gui/style/basic.go
@@ -4,10 +4,33 @@ import (
"github.com/gookit/color"
)
+// A TextStyle contains a foreground color, background color, and
+// decorations (bold/underline/reverse).
+//
+// Colors may each be either 16-bit or 256-bit RGB colors. When
+// we need to produce a string with a TextStyle, if either foreground or
+// background color is RGB, we'll promote the other color component to RGB as well.
+// We could simplify this code by forcing everything to be RGB, but we're not
+// sure how compatible or efficient that would be with various terminals.
+// Lazygit will typically stick to 16-bit colors, but users may configure RGB colors.
+//
+// TextStyles are value objects, not entities, so for example if you want to
+// add the bold decoration to a TextStyle, we'll create a new TextStyle with
+// that decoration applied.
+//
+// Decorations are additive, so when we merge two TextStyles, if either is bold
+// then the resulting style will also be bold.
+//
+// So that we aren't rederiving the underlying style each time we want to print
+// a string, we derive it when a new TextStyle is created and store it in the
+// `style` field.
+
type TextStyle struct {
fg *Color
bg *Color
decoration Decoration
+
+ style Sprinter
}
type Sprinter interface {
@@ -15,84 +38,123 @@ type Sprinter interface {
Sprintf(format string, a ...interface{}) string
}
+func New() TextStyle {
+ s := TextStyle{}
+ s.style = s.deriveStyle()
+ return s
+}
+
+func FromBasicFg(fg color.Color) TextStyle {
+ s := New()
+ c := NewBasicColor(fg)
+ s.fg = &c
+ s.style = s.deriveStyle()
+ return s
+}
+
+func FromBasicBg(bg color.Color) TextStyle {
+ s := New()
+ c := NewBasicColor(bg)
+ s.bg = &c
+ s.style = s.deriveStyle()
+ return s
+}
+
func (b TextStyle) Sprint(a ...interface{}) string {
- return b.deriveStyle().Sprint(a...)
+ return b.style.Sprint(a...)
}
func (b TextStyle) Sprintf(format string, a ...interface{}) string {
- return b.deriveStyle().Sprintf(format, a...)
+ return b.style.Sprintf(format, a...)
}
func (b TextStyle) SetBold() TextStyle {
b.decoration.SetBold()
+ b.style = b.deriveStyle()
return b
}
func (b TextStyle) SetUnderline() TextStyle {
b.decoration.SetUnderline()
+ b.style = b.deriveStyle()
return b
}
func (b TextStyle) SetReverse() TextStyle {
b.decoration.SetReverse()
+ b.style = b.deriveStyle()
return b
}
-func (b TextStyle) deriveStyle() Sprinter {
- // TODO: consider caching
- return deriveStyle(b.fg, b.bg, b.decoration)
+func (b TextStyle) SetBg(color Color) TextStyle {
+ b.bg = &color
+ b.style = b.deriveStyle()
+ return b
+}
+
+func (b TextStyle) SetFg(color Color) TextStyle {
+ b.fg = &color
+ b.style = b.deriveStyle()
+ return b
+}
+
+func (b TextStyle) MergeStyle(other TextStyle) TextStyle {
+ b.decoration = b.decoration.Merge(other.decoration)
+
+ if other.fg != nil {
+ b.fg = other.fg
+ }
+
+ if other.bg != nil {
+ b.bg = other.bg
+ }
+
+ b.style = b.deriveStyle()
+
+ return b
}
-func deriveStyle(fg *Color, bg *Color, decoration Decoration) Sprinter {
- if fg == nil && bg == nil {
- return color.Style(decoration.ToOpts())
+func (b TextStyle) deriveStyle() Sprinter {
+ if b.fg == nil && b.bg == nil {
+ return color.Style(b.decoration.ToOpts())
}
- isRgb := (fg != nil && fg.IsRGB()) || (bg != nil && bg.IsRGB())
+ isRgb := (b.fg != nil && b.fg.IsRGB()) || (b.bg != nil && b.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
+ return b.deriveRGBStyle()
}
+ return b.deriveBasicStyle()
+}
+
+func (b TextStyle) deriveBasicStyle() color.Style {
style := make([]color.Color, 0, 5)
- if fg != nil {
- style = append(style, *fg.basic)
+ if b.fg != nil {
+ style = append(style, *b.fg.basic)
}
- if bg != nil {
- style = append(style, *bg.basic)
+ if b.bg != nil {
+ style = append(style, *b.bg.basic)
}
- style = append(style, decoration.ToOpts()...)
+ style = append(style, b.decoration.ToOpts()...)
return color.Style(style)
}
-// // 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)
+func (b TextStyle) deriveRGBStyle() *color.RGBStyle {
+ style := &color.RGBStyle{}
-func (b TextStyle) MergeStyle(other TextStyle) TextStyle {
- b.decoration = b.decoration.Merge(other.decoration)
-
- if other.fg != nil {
- b.fg = other.fg
+ if b.fg != nil {
+ style.SetFg(*b.fg.ToRGB().rgb)
}
- if other.bg != nil {
- b.bg = other.bg
+ if b.bg != nil {
+ style.SetBg(*b.bg.ToRGB().rgb)
}
- return b
+ style.SetOpts(b.decoration.ToOpts())
+
+ return style
}
diff --git a/pkg/gui/style/color.go b/pkg/gui/style/color.go
index ddd8549aa..b2df21bbf 100644
--- a/pkg/gui/style/color.go
+++ b/pkg/gui/style/color.go
@@ -19,17 +19,14 @@ func NewBasicColor(cl color.Color) Color {
return c
}
-func (c *Color) IsRGB() bool {
+func (c Color) IsRGB() bool {
return c.rgb != nil
}
-func (c *Color) ToRGB() Color {
+func (c Color) ToRGB() Color {
if c.IsRGB() {
- return *c
+ return c
}
- rgb := c.basic.RGB()
- c.rgb = &rgb
-
- return NewRGBColor(rgb)
+ return NewRGBColor(c.basic.RGB())
}
diff --git a/pkg/gui/style/decoration.go b/pkg/gui/style/decoration.go
index 165a54084..51887fbdd 100644
--- a/pkg/gui/style/decoration.go
+++ b/pkg/gui/style/decoration.go
@@ -42,9 +42,11 @@ 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
}
diff --git a/pkg/gui/style/style.go b/pkg/gui/style/style.go
index 5911546c7..d6c8149b2 100644
--- a/pkg/gui/style/style.go
+++ b/pkg/gui/style/style.go
@@ -2,11 +2,9 @@ package style
import (
"github.com/gookit/color"
- "github.com/jesseduffield/lazygit/pkg/utils"
)
var (
- // FgWhite = New(pointerTo(color.FgWhite), nil)
FgWhite = FromBasicFg(color.FgWhite)
FgLightWhite = FromBasicFg(color.FgLightWhite)
FgBlack = FromBasicFg(color.FgBlack)
@@ -30,67 +28,3 @@ var (
AttrUnderline = New().SetUnderline()
AttrBold = New().SetBold()
)
-
-func New() TextStyle {
- return TextStyle{}
-}
-
-func FromBasicFg(fg color.Color) TextStyle {
- s := New()
- c := NewBasicColor(fg)
- s.fg = &c
- return s
-}
-
-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()
-
- 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
- }
- }
- }
-
- return s
-}
diff --git a/pkg/theme/theme.go b/pkg/theme/theme.go
index ffccc606a..d05897540 100644
--- a/pkg/theme/theme.go
+++ b/pkg/theme/theme.go
@@ -42,13 +42,13 @@ var (
// UpdateTheme updates all theme variables
func UpdateTheme(themeConfig config.ThemeConfig) {
- ActiveBorderColor = GetGocuiColor(themeConfig.ActiveBorderColor)
- InactiveBorderColor = GetGocuiColor(themeConfig.InactiveBorderColor)
- SelectedLineBgColor = style.SetConfigStyles(themeConfig.SelectedLineBgColor, true)
- SelectedRangeBgColor = style.SetConfigStyles(themeConfig.SelectedRangeBgColor, true)
- GocuiSelectedLineBgColor = GetGocuiColor(themeConfig.SelectedLineBgColor)
- OptionsColor = GetGocuiColor(themeConfig.OptionsTextColor)
- OptionsFgColor = style.SetConfigStyles(themeConfig.OptionsTextColor, false)
+ ActiveBorderColor = GetGocuiStyle(themeConfig.ActiveBorderColor)
+ InactiveBorderColor = GetGocuiStyle(themeConfig.InactiveBorderColor)
+ SelectedLineBgColor = GetTextStyle(themeConfig.SelectedLineBgColor, true)
+ SelectedRangeBgColor = GetTextStyle(themeConfig.SelectedRangeBgColor, true)
+ GocuiSelectedLineBgColor = GetGocuiStyle(themeConfig.SelectedLineBgColor)
+ OptionsColor = GetGocuiStyle(themeConfig.OptionsTextColor)
+ OptionsFgColor = GetTextStyle(themeConfig.OptionsTextColor, false)
isLightTheme := themeConfig.LightTheme
if isLightTheme {
@@ -90,11 +90,61 @@ func GetGocuiAttribute(key string) gocui.Attribute {
return gocui.ColorWhite
}
-// GetGocuiColor bitwise OR's a list of attributes obtained via the given keys
-func GetGocuiColor(keys []string) gocui.Attribute {
+// GetGocuiStyle bitwise OR's a list of attributes obtained via the given keys
+func GetGocuiStyle(keys []string) gocui.Attribute {
var attribute gocui.Attribute
for _, key := range keys {
attribute |= GetGocuiAttribute(key)
}
return attribute
}
+
+var colorMap = map[string]struct {
+ foreground style.TextStyle
+ background style.TextStyle
+}{
+ "default": {style.FgWhite, style.BgBlack},
+ "black": {style.FgBlack, style.BgBlack},
+ "red": {style.FgRed, style.BgRed},
+ "green": {style.FgGreen, style.BgGreen},
+ "yellow": {style.FgYellow, style.BgYellow},
+ "blue": {style.FgBlue, style.BgBlue},
+ "magenta": {style.FgMagenta, style.BgMagenta},
+ "cyan": {style.FgCyan, style.BgCyan},
+ "white": {style.FgWhite, style.BgWhite},
+}
+
+func GetTextStyle(keys []string, background bool) style.TextStyle {
+ s := style.New()
+
+ 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 style.TextStyle
+ if background {
+ c = value.background
+ } else {
+ c = value.foreground
+ }
+ s = s.MergeStyle(c)
+ } else if utils.IsValidHexValue(key) {
+ c := style.NewRGBColor(color.HEX(key, background))
+ if background {
+ s.SetBg(c)
+ } else {
+ s.SetFg(c)
+ }
+ }
+ }
+ }
+
+ return s
+}