Handle CJK wide characters in helper.go Add multi width text ouput support in widgets
This commit is contained in:
parent
9e538ed468
commit
b436024302
6
bar.go
6
bar.go
@ -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
6
box.go
@ -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
|
||||
|
17
chart.go
17
chart.go
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
16
helper.go
16
helper.go
@ -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)
|
||||
}
|
||||
|
@ -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{'漢', '字', '한', '자', '你', '好', 'だ', '。', '%', 's', 'E', 'ョ', '、', 'ヲ'}
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
6
list.go
6
list.go
@ -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
9
p.go
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user