From 9bc5c636ae92df967d0e87dbac75f587c13cc1d2 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Mon, 4 Mar 2024 23:56:23 -0800 Subject: [PATCH] Underline API change. The underline styles are mutually exclusive. And let's simplify passing the color with the underline style in a single function call. --- _demos/style.go | 14 ++++----- attr.go | 6 +--- console_win.go | 19 +++++++------ style.go | 76 +++++++++++++++++++++++++++++++------------------ tscreen.go | 17 +++++------ 5 files changed, 75 insertions(+), 57 deletions(-) diff --git a/_demos/style.go b/_demos/style.go index d8a801e..d792803 100644 --- a/_demos/style.go +++ b/_demos/style.go @@ -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++ diff --git a/attr.go b/attr.go index 960560e..1e75435 100644 --- a/attr.go +++ b/attr.go @@ -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. ) diff --git a/console_win.go b/console_win.go index eab107c..3031d44 100644 --- a/console_win.go +++ b/console_win.go @@ -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) } } diff --git a/style.go b/style.go index 04dfe13..14d05b1 100644 --- a/style.go +++ b/style.go @@ -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. @@ -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 } diff --git a/tscreen.go b/tscreen.go index 7be2dab..0694770 100644 --- a/tscreen.go +++ b/tscreen.go @@ -796,7 +796,7 @@ 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) @@ -804,8 +804,8 @@ func (t *tScreen) drawCell(x, y int) int { 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() { @@ -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) } }