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:
parent
64819efa3c
commit
2071fd15bb
@ -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
203
cell/color_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
50
terminal/termbox/cell_options_test.go
Normal file
50
terminal/termbox/cell_options_test.go
Normal 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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user