mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-11-24 08:52:21 +02:00
Merge pull request #1409 from jesseduffield/jesse-switch-text-color-library
This commit is contained in:
commit
3c78ba7ed3
@ -98,35 +98,35 @@ func (l *PatchLine) render(selected bool, included bool) string {
|
||||
return coloredString(style.FgCyan, match[1], selected, included) + coloredString(theme.DefaultTextColor, match[2], selected, false)
|
||||
}
|
||||
|
||||
colorAttr := theme.DefaultTextColor
|
||||
textStyle := theme.DefaultTextColor
|
||||
switch l.Kind {
|
||||
case PATCH_HEADER:
|
||||
colorAttr = colorAttr.SetBold(true)
|
||||
textStyle = textStyle.SetBold()
|
||||
case ADDITION:
|
||||
colorAttr = colorAttr.SetColor(style.FgGreen)
|
||||
textStyle = style.FgGreen
|
||||
case DELETION:
|
||||
colorAttr = colorAttr.SetColor(style.FgRed)
|
||||
textStyle = style.FgRed
|
||||
case COMMIT_SHA:
|
||||
colorAttr = colorAttr.SetColor(style.FgYellow)
|
||||
textStyle = style.FgYellow
|
||||
}
|
||||
|
||||
return coloredString(colorAttr, content, selected, included)
|
||||
return coloredString(textStyle, content, selected, included)
|
||||
}
|
||||
|
||||
func coloredString(colorAttr style.TextStyle, str string, selected bool, included bool) string {
|
||||
func coloredString(textStyle style.TextStyle, str string, selected bool, included bool) string {
|
||||
if selected {
|
||||
colorAttr = colorAttr.SetColor(theme.SelectedRangeBgColor)
|
||||
textStyle = textStyle.MergeStyle(theme.SelectedRangeBgColor)
|
||||
}
|
||||
|
||||
if len(str) < 2 {
|
||||
return colorAttr.Sprint(str)
|
||||
return textStyle.Sprint(str)
|
||||
}
|
||||
|
||||
res := colorAttr.Sprint(str[:1])
|
||||
res := textStyle.Sprint(str[:1])
|
||||
if included {
|
||||
return res + colorAttr.SetColor(style.BgGreen).Sprint(str[1:])
|
||||
return res + textStyle.MergeStyle(style.BgGreen).Sprint(str[1:])
|
||||
}
|
||||
return res + colorAttr.Sprint(str[1:])
|
||||
return res + textStyle.Sprint(str[1:])
|
||||
}
|
||||
|
||||
func parsePatch(patch string) ([]int, []int, []*PatchLine) {
|
||||
|
@ -28,13 +28,13 @@ func (gui *Gui) GetOnRunCommand() func(entry oscommands.CmdLogEntry) {
|
||||
currentSpan = entry.GetSpan()
|
||||
}
|
||||
|
||||
clrAttr := theme.DefaultTextColor
|
||||
textStyle := theme.DefaultTextColor
|
||||
if !entry.GetCommandLine() {
|
||||
clrAttr = clrAttr.SetColor(style.FgMagenta)
|
||||
textStyle = style.FgMagenta
|
||||
}
|
||||
gui.CmdLog = append(gui.CmdLog, entry.GetCmdStr())
|
||||
indentedCmdStr := " " + strings.Replace(entry.GetCmdStr(), "\n", "\n ", -1)
|
||||
fmt.Fprint(gui.Views.Extras, "\n"+clrAttr.Sprint(indentedCmdStr))
|
||||
fmt.Fprint(gui.Views.Extras, "\n"+textStyle.Sprint(indentedCmdStr))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@ func (gui *Gui) informationStr() string {
|
||||
}
|
||||
|
||||
if gui.g.Mouse {
|
||||
donate := style.FgMagenta.SetUnderline(true).Sprint(gui.Tr.Donate)
|
||||
askQuestion := style.FgYellow.SetUnderline(true).Sprint(gui.Tr.AskQuestion)
|
||||
donate := style.FgMagenta.SetUnderline().Sprint(gui.Tr.Donate)
|
||||
askQuestion := style.FgYellow.SetUnderline().Sprint(gui.Tr.AskQuestion)
|
||||
return fmt.Sprintf("%s %s %s", donate, askQuestion, gui.Config.GetVersion())
|
||||
} else {
|
||||
return gui.Config.GetVersion()
|
||||
|
@ -15,18 +15,18 @@ func ColoredConflictFile(content string, state *State, hasFocus bool) string {
|
||||
conflict, remainingConflicts := shiftConflict(state.conflicts)
|
||||
var outputBuffer bytes.Buffer
|
||||
for i, line := range utils.SplitLines(content) {
|
||||
colour := theme.DefaultTextColor
|
||||
textStyle := theme.DefaultTextColor
|
||||
if i == conflict.start || i == conflict.middle || i == conflict.end {
|
||||
colour.SetColor(style.FgRed)
|
||||
textStyle = style.FgRed
|
||||
}
|
||||
|
||||
if hasFocus && state.conflictIndex < len(state.conflicts) && *state.conflicts[state.conflictIndex] == *conflict && shouldHighlightLine(i, conflict, state.conflictTop) {
|
||||
colour = theme.SelectedRangeBgColor.SetBold(true)
|
||||
textStyle = theme.SelectedRangeBgColor.SetBold()
|
||||
}
|
||||
if i == conflict.end && len(remainingConflicts) > 0 {
|
||||
conflict, remainingConflicts = shiftConflict(remainingConflicts)
|
||||
}
|
||||
outputBuffer.WriteString(colour.Sprint(line) + "\n")
|
||||
outputBuffer.WriteString(textStyle.Sprint(line) + "\n")
|
||||
}
|
||||
return outputBuffer.String()
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.GitCommand.PatchManager.Active,
|
||||
description: func() string {
|
||||
return style.FgYellow.SetBold(true).Sprintf(
|
||||
return style.FgYellow.SetBold().Sprintf(
|
||||
"%s %s",
|
||||
gui.Tr.LcBuildingPatch,
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
@ -38,7 +38,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.State.Modes.Filtering.Active,
|
||||
description: func() string {
|
||||
return style.FgRed.SetBold(true).Sprintf(
|
||||
return style.FgRed.SetBold().Sprintf(
|
||||
"%s '%s' %s",
|
||||
gui.Tr.LcFilteringBy,
|
||||
gui.State.Modes.Filtering.GetPath(),
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"github.com/kyokomi/emoji/v2"
|
||||
)
|
||||
|
||||
var cherryPickedCommitTextStyle = style.FgCyan.MergeStyle(style.BgBlue)
|
||||
|
||||
func GetCommitListDisplayStrings(commits []*models.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string, parseEmoji bool) [][]string {
|
||||
lines := make([][]string, len(commits))
|
||||
|
||||
@ -49,7 +51,7 @@ func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCom
|
||||
// for some reason, setting the background to blue pads out the other commits
|
||||
// horizontally. For the sake of accessibility I'm considering this a feature,
|
||||
// not a bug
|
||||
shaColor = style.FgCyan.SetColor(style.BgBlue)
|
||||
shaColor = cherryPickedCommitTextStyle
|
||||
}
|
||||
|
||||
tagString := ""
|
||||
@ -57,7 +59,7 @@ func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCom
|
||||
if c.Action != "" {
|
||||
secondColumnString = actionColorMap(c.Action).Sprint(c.Action)
|
||||
} else if c.ExtraInfo != "" {
|
||||
tagString = theme.DiffTerminalColor.SetBold(true).Sprint(c.ExtraInfo) + " "
|
||||
tagString = theme.DiffTerminalColor.SetBold().Sprint(c.ExtraInfo) + " "
|
||||
}
|
||||
|
||||
truncatedAuthor := utils.TruncateWithEllipsis(c.Author, 17)
|
||||
@ -96,7 +98,7 @@ func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[s
|
||||
// for some reason, setting the background to blue pads out the other commits
|
||||
// horizontally. For the sake of accessibility I'm considering this a feature,
|
||||
// not a bug
|
||||
shaColor = style.FgCyan.SetColor(style.BgBlue)
|
||||
shaColor = cherryPickedCommitTextStyle
|
||||
}
|
||||
|
||||
actionString := ""
|
||||
@ -104,7 +106,7 @@ func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[s
|
||||
if c.Action != "" {
|
||||
actionString = actionColorMap(c.Action).Sprint(utils.WithPadding(c.Action, 7)) + " "
|
||||
} else if len(c.Tags) > 0 {
|
||||
tagString = theme.DiffTerminalColor.SetBold(true).Sprint(strings.Join(c.Tags, " ")) + " "
|
||||
tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(c.Tags, " ")) + " "
|
||||
}
|
||||
|
||||
name := c.Name
|
||||
|
@ -29,7 +29,7 @@ func GetReflogCommitListDisplayStrings(commits []*models.Commit, fullDescription
|
||||
func coloredReflogSha(c *models.Commit, cherryPickedCommitShaMap map[string]bool) string {
|
||||
shaColor := style.FgBlue
|
||||
if cherryPickedCommitShaMap[c.Sha] {
|
||||
shaColor = style.FgCyan.SetColor(style.BgBlue)
|
||||
shaColor = cherryPickedCommitTextStyle
|
||||
}
|
||||
|
||||
return shaColor.Sprint(c.ShortSha())
|
||||
|
@ -1,147 +0,0 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
type BasicTextStyle struct {
|
||||
fg color.Color
|
||||
bg color.Color
|
||||
opts []color.Color
|
||||
|
||||
style color.Style
|
||||
}
|
||||
|
||||
func (b BasicTextStyle) Sprint(a ...interface{}) string {
|
||||
return b.style.Sprint(a...)
|
||||
}
|
||||
|
||||
func (b BasicTextStyle) Sprintf(format string, a ...interface{}) string {
|
||||
return b.style.Sprintf(format, 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)
|
||||
}
|
||||
|
||||
b.style = append(b.style, b.opts...)
|
||||
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()
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b BasicTextStyle) SetBold(v bool) TextStyle {
|
||||
return b.setOpt(color.OpBold, v, true)
|
||||
}
|
||||
|
||||
func (b BasicTextStyle) SetReverse(v bool) TextStyle {
|
||||
return b.setOpt(color.OpReverse, v, true)
|
||||
}
|
||||
|
||||
func (b BasicTextStyle) SetUnderline(v bool) TextStyle {
|
||||
return b.setOpt(color.OpUnderscore, v, true)
|
||||
}
|
||||
|
||||
func (b BasicTextStyle) SetRGBColor(red, green, blue uint8, background bool) TextStyle {
|
||||
return b.convertToRGB().SetRGBColor(red, green, blue, background)
|
||||
}
|
||||
|
||||
func (b BasicTextStyle) convertToRGB() RGBTextStyle {
|
||||
res := RGBTextStyle{
|
||||
fg: b.fg.RGB(),
|
||||
fgSet: b.fg != 0,
|
||||
opts: b.opts,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
res.style.SetOpts(b.opts)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
for _, opt := range typedOther.opts {
|
||||
bAsRGB.setOpt(opt, true)
|
||||
}
|
||||
|
||||
if typedOther.fgSet {
|
||||
bAsRGB.fg = typedOther.fg
|
||||
bAsRGB.style.SetFg(typedOther.fg)
|
||||
}
|
||||
|
||||
if typedOther.bg != nil {
|
||||
// Making sure to copy the value
|
||||
bAsRGB.bg = &color.RGBColor{}
|
||||
*bAsRGB.bg = *typedOther.bg
|
||||
bAsRGB.style.SetBg(*typedOther.bg)
|
||||
}
|
||||
|
||||
return bAsRGB
|
||||
default:
|
||||
panic(fmt.Sprintf("got %T but expected BasicTextStyle or RGBTextStyle", typedOther))
|
||||
}
|
||||
}
|
38
pkg/gui/style/basic_styles.go
Normal file
38
pkg/gui/style/basic_styles.go
Normal file
@ -0,0 +1,38 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
var (
|
||||
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 = 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().SetUnderline()
|
||||
AttrBold = New().SetBold()
|
||||
)
|
||||
|
||||
func FromBasicFg(fg color.Color) TextStyle {
|
||||
return New().SetFg(NewBasicColor(fg))
|
||||
}
|
||||
|
||||
func FromBasicBg(bg color.Color) TextStyle {
|
||||
return New().SetBg(NewBasicColor(bg))
|
||||
}
|
32
pkg/gui/style/color.go
Normal file
32
pkg/gui/style/color.go
Normal file
@ -0,0 +1,32 @@
|
||||
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
|
||||
}
|
||||
|
||||
return NewRGBColor(c.basic.RGB())
|
||||
}
|
55
pkg/gui/style/decoration.go
Normal file
55
pkg/gui/style/decoration.go
Normal file
@ -0,0 +1,55 @@
|
||||
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
|
||||
}
|
@ -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
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"github.com/gookit/color"
|
||||
"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)
|
||||
|
||||
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)
|
||||
|
||||
AttrUnderline = New(0, 0).SetUnderline(true)
|
||||
AttrBold = New(0, 0).SetUnderline(true)
|
||||
)
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
r, g, b, validHexColor := utils.GetHexColorValues(key)
|
||||
if validHexColor {
|
||||
s = s.SetRGBColor(r, g, b, background)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
@ -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{}
|
||||
|
147
pkg/gui/style/text_style.go
Normal file
147
pkg/gui/style/text_style.go
Normal file
@ -0,0 +1,147 @@
|
||||
package style
|
||||
|
||||
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 {
|
||||
Sprint(a ...interface{}) string
|
||||
Sprintf(format string, a ...interface{}) string
|
||||
}
|
||||
|
||||
func New() TextStyle {
|
||||
s := TextStyle{}
|
||||
s.style = s.deriveStyle()
|
||||
return s
|
||||
}
|
||||
|
||||
func (b TextStyle) Sprint(a ...interface{}) string {
|
||||
return b.style.Sprint(a...)
|
||||
}
|
||||
|
||||
func (b TextStyle) Sprintf(format string, a ...interface{}) string {
|
||||
return b.style.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
// note that our receiver here is not a pointer which means we're receiving a
|
||||
// copy of the original TextStyle. This allows us to mutate and return that
|
||||
// TextStyle receiver without actually modifying the original.
|
||||
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) 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 (b TextStyle) deriveStyle() Sprinter {
|
||||
if b.fg == nil && b.bg == nil {
|
||||
return color.Style(b.decoration.ToOpts())
|
||||
}
|
||||
|
||||
isRgb := (b.fg != nil && b.fg.IsRGB()) || (b.bg != nil && b.bg.IsRGB())
|
||||
if isRgb {
|
||||
return b.deriveRGBStyle()
|
||||
}
|
||||
|
||||
return b.deriveBasicStyle()
|
||||
}
|
||||
|
||||
func (b TextStyle) deriveBasicStyle() color.Style {
|
||||
style := make([]color.Color, 0, 5)
|
||||
|
||||
if b.fg != nil {
|
||||
style = append(style, *b.fg.basic)
|
||||
}
|
||||
|
||||
if b.bg != nil {
|
||||
style = append(style, *b.bg.basic)
|
||||
}
|
||||
|
||||
style = append(style, b.decoration.ToOpts()...)
|
||||
|
||||
return color.Style(style)
|
||||
}
|
||||
|
||||
func (b TextStyle) deriveRGBStyle() *color.RGBStyle {
|
||||
style := &color.RGBStyle{}
|
||||
|
||||
if b.fg != nil {
|
||||
style.SetFg(*b.fg.ToRGB().rgb)
|
||||
}
|
||||
|
||||
if b.bg != nil {
|
||||
style.SetBg(*b.bg.ToRGB().rgb)
|
||||
}
|
||||
|
||||
style.SetOpts(b.decoration.ToOpts())
|
||||
|
||||
return style
|
||||
}
|
45
pkg/theme/gocui.go
Normal file
45
pkg/theme/gocui.go
Normal file
@ -0,0 +1,45 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
var gocuiColorMap = map[string]gocui.Attribute{
|
||||
"default": gocui.ColorDefault,
|
||||
"black": gocui.ColorBlack,
|
||||
"red": gocui.ColorRed,
|
||||
"green": gocui.ColorGreen,
|
||||
"yellow": gocui.ColorYellow,
|
||||
"blue": gocui.ColorBlue,
|
||||
"magenta": gocui.ColorMagenta,
|
||||
"cyan": gocui.ColorCyan,
|
||||
"white": gocui.ColorWhite,
|
||||
"bold": gocui.AttrBold,
|
||||
"reverse": gocui.AttrReverse,
|
||||
"underline": gocui.AttrUnderline,
|
||||
}
|
||||
|
||||
// GetAttribute gets the gocui color attribute from the string
|
||||
func GetGocuiAttribute(key string) gocui.Attribute {
|
||||
if utils.IsValidHexValue(key) {
|
||||
values := color.HEX(key).Values()
|
||||
return gocui.NewRGBColor(int32(values[0]), int32(values[1]), int32(values[2]))
|
||||
}
|
||||
|
||||
value, present := gocuiColorMap[key]
|
||||
if present {
|
||||
return value
|
||||
}
|
||||
return gocui.ColorWhite
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
57
pkg/theme/style.go
Normal file
57
pkg/theme/style.go
Normal file
@ -0,0 +1,57 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -24,31 +22,31 @@ var (
|
||||
OptionsColor gocui.Attribute
|
||||
|
||||
// DefaultTextColor is the default text color
|
||||
DefaultTextColor = style.New(color.FgWhite, 0)
|
||||
DefaultTextColor = style.FgWhite
|
||||
|
||||
// DefaultHiTextColor is the default highlighted text color
|
||||
DefaultHiTextColor = style.New(color.FgLightWhite, 0)
|
||||
DefaultHiTextColor = style.FgLightWhite
|
||||
|
||||
// SelectedLineBgColor is the background color for the selected line
|
||||
SelectedLineBgColor = style.New(0, 0)
|
||||
SelectedLineBgColor = style.New()
|
||||
|
||||
// SelectedRangeBgColor is the background color of the selected range of lines
|
||||
SelectedRangeBgColor = style.New(0, 0)
|
||||
SelectedRangeBgColor = style.New()
|
||||
|
||||
OptionsFgColor = style.New(0, 0)
|
||||
OptionsFgColor = style.New()
|
||||
|
||||
DiffTerminalColor = style.New(color.FgMagenta, 0)
|
||||
DiffTerminalColor = style.FgMagenta
|
||||
)
|
||||
|
||||
// UpdateTheme updates all theme variables
|
||||
func UpdateTheme(themeConfig config.ThemeConfig) {
|
||||
ActiveBorderColor = GetGocuiColor(themeConfig.ActiveBorderColor)
|
||||
InactiveBorderColor = GetGocuiColor(themeConfig.InactiveBorderColor)
|
||||
SelectedLineBgColor = style.SetConfigStyles(SelectedLineBgColor, themeConfig.SelectedLineBgColor, true)
|
||||
SelectedRangeBgColor = style.SetConfigStyles(SelectedRangeBgColor, themeConfig.SelectedRangeBgColor, true)
|
||||
GocuiSelectedLineBgColor = GetGocuiColor(themeConfig.SelectedLineBgColor)
|
||||
OptionsColor = GetGocuiColor(themeConfig.OptionsTextColor)
|
||||
OptionsFgColor = style.SetConfigStyles(OptionsFgColor, 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 {
|
||||
@ -61,40 +59,3 @@ func UpdateTheme(themeConfig config.ThemeConfig) {
|
||||
GocuiDefaultTextColor = gocui.ColorWhite
|
||||
}
|
||||
}
|
||||
|
||||
// GetAttribute gets the gocui color attribute from the string
|
||||
func GetGocuiAttribute(key string) gocui.Attribute {
|
||||
r, g, b, validHexColor := utils.GetHexColorValues(key)
|
||||
if validHexColor {
|
||||
return gocui.NewRGBColor(int32(r), int32(g), int32(b))
|
||||
}
|
||||
|
||||
colorMap := map[string]gocui.Attribute{
|
||||
"default": gocui.ColorDefault,
|
||||
"black": gocui.ColorBlack,
|
||||
"red": gocui.ColorRed,
|
||||
"green": gocui.ColorGreen,
|
||||
"yellow": gocui.ColorYellow,
|
||||
"blue": gocui.ColorBlue,
|
||||
"magenta": gocui.ColorMagenta,
|
||||
"cyan": gocui.ColorCyan,
|
||||
"white": gocui.ColorWhite,
|
||||
"bold": gocui.AttrBold,
|
||||
"reverse": gocui.AttrReverse,
|
||||
"underline": gocui.AttrUnderline,
|
||||
}
|
||||
value, present := colorMap[key]
|
||||
if present {
|
||||
return value
|
||||
}
|
||||
return gocui.ColorWhite
|
||||
}
|
||||
|
||||
// GetGocuiColor bitwise OR's a list of attributes obtained via the given keys
|
||||
func GetGocuiColor(keys []string) gocui.Attribute {
|
||||
var attribute gocui.Attribute
|
||||
for _, key := range keys {
|
||||
attribute |= GetGocuiAttribute(key)
|
||||
}
|
||||
return attribute
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
@ -33,22 +32,23 @@ func getPadWidths(stringArrays [][]string) []int {
|
||||
return padWidths
|
||||
}
|
||||
|
||||
// GetHexColorValues returns the rgb values of a hex color
|
||||
func GetHexColorValues(v string) (r uint8, g uint8, b uint8, valid bool) {
|
||||
if len(v) == 4 {
|
||||
v = string([]byte{v[0], v[1], v[1], v[2], v[2], v[3], v[3]})
|
||||
} else if len(v) != 7 {
|
||||
return
|
||||
func IsValidHexValue(v string) bool {
|
||||
if len(v) != 4 && len(v) != 7 {
|
||||
return false
|
||||
}
|
||||
|
||||
if v[0] != '#' {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
rgb, err := hex.DecodeString(v[1:])
|
||||
if err != nil {
|
||||
return
|
||||
for _, char := range v[1:] {
|
||||
switch char {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F':
|
||||
continue
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return rgb[0], rgb[1], rgb[2], true
|
||||
return true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user