Skip to content

Commit

Permalink
Merge pull request #492 from pterm/491-proposal-rgbstyle
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinJWendt committed Apr 28, 2023
2 parents 064ddfe + 266798d commit ff7c49b
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 0 deletions.
67 changes: 67 additions & 0 deletions _examples/coloring/fade-colors-rgb-style/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"strings"

"github.com/pterm/pterm"
)

func main() {
white := pterm.NewRGB(255, 255, 255) // This RGB value is used as the gradients start point.
grey := pterm.NewRGB(128, 128, 128) // This RGB value is used as the gradients start point.
black := pterm.NewRGB(0, 0, 0) // This RGB value is used as the gradients start point.
red := pterm.NewRGB(255, 0, 0) // This RGB value is used as the gradients start point.
purple := pterm.NewRGB(255, 0, 255) // This RGB value is used as the gradients start point.
green := pterm.NewRGB(0, 255, 0) // This RGB value is used as the gradients start point.

str := "RGB colors only work in Terminals which support TrueColor."
strs := strings.Split(str, "")
var fadeInfo string // String which will be used to print.
for i := 0; i < len(str); i++ {
// Append faded letter to info string.
fadeInfo += pterm.NewRGBStyle(white.Fade(0, float32(len(str)), float32(i), purple), grey.Fade(0, float32(len(str)), float32(i), black)).Sprint(strs[i])
}

pterm.Info.Println(fadeInfo)

str = "The background and foreground colors can be customized individually."
strs = strings.Split(str, "")
var fade2 string // String which will be used to print info.
for i := 0; i < len(str); i++ {
// Append faded letter to info string.
fade2 += pterm.NewRGBStyle(black, purple.Fade(0, float32(len(str)), float32(i), red)).Sprint(strs[i])
}

pterm.Println(fade2)

str = "Styles can also be applied. For example: Bold or Italic."
strs = strings.Split(str, "")
var fade3 string // String which will be used to print.

bold := 0
boldStr := strings.Split("Bold", "")
italic := 0
italicStr := strings.Split("Italic", "")

for i := 0; i < len(str); i++ {
// Append faded letter to info string.
s := pterm.NewRGBStyle(white.Fade(0, float32(len(str)), float32(i), green), red.Fade(0, float32(len(str)), float32(i), black))

// if the next letters are "Bold", then add the style "Bold".
// else if the next letters are "Italic", then add the style "Italic".
if bold < len(boldStr) && i+len(boldStr) <= len(strs) {
if strings.Join(strs[i:i+len(boldStr)-bold], "") == strings.Join(boldStr[bold:], "") {
s = s.AddOptions(pterm.Bold)
bold++
}
} else if italic < len(italicStr) && i+len(italicStr)-italic < len(strs) {
if strings.Join(strs[i:i+len(italicStr)-italic], "") == strings.Join(italicStr[italic:], "") {
s = s.AddOptions(pterm.Italic)
italic++
}
}
fade3 += s.Sprint(strs[i])
}

pterm.Println(fade3)
}
19 changes: 19 additions & 0 deletions _examples/coloring/print-color-rgb-style/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"github.com/pterm/pterm"
)

func main() {
foregroundRGB := pterm.RGB{R: 187, G: 80, B: 0}
backgroundRGB := pterm.RGB{R: 0, G: 50, B: 123}

// Print string with a custom foreground and background RGB color.
pterm.NewRGBStyle(foregroundRGB, backgroundRGB).Println("This text is not styled.")

// Print string with a custom foreground and background RGB color and style bold.
pterm.NewRGBStyle(foregroundRGB, backgroundRGB).AddOptions(pterm.Bold).Println("This text is bold.")

// Print string with a custom foreground and background RGB color and style italic.
pterm.NewRGBStyle(foregroundRGB, backgroundRGB).AddOptions(pterm.Italic).Println("This text is italic.")
}
135 changes: 135 additions & 0 deletions rgb.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,133 @@ type RGB struct {
Background bool
}

type RGBStyle struct {
Options []Color
Foreground, Background RGB

hasBg bool
}

// NewRGBStyle returns a new RGBStyle.
// The foreground color is required, the background color is optional.
// The colors will be set as is, ignoring the RGB.Background property.
func NewRGBStyle(foreground RGB, background ...RGB) RGBStyle {
var s RGBStyle
s.Foreground = foreground
if len(background) > 0 {
s.Background = background[0]
s.hasBg = true
}
return s
}

// AddOptions adds options to the RGBStyle.
func (p RGBStyle) AddOptions(opts ...Color) RGBStyle {
p.Options = append(p.Options, opts...)
return p
}

// Print formats using the default formats for its operands and writes to standard output.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
func (p RGBStyle) Print(a ...interface{}) *TextPrinter {
Print(p.Sprint(a...))
tp := TextPrinter(p)
return &tp
}

// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func (p RGBStyle) Println(a ...interface{}) *TextPrinter {
Println(p.Sprint(a...))
tp := TextPrinter(p)
return &tp
}

// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
func (p RGBStyle) Printf(format string, a ...interface{}) *TextPrinter {
Printf(format, p.Sprint(a...))
tp := TextPrinter(p)
return &tp
}

// Printfln formats according to a format specifier and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func (p RGBStyle) Printfln(format string, a ...interface{}) *TextPrinter {
Printf(format, p.Sprint(a...))
tp := TextPrinter(p)
return &tp
}

// PrintOnError prints every error which is not nil.
// If every error is nil, nothing will be printed.
// This can be used for simple error checking.
func (p RGBStyle) PrintOnError(a ...interface{}) *TextPrinter {
for _, arg := range a {
if err, ok := arg.(error); ok {
if err != nil {
p.Println(err)
}
}
}

tp := TextPrinter(p)
return &tp
}

// PrintOnErrorf wraps every error which is not nil and prints it.
// If every error is nil, nothing will be printed.
// This can be used for simple error checking.
func (p RGBStyle) PrintOnErrorf(format string, a ...interface{}) *TextPrinter {
for _, arg := range a {
if err, ok := arg.(error); ok {
if err != nil {
p.Println(fmt.Errorf(format, err))
}
}
}

tp := TextPrinter(p)
return &tp
}

// Sprint formats using the default formats for its operands and returns the resulting string.
// Spaces are added between operands when neither is a string.
func (p RGBStyle) Sprint(a ...interface{}) string {
var rgbStyle *color.RGBStyle
if !p.hasBg {
rgbStyle = color.NewRGBStyle(color.RGB(p.Foreground.R, p.Foreground.G, p.Foreground.B))
} else {
rgbStyle = color.NewRGBStyle(color.RGB(p.Foreground.R, p.Foreground.G, p.Foreground.B), color.RGB(p.Background.R, p.Background.G, p.Background.B))
}
if len(p.Options) > 0 {
for _, opt := range p.Options {
rgbStyle.AddOpts(color.Color(opt))
}
}
return rgbStyle.Sprint(a...)
}

// Sprintln formats using the default formats for its operands and returns the resulting string.
// Spaces are always added between operands and a newline is appended.
func (p RGBStyle) Sprintln(a ...interface{}) string {
return p.Sprint(a...) + "\n"
}

// Sprintf formats according to a format specifier and returns the resulting string.
func (p RGBStyle) Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, p.Sprint(a...))
}

// Sprintfln formats according to a format specifier and returns the resulting string.
// Spaces are always added between operands and a newline is appended.
func (p RGBStyle) Sprintfln(format string, a ...interface{}) string {
return fmt.Sprintf(format, p.Sprint(a...)) + "\n"
}

// GetValues returns the RGB values separately.
func (p RGB) GetValues() (r, g, b uint8) {
return p.R, p.G, p.B
Expand Down Expand Up @@ -160,3 +287,11 @@ func (p RGB) PrintOnErrorf(format string, a ...interface{}) *TextPrinter {
tp := TextPrinter(p)
return &tp
}

func (p RGB) ToRGBStyle() RGBStyle {
if p.Background {
return RGBStyle{Background: p}
}

return RGBStyle{Foreground: p}
}
68 changes: 68 additions & 0 deletions rgb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,71 @@ func TestRGB_PrintIfError_WithoutErrorf(t *testing.T) {
})
}
}

func TestRGBStyle_PrintOnError(t *testing.T) {
RGBs := []pterm.RGBStyle{{Foreground: pterm.RGB{R: 10, G: 10, B: 10}}, {Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold}},
{Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("PrintOnError", func(t *testing.T) {
result := captureStdout(func(w io.Writer) {
rgb.PrintOnError(errors.New("hello world"))
})
testza.AssertContains(t, result, "hello world")
})
}
}

func TestRGBStyle_PrintIfError_WithoutError(t *testing.T) {
RGBs := []pterm.RGBStyle{{Foreground: pterm.RGB{R: 10, G: 10, B: 10}}, {Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold}},
{Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("PrintIfError_WithoutError", func(t *testing.T) {
result := captureStdout(func(w io.Writer) {
rgb.PrintOnError(nil)
})
testza.AssertZero(t, result)
})
}
}

func TestRGBStyle_PrintOnErrorf(t *testing.T) {
RGBs := []pterm.RGBStyle{{Foreground: pterm.RGB{R: 10, G: 10, B: 10}}, {Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold}},
{Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("PrintOnErrorf", func(t *testing.T) {
result := captureStdout(func(w io.Writer) {
rgb.PrintOnErrorf("wrapping error : %w", errors.New("hello world"))
})
testza.AssertContains(t, result, "hello world")
})
}
}

func TestRGBStyle_PrintIfError_WithoutErrorf(t *testing.T) {
RGBs := []pterm.RGBStyle{{Foreground: pterm.RGB{R: 10, G: 10, B: 10}}, {Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold}},
{Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("PrintIfError_WithoutErrorf", func(t *testing.T) {
result := captureStdout(func(w io.Writer) {
rgb.PrintOnErrorf("", nil)
})
testza.AssertZero(t, result)
})
}
}

func TestRGBStyle_NewRGBStyle(t *testing.T) {
RGBs := []pterm.RGBStyle{{Foreground: pterm.RGB{R: 10, G: 10, B: 10}}, {Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold}},
{Foreground: pterm.RGB{R: 0, G: 0, B: 255}, Background: pterm.RGB{R: 255, G: 0, B: 255}, Options: []pterm.Color{pterm.Bold, pterm.Italic}}}
for _, rgb := range RGBs {
t.Run("NewRGBStyle", func(t *testing.T) {
result := pterm.NewRGBStyle(rgb.Foreground, rgb.Background)
if len(rgb.Options) > 0 {
result = result.AddOptions(rgb.Options...)
}
testza.AssertEqual(t, rgb.Foreground, result.Foreground)
testza.AssertEqual(t, rgb.Background, result.Background)
testza.AssertEqual(t, rgb.Options, result.Options)
})
}
}

0 comments on commit ff7c49b

Please sign in to comment.