Skip to content

Commit

Permalink
fixes #606 Want ColorNone to preserve existing color(s)
Browse files Browse the repository at this point in the history
While here, consolidate the use of the Fill() function from
CellBuffer (eliminating redundant code).
  • Loading branch information
gdamore committed Dec 8, 2023
1 parent 8041b8e commit 337e381
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 22 deletions.
34 changes: 25 additions & 9 deletions cell.go
@@ -1,4 +1,4 @@
// Copyright 2022 The TCell Authors
// Copyright 2023 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
Expand Down Expand Up @@ -31,7 +31,7 @@ type cell struct {
lock bool
}

// CellBuffer represents a two dimensional array of character cells.
// CellBuffer represents a two-dimensional array of character cells.
// This is primarily intended for use by Screen implementors; it
// contains much of the common code they need. To create one, just
// declare a variable of its type; no explicit initialization is necessary.
Expand All @@ -44,7 +44,9 @@ type CellBuffer struct {
}

// SetContent sets the contents (primary rune, combining runes,
// and style) for a cell at a given location.
// and style) for a cell at a given location. If the background or
// foreground of the style is set to ColorNone, then the respective
// color is left un changed.
func (cb *CellBuffer) SetContent(x int, y int,
mainc rune, combc []rune, style Style,
) {
Expand All @@ -61,6 +63,12 @@ func (cb *CellBuffer) SetContent(x int, y int,
c.width = runewidth.RuneWidth(mainc)
}
c.currMain = mainc
if style.fg == ColorNone {
style.fg = c.currStyle.fg
}
if style.bg == ColorNone {
style.bg = c.currStyle.bg
}
c.currStyle = style
}
}
Expand Down Expand Up @@ -97,10 +105,9 @@ func (cb *CellBuffer) Invalidate() {
}
}

// Dirty checks if a character at the given location needs an
// to be refreshed on the physical display. This returns true
// if the cell content is different since the last time it was
// marked clean.
// Dirty checks if a character at the given location needs to be
// refreshed on the physical display. This returns true if the cell
// content is different since the last time it was marked clean.
func (cb *CellBuffer) Dirty(x, y int) bool {
if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
c := &cb.cells[(y*cb.w)+x]
Expand Down Expand Up @@ -204,12 +211,21 @@ func (cb *CellBuffer) Resize(w, h int) {
// Fill fills the entire cell buffer array with the specified character
// and style. Normally choose ' ' to clear the screen. This API doesn't
// support combining characters, or characters with a width larger than one.
// If either the foreground or background are ColorNone, then the respective
// color is unchanged.
func (cb *CellBuffer) Fill(r rune, style Style) {
for i := range cb.cells {
c := &cb.cells[i]
c.currMain = r
c.currComb = nil
c.currStyle = style
cs := style
if cs.fg == ColorNone {
cs.fg = c.currStyle.fg
}
if cs.bg == ColorNone {
cs.bg = c.currStyle.bg
}
c.currStyle = cs
c.width = 1
}
}
Expand All @@ -224,7 +240,7 @@ func init() {
runewidth.DefaultCondition.EastAsianWidth = false
}

// For performance reasons, we create a lookup table. However some users
// For performance reasons, we create a lookup table. However, some users
// might be more memory conscious. If that's you, set the TCELL_MINIMIZE
// environment variable.
if os.Getenv("TCELL_MINIMIZE") == "" {
Expand Down
21 changes: 17 additions & 4 deletions color.go
@@ -1,4 +1,4 @@
// Copyright 2020 The TCell Authors
// Copyright 2023 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
Expand Down Expand Up @@ -839,6 +839,11 @@ const (
// ColorReset is used to indicate that the color should use the
// vanilla terminal colors. (Basically go back to the defaults.)
ColorReset = ColorSpecial | iota

// ColorNone indicates that we should not change the color from
// whatever is already displayed. This can only be used in limited
// circumstances.
ColorNone
)

// ColorNames holds the written names of colors. Useful to present a list of
Expand Down Expand Up @@ -1002,8 +1007,8 @@ func (c Color) IsRGB() bool {
return c&(ColorValid|ColorIsRGB) == (ColorValid | ColorIsRGB)
}

// CSS returns the CSS hex string ( #ABCDEF ) if valid
// if not a valid color returns empty string
// CSS returns the CSS hex string ( #ABCDEF ) if valid
// if not a valid color returns empty string
func (c Color) CSS() string {
if !c.Valid() {
return ""
Expand All @@ -1015,13 +1020,21 @@ func (c Color) CSS() string {
// W3C name if it has one or the CSS hex string '#ABCDEF'
func (c Color) String() string {
if !c.Valid() {
switch c {
case ColorNone:
return "none"
case ColorDefault:
return "default"
case ColorReset:
return "reset"
}
return ""
}
return c.Name(true)
}

// Name returns W3C name or an empty string if no arguments
// if passed true as an argument it will falls back to
// if passed true as an argument it will falls back to
// the CSS hex string if no W3C name found '#ABCDEF'
func (c Color) Name(css ...bool) string {
for name, hex := range ColorNames {
Expand Down
46 changes: 44 additions & 2 deletions color_test.go
@@ -1,4 +1,4 @@
// Copyright 2018 The TCell Authors
// Copyright 2023 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
Expand Down Expand Up @@ -46,7 +46,7 @@ func TestColorValues(t *testing.T) {
}

func TestColorFitting(t *testing.T) {
pal := []Color{}
var pal []Color
for i := 0; i < 255; i++ {
pal = append(pal, PaletteColor(i))
}
Expand Down Expand Up @@ -108,6 +108,17 @@ func TestColorNameLookup(t *testing.T) {
t.Errorf("TrueColor did not match")
}
}

// these colors only have strings (for debugging), you cannot use them to create a color
if ColorNone.String() != "none" {
t.Errorf("ColorNone did not match")
}
if ColorReset.String() != "reset" {
t.Errorf("ColorReset did not match")
}
if ColorDefault.String() != "default" {
t.Errorf("ColorDefault did not match")
}
}

func TestColorRGB(t *testing.T) {
Expand All @@ -132,3 +143,34 @@ func TestFromImageColor(t *testing.T) {
t.Errorf("%v is not 0x00FFFF", hex)
}
}

func TestColorNone(t *testing.T) {
s := mkTestScreen(t, "")
s.Init()
s.SetSize(80, 24)
st := StyleDefault.Foreground(ColorBlack).Background(ColorWhite)
s.Fill(' ', st)
if _, _, s1, _ := s.GetContent(0, 0); s1 != st {
t.Errorf("Wrong style! fg %s bg %s", s1.fg.String(), s1.bg.String())
}
st2 := st.Foreground(ColorNone).Background(ColorNone)
s.Fill('X', st2)
if _, _, s1, _ := s.GetContent(0, 0); s1 != st {
t.Errorf("Wrong style! fg %s bg %s", s1.fg.String(), s1.bg.String())
}
red := st.Foreground(ColorRed).Background(ColorNone)
s.SetContent(1, 0, ' ', nil, red)
if _, _, s1, _ := s.GetContent(1, 0); s1 != red.Background(st.bg) {
t.Errorf("Wrong style! fg %s bg %s", s1.fg.String(), s1.bg.String())
}
if _, _, s1, _ := s.GetContent(0, 0); s1 != st {
t.Errorf("Wrong style! fg %s bg %s", s1.fg.String(), s1.bg.String())
}
pink := st.Background(ColorPink).Foreground(ColorNone)
s.SetContent(1, 0, ' ', nil, pink)
combined := pink.Foreground(ColorRed)

if _, _, s1, _ := s.GetContent(1, 0); s1 != combined {
t.Errorf("Wrong style! fg %s bg %s", s1.fg.String(), s1.bg.String())
}
}
8 changes: 1 addition & 7 deletions screen.go
Expand Up @@ -376,13 +376,7 @@ func (b *baseScreen) Clear() {
func (b *baseScreen) Fill(r rune, style Style) {
cb := b.GetCells()
b.Lock()
for i := range cb.cells {
c := &cb.cells[i]
c.currMain = r
c.currComb = nil
c.currStyle = style
c.width = 1
}
cb.Fill(r, style)
b.Unlock()
}

Expand Down

0 comments on commit 337e381

Please sign in to comment.