1
0
mirror of https://github.com/mum4k/termdash.git synced 2025-04-25 13:48:50 +08:00

Support for 256 colors.

- Documenting color modes.
- Providing helper functions to set colors in different formats.
- Allowing users to set other than the 8 system colors.

Fixes #66
This commit is contained in:
Jakub Sobon 2019-01-13 21:53:26 -05:00
parent 64819efa3c
commit 2071fd15bb
No known key found for this signature in database
GPG Key ID: F2451A77FB05D3B7
7 changed files with 331 additions and 51 deletions

View File

@ -14,6 +14,10 @@
package cell
import (
"fmt"
)
// color.go defines constants for cell colors.
// Color is the color of a cell.
@ -24,7 +28,7 @@ func (cc Color) String() string {
if n, ok := colorNames[cc]; ok {
return n
}
return "ColorUnknown"
return fmt.Sprintf("Color:%d", cc)
}
// colorNames maps Color values to human readable names.
@ -43,6 +47,8 @@ var colorNames = map[Color]string{
// The supported terminal colors.
const (
ColorDefault Color = iota
// 8 "system" colors.
ColorBlack
ColorRed
ColorGreen
@ -52,3 +58,49 @@ const (
ColorCyan
ColorWhite
)
// ColorNumber sets a color using its number.
// Make sure your terminal is set to a terminalapi.ColorMode that supports the
// target color. The provided value must be in the range 0-255.
// Larger or smaller values will be reset to the default color.
//
// For reference on these colors see the Xterm number in:
// https://jonasjacek.github.io/colors/
func ColorNumber(n int) Color {
if n < 0 || n > 255 {
return ColorDefault
}
return Color(n + 1) // Colors are off-by-one due to ColorDefault being zero.
}
// ColorRGB6 sets a color using the 6x6x6 terminal color.
// Make sure your terminal is set to the terminalapi.ColorMode256 mode.
// The provided values (r, g, b) must be in the range 0-5.
// Larger or smaller values will be reset to the default color.
//
// For reference on these colors see:
// https://superuser.com/questions/783656/whats-the-deal-with-terminal-colors
func ColorRGB6(r, g, b int) Color {
for _, c := range []int{r, g, b} {
if c < 0 || c > 5 {
return ColorDefault
}
}
return Color(0x10 + 36*r + 6*g + b + 1) // Colors are off-by-one due to ColorDefault being zero.
}
// ColorRGB24 sets a color using the 24 bit web color scheme.
// Make sure your terminal is set to the terminalapi.ColorMode256 mode.
// The provided values (r, g, b) must be in the range 0-255.
// Larger or smaller values will be reset to the default color.
//
// For reference on these colors see the RGB column in:
// https://jonasjacek.github.io/colors/
func ColorRGB24(r, g, b int) Color {
for _, c := range []int{r, g, b} {
if c < 0 || c > 255 {
return ColorDefault
}
}
return ColorRGB6(r/51, g/51, b/51)
}

203
cell/color_test.go Normal file
View File

@ -0,0 +1,203 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cell
import "testing"
func TestColorNumber(t *testing.T) {
tests := []struct {
desc string
number int
want Color
}{
{
desc: "default when too small",
number: -1,
want: ColorDefault,
},
{
desc: "default when too large",
number: 256,
want: ColorDefault,
},
{
desc: "translates system color",
number: 0,
want: ColorBlack,
},
{
desc: "adds one to the value",
number: 42,
want: Color(43),
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got := ColorNumber(tc.number)
if got != tc.want {
t.Errorf("ColorNumber(%v) => %v, want %v", tc.number, got, tc.want)
}
})
}
}
func TestColorRGB6(t *testing.T) {
tests := []struct {
desc string
r, g, b int
want Color
}{
{
desc: "default when r too small",
r: -1,
g: 0,
b: 0,
want: ColorDefault,
},
{
desc: "default when r too large",
r: 6,
g: 0,
b: 0,
want: ColorDefault,
},
{
desc: "default when g too small",
r: 0,
g: -1,
b: 0,
want: ColorDefault,
},
{
desc: "default when g too large",
r: 0,
g: 6,
b: 0,
want: ColorDefault,
},
{
desc: "default when b too small",
r: 0,
g: 0,
b: -1,
want: ColorDefault,
},
{
desc: "default when b too large",
r: 0,
g: 0,
b: 6,
want: ColorDefault,
},
{
desc: "translates black",
r: 0,
g: 0,
b: 0,
want: Color(17),
},
{
desc: "adds one to value",
r: 2,
g: 1,
b: 3,
want: Color(98),
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got := ColorRGB6(tc.r, tc.g, tc.b)
if got != tc.want {
t.Errorf("ColorRGB6(%v, %v, %v) => %v, want %v", tc.r, tc.g, tc.b, got, tc.want)
}
})
}
}
func TestColorRGB24(t *testing.T) {
tests := []struct {
desc string
r, g, b int
want Color
}{
{
desc: "default when r too small",
r: -1,
g: 0,
b: 0,
want: ColorDefault,
},
{
desc: "default when r too large",
r: 256,
g: 0,
b: 0,
want: ColorDefault,
},
{
desc: "default when g too small",
r: 0,
g: -1,
b: 0,
want: ColorDefault,
},
{
desc: "default when g too large",
r: 0,
g: 256,
b: 0,
want: ColorDefault,
},
{
desc: "default when b too small",
r: 0,
g: 0,
b: -1,
want: ColorDefault,
},
{
desc: "default when b too large",
r: 0,
g: 0,
b: 256,
want: ColorDefault,
},
{
desc: "translates black",
r: 0,
g: 0,
b: 0,
want: Color(17),
},
{
desc: "adds one to value",
r: 95,
g: 255,
b: 135,
want: Color(85),
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got := ColorRGB24(tc.r, tc.g, tc.b)
if got != tc.want {
t.Errorf("ColorRGB24(%v, %v, %v) => %v, want %v", tc.r, tc.g, tc.b, got, tc.want)
}
})
}
}

View File

@ -17,44 +17,21 @@ package termbox
// cell_options.go converts termdash cell options to the termbox format.
import (
"fmt"
"github.com/mum4k/termdash/cell"
tbx "github.com/nsf/termbox-go"
)
// cellColor converts termdash cell color to the termbox format.
func cellColor(c cell.Color) (tbx.Attribute, error) {
switch c {
case cell.ColorDefault:
return tbx.ColorDefault, nil
case cell.ColorBlack:
return tbx.ColorBlack, nil
case cell.ColorRed:
return tbx.ColorRed, nil
case cell.ColorGreen:
return tbx.ColorGreen, nil
case cell.ColorYellow:
return tbx.ColorYellow, nil
case cell.ColorBlue:
return tbx.ColorBlue, nil
case cell.ColorMagenta:
return tbx.ColorMagenta, nil
case cell.ColorCyan:
return tbx.ColorCyan, nil
case cell.ColorWhite:
return tbx.ColorWhite, nil
default:
return 0, fmt.Errorf("don't know how to convert cell color %v to the termbox format", c)
}
func cellColor(c cell.Color) tbx.Attribute {
return tbx.Attribute(c)
}
// cellOptsToFg converts the cell options to the termbox foreground attribute.
func cellOptsToFg(opts *cell.Options) (tbx.Attribute, error) {
func cellOptsToFg(opts *cell.Options) tbx.Attribute {
return cellColor(opts.FgColor)
}
// cellOptsToBg converts the cell options to the termbox background attribute.
func cellOptsToBg(opts *cell.Options) (tbx.Attribute, error) {
func cellOptsToBg(opts *cell.Options) tbx.Attribute {
return cellColor(opts.BgColor)
}

View File

@ -0,0 +1,50 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package termbox
import (
"testing"
"github.com/mum4k/termdash/cell"
tbx "github.com/nsf/termbox-go"
)
func TestCellColor(t *testing.T) {
tests := []struct {
color cell.Color
want tbx.Attribute
}{
{cell.ColorDefault, tbx.ColorDefault},
{cell.ColorBlack, tbx.ColorBlack},
{cell.ColorRed, tbx.ColorRed},
{cell.ColorGreen, tbx.ColorGreen},
{cell.ColorYellow, tbx.ColorYellow},
{cell.ColorBlue, tbx.ColorBlue},
{cell.ColorMagenta, tbx.ColorMagenta},
{cell.ColorCyan, tbx.ColorCyan},
{cell.ColorWhite, tbx.ColorWhite},
{cell.Color(42), tbx.Attribute(42)},
}
for _, tc := range tests {
t.Run(tc.color.String(), func(t *testing.T) {
got := cellColor(tc.color)
if got != tc.want {
t.Errorf("cellColor(%v) => got %v, want %v", tc.color, got, tc.want)
}
})
}
}

View File

@ -24,7 +24,7 @@ import (
// colorMode converts termdash color modes to the termbox format.
func colorMode(cm terminalapi.ColorMode) (tbx.OutputMode, error) {
switch cm {
case terminalapi.ColorMode8:
case terminalapi.ColorModeNormal:
return tbx.OutputNormal, nil
case terminalapi.ColorMode256:
return tbx.Output256, nil

View File

@ -95,16 +95,7 @@ func (t *Terminal) Size() image.Point {
// Clear implements terminalapi.Terminal.Clear.
func (t *Terminal) Clear(opts ...cell.Option) error {
o := cell.NewOptions(opts...)
fg, err := cellOptsToFg(o)
if err != nil {
return err
}
bg, err := cellOptsToBg(o)
if err != nil {
return err
}
return tbx.Clear(fg, bg)
return tbx.Clear(cellOptsToFg(o), cellOptsToBg(o))
}
// Flush implements terminalapi.Terminal.Flush.
@ -125,16 +116,7 @@ func (t *Terminal) HideCursor() {
// SetCell implements terminalapi.Terminal.SetCell.
func (t *Terminal) SetCell(p image.Point, r rune, opts ...cell.Option) error {
o := cell.NewOptions(opts...)
fg, err := cellOptsToFg(o)
if err != nil {
return err
}
bg, err := cellOptsToBg(o)
if err != nil {
return err
}
tbx.SetCell(p.X, p.Y, r, fg, bg)
tbx.SetCell(p.X, p.Y, r, cellOptsToFg(o), cellOptsToBg(o))
return nil
}

View File

@ -29,7 +29,7 @@ func (cm ColorMode) String() string {
// colorModeNames maps ColorMode values to human readable names.
var colorModeNames = map[ColorMode]string{
ColorMode8: "ColorMode8",
ColorModeNormal: "ColorModeNormal",
ColorMode256: "ColorMode256",
ColorMode216: "ColorMode216",
ColorModeGrayscale: "ColorModeGrayscale",
@ -37,8 +37,24 @@ var colorModeNames = map[ColorMode]string{
// Supported color modes.
const (
ColorMode8 ColorMode = iota
// ColorModeNormal supports 8 "system" colors.
// These are defined as constants in the cell package.
ColorModeNormal ColorMode = iota
// ColorMode256 enables using any of the 256 terminal colors.
// 0-7: the 8 "system" colors accessible in ColorModeNormal.
// 8-15: the 8 "bright system" colors.
// 16-231: the 216 different terminal colors.
// 232-255: the 24 different shades of grey.
ColorMode256
// ColorMode216 supports only the third range of the ColorMode256, i.e the
// 216 different terminal colors. However in this mode the colors are zero
// based, so the called doesn't need to provide an offset.
ColorMode216
// ColorModeGrayscale supports only the fourth range of the ColorMode256,
// i.e the 24 different shades of grey. However in this mode the colors are
// zero based, so the called doesn't need to provide an offset.
ColorModeGrayscale
)