-
Notifications
You must be signed in to change notification settings - Fork 31
/
cell.go
170 lines (149 loc) · 5.33 KB
/
cell.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
// code is governed by the MIT license that can be found in the LICENSE
// file.
package gowid
//======================================================================
// Cell represents a single element of terminal output. The empty value
// is a blank cell with default colors, style, and a 'blank' rune. It is
// closely tied to TCell's underlying cell representation - colors are
// TCell-specific, so are translated from anything more general before a
// Cell is instantiated.
type Cell struct {
codePoint rune
fg TCellColor
bg TCellColor
style StyleAttrs
}
// MakeCell returns a Cell initialized with the supplied run (char to display),
// foreground color, background color and style attributes. Each color can specify
// "default" meaning whatever the terminal default foreground/background is, or
// "none" meaning no preference, allowing it to be overridden when laid on top
// of another Cell during the render process.
func MakeCell(codePoint rune, fg TCellColor, bg TCellColor, Attr StyleAttrs) Cell {
return Cell{
codePoint: codePoint,
fg: fg,
bg: bg,
style: Attr,
}
}
// MergeUnder returns a Cell representing the receiver merged "underneath" the
// Cell argument provided. This means the argument's rune value will be used
// unless it is "empty", and the cell's color and styling come from the
// argument's value in a similar fashion.
func (c Cell) MergeUnder(upper Cell) Cell {
res := c
if upper.codePoint != 0 {
res.codePoint = upper.codePoint
}
return res.MergeDisplayAttrsUnder(upper)
}
// MergeDisplayAttrsUnder returns a Cell representing the receiver Cell with the
// argument Cell's color and styling applied, if they are explicitly set.
func (c Cell) MergeDisplayAttrsUnder(upper Cell) Cell {
res := c
ufg, ubg, ust := upper.GetDisplayAttrs()
if ubg != ColorNone {
res = res.WithBackgroundColor(ubg)
}
if ufg != ColorNone {
res = res.WithForegroundColor(ufg)
}
res.style = res.style.MergeUnder(ust)
return res
}
// GetDisplayAttrs returns the receiver Cell's foreground and background color
// and styling.
func (c Cell) GetDisplayAttrs() (x TCellColor, y TCellColor, z StyleAttrs) {
x = c.ForegroundColor()
y = c.BackgroundColor()
z = c.Style()
return
}
// HasRune returns true if the Cell actively specifies a rune to display; otherwise
// false, meaning there it is "empty", and a Cell layered underneath it will have its
// rune displayed.
func (c Cell) HasRune() bool {
return c.codePoint != 0
}
// Rune will return a rune that can be displayed, if this Cell is being rendered in some
// fashion. If the Cell is empty, then a space rune is returned.
func (c Cell) Rune() rune {
if !c.HasRune() {
return ' '
} else {
return c.codePoint
}
}
// WithRune returns a Cell equal to the receiver Cell but that will render the supplied
// rune instead.
func (c Cell) WithRune(r rune) Cell {
c.codePoint = r
return c
}
// BackgroundColor returns the background color of the receiver Cell.
func (c Cell) BackgroundColor() TCellColor {
return c.bg
}
// ForegroundColor returns the foreground color of the receiver Cell.
func (c Cell) ForegroundColor() TCellColor {
return c.fg
}
// Style returns the style of the receiver Cell.
func (c Cell) Style() StyleAttrs {
return c.style
}
// WithRune returns a Cell equal to the receiver Cell but that will render no
// rune instead i.e. it is "empty".
func (c Cell) WithNoRune() Cell {
c.codePoint = 0
return c
}
// WithBackgroundColor returns a Cell equal to the receiver Cell but that
// will render with the supplied background color instead. Note that this color
// can be set to "none" by passing the value gowid.ColorNone, meaning allow
// Cells layered underneath to determine the background color.
func (c Cell) WithBackgroundColor(a TCellColor) Cell {
c.bg = a
return c
}
// WithForegroundColor returns a Cell equal to the receiver Cell but that
// will render with the supplied foreground color instead. Note that this color
// can be set to "none" by passing the value gowid.ColorNone, meaning allow
// Cells layered underneath to determine the background color.
func (c Cell) WithForegroundColor(a TCellColor) Cell {
c.fg = a
return c
}
// WithStyle returns a Cell equal to the receiver Cell but that will render
// with the supplied style (e.g. underline) instead. Note that this style
// can be set to "none" by passing the value gowid.AttrNone, meaning allow
// Cells layered underneath to determine the style.
func (c Cell) WithStyle(attr StyleAttrs) Cell {
c.style = attr
return c
}
//======================================================================
// CellFromRune returns a Cell with the supplied rune and with default
// coloring and styling.
func CellFromRune(r rune) Cell {
return MakeCell(r, ColorNone, ColorNone, StyleNone)
}
// CellsFromString is a utility function to turn a string into an array
// of Cells. Note that each Cell has no color or style set.
func CellsFromString(s string) []Cell {
res := make([]Cell, 0, len(s)) // overcommits, counts chars and not runes, but minimizes reallocations.
for _, r := range s {
if r != ' ' {
res = append(res, CellFromRune(r))
} else {
res = append(res, Cell{})
}
}
return res
}
//======================================================================
// Local Variables:
// mode: Go
// fill-column: 110
// End: