mirror of
https://github.com/rivo/tview.git
synced 2025-04-30 13:48:50 +08:00
Replaced runewidth.StringWidth() with my own implementation. Fixes #236
This commit is contained in:
parent
2cc825800b
commit
8d5eba0c2f
@ -33,7 +33,7 @@ type Button struct {
|
|||||||
// NewButton returns a new input field.
|
// NewButton returns a new input field.
|
||||||
func NewButton(label string) *Button {
|
func NewButton(label string) *Button {
|
||||||
box := NewBox().SetBackgroundColor(Styles.ContrastBackgroundColor)
|
box := NewBox().SetBackgroundColor(Styles.ContrastBackgroundColor)
|
||||||
box.SetRect(0, 0, StringWidth(label)+4, 1)
|
box.SetRect(0, 0, TaggedStringWidth(label)+4, 1)
|
||||||
return &Button{
|
return &Button{
|
||||||
Box: box,
|
Box: box,
|
||||||
label: label,
|
label: label,
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
runewidth "github.com/mattn/go-runewidth"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// dropDownOption is one option that can be selected in a drop-down primitive.
|
// dropDownOption is one option that can be selected in a drop-down primitive.
|
||||||
@ -182,7 +181,7 @@ func (d *DropDown) GetFieldWidth() int {
|
|||||||
}
|
}
|
||||||
fieldWidth := 0
|
fieldWidth := 0
|
||||||
for _, option := range d.options {
|
for _, option := range d.options {
|
||||||
width := StringWidth(option.Text)
|
width := TaggedStringWidth(option.Text)
|
||||||
if width > fieldWidth {
|
if width > fieldWidth {
|
||||||
fieldWidth = width
|
fieldWidth = width
|
||||||
}
|
}
|
||||||
@ -268,7 +267,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
|
|||||||
// What's the longest option text?
|
// What's the longest option text?
|
||||||
maxWidth := 0
|
maxWidth := 0
|
||||||
for _, option := range d.options {
|
for _, option := range d.options {
|
||||||
strWidth := StringWidth(option.Text)
|
strWidth := TaggedStringWidth(option.Text)
|
||||||
if strWidth > maxWidth {
|
if strWidth > maxWidth {
|
||||||
maxWidth = strWidth
|
maxWidth = strWidth
|
||||||
}
|
}
|
||||||
@ -294,7 +293,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
|
|||||||
if d.open && len(d.prefix) > 0 {
|
if d.open && len(d.prefix) > 0 {
|
||||||
// Show the prefix.
|
// Show the prefix.
|
||||||
Print(screen, d.prefix, x, y, fieldWidth, AlignLeft, d.prefixTextColor)
|
Print(screen, d.prefix, x, y, fieldWidth, AlignLeft, d.prefixTextColor)
|
||||||
prefixWidth := runewidth.StringWidth(d.prefix)
|
prefixWidth := stringWidth(d.prefix)
|
||||||
listItemText := d.options[d.list.GetCurrentItem()].Text
|
listItemText := d.options[d.list.GetCurrentItem()].Text
|
||||||
if prefixWidth < fieldWidth && len(d.prefix) < len(listItemText) {
|
if prefixWidth < fieldWidth && len(d.prefix) < len(listItemText) {
|
||||||
Print(screen, listItemText[len(d.prefix):], x+prefixWidth, y, fieldWidth-prefixWidth, AlignLeft, d.fieldTextColor)
|
Print(screen, listItemText[len(d.prefix):], x+prefixWidth, y, fieldWidth-prefixWidth, AlignLeft, d.fieldTextColor)
|
||||||
|
6
form.go
6
form.go
@ -335,7 +335,7 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||||||
// Find the longest label.
|
// Find the longest label.
|
||||||
var maxLabelWidth int
|
var maxLabelWidth int
|
||||||
for _, item := range f.items {
|
for _, item := range f.items {
|
||||||
labelWidth := StringWidth(item.GetLabel())
|
labelWidth := TaggedStringWidth(item.GetLabel())
|
||||||
if labelWidth > maxLabelWidth {
|
if labelWidth > maxLabelWidth {
|
||||||
maxLabelWidth = labelWidth
|
maxLabelWidth = labelWidth
|
||||||
}
|
}
|
||||||
@ -347,7 +347,7 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||||||
var focusedPosition struct{ x, y, width, height int }
|
var focusedPosition struct{ x, y, width, height int }
|
||||||
for index, item := range f.items {
|
for index, item := range f.items {
|
||||||
// Calculate the space needed.
|
// Calculate the space needed.
|
||||||
labelWidth := StringWidth(item.GetLabel())
|
labelWidth := TaggedStringWidth(item.GetLabel())
|
||||||
var itemWidth int
|
var itemWidth int
|
||||||
if f.horizontal {
|
if f.horizontal {
|
||||||
fieldWidth := item.GetFieldWidth()
|
fieldWidth := item.GetFieldWidth()
|
||||||
@ -401,7 +401,7 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||||||
buttonWidths := make([]int, len(f.buttons))
|
buttonWidths := make([]int, len(f.buttons))
|
||||||
buttonsWidth := 0
|
buttonsWidth := 0
|
||||||
for index, button := range f.buttons {
|
for index, button := range f.buttons {
|
||||||
w := StringWidth(button.GetLabel()) + 4
|
w := TaggedStringWidth(button.GetLabel()) + 4
|
||||||
buttonWidths[index] = w
|
buttonWidths[index] = w
|
||||||
buttonsWidth += w + 1
|
buttonsWidth += w + 1
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
runewidth "github.com/mattn/go-runewidth"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// InputField is a one-line box (three lines if there is a title) where the
|
// InputField is a one-line box (three lines if there is a title) where the
|
||||||
@ -276,8 +275,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||||||
if i.maskCharacter > 0 {
|
if i.maskCharacter > 0 {
|
||||||
text = strings.Repeat(string(i.maskCharacter), utf8.RuneCountInString(i.text))
|
text = strings.Repeat(string(i.maskCharacter), utf8.RuneCountInString(i.text))
|
||||||
}
|
}
|
||||||
stringWidth := runewidth.StringWidth(text)
|
if fieldWidth >= stringWidth(text) {
|
||||||
if fieldWidth >= stringWidth {
|
|
||||||
// We have enough space for the full text.
|
// We have enough space for the full text.
|
||||||
Print(screen, Escape(text), x, y, fieldWidth, AlignLeft, i.fieldTextColor)
|
Print(screen, Escape(text), x, y, fieldWidth, AlignLeft, i.fieldTextColor)
|
||||||
i.offset = 0
|
i.offset = 0
|
||||||
@ -299,7 +297,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||||||
var shiftLeft int
|
var shiftLeft int
|
||||||
if i.offset > i.cursorPos {
|
if i.offset > i.cursorPos {
|
||||||
i.offset = i.cursorPos
|
i.offset = i.cursorPos
|
||||||
} else if subWidth := runewidth.StringWidth(text[i.offset:i.cursorPos]); subWidth > fieldWidth-1 {
|
} else if subWidth := stringWidth(text[i.offset:i.cursorPos]); subWidth > fieldWidth-1 {
|
||||||
shiftLeft = subWidth - fieldWidth + 1
|
shiftLeft = subWidth - fieldWidth + 1
|
||||||
}
|
}
|
||||||
currentOffset := i.offset
|
currentOffset := i.offset
|
||||||
|
2
list.go
2
list.go
@ -426,7 +426,7 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||||||
if index == l.currentItem && (!l.selectedFocusOnly || l.HasFocus()) {
|
if index == l.currentItem && (!l.selectedFocusOnly || l.HasFocus()) {
|
||||||
textWidth := width
|
textWidth := width
|
||||||
if !l.highlightFullLine {
|
if !l.highlightFullLine {
|
||||||
if w := StringWidth(item.MainText); w < textWidth {
|
if w := TaggedStringWidth(item.MainText); w < textWidth {
|
||||||
textWidth = w
|
textWidth = w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
modal.go
2
modal.go
@ -116,7 +116,7 @@ func (m *Modal) Draw(screen tcell.Screen) {
|
|||||||
// Calculate the width of this modal.
|
// Calculate the width of this modal.
|
||||||
buttonsWidth := 0
|
buttonsWidth := 0
|
||||||
for _, button := range m.form.buttons {
|
for _, button := range m.form.buttons {
|
||||||
buttonsWidth += StringWidth(button.label) + 4 + 2
|
buttonsWidth += TaggedStringWidth(button.label) + 4 + 2
|
||||||
}
|
}
|
||||||
buttonsWidth -= 2
|
buttonsWidth -= 2
|
||||||
screenWidth, screenHeight := screen.Size()
|
screenWidth, screenHeight := screen.Size()
|
||||||
|
2
table.go
2
table.go
@ -792,7 +792,7 @@ ColumnLoop:
|
|||||||
}
|
}
|
||||||
cell.x, cell.y, cell.width = x+columnX+1, y+rowY, finalWidth
|
cell.x, cell.y, cell.width = x+columnX+1, y+rowY, finalWidth
|
||||||
_, printed := printWithStyle(screen, cell.Text, x+columnX+1, y+rowY, finalWidth, cell.Align, tcell.StyleDefault.Foreground(cell.Color)|tcell.Style(cell.Attributes))
|
_, printed := printWithStyle(screen, cell.Text, x+columnX+1, y+rowY, finalWidth, cell.Align, tcell.StyleDefault.Foreground(cell.Color)|tcell.Style(cell.Attributes))
|
||||||
if StringWidth(cell.Text)-printed > 0 && printed > 0 {
|
if TaggedStringWidth(cell.Text)-printed > 0 && printed > 0 {
|
||||||
_, _, style, _ := screen.GetContent(x+columnX+1+finalWidth-1, y+rowY)
|
_, _, style, _ := screen.GetContent(x+columnX+1+finalWidth-1, y+rowY)
|
||||||
printWithStyle(screen, string(SemigraphicsHorizontalEllipsis), x+columnX+1+finalWidth-1, y+rowY, 1, AlignLeft, style)
|
printWithStyle(screen, string(SemigraphicsHorizontalEllipsis), x+columnX+1+finalWidth-1, y+rowY, 1, AlignLeft, style)
|
||||||
}
|
}
|
||||||
|
@ -691,7 +691,7 @@ func (t *TextView) reindexBuffer(width int) {
|
|||||||
line := len(t.index)
|
line := len(t.index)
|
||||||
if t.fromHighlight < 0 {
|
if t.fromHighlight < 0 {
|
||||||
t.fromHighlight, t.toHighlight = line, line
|
t.fromHighlight, t.toHighlight = line, line
|
||||||
t.posHighlight = runewidth.StringWidth(splitLine[:strippedTagStart])
|
t.posHighlight = stringWidth(splitLine[:strippedTagStart])
|
||||||
} else if line > t.toHighlight {
|
} else if line > t.toHighlight {
|
||||||
t.toHighlight = line
|
t.toHighlight = line
|
||||||
}
|
}
|
||||||
@ -709,7 +709,7 @@ func (t *TextView) reindexBuffer(width int) {
|
|||||||
|
|
||||||
// Append this line.
|
// Append this line.
|
||||||
line.NextPos = originalPos
|
line.NextPos = originalPos
|
||||||
line.Width = runewidth.StringWidth(splitLine)
|
line.Width = stringWidth(splitLine)
|
||||||
t.index = append(t.index, line)
|
t.index = append(t.index, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,7 +721,7 @@ func (t *TextView) reindexBuffer(width int) {
|
|||||||
if spaces != nil && spaces[len(spaces)-1][1] == len(str) {
|
if spaces != nil && spaces[len(spaces)-1][1] == len(str) {
|
||||||
oldNextPos := line.NextPos
|
oldNextPos := line.NextPos
|
||||||
line.NextPos -= spaces[len(spaces)-1][1] - spaces[len(spaces)-1][0]
|
line.NextPos -= spaces[len(spaces)-1][1] - spaces[len(spaces)-1][0]
|
||||||
line.Width -= runewidth.StringWidth(t.buffer[line.Line][line.NextPos:oldNextPos])
|
line.Width -= stringWidth(t.buffer[line.Line][line.NextPos:oldNextPos])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
util.go
28
util.go
@ -171,7 +171,7 @@ func overlayStyle(background tcell.Color, defaultStyle tcell.Style, fgColor, bgC
|
|||||||
func decomposeString(text string, findColors, findRegions bool) (colorIndices [][]int, colors [][]string, regionIndices [][]int, regions [][]string, escapeIndices [][]int, stripped string, width int) {
|
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.
|
// Shortcut for the trivial case.
|
||||||
if !findColors && !findRegions {
|
if !findColors && !findRegions {
|
||||||
return nil, nil, nil, nil, nil, text, runewidth.StringWidth(text)
|
return nil, nil, nil, nil, nil, text, stringWidth(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get positions of any tags.
|
// Get positions of any tags.
|
||||||
@ -222,7 +222,7 @@ func decomposeString(text string, findColors, findRegions bool) (colorIndices []
|
|||||||
stripped = string(escapePattern.ReplaceAll(buf, []byte("[$1$2]")))
|
stripped = string(escapePattern.ReplaceAll(buf, []byte("[$1$2]")))
|
||||||
|
|
||||||
// Get the width of the stripped string.
|
// Get the width of the stripped string.
|
||||||
width = runewidth.StringWidth(stripped)
|
width = stringWidth(stripped)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -409,13 +409,31 @@ func PrintSimple(screen tcell.Screen, text string, x, y int) {
|
|||||||
Print(screen, text, x, y, math.MaxInt32, AlignLeft, Styles.PrimaryTextColor)
|
Print(screen, text, x, y, math.MaxInt32, AlignLeft, Styles.PrimaryTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringWidth returns the width of the given string needed to print it on
|
// TaggedStringWidth returns the width of the given string needed to print it on
|
||||||
// screen. The text may contain color tags which are not counted.
|
// screen. The text may contain color tags which are not counted.
|
||||||
func StringWidth(text string) int {
|
func TaggedStringWidth(text string) int {
|
||||||
_, _, _, _, _, _, width := decomposeString(text, true, false)
|
_, _, _, _, _, _, width := decomposeString(text, true, false)
|
||||||
return width
|
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) {
|
||||||
|
g := uniseg.NewGraphemes(text)
|
||||||
|
for g.Next() {
|
||||||
|
var chWidth int
|
||||||
|
for _, r := range g.Runes() {
|
||||||
|
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
|
// WordWrap splits a text such that each resulting line does not exceed the
|
||||||
// given screen width. Possible split points are after any punctuation or
|
// given screen width. Possible split points are after any punctuation or
|
||||||
// whitespace. Whitespace after split points will be dropped.
|
// whitespace. Whitespace after split points will be dropped.
|
||||||
@ -534,7 +552,7 @@ func iterateString(text string, callback func(main rune, comb []rune, textPos, t
|
|||||||
for gr.Next() {
|
for gr.Next() {
|
||||||
r := gr.Runes()
|
r := gr.Runes()
|
||||||
from, to := gr.Positions()
|
from, to := gr.Positions()
|
||||||
width := runewidth.StringWidth(gr.Str())
|
width := stringWidth(gr.Str())
|
||||||
var comb []rune
|
var comb []rune
|
||||||
if len(r) > 1 {
|
if len(r) > 1 {
|
||||||
comb = r[1:]
|
comb = r[1:]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user