Replaced mattn/go-runewidth string width calculation with rivo/uniseg for improved character display.

pull/766/head
Oliver 2 years ago
parent 4664d8bf22
commit 55965cf21d

@ -4,6 +4,7 @@ import (
"strings"
"github.com/gdamore/tcell/v2"
"github.com/rivo/uniseg"
)
// Checkbox implements a simple box for boolean values which can be checked and
@ -184,7 +185,7 @@ func (c *Checkbox) Draw(screen tcell.Screen) {
if c.HasFocus() {
fieldStyle = fieldStyle.Background(c.fieldTextColor).Foreground(c.fieldBackgroundColor)
}
checkboxWidth := stringWidth(c.checkedString)
checkboxWidth := uniseg.StringWidth(c.checkedString)
checkedString := c.checkedString
if !c.checked {
checkedString = strings.Repeat(" ", checkboxWidth)

@ -4,6 +4,7 @@ import (
"strings"
"github.com/gdamore/tcell/v2"
"github.com/rivo/uniseg"
)
// dropDownOption is one option that can be selected in a drop-down primitive.
@ -373,7 +374,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
if d.open && len(d.prefix) > 0 {
// Show the prefix.
currentOptionPrefixWidth := TaggedStringWidth(d.currentOptionPrefix)
prefixWidth := stringWidth(d.prefix)
prefixWidth := uniseg.StringWidth(d.prefix)
listItemText := d.options[d.list.GetCurrentItem()].Text
Print(screen, d.currentOptionPrefix, x, y, fieldWidth, AlignLeft, d.fieldTextColor)
Print(screen, d.prefix, x+currentOptionPrefixWidth, y, fieldWidth-currentOptionPrefixWidth, AlignLeft, d.prefixTextColor)

@ -6,7 +6,7 @@ require (
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mattn/go-runewidth v0.0.13
github.com/rivo/uniseg v0.3.4
github.com/rivo/uniseg v0.4.2
)
require (

@ -7,8 +7,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

@ -8,6 +8,7 @@ import (
"unicode/utf8"
"github.com/gdamore/tcell/v2"
"github.com/rivo/uniseg"
)
// InputField is a one-line box (three lines if there is a title) where the
@ -414,7 +415,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
if i.maskCharacter > 0 {
text = strings.Repeat(string(i.maskCharacter), utf8.RuneCountInString(i.text))
}
if fieldWidth >= stringWidth(text) {
if fieldWidth >= uniseg.StringWidth(text) {
// We have enough space for the full text.
printWithStyle(screen, Escape(text), x, y, 0, fieldWidth, AlignLeft, i.fieldStyle, true)
i.offset = 0
@ -436,7 +437,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
var shiftLeft int
if i.offset > i.cursorPos {
i.offset = i.cursorPos
} else if subWidth := stringWidth(text[i.offset:i.cursorPos]); subWidth > fieldWidth-1 {
} else if subWidth := uniseg.StringWidth(text[i.offset:i.cursorPos]); subWidth > fieldWidth-1 {
shiftLeft = subWidth - fieldWidth + 1
}
currentOffset := i.offset

@ -1421,7 +1421,7 @@ func (t *TextArea) step(text string, pos, endPos [3]int) (cluster, rest string,
if cluster == "\t" {
width = TabSize
} else {
width = stringWidth(cluster)
width = boundaries >> uniseg.ShiftWidth
}
return cluster, text, boundaries, width, pos, endPos

@ -10,7 +10,6 @@ import (
"github.com/gdamore/tcell/v2"
colorful "github.com/lucasb-eyer/go-colorful"
runewidth "github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
)
@ -810,11 +809,21 @@ func (t *TextView) reindexBuffer(width int) {
str = strippedStr
if t.wrap && len(str) > 0 {
for len(str) > 0 {
extract := runewidth.Truncate(str, width, "")
if len(extract) == 0 {
// We'll extract at least one grapheme cluster.
extract, _, _, _ = uniseg.FirstGraphemeClusterInString(str, -1)
// Truncate str to width.
var splitPos, clusterWidth, lineWidth int
state := -1
remaining := str
for splitPos == 0 || len(remaining) > 0 { // We'll extract at least one grapheme cluster.
var cluster string
cluster, remaining, clusterWidth, state = uniseg.FirstGraphemeClusterInString(remaining, state)
lineWidth += clusterWidth
if splitPos > 0 && lineWidth > width {
break
}
splitPos += len(cluster)
}
extract := str[:splitPos]
if t.wordWrap && len(extract) < len(str) {
// Add any spaces from the next line.
if spaces := spacePattern.FindStringIndex(str[len(extract):]); spaces != nil && spaces[0] == 0 {
@ -905,7 +914,7 @@ func (t *TextView) reindexBuffer(width int) {
line := len(t.index)
if t.fromHighlight < 0 {
t.fromHighlight, t.toHighlight = line, line
t.posHighlight = stringWidth(splitLine[:strippedTagStart])
t.posHighlight = uniseg.StringWidth(splitLine[:strippedTagStart])
} else if line > t.toHighlight {
t.toHighlight = line
}
@ -923,7 +932,7 @@ func (t *TextView) reindexBuffer(width int) {
// Append this line.
line.NextPos = originalPos
line.Width = stringWidth(splitLine)
line.Width = uniseg.StringWidth(splitLine)
t.index = append(t.index, line)
}
@ -935,7 +944,7 @@ func (t *TextView) reindexBuffer(width int) {
if spaces != nil && spaces[len(spaces)-1][1] == len(str) {
oldNextPos := line.NextPos
line.NextPos -= spaces[len(spaces)-1][1] - spaces[len(spaces)-1][0]
line.Width -= stringWidth(t.buffer[line.Line][line.NextPos:oldNextPos])
line.Width -= uniseg.StringWidth(t.buffer[line.Line][line.NextPos:oldNextPos])
}
}
}

@ -7,7 +7,6 @@ import (
"strconv"
"github.com/gdamore/tcell/v2"
runewidth "github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
)
@ -166,7 +165,7 @@ func overlayStyle(style tcell.Style, fgColor, bgColor, attributes string) tcell.
func decomposeString(text string, findColors, findRegions bool) (colorIndices [][]int, colors [][]string, regionIndices [][]int, regions [][]string, escapeIndices [][]int, stripped string, width int) {
// Shortcut for the trivial case.
if !findColors && !findRegions {
return nil, nil, nil, nil, nil, text, stringWidth(text)
return nil, nil, nil, nil, nil, text, uniseg.StringWidth(text)
}
// Get positions of any tags.
@ -216,7 +215,7 @@ func decomposeString(text string, findColors, findRegions bool) (colorIndices []
stripped = string(buf)
// Get the width of the stripped string.
width = stringWidth(stripped)
width = uniseg.StringWidth(stripped)
return
}
@ -441,28 +440,6 @@ func TaggedStringWidth(text string) int {
return width
}
// stringWidth returns the number of horizontal cells needed to print the given
// text. It splits the text into its grapheme clusters, calculates each
// cluster's width, and adds them up to a total.
func stringWidth(text string) (width int) {
state := -1
for len(text) > 0 {
var (
chWidth int
cl string
)
cl, text, _, state = uniseg.FirstGraphemeClusterInString(text, state)
for _, r := range cl {
chWidth = runewidth.RuneWidth(r)
if chWidth > 0 {
break // Our best guess at this point is to use the width of the first non-zero-width rune.
}
}
width += chWidth
}
return
}
// WordWrap splits a text such that each resulting line does not exceed the
// given screen width. Possible split points are after any punctuation or
// whitespace. Whitespace after split points will be dropped.
@ -571,8 +548,8 @@ func WordWrap(text string, width int) (lines []string) {
// recognized and substituted by the print functions of this package. For
// example, to include a tag-like string in a box title or in a TextView:
//
// box.SetTitle(tview.Escape("[squarebrackets]"))
// fmt.Fprint(textView, tview.Escape(`["quoted"]`))
// box.SetTitle(tview.Escape("[squarebrackets]"))
// fmt.Fprint(textView, tview.Escape(`["quoted"]`))
func Escape(text string) string {
return nonEscapePattern.ReplaceAllString(text, "$1[]")
}
@ -595,16 +572,8 @@ func iterateString(text string, callback func(main rune, comb []rune, textPos, t
var cluster string
cluster, text, boundaries, state = uniseg.StepString(text, state)
var width int
runes := make([]rune, 0, len(cluster))
for _, r := range cluster {
runes = append(runes, r)
w := runewidth.RuneWidth(r)
if width == 0 && w > 0 {
width = w // Our best guess at this point is to use the width of the first non-zero-width rune.
}
}
width := boundaries >> uniseg.ShiftWidth
runes := []rune(cluster)
var comb []rune
if len(runes) > 1 {
comb = runes[1:]

Loading…
Cancel
Save