From bbf3bb67979d99b22bc13690944b1ab56e7e8ac6 Mon Sep 17 00:00:00 2001 From: Anson Chan Date: Fri, 22 Feb 2019 13:08:41 +0800 Subject: [PATCH 1/3] fix: #226, unicode strings display in paragraph, table and list --- buffer.go | 9 +++++++-- utils.go | 19 ++++++++++++++++++- widgets/list.go | 3 ++- widgets/paragraph.go | 3 ++- widgets/table.go | 9 ++++++--- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/buffer.go b/buffer.go index 2c33a1d..ac22934 100644 --- a/buffer.go +++ b/buffer.go @@ -6,6 +6,8 @@ package termui import ( "image" + + rw "github.com/mattn/go-runewidth" ) // Cell represents a viewable terminal cell @@ -65,7 +67,10 @@ func (self *Buffer) Fill(c Cell, rect image.Rectangle) { } func (self *Buffer) SetString(s string, style Style, p image.Point) { - for i, char := range s { - self.SetCell(Cell{char, style}, image.Pt(p.X+i, p.Y)) + runes := []rune(s) + x := 0 + for _, char := range runes { + self.SetCell(Cell{char, style}, image.Pt(p.X+x, p.Y)) + x += rw.RuneWidth(char) } } diff --git a/utils.go b/utils.go index f780451..eea7b84 100644 --- a/utils.go +++ b/utils.go @@ -189,8 +189,9 @@ func CellsToString(cells []Cell) string { func TrimCells(cells []Cell, w int) []Cell { s := CellsToString(cells) s = TrimString(s, w) + runes := []rune(s) newCells := []Cell{} - for i, r := range s { + for i, r := range runes { newCells = append(newCells, Cell{r, cells[i].Style}) } return newCells @@ -212,3 +213,19 @@ func SplitCells(cells []Cell, r rune) [][]Cell { } return splitCells } + +type CellWithX struct { + X int + Cell Cell +} + +func BuildCellChannel(cells []Cell) chan CellWithX { + c := make(chan CellWithX, len(cells)) + index := 0 + for _, cell := range cells { + c <- CellWithX{X: index, Cell: cell} + index += rw.RuneWidth(cell.Rune) + } + close(c) + return c +} diff --git a/widgets/list.go b/widgets/list.go index 4e6c463..0654de6 100644 --- a/widgets/list.go +++ b/widgets/list.go @@ -8,6 +8,7 @@ import ( "image" . "github.com/gizak/termui" + rw "github.com/mattn/go-runewidth" ) type List struct { @@ -57,7 +58,7 @@ func (self *List) Draw(buf *Buffer) { break } else { buf.SetCell(NewCell(cells[j].Rune, style), point) - point = point.Add(image.Pt(1, 0)) + point = point.Add(image.Pt(rw.RuneWidth(cells[j].Rune), 0)) } } } diff --git a/widgets/paragraph.go b/widgets/paragraph.go index 9498f16..34ca187 100644 --- a/widgets/paragraph.go +++ b/widgets/paragraph.go @@ -40,7 +40,8 @@ func (self *Paragraph) Draw(buf *Buffer) { break } row = TrimCells(row, self.Inner.Dx()) - for x, cell := range row { + for signal := range BuildCellChannel(row) { + x, cell := signal.X, signal.Cell buf.SetCell(cell, image.Pt(x, y).Add(self.Inner.Min)) } } diff --git a/widgets/table.go b/widgets/table.go index 14f58ef..3660ff1 100644 --- a/widgets/table.go +++ b/widgets/table.go @@ -74,7 +74,8 @@ func (self *Table) Draw(buf *Buffer) { col := ParseText(row[j], rowStyle) // draw row cell if len(col) > columnWidths[j] || self.TextAlign == AlignLeft { - for k, cell := range col { + for signal := range BuildCellChannel(col) { + k, cell := signal.X, signal.Cell if k == columnWidths[j] || colXCoordinate+k == self.Inner.Max.X { cell.Rune = ELLIPSES buf.SetCell(cell, image.Pt(colXCoordinate+k-1, yCoordinate)) @@ -86,12 +87,14 @@ func (self *Table) Draw(buf *Buffer) { } else if self.TextAlign == AlignCenter { xCoordinateOffset := (columnWidths[j] - len(col)) / 2 stringXCoordinate := xCoordinateOffset + colXCoordinate - for k, cell := range col { + for signal := range BuildCellChannel(col) { + k, cell := signal.X, signal.Cell buf.SetCell(cell, image.Pt(stringXCoordinate+k, yCoordinate)) } } else if self.TextAlign == AlignRight { stringXCoordinate := MinInt(colXCoordinate+columnWidths[j], self.Inner.Max.X) - len(col) - for k, cell := range col { + for signal := range BuildCellChannel(col) { + k, cell := signal.X, signal.Cell buf.SetCell(cell, image.Pt(stringXCoordinate+k, yCoordinate)) } } From 179acca35e9e53a49515703a80c3de18aaf48380 Mon Sep 17 00:00:00 2001 From: Caleb Bassi Date: Fri, 22 Feb 2019 12:44:48 -0800 Subject: [PATCH 2/3] Formatting --- widgets/list.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widgets/list.go b/widgets/list.go index 0654de6..f85bbad 100644 --- a/widgets/list.go +++ b/widgets/list.go @@ -7,8 +7,9 @@ package widgets import ( "image" - . "github.com/gizak/termui" rw "github.com/mattn/go-runewidth" + + . "github.com/gizak/termui" ) type List struct { From b5e7ca951a240584e38512c85ec61915d2bd9695 Mon Sep 17 00:00:00 2001 From: Anson Chan Date: Sun, 24 Feb 2019 11:29:35 +0800 Subject: [PATCH 3/3] refactor: use a []CellWithX instead a chan of CellWithX to make it more sense --- utils.go | 11 +++++------ widgets/paragraph.go | 4 ++-- widgets/table.go | 12 ++++++------ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/utils.go b/utils.go index eea7b84..e53d531 100644 --- a/utils.go +++ b/utils.go @@ -219,13 +219,12 @@ type CellWithX struct { Cell Cell } -func BuildCellChannel(cells []Cell) chan CellWithX { - c := make(chan CellWithX, len(cells)) +func BuildCellWithXArray(cells []Cell) []CellWithX { + cellWithXArray := make([]CellWithX, len(cells)) index := 0 - for _, cell := range cells { - c <- CellWithX{X: index, Cell: cell} + for i, cell := range cells { + cellWithXArray[i] = CellWithX{X: index, Cell: cell} index += rw.RuneWidth(cell.Rune) } - close(c) - return c + return cellWithXArray } diff --git a/widgets/paragraph.go b/widgets/paragraph.go index 34ca187..7eb56bd 100644 --- a/widgets/paragraph.go +++ b/widgets/paragraph.go @@ -40,8 +40,8 @@ func (self *Paragraph) Draw(buf *Buffer) { break } row = TrimCells(row, self.Inner.Dx()) - for signal := range BuildCellChannel(row) { - x, cell := signal.X, signal.Cell + for _, cx := range BuildCellWithXArray(row) { + x, cell := cx.X, cx.Cell buf.SetCell(cell, image.Pt(x, y).Add(self.Inner.Min)) } } diff --git a/widgets/table.go b/widgets/table.go index 3660ff1..6095954 100644 --- a/widgets/table.go +++ b/widgets/table.go @@ -74,8 +74,8 @@ func (self *Table) Draw(buf *Buffer) { col := ParseText(row[j], rowStyle) // draw row cell if len(col) > columnWidths[j] || self.TextAlign == AlignLeft { - for signal := range BuildCellChannel(col) { - k, cell := signal.X, signal.Cell + for _, cx := range BuildCellWithXArray(col) { + k, cell := cx.X, cx.Cell if k == columnWidths[j] || colXCoordinate+k == self.Inner.Max.X { cell.Rune = ELLIPSES buf.SetCell(cell, image.Pt(colXCoordinate+k-1, yCoordinate)) @@ -87,14 +87,14 @@ func (self *Table) Draw(buf *Buffer) { } else if self.TextAlign == AlignCenter { xCoordinateOffset := (columnWidths[j] - len(col)) / 2 stringXCoordinate := xCoordinateOffset + colXCoordinate - for signal := range BuildCellChannel(col) { - k, cell := signal.X, signal.Cell + for _, cx := range BuildCellWithXArray(col) { + k, cell := cx.X, cx.Cell buf.SetCell(cell, image.Pt(stringXCoordinate+k, yCoordinate)) } } else if self.TextAlign == AlignRight { stringXCoordinate := MinInt(colXCoordinate+columnWidths[j], self.Inner.Max.X) - len(col) - for signal := range BuildCellChannel(col) { - k, cell := signal.X, signal.Cell + for _, cx := range BuildCellWithXArray(col) { + k, cell := cx.X, cx.Cell buf.SetCell(cell, image.Pt(stringXCoordinate+k, yCoordinate)) } }