Skip to content

Commit

Permalink
Underline API change.
Browse files Browse the repository at this point in the history
The underline styles are mutually exclusive.  And let's simplify
passing the color with the underline style in a single function call.
  • Loading branch information
gdamore committed Mar 5, 2024
1 parent 826c271 commit 9bc5c63
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 57 deletions.
14 changes: 7 additions & 7 deletions _demos/style.go
Expand Up @@ -155,31 +155,31 @@ func main() {
puts(s, style, 2, row, "Strikethrough")
row++

style = plain.DoubleUnderline(true)
style = plain.Underline(tcell.UnderlineStyleDouble)
puts(s, style, 2, row, "Double Underline")
row++

style = plain.CurlyUnderline(true)
style = plain.Underline(tcell.UnderlineStyleCurly)
puts(s, style, 2, row, "Curly Underline")
row++

style = plain.DottedUnderline(true)
style = plain.Underline(tcell.UnderlineStyleDotted)
puts(s, style, 2, row, "Dotted Underline")
row++

style = plain.DashedUnderline(true)
style = plain.Underline(tcell.UnderlineStyleDashed)
puts(s, style, 2, row, "Dashed Underline")
row++

style = plain.Underline(true).UnderlineColor(tcell.ColorBlue)
style = plain.Underline(true, tcell.ColorBlue)
puts(s, style, 2, row, "Blue Underline")
row++

style = plain.Underline(true).UnderlineColor(tcell.ColorHoneydew)
style = plain.Underline(tcell.UnderlineStyleSolid, tcell.ColorHoneydew)
puts(s, style, 2, row, "Honeydew Underline")
row++

style = plain.CurlyUnderline(true).UnderlineColor(tcell.NewRGBColor(0xc5, 0x8a, 0xf9))
style = plain.Underline(tcell.UnderlineStyleCurly, tcell.NewRGBColor(0xc5, 0x8a, 0xf9))
puts(s, style, 2, row, "Pink Curly Underline")
row++

Expand Down
6 changes: 1 addition & 5 deletions attr.go
Expand Up @@ -25,14 +25,10 @@ const (
AttrBold AttrMask = 1 << iota
AttrBlink
AttrReverse
AttrUnderline
AttrUnderline // Deprecated: Use UnderlineStyle
AttrDim
AttrItalic
AttrStrikeThrough
AttrDoubleUnderline
AttrCurlyUnderline
AttrDottedUnderline
AttrDashedUnderline
AttrInvalid AttrMask = 1 << 31 // Mark the style or attributes invalid
AttrNone AttrMask = 0 // Just normal text.
)
19 changes: 10 additions & 9 deletions console_win.go
Expand Up @@ -919,37 +919,38 @@ func (s *cScreen) mapStyle(style Style) uint16 {
func (s *cScreen) sendVtStyle(style Style) {
esc := &strings.Builder{}

fg, bg, uc, attrs := style.fg, style.bg, style.under, style.attrs
fg, bg, attrs := style.fg, style.bg, style.attrs
us, uc := style.ulStyle, style.ulColor

esc.WriteString(vtSgr0)

if attrs&(AttrBold|AttrDim) == AttrBold {
esc.WriteString(vtBold)
}
if attrs&AttrBlink != 0 {
esc.WriteString(vtBlink)
}
if attrs&(AttrUnderline|AttrDoubleUnderline|AttrCurlyUnderline|AttrDottedUnderline|AttrDashedUnderline) != 0 {
if uc.Valid() {
if us != UnderlineStyleNone {
if uc == ColorReset {
esc.WriteString(vtUnderColorReset)
} else if uc.IsRGB() {
r, g, b := uc.RGB()
_, _ = fmt.Fprintf(esc, vtUnderColorRGB, int(r), int(g), int(b))
} else {
} else if uc.Valid() {
_, _ = fmt.Fprintf(esc, vtUnderColor, uc&0xff)
}
}

esc.WriteString(vtUnderline)
// legacy ConHost does not understand these but Terminal does
if (attrs & AttrDoubleUnderline) != 0 {
switch us {
case UnderlineStyleSolid:
case UnderlineStyleDouble:
esc.WriteString(vtDoubleUnderline)
} else if (attrs & AttrCurlyUnderline) != 0 {
case UnderlineStyleCurly:
esc.WriteString(vtCurlyUnderline)
} else if (attrs & AttrDottedUnderline) != 0 {
case UnderlineStyleDotted:
esc.WriteString(vtDottedUnderline)
} else if (attrs & AttrDashedUnderline) != 0 {
case UnderlineStyleDashed:
esc.WriteString(vtDashedUnderline)
}
}
Expand Down
76 changes: 48 additions & 28 deletions style.go
Expand Up @@ -23,12 +23,13 @@ package tcell
//
// To use Style, just declare a variable of its type.
type Style struct {
fg Color
bg Color
under Color
attrs AttrMask
url string
urlId string
fg Color
bg Color
ulStyle UnderlineStyle
ulColor Color
attrs AttrMask
url string
urlId string
}

// StyleDefault represents a default style, based upon the context.
Expand Down Expand Up @@ -111,36 +112,55 @@ func (s Style) Reverse(on bool) Style {
return s.setAttrs(AttrReverse, on)
}

// Underline returns a new style based on s, with the underline attribute set
// as requested.
func (s Style) Underline(on bool) Style {
return s.setAttrs(AttrUnderline, on)
}

// StrikeThrough sets strikethrough mode.
func (s Style) StrikeThrough(on bool) Style {
return s.setAttrs(AttrStrikeThrough, on)
}

func (s Style) DoubleUnderline(on bool) Style {
return s.setAttrs(AttrDoubleUnderline, on)
}
// Underline style. Modern terminals have the option of rendering the
// underline using different styles, and even different colors.
type UnderlineStyle int

func (s Style) CurlyUnderline(on bool) Style {
return s.setAttrs(AttrCurlyUnderline, on)
}
const (
UnderlineStyleNone = UnderlineStyle(iota)
UnderlineStyleSolid
UnderlineStyleDouble
UnderlineStyleCurly
UnderlineStyleDotted
UnderlineStyleDashed
)

func (s Style) DottedUnderline(on bool) Style {
return s.setAttrs(AttrDottedUnderline, on)
}

func (s Style) DashedUnderline(on bool) Style {
return s.setAttrs(AttrDashedUnderline, on)
}

func (s Style) UnderlineColor(c Color) Style {
// Underline returns a new style based on s, with the underline attribute set
// as requested. The parameters can be:
//
// bool: on / off - enables just a simple underline
// UnderlineStyle: sets a specific style (should not coexist with the bool)
// Color: the color to use
func (s Style) Underline(params ...interface{}) Style {
s2 := s
s2.under = c
for _, param := range params {
switch v := param.(type) {
case bool:
if v {
s2.ulStyle = UnderlineStyleSolid
s2.attrs |= AttrUnderline
} else {
s2.ulStyle = UnderlineStyleNone
s2.attrs &^= AttrUnderline
}
case UnderlineStyle:
if v == UnderlineStyleNone {
s2.attrs &^= AttrUnderline
} else {
s2.attrs |= AttrUnderline
}
s2.ulStyle = v
case Color:
s2.ulColor = v
default:
panic("Bad type for underline")
}
}
return s2
}

Expand Down
17 changes: 9 additions & 8 deletions tscreen.go
Expand Up @@ -796,16 +796,16 @@ func (t *tScreen) drawCell(x, y int) int {
style = t.style
}
if style != t.curstyle {
fg, bg, attrs, uc := style.fg, style.bg, style.attrs, style.under
fg, bg, attrs := style.fg, style.bg, style.attrs

t.TPuts(ti.AttrOff)

attrs = t.sendFgBg(fg, bg, attrs)
if attrs&AttrBold != 0 {
t.TPuts(ti.Bold)
}
if attrs&(AttrUnderline|AttrDoubleUnderline|AttrCurlyUnderline|AttrDottedUnderline|AttrDashedUnderline) != 0 {
if uc.Valid() && (t.underColor != "" || t.underRGB != "") {
if us, uc := style.ulStyle, style.ulColor; us != UnderlineStyleNone {
if t.underColor != "" || t.underRGB != "" {
if uc == ColorReset {
t.TPuts(t.underFg)
} else if uc.IsRGB() {
Expand All @@ -822,18 +822,19 @@ func (t *tScreen) drawCell(x, y int) int {
}
t.TPuts(ti.TParm(t.underColor, int(uc&0xff)))
}
} else {
} else if uc.Valid() {
t.TPuts(ti.TParm(t.underColor, int(uc&0xff)))
}
}
t.TPuts(ti.Underline) // to ensure everyone gets at least a basic underline
if (attrs & AttrDoubleUnderline) != 0 {
switch us {
case UnderlineStyleDouble:
t.TPuts(t.doubleUnder)
} else if (attrs & AttrCurlyUnderline) != 0 {
case UnderlineStyleCurly:
t.TPuts(t.curlyUnder)
} else if (attrs & AttrDottedUnderline) != 0 {
case UnderlineStyleDotted:
t.TPuts(t.dottedUnder)
} else if (attrs & AttrDashedUnderline) != 0 {
case UnderlineStyleDashed:
t.TPuts(t.dashedUnder)
}
}
Expand Down

0 comments on commit 9bc5c63

Please sign in to comment.