mirror of
https://github.com/zoriya/bubbles.git
synced 2026-08-05 04:36:07 +00:00
fix(progress): last gradient color off by one (#338)
* fix(progress): add failing test The progress bar component does not render the gradient 100% correctly: The very last color that appears in the rendered progress bar should be the second color defined in the gradient but it is not, due to an off-by-one one error. This test case shows this. The next commit will contain the fix. * fix(progress): use the second gradient color as the last char of the bar Due to an off-by-one error, the very last rendered color+char was never exactly the second specified gradient color. The fixed code looks less elegant unfortunately, but now the last color is the desired one.
This commit is contained in:
@@ -299,10 +299,15 @@ func (m Model) barView(b *strings.Builder, percent float64, textWidth int) {
|
||||
if m.useRamp {
|
||||
// Gradient fill
|
||||
for i := 0; i < fw; i++ {
|
||||
if m.scaleRamp {
|
||||
p = float64(i) / float64(fw)
|
||||
if fw == 1 {
|
||||
// this is up for debate: in a gradient of width=1, should the
|
||||
// single character rendered be the first color, the last color
|
||||
// or exactly 50% inbetween? I opted for 50%
|
||||
p = 0.5
|
||||
} else if m.scaleRamp {
|
||||
p = float64(i) / float64(fw-1)
|
||||
} else {
|
||||
p = float64(i) / float64(tw)
|
||||
p = float64(i) / float64(tw-1)
|
||||
}
|
||||
c := m.rampColorA.BlendLuv(m.rampColorB, p).Hex()
|
||||
b.WriteString(termenv.
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package progress
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
const (
|
||||
AnsiReset = "\x1b[0m"
|
||||
)
|
||||
|
||||
func TestGradient(t *testing.T) {
|
||||
|
||||
colA := "#FF0000"
|
||||
colB := "#00FF00"
|
||||
|
||||
var p Model
|
||||
var descr string
|
||||
|
||||
for _, scale := range []bool{false, true} {
|
||||
opts := []Option{
|
||||
WithColorProfile(termenv.TrueColor), WithoutPercentage(),
|
||||
}
|
||||
if scale {
|
||||
descr = "progress bar with scaled gradient"
|
||||
opts = append(opts, WithScaledGradient(colA, colB))
|
||||
} else {
|
||||
descr = "progress bar with gradient"
|
||||
opts = append(opts, WithGradient(colA, colB))
|
||||
}
|
||||
|
||||
t.Run(descr, func(t *testing.T) {
|
||||
p = New(opts...)
|
||||
|
||||
// build the expected colors by colorizing an empty string and then cutting off the following reset sequence
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(termenv.String("").Foreground(p.color(colA)).String())
|
||||
expFirst := strings.Split(sb.String(), AnsiReset)[0]
|
||||
sb.Reset()
|
||||
sb.WriteString(termenv.String("").Foreground(p.color(colB)).String())
|
||||
expLast := strings.Split(sb.String(), AnsiReset)[0]
|
||||
|
||||
for _, width := range []int{3, 5, 50} {
|
||||
p.Width = width
|
||||
res := p.ViewAs(1.0)
|
||||
|
||||
// extract colors from the progrss bar by splitting at p.Full+AnsiReset, leaving us with just the color sequences
|
||||
colors := strings.Split(res, string(p.Full)+AnsiReset)
|
||||
|
||||
// discard the last color, because it is empty (no new color comes after the last char of the bar)
|
||||
colors = colors[0 : len(colors)-1]
|
||||
|
||||
if expFirst != colors[0] {
|
||||
t.Errorf("expected first color of bar to be first gradient color %q, instead got %q", expFirst, colors[0])
|
||||
}
|
||||
|
||||
if expLast != colors[len(colors)-1] {
|
||||
t.Errorf("expected last color of bar to be second gradient color %q, instead got %q", expLast, colors[len(colors)-1])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user