170 lines
3.4 KiB
Go
170 lines
3.4 KiB
Go
package clui
|
|
|
|
import (
|
|
term "github.com/nsf/termbox-go"
|
|
)
|
|
|
|
// TextElementType type of the parsed element of the string
|
|
type TextElementType int
|
|
|
|
// TextElementType values
|
|
const (
|
|
// ElemPrintable - the item is a rune
|
|
ElemPrintable = iota
|
|
// ElemBackColor - the item sets new background color
|
|
ElemBackColor
|
|
// ElemTextColor - the item sets new text color
|
|
ElemTextColor
|
|
// ElemLineBreak - line break
|
|
ElemLineBreak
|
|
// ElemEndOfText - the string parsing has complited
|
|
ElemEndOfText
|
|
)
|
|
|
|
// TextElement is currently parsed text element
|
|
type TextElement struct {
|
|
// Type is an element type
|
|
Type TextElementType
|
|
// Ch is a parsed rune, it is filled only if Type is ElemPrintable
|
|
Ch rune
|
|
// Fg is a text color for the rune
|
|
Fg term.Attribute
|
|
// Bg is a background color for the rune
|
|
Bg term.Attribute
|
|
}
|
|
|
|
// ColorParser is a string parser to process a text with color tags
|
|
// inside the string
|
|
type ColorParser struct {
|
|
text []rune
|
|
index int
|
|
defBack term.Attribute
|
|
defText term.Attribute
|
|
currBack term.Attribute
|
|
currText term.Attribute
|
|
}
|
|
|
|
// NewColorParser creates a new string parser.
|
|
// str is a string to parse.
|
|
// defText is a default text color.
|
|
// defBack is a default background color.
|
|
// Default colors are applied in case of reset color tag
|
|
func NewColorParser(str string, defText, defBack term.Attribute) *ColorParser {
|
|
p := new(ColorParser)
|
|
p.text = []rune(str)
|
|
p.defBack, p.defText = defBack, defText
|
|
p.currBack, p.currText = defBack, defText
|
|
return p
|
|
}
|
|
|
|
func (p *ColorParser) parseColor() (term.Attribute, TextElementType, bool) {
|
|
newIdx := p.index + 1
|
|
length := len(p.text)
|
|
ok := true
|
|
|
|
const (
|
|
StepType = iota
|
|
StepColon
|
|
StepValue
|
|
)
|
|
|
|
var (
|
|
cText string
|
|
attr term.Attribute
|
|
t TextElementType
|
|
done bool
|
|
)
|
|
step := StepType
|
|
|
|
for {
|
|
if newIdx >= length {
|
|
ok = false
|
|
break
|
|
}
|
|
|
|
switch step {
|
|
case StepType:
|
|
c := p.text[newIdx]
|
|
if c == 't' || c == 'f' {
|
|
t = ElemTextColor
|
|
} else if c == 'b' {
|
|
t = ElemBackColor
|
|
} else {
|
|
ok = false
|
|
break
|
|
}
|
|
step = StepColon
|
|
newIdx++
|
|
case StepColon:
|
|
c := p.text[newIdx]
|
|
if c != ':' {
|
|
ok = false
|
|
break
|
|
}
|
|
newIdx++
|
|
step = StepValue
|
|
case StepValue:
|
|
c := p.text[newIdx]
|
|
if c == '>' {
|
|
p.index = newIdx + 1
|
|
if cText == "" {
|
|
attr = ColorDefault
|
|
} else {
|
|
attr = StringToColor(cText)
|
|
}
|
|
done = true
|
|
break
|
|
} else {
|
|
if c != ' ' || cText != "" {
|
|
cText += string(c)
|
|
}
|
|
newIdx++
|
|
}
|
|
}
|
|
|
|
if done || !ok {
|
|
break
|
|
}
|
|
}
|
|
|
|
return attr, t, ok
|
|
}
|
|
|
|
// NextElement parses and returns the next string element
|
|
func (p *ColorParser) NextElement() TextElement {
|
|
if p.index >= len(p.text) {
|
|
return TextElement{Type: ElemEndOfText}
|
|
}
|
|
|
|
if p.text[p.index] == '\n' {
|
|
p.index++
|
|
return TextElement{Type: ElemLineBreak}
|
|
}
|
|
|
|
if p.text[p.index] != '<' {
|
|
p.index++
|
|
return TextElement{Type: ElemPrintable, Ch: p.text[p.index-1], Fg: p.currText, Bg: p.currBack}
|
|
}
|
|
|
|
attr, atype, ok := p.parseColor()
|
|
if !ok {
|
|
p.index++
|
|
return TextElement{Type: ElemPrintable, Ch: p.text[p.index-1], Fg: p.currText, Bg: p.currBack}
|
|
}
|
|
|
|
if atype == ElemBackColor {
|
|
if attr == ColorDefault {
|
|
p.currBack = p.defBack
|
|
} else {
|
|
p.currBack = attr
|
|
}
|
|
} else if atype == ElemTextColor {
|
|
if attr == ColorDefault {
|
|
p.currText = p.defText
|
|
} else {
|
|
p.currText = attr
|
|
}
|
|
}
|
|
return TextElement{Type: atype, Fg: p.currText, Bg: p.currBack}
|
|
}
|