2017-12-15 22:03:01 +00:00
|
|
|
package tview
|
|
|
|
|
2017-12-16 21:48:26 +00:00
|
|
|
import (
|
|
|
|
"math"
|
2022-12-25 22:21:11 +00:00
|
|
|
"os"
|
2018-01-17 16:13:36 +00:00
|
|
|
"regexp"
|
2017-12-16 21:48:26 +00:00
|
|
|
|
2020-10-18 12:15:57 +00:00
|
|
|
"github.com/gdamore/tcell/v2"
|
2017-12-16 21:48:26 +00:00
|
|
|
)
|
2017-12-15 22:03:01 +00:00
|
|
|
|
2022-12-25 10:54:49 +00:00
|
|
|
// Text alignment within a box. Also used to align images.
|
2017-12-15 22:03:01 +00:00
|
|
|
const (
|
|
|
|
AlignLeft = iota
|
|
|
|
AlignCenter
|
|
|
|
AlignRight
|
2022-12-25 10:54:49 +00:00
|
|
|
AlignTop = 0
|
|
|
|
AlignBottom = 2
|
2017-12-15 22:03:01 +00:00
|
|
|
)
|
|
|
|
|
2018-01-17 16:13:36 +00:00
|
|
|
var (
|
2023-08-26 14:19:31 +00:00
|
|
|
// Regular expression used to escape style/region tags.
|
2018-04-12 11:09:06 +00:00
|
|
|
nonEscapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\]`)
|
2017-12-27 15:04:21 +00:00
|
|
|
|
2023-08-26 14:19:31 +00:00
|
|
|
// The number of colors available in the terminal.
|
|
|
|
availableColors = 256
|
2017-12-27 15:04:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Package initialization.
|
|
|
|
func init() {
|
2022-12-25 22:21:11 +00:00
|
|
|
// Determine the number of colors available in the terminal.
|
|
|
|
info, err := tcell.LookupTerminfo(os.Getenv("TERM"))
|
|
|
|
if err == nil {
|
|
|
|
availableColors = info.Colors
|
|
|
|
}
|
2017-12-27 15:04:21 +00:00
|
|
|
}
|
|
|
|
|
2017-12-26 00:07:30 +00:00
|
|
|
// Print prints text onto the screen into the given box at (x,y,maxWidth,1),
|
2018-01-06 21:49:12 +00:00
|
|
|
// not exceeding that box. "align" is one of AlignLeft, AlignCenter, or
|
|
|
|
// AlignRight. The screen's background color will not be changed.
|
2017-12-15 22:03:01 +00:00
|
|
|
//
|
2023-08-22 20:16:59 +00:00
|
|
|
// You can change the colors and text styles mid-text by inserting a style tag.
|
2018-04-01 19:19:10 +00:00
|
|
|
// See the package description for details.
|
2018-01-17 16:13:36 +00:00
|
|
|
//
|
2023-08-22 20:16:59 +00:00
|
|
|
// Returns the number of actual bytes of the text printed (including style tags)
|
2018-10-17 05:36:54 +00:00
|
|
|
// and the actual width used for the printed runes.
|
2018-01-11 14:45:52 +00:00
|
|
|
func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tcell.Color) (int, int) {
|
2023-08-22 20:16:59 +00:00
|
|
|
start, end, width := printWithStyle(screen, text, x, y, 0, maxWidth, align, tcell.StyleDefault.Foreground(color), true)
|
|
|
|
return end - start, width
|
2018-04-01 19:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// printWithStyle works like Print() but it takes a style instead of just a
|
2021-02-15 17:26:27 +00:00
|
|
|
// foreground color. The skipWidth parameter specifies the number of cells
|
2023-08-22 20:16:59 +00:00
|
|
|
// skipped at the beginning of the text. It returns the start index, end index
|
|
|
|
// (exclusively), and screen width of the text actually printed. If
|
|
|
|
// maintainBackground is "true", the existing screen background is not changed
|
|
|
|
// (i.e. the style's background color is ignored).
|
|
|
|
func printWithStyle(screen tcell.Screen, text string, x, y, skipWidth, maxWidth, align int, style tcell.Style, maintainBackground bool) (start, end, printedWidth int) {
|
2020-02-19 17:31:32 +00:00
|
|
|
totalWidth, totalHeight := screen.Size()
|
|
|
|
if maxWidth <= 0 || len(text) == 0 || y < 0 || y >= totalHeight {
|
2023-08-22 20:16:59 +00:00
|
|
|
return 0, 0, 0
|
2017-12-15 22:03:01 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 20:16:59 +00:00
|
|
|
// If we don't overwrite the background, we use the default color.
|
|
|
|
if maintainBackground {
|
|
|
|
style = style.Background(tcell.ColorDefault)
|
2017-12-15 22:03:01 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 20:16:59 +00:00
|
|
|
// Skip beginning and measure width.
|
2018-04-13 22:05:25 +00:00
|
|
|
var (
|
2023-08-22 20:16:59 +00:00
|
|
|
state *stepState
|
|
|
|
textWidth int
|
2018-04-13 22:05:25 +00:00
|
|
|
)
|
2023-08-22 20:16:59 +00:00
|
|
|
str := text
|
|
|
|
for len(str) > 0 {
|
|
|
|
_, str, state = step(str, state, stepOptionsStyle)
|
2021-02-15 17:26:27 +00:00
|
|
|
if skipWidth > 0 {
|
2023-08-22 20:16:59 +00:00
|
|
|
skipWidth -= state.Width()
|
|
|
|
if skipWidth <= 0 {
|
|
|
|
text = str
|
|
|
|
style = state.Style()
|
|
|
|
}
|
|
|
|
start += state.GrossLength()
|
|
|
|
} else {
|
|
|
|
textWidth += state.Width()
|
2021-02-15 17:26:27 +00:00
|
|
|
}
|
2023-08-22 20:16:59 +00:00
|
|
|
}
|
2021-02-15 17:26:27 +00:00
|
|
|
|
2023-08-22 20:16:59 +00:00
|
|
|
// Reduce all alignments to AlignLeft.
|
|
|
|
if align == AlignRight {
|
|
|
|
// Chop off characters on the left until it fits.
|
|
|
|
state = nil
|
|
|
|
for len(text) > 0 && textWidth > maxWidth {
|
|
|
|
_, text, state = step(text, state, stepOptionsStyle)
|
|
|
|
textWidth -= state.Width()
|
|
|
|
start += state.GrossLength()
|
|
|
|
style = state.Style()
|
2018-10-17 05:36:54 +00:00
|
|
|
}
|
2023-08-22 20:16:59 +00:00
|
|
|
x, maxWidth = x+maxWidth-textWidth, textWidth
|
|
|
|
} else if align == AlignCenter {
|
|
|
|
// Chop off characters on the left until it fits.
|
|
|
|
state = nil
|
|
|
|
subtracted := (textWidth - maxWidth) / 2
|
|
|
|
for len(text) > 0 && subtracted > 0 {
|
|
|
|
_, text, state = step(text, state, stepOptionsStyle)
|
|
|
|
subtracted -= state.Width()
|
|
|
|
textWidth -= state.Width()
|
|
|
|
start += state.GrossLength()
|
|
|
|
style = state.Style()
|
2018-10-17 05:36:54 +00:00
|
|
|
}
|
2023-08-22 20:16:59 +00:00
|
|
|
if textWidth < maxWidth {
|
|
|
|
x, maxWidth = x+maxWidth/2-textWidth/2, textWidth
|
2018-05-03 06:05:11 +00:00
|
|
|
}
|
2023-08-22 20:16:59 +00:00
|
|
|
}
|
2018-05-03 06:05:11 +00:00
|
|
|
|
2023-08-22 20:16:59 +00:00
|
|
|
// Draw left-aligned text.
|
|
|
|
end = start
|
|
|
|
rightBorder := x + maxWidth
|
|
|
|
state = &stepState{
|
|
|
|
unisegState: -1,
|
|
|
|
style: style,
|
|
|
|
}
|
|
|
|
for len(text) > 0 && x < rightBorder && x < totalWidth {
|
|
|
|
var c string
|
|
|
|
c, text, state = step(text, state, stepOptionsStyle)
|
|
|
|
if c == "" {
|
|
|
|
break // We don't care about the style at the end.
|
|
|
|
}
|
|
|
|
width := state.Width()
|
2021-02-15 17:26:27 +00:00
|
|
|
|
2023-08-26 14:19:31 +00:00
|
|
|
if width > 0 {
|
|
|
|
finalStyle := state.Style()
|
|
|
|
if maintainBackground {
|
|
|
|
_, backgroundColor, _ := finalStyle.Decompose()
|
|
|
|
if backgroundColor == tcell.ColorDefault {
|
|
|
|
_, _, existingStyle, _ := screen.GetContent(x, y)
|
|
|
|
_, background, _ := existingStyle.Decompose()
|
|
|
|
finalStyle = finalStyle.Background(background)
|
|
|
|
}
|
2023-08-22 20:16:59 +00:00
|
|
|
}
|
2023-08-26 14:19:31 +00:00
|
|
|
for offset := width - 1; offset >= 0; offset-- {
|
|
|
|
// To avoid undesired effects, we populate all cells.
|
|
|
|
runes := []rune(c)
|
|
|
|
if offset == 0 {
|
|
|
|
screen.SetContent(x+offset, y, runes[0], runes[1:], finalStyle)
|
|
|
|
} else {
|
|
|
|
screen.SetContent(x+offset, y, ' ', nil, finalStyle)
|
|
|
|
}
|
2018-09-25 15:31:49 +00:00
|
|
|
}
|
2018-05-03 06:05:11 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 20:16:59 +00:00
|
|
|
x += width
|
|
|
|
end += state.GrossLength()
|
|
|
|
printedWidth += width
|
|
|
|
}
|
2018-01-17 16:13:36 +00:00
|
|
|
|
2023-08-22 20:16:59 +00:00
|
|
|
return
|
2017-12-15 22:03:01 +00:00
|
|
|
}
|
2017-12-16 21:48:26 +00:00
|
|
|
|
|
|
|
// PrintSimple prints white text to the screen at the given position.
|
|
|
|
func PrintSimple(screen tcell.Screen, text string, x, y int) {
|
2018-01-12 07:35:30 +00:00
|
|
|
Print(screen, text, x, y, math.MaxInt32, AlignLeft, Styles.PrimaryTextColor)
|
2017-12-16 21:48:26 +00:00
|
|
|
}
|