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
|
// 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 := Point{}
|
||||||
p.Ch = bc.labels[i][j]
|
p.Ch = bc.labels[i][j]
|
||||||
p.Bg = bc.BgColor
|
p.Bg = bc.BgColor
|
||||||
p.Fg = bc.TextColor
|
p.Fg = bc.TextColor
|
||||||
p.Y = bc.innerY + bc.innerHeight - 1
|
p.Y = bc.innerY + bc.innerHeight - 1
|
||||||
p.X = bc.innerX + oftX + j
|
p.X = bc.innerX + oftX + k
|
||||||
ps = append(ps, p)
|
ps = append(ps, p)
|
||||||
|
k += w
|
||||||
}
|
}
|
||||||
// plot num
|
// plot num
|
||||||
for j := 0; j < len(bc.dataNum[i]); j++ {
|
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
|
maxTxtW := lb.Width - 2
|
||||||
rs := trimStr2Runes(lb.Label, maxTxtW)
|
rs := trimStr2Runes(lb.Label, maxTxtW)
|
||||||
|
|
||||||
for i := 0; i < len(rs); i++ {
|
for i, j, w := 0, 0, 0; i < len(rs); i++ {
|
||||||
ps = append(ps, newPointWithAttrs(rs[i], lb.X+1+i, lb.Y, lb.LabelFgColor, lb.LabelBgColor))
|
w = charWidth(rs[i])
|
||||||
|
ps = append(ps, newPointWithAttrs(rs[i], lb.X+1+j, lb.Y, lb.LabelFgColor, lb.LabelBgColor))
|
||||||
|
j += w
|
||||||
}
|
}
|
||||||
|
|
||||||
return ps
|
return ps
|
||||||
|
17
chart.go
17
chart.go
@ -47,8 +47,8 @@ var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
|
|||||||
type LineChart struct {
|
type LineChart struct {
|
||||||
Block
|
Block
|
||||||
Data []float64
|
Data []float64
|
||||||
DataLabels []string
|
DataLabels []string // if unset, the data indices will be used
|
||||||
Mode string // braille | dot
|
Mode string // braille | dot
|
||||||
DotStyle rune
|
DotStyle rune
|
||||||
LineColor Attribute
|
LineColor Attribute
|
||||||
scale float64 // data span per cell on y-axis
|
scale float64 // data span per cell on y-axis
|
||||||
@ -149,20 +149,22 @@ func (lc *LineChart) calcLabelX() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s := str2runes(lc.DataLabels[l])
|
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)
|
lc.labelX = append(lc.labelX, s)
|
||||||
}
|
}
|
||||||
l += (len(s) + lc.axisXLebelGap) // -1 needed
|
l += w + lc.axisXLebelGap
|
||||||
} else {
|
} else { // braille
|
||||||
if 2*l >= len(lc.DataLabels) {
|
if 2*l >= len(lc.DataLabels) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
s := str2runes(lc.DataLabels[2*l])
|
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)
|
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() {
|
func (lc *LineChart) calcLayout() {
|
||||||
|
// set datalabels if it is not provided
|
||||||
if lc.DataLabels == nil || len(lc.DataLabels) == 0 {
|
if lc.DataLabels == nil || len(lc.DataLabels) == 0 {
|
||||||
lc.DataLabels = make([]string, len(lc.Data))
|
lc.DataLabels = make([]string, len(lc.Data))
|
||||||
for i := range lc.Data {
|
for i := range lc.Data {
|
||||||
|
@ -20,8 +20,8 @@ func main() {
|
|||||||
|
|
||||||
strs := []string{
|
strs := []string{
|
||||||
"[0] github.com/gizak/termui",
|
"[0] github.com/gizak/termui",
|
||||||
"[1] editbox.go",
|
"[1] 你好,世界",
|
||||||
"[2] iterrupt.go",
|
"[2] こんにちは世界",
|
||||||
"[3] keyboard.go",
|
"[3] keyboard.go",
|
||||||
"[4] output.go",
|
"[4] output.go",
|
||||||
"[5] random_out.go",
|
"[5] random_out.go",
|
||||||
|
@ -24,11 +24,11 @@ func main() {
|
|||||||
par0.Y = 1
|
par0.Y = 1
|
||||||
par0.HasBorder = false
|
par0.HasBorder = false
|
||||||
|
|
||||||
par1 := termui.NewPar("Simple Text")
|
par1 := termui.NewPar("你好,世界。")
|
||||||
par1.Height = 3
|
par1.Height = 3
|
||||||
par1.Width = 17
|
par1.Width = 17
|
||||||
par1.X = 20
|
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 := termui.NewPar("Simple text\nwith label. It can be multilined with \\n or break automatically")
|
||||||
par2.Height = 5
|
par2.Height = 5
|
||||||
|
16
helper.go
16
helper.go
@ -9,6 +9,7 @@ import rw "github.com/mattn/go-runewidth"
|
|||||||
|
|
||||||
/* ---------------Port from termbox-go --------------------- */
|
/* ---------------Port from termbox-go --------------------- */
|
||||||
|
|
||||||
|
// Attribute is printable cell's color and style.
|
||||||
type Attribute uint16
|
type Attribute uint16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -49,9 +50,16 @@ func trimStr2Runes(s string, w int) []rune {
|
|||||||
return []rune{}
|
return []rune{}
|
||||||
}
|
}
|
||||||
sw := rw.StringWidth(s)
|
sw := rw.StringWidth(s)
|
||||||
if sw+dotw >= w {
|
if sw > w {
|
||||||
return []rune(rw.Truncate(s, w, "…"))
|
return []rune(rw.Truncate(s, w, dot))
|
||||||
} else {
|
|
||||||
return []rune(rw.Truncate(s, w, ""))
|
|
||||||
}
|
}
|
||||||
|
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
|
package termui
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
func TestStr2Rune(t *testing.T) {
|
func TestStr2Rune(t *testing.T) {
|
||||||
s := "你好,世界."
|
s := "你好,世界."
|
||||||
@ -13,3 +17,42 @@ func TestStr2Rune(t *testing.T) {
|
|||||||
t.Error()
|
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"))
|
rs := str2runes(strings.Join(l.Items, "\n"))
|
||||||
i, j, k := 0, 0, 0
|
i, j, k := 0, 0, 0
|
||||||
for i < l.innerHeight && k < len(rs) {
|
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++
|
i++
|
||||||
j = 0
|
j = 0
|
||||||
if rs[k] == '\n' {
|
if rs[k] == '\n' {
|
||||||
@ -85,6 +86,7 @@ func (l *List) Buffer() []Point {
|
|||||||
|
|
||||||
j := 0
|
j := 0
|
||||||
for _, vv := range rs {
|
for _, vv := range rs {
|
||||||
|
w := charWidth(vv)
|
||||||
p := Point{}
|
p := Point{}
|
||||||
p.X = l.innerX + j
|
p.X = l.innerX + j
|
||||||
p.Y = l.innerY + i
|
p.Y = l.innerY + i
|
||||||
@ -94,7 +96,7 @@ func (l *List) Buffer() []Point {
|
|||||||
p.Fg = l.ItemFgColor
|
p.Fg = l.ItemFgColor
|
||||||
|
|
||||||
ps = append(ps, p)
|
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)
|
rs := str2runes(p.Text)
|
||||||
i, j, k := 0, 0, 0
|
i, j, k := 0, 0, 0
|
||||||
for i < p.innerHeight && k < len(rs) {
|
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++
|
i++
|
||||||
j = 0
|
j = 0 // set x = 0
|
||||||
if rs[k] == '\n' {
|
if rs[k] == '\n' {
|
||||||
k++
|
k++
|
||||||
}
|
}
|
||||||
@ -62,7 +65,7 @@ func (p *Par) Buffer() []Point {
|
|||||||
ps = append(ps, pi)
|
ps = append(ps, pi)
|
||||||
|
|
||||||
k++
|
k++
|
||||||
j++
|
j += w
|
||||||
}
|
}
|
||||||
return ps
|
return ps
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,9 @@ func (sl *Sparklines) Buffer() []Point {
|
|||||||
|
|
||||||
if l.Title != "" {
|
if l.Title != "" {
|
||||||
rs := trimStr2Runes(l.Title, sl.innerWidth)
|
rs := trimStr2Runes(l.Title, sl.innerWidth)
|
||||||
for oftX, v := range rs {
|
oftX := 0
|
||||||
|
for _, v := range rs {
|
||||||
|
w := charWidth(v)
|
||||||
p := Point{}
|
p := Point{}
|
||||||
p.Ch = v
|
p.Ch = v
|
||||||
p.Fg = l.TitleColor
|
p.Fg = l.TitleColor
|
||||||
@ -119,6 +121,7 @@ func (sl *Sparklines) Buffer() []Point {
|
|||||||
p.X = sl.innerX + oftX
|
p.X = sl.innerX + oftX
|
||||||
p.Y = sl.innerY + oftY
|
p.Y = sl.innerY + oftY
|
||||||
ps = append(ps, p)
|
ps = append(ps, p)
|
||||||
|
oftX += w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +133,7 @@ func (sl *Sparklines) Buffer() []Point {
|
|||||||
p := Point{}
|
p := Point{}
|
||||||
p.X = sl.innerX + j
|
p.X = sl.innerX + j
|
||||||
p.Y = sl.innerY + oftY + l.Height - jj
|
p.Y = sl.innerY + oftY + l.Height - jj
|
||||||
p.Ch = ' ' //sparks[7]
|
p.Ch = ' ' // => sparks[7]
|
||||||
p.Bg = l.LineColor
|
p.Bg = l.LineColor
|
||||||
//p.Bg = sl.BgColor
|
//p.Bg = sl.BgColor
|
||||||
ps = append(ps, p)
|
ps = append(ps, p)
|
||||||
|
Loading…
Reference in New Issue
Block a user