Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added RGBStyle #492

Merged
merged 7 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 67 additions & 0 deletions _examples/coloring/fade-colors-rgb-style/main.go
@@ -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
@@ -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.")
}
140 changes: 140 additions & 0 deletions rgb.go
Expand Up @@ -18,6 +18,138 @@ 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
}

// GetValues returns the foreground, background and options of the RGBStyle.
func (p RGBStyle) GetValues() (RGB, RGB, []Color) {
return p.Foreground, p.Background, p.Options
}
MarvinJWendt marked this conversation as resolved.
Show resolved Hide resolved

// 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 +292,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
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)
})
}
}