1
1
mirror of https://github.com/walles/moar.git synced 2024-11-26 13:46:16 +03:00
moar/twin/styles.go
Steven Penny f76fd6ddd3 Simplify StyleDefault definition
StyleDefault is all zero values, so we can simplify the definition. If that
changes in the future (not likely) we can go back to the old definition method.

Fixes #57
2021-05-11 08:14:15 -05:00

180 lines
3.8 KiB
Go

package twin
import (
"fmt"
"strings"
)
type AttrMask uint
const (
AttrBold AttrMask = 1 << iota
AttrBlink
AttrReverse
AttrUnderline
AttrDim
AttrItalic
AttrStrikeThrough
AttrNone AttrMask = 0 // Normal text
)
type Style struct {
fg Color
bg Color
attrs AttrMask
}
var StyleDefault Style
func (style Style) String() string {
if style.attrs == AttrNone {
return fmt.Sprint(style.fg, " on ", style.bg)
}
attrNames := make([]string, 0)
if style.attrs.has(AttrBold) {
attrNames = append(attrNames, "bold")
}
if style.attrs.has(AttrBlink) {
attrNames = append(attrNames, "blinking")
}
if style.attrs.has(AttrReverse) {
attrNames = append(attrNames, "reverse")
}
if style.attrs.has(AttrUnderline) {
attrNames = append(attrNames, "underlined")
}
if style.attrs.has(AttrDim) {
attrNames = append(attrNames, "dim")
}
if style.attrs.has(AttrItalic) {
attrNames = append(attrNames, "italic")
}
if style.attrs.has(AttrStrikeThrough) {
attrNames = append(attrNames, "strikethrough")
}
return fmt.Sprint(strings.Join(attrNames, " "), " ", style.fg, " on ", style.bg)
}
func (style Style) WithAttr(attr AttrMask) Style {
result := Style{
fg: style.fg,
bg: style.bg,
attrs: style.attrs | attr,
}
// Bold and dim are mutually exclusive
if attr.has(AttrBold) {
return result.WithoutAttr(AttrDim)
}
if attr.has(AttrDim) {
return result.WithoutAttr(AttrBold)
}
return result
}
func (style Style) WithoutAttr(attr AttrMask) Style {
return Style{
fg: style.fg,
bg: style.bg,
attrs: style.attrs & ^attr,
}
}
func (attr AttrMask) has(attrs AttrMask) bool {
return attr&attrs != 0
}
func (style Style) Background(color Color) Style {
return Style{
fg: style.fg,
bg: color,
attrs: style.attrs,
}
}
func (style Style) Foreground(color Color) Style {
return Style{
fg: color,
bg: style.bg,
attrs: style.attrs,
}
}
// Emit an ANSI escape sequence switching from a previous style to the current
// one.
func (current Style) RenderUpdateFrom(previous Style) string {
var builder strings.Builder
if current.fg != previous.fg {
builder.WriteString(current.fg.ForegroundAnsiString())
}
if current.bg != previous.bg {
builder.WriteString(current.bg.BackgroundAnsiString())
}
// Handle AttrDim / AttrBold changes
previousBoldDim := previous.attrs & (AttrBold | AttrDim)
currentBoldDim := current.attrs & (AttrBold | AttrDim)
if currentBoldDim != previousBoldDim {
if previousBoldDim != 0 {
builder.WriteString("\x1b[22m") // Reset to neither bold nor dim
}
if current.attrs.has(AttrBold) {
builder.WriteString("\x1b[1m")
}
if current.attrs.has(AttrDim) {
builder.WriteString("\x1b[2m")
}
}
// Handle AttrBlink changes
if current.attrs.has(AttrBlink) != previous.attrs.has(AttrBlink) {
if current.attrs.has(AttrBlink) {
builder.WriteString("\x1b[5m")
} else {
builder.WriteString("\x1b[25m")
}
}
// Handle AttrReverse changes
if current.attrs.has(AttrReverse) != previous.attrs.has(AttrReverse) {
if current.attrs.has(AttrReverse) {
builder.WriteString("\x1b[7m")
} else {
builder.WriteString("\x1b[27m")
}
}
// Handle AttrUnderline changes
if current.attrs.has(AttrUnderline) != previous.attrs.has(AttrUnderline) {
if current.attrs.has(AttrUnderline) {
builder.WriteString("\x1b[4m")
} else {
builder.WriteString("\x1b[24m")
}
}
// Handle AttrItalic changes
if current.attrs.has(AttrItalic) != previous.attrs.has(AttrItalic) {
if current.attrs.has(AttrItalic) {
builder.WriteString("\x1b[3m")
} else {
builder.WriteString("\x1b[23m")
}
}
// Handle AttrStrikeThrough changes
if current.attrs.has(AttrStrikeThrough) != previous.attrs.has(AttrStrikeThrough) {
if current.attrs.has(AttrStrikeThrough) {
builder.WriteString("\x1b[9m")
} else {
builder.WriteString("\x1b[29m")
}
}
return builder.String()
}