Handle CJK wide characters in helper.go
Add multi width text ouput support in widgets
This commit is contained in:
gizak 2015-03-25 18:04:15 -04:00
parent 9e538ed468
commit b436024302
10 changed files with 93 additions and 27 deletions

6
bar.go
View File

@ -91,14 +91,16 @@ func (bc *BarChart) Buffer() []Point {
}
}
// plot text
for j := 0; j < len(bc.labels[i]); j++ {
for j, k := 0, 0; j < len(bc.labels[i]); j++ {
w := charWidth(bc.labels[i][j])
p := Point{}
p.Ch = bc.labels[i][j]
p.Bg = bc.BgColor
p.Fg = bc.TextColor
p.Y = bc.innerY + bc.innerHeight - 1
p.X = bc.innerX + oftX + j
p.X = bc.innerX + oftX + k
ps = append(ps, p)
k += w
}
// plot num
for j := 0; j < len(bc.dataNum[i]); j++ {

6
box.go
View File

@ -107,8 +107,10 @@ func (lb labeledBorder) Buffer() []Point {
maxTxtW := lb.Width - 2
rs := trimStr2Runes(lb.Label, maxTxtW)
for i := 0; i < len(rs); i++ {
ps = append(ps, newPointWithAttrs(rs[i], lb.X+1+i, lb.Y, lb.LabelFgColor, lb.LabelBgColor))
for i, j, w := 0, 0, 0; i < len(rs); i++ {
w = charWidth(rs[i])
ps = append(ps, newPointWithAttrs(rs[i], lb.X+1+j, lb.Y, lb.LabelFgColor, lb.LabelBgColor))
j += w
}
return ps

View File

@ -47,8 +47,8 @@ var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
type LineChart struct {
Block
Data []float64
DataLabels []string
Mode string // braille | dot
DataLabels []string // if unset, the data indices will be used
Mode string // braille | dot
DotStyle rune
LineColor Attribute
scale float64 // data span per cell on y-axis
@ -149,20 +149,22 @@ func (lc *LineChart) calcLabelX() {
}
s := str2runes(lc.DataLabels[l])
if l+len(s) <= lc.axisXWidth {
w := strWidth(lc.DataLabels[l])
if l+w <= lc.axisXWidth {
lc.labelX = append(lc.labelX, s)
}
l += (len(s) + lc.axisXLebelGap) // -1 needed
} else {
l += w + lc.axisXLebelGap
} else { // braille
if 2*l >= len(lc.DataLabels) {
break
}
s := str2runes(lc.DataLabels[2*l])
if l+len(s) <= lc.axisXWidth {
w := strWidth(lc.DataLabels[2*l])
if l+w <= lc.axisXWidth {
lc.labelX = append(lc.labelX, s)
}
l += (len(s) + lc.axisXLebelGap) // -1 needed
l += w + lc.axisXLebelGap
}
}
@ -199,6 +201,7 @@ func (lc *LineChart) calcLabelY() {
}
func (lc *LineChart) calcLayout() {
// set datalabels if it is not provided
if lc.DataLabels == nil || len(lc.DataLabels) == 0 {
lc.DataLabels = make([]string, len(lc.Data))
for i := range lc.Data {

View File

@ -20,8 +20,8 @@ func main() {
strs := []string{
"[0] github.com/gizak/termui",
"[1] editbox.go",
"[2] iterrupt.go",
"[1] 你好,世界",
"[2] こんにちは世界",
"[3] keyboard.go",
"[4] output.go",
"[5] random_out.go",

View File

@ -24,11 +24,11 @@ func main() {
par0.Y = 1
par0.HasBorder = false
par1 := termui.NewPar("Simple Text")
par1 := termui.NewPar("你好,世界。")
par1.Height = 3
par1.Width = 17
par1.X = 20
par1.Border.Label = "Label"
par1.Border.Label = "标签"
par2 := termui.NewPar("Simple text\nwith label. It can be multilined with \\n or break automatically")
par2.Height = 5

View File

@ -9,6 +9,7 @@ import rw "github.com/mattn/go-runewidth"
/* ---------------Port from termbox-go --------------------- */
// Attribute is printable cell's color and style.
type Attribute uint16
const (
@ -49,9 +50,16 @@ func trimStr2Runes(s string, w int) []rune {
return []rune{}
}
sw := rw.StringWidth(s)
if sw+dotw >= w {
return []rune(rw.Truncate(s, w, "…"))
} else {
return []rune(rw.Truncate(s, w, ""))
if sw > w {
return []rune(rw.Truncate(s, w, dot))
}
return str2runes(s) //[]rune(rw.Truncate(s, w, ""))
}
func strWidth(s string) int {
return rw.StringWidth(s)
}
func charWidth(ch rune) int {
return rw.RuneWidth(ch)
}

View File

@ -4,7 +4,11 @@
package termui
import "testing"
import (
"testing"
"github.com/davecgh/go-spew/spew"
)
func TestStr2Rune(t *testing.T) {
s := "你好,世界."
@ -13,3 +17,42 @@ func TestStr2Rune(t *testing.T) {
t.Error()
}
}
func TestWidth(t *testing.T) {
s0 := "つのだ☆HIRO"
s1 := "11111111111"
spew.Dump(s0)
spew.Dump(s1)
// above not align for setting East Asian Ambiguous to wide!!
if strWidth(s0) != strWidth(s1) {
t.Error("str len failed")
}
len1 := []rune{'a', '2', '&', '「', 'オ', '。'} //will false: 'ᆵ', 'ᄚ', 'ᄒ'
for _, v := range len1 {
if charWidth(v) != 1 {
t.Error("len1 failed")
}
}
len2 := []rune{'漢', '字', '한', '자', '你', '好', 'だ', '。', '', '', '', 'ョ', '、', 'ヲ'}
for _, v := range len2 {
if charWidth(v) != 2 {
t.Error("len2 failed")
}
}
}
func TestTrim(t *testing.T) {
s := "つのだ☆HIRO"
if string(trimStr2Runes(s, 10)) != "つのだ☆HI"+dot {
t.Error("trim failed")
}
if string(trimStr2Runes(s, 11)) != "つのだ☆HIRO" {
t.Error("avoid tail trim failed")
}
if string(trimStr2Runes(s, 15)) != "つのだ☆HIRO" {
t.Error("avoid trim failed")
}
}

View File

@ -54,7 +54,8 @@ func (l *List) Buffer() []Point {
rs := str2runes(strings.Join(l.Items, "\n"))
i, j, k := 0, 0, 0
for i < l.innerHeight && k < len(rs) {
if rs[k] == '\n' || j == l.innerWidth {
w := charWidth(rs[k])
if rs[k] == '\n' || j+w > l.innerWidth {
i++
j = 0
if rs[k] == '\n' {
@ -85,6 +86,7 @@ func (l *List) Buffer() []Point {
j := 0
for _, vv := range rs {
w := charWidth(vv)
p := Point{}
p.X = l.innerX + j
p.Y = l.innerY + i
@ -94,7 +96,7 @@ func (l *List) Buffer() []Point {
p.Fg = l.ItemFgColor
ps = append(ps, p)
j++
j += w
}
}
}

9
p.go
View File

@ -34,9 +34,12 @@ func (p *Par) Buffer() []Point {
rs := str2runes(p.Text)
i, j, k := 0, 0, 0
for i < p.innerHeight && k < len(rs) {
if rs[k] == '\n' || j == p.innerWidth {
// the width of char is about to print
w := charWidth(rs[k])
if rs[k] == '\n' || j+w > p.innerWidth {
i++
j = 0
j = 0 // set x = 0
if rs[k] == '\n' {
k++
}
@ -62,7 +65,7 @@ func (p *Par) Buffer() []Point {
ps = append(ps, pi)
k++
j++
j += w
}
return ps
}

View File

@ -111,7 +111,9 @@ func (sl *Sparklines) Buffer() []Point {
if l.Title != "" {
rs := trimStr2Runes(l.Title, sl.innerWidth)
for oftX, v := range rs {
oftX := 0
for _, v := range rs {
w := charWidth(v)
p := Point{}
p.Ch = v
p.Fg = l.TitleColor
@ -119,6 +121,7 @@ func (sl *Sparklines) Buffer() []Point {
p.X = sl.innerX + oftX
p.Y = sl.innerY + oftY
ps = append(ps, p)
oftX += w
}
}
@ -130,7 +133,7 @@ func (sl *Sparklines) Buffer() []Point {
p := Point{}
p.X = sl.innerX + j
p.Y = sl.innerY + oftY + l.Height - jj
p.Ch = ' ' //sparks[7]
p.Ch = ' ' // => sparks[7]
p.Bg = l.LineColor
//p.Bg = sl.BgColor
ps = append(ps, p)