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