diff --git a/widgets/view.go b/widgets/view.go index a6fcd57..4681e68 100644 --- a/widgets/view.go +++ b/widgets/view.go @@ -1,8 +1,6 @@ package widgets import ( - "fmt" - ui "github.com/gizak/termui" ) @@ -18,7 +16,7 @@ type TextView struct { } func NewTextView(lines <-chan string) *TextView { - i := &TextView{ + t := &TextView{ Block: *ui.NewBlock(), inputStream: lines, render: make(chan bool), @@ -29,68 +27,83 @@ func NewTextView(lines <-chan string) *TextView { padding: Padding{4, 2}, } - i.BorderFg = ui.ThemeAttr("menu.border.fg") - i.BorderLabelFg = ui.ThemeAttr("menu.label.fg") + t.BorderFg = ui.ThemeAttr("menu.border.fg") + t.BorderLabelFg = ui.ThemeAttr("menu.label.fg") + t.Height = ui.TermHeight() + t.Width = ui.TermWidth() - i.Resize() - - i.readInputLoop() - i.renderLoop() - return i + t.readInputLoop() + t.renderLoop() + return t } -func (i *TextView) Resize() { +func (t *TextView) Resize() { ui.Clear() - i.Height = ui.TermHeight() - i.Width = ui.TermWidth() + t.Height = ui.TermHeight() + t.Width = ui.TermWidth() + t.render <- true } -func (i *TextView) Buffer() ui.Buffer { - +func (t *TextView) Buffer() ui.Buffer { var cell ui.Cell - buf := i.Block.Buffer() + buf := t.Block.Buffer() - x := i.Block.X + i.padding[0] - y := i.Block.Y + i.padding[1] + x := t.Block.X + t.padding[0] + y := t.Block.Y + t.padding[1] - maxWidth := i.Width - (i.padding[0] * 2) - - for _, line := range i.TextOut { - // truncate lines longer than maxWidth - if len(line) > maxWidth { - line = fmt.Sprintf("%s...", line[:maxWidth-3]) - } + for _, line := range t.TextOut { for _, ch := range line { - cell = ui.Cell{Ch: ch, Fg: i.TextFgColor, Bg: i.TextBgColor} + cell = ui.Cell{Ch: ch, Fg: t.TextFgColor, Bg: t.TextBgColor} buf.Set(x, y, cell) x++ } - x = i.Block.X + i.padding[0] + x = t.Block.X + t.padding[0] y++ } return buf } -func (i *TextView) renderLoop() { +func (t *TextView) renderLoop() { go func() { - for range i.render { - size := i.Height - (i.padding[1] * 2) - if size > len(i.Text) { - size = len(i.Text) + for range t.render { + maxWidth := t.Width - (t.padding[0] * 2) + height := t.Height - (t.padding[1] * 2) + t.TextOut = []string{} + for i := len(t.Text) - 1; i >= 0; i-- { + lines := splitLine(t.Text[i], maxWidth) + t.TextOut = append(lines, t.TextOut...) + if len(t.TextOut) > height { + t.TextOut = t.TextOut[:height] + break + } } - i.TextOut = i.Text[len(i.Text)-size:] - - ui.Render(i) + ui.Render(t) } }() } -func (i *TextView) readInputLoop() { +func (t *TextView) readInputLoop() { go func() { - for line := range i.inputStream { - i.Text = append(i.Text, line) - i.render <- true + for line := range t.inputStream { + t.Text = append(t.Text, line) + t.render <- true } - close(i.render) + close(t.render) }() } + +func splitLine(line string, lineSize int) []string { + if line == "" { + return []string{} + } + + var lines []string + for { + if len(line) <= lineSize { + lines = append(lines, line) + return lines + } + lines = append(lines, line[:lineSize]) + line = line[lineSize:] + } +} diff --git a/widgets/view_test.go b/widgets/view_test.go new file mode 100644 index 0000000..33c8aee --- /dev/null +++ b/widgets/view_test.go @@ -0,0 +1,35 @@ +package widgets + +import "testing" + +func TestSplitEmptyLine(t *testing.T) { + + result := splitLine("", 5) + if len(result) != 0 { + t.Errorf("expected: 0 lines, got: %d", len(result)) + } +} + +func TestSplitLineShorterThanLimit(t *testing.T) { + + result := splitLine("hello", 7) + if len(result) != 1 { + t.Errorf("expected: 0 lines, got: %d", len(result)) + } +} + +func TestSplitLineLongerThanLimit(t *testing.T) { + + result := splitLine("hello", 3) + if len(result) != 2 { + t.Errorf("expected: 0 lines, got: %d", len(result)) + } +} + +func TestSplitLineSameAsLimit(t *testing.T) { + + result := splitLine("hello", 5) + if len(result) != 1 { + t.Errorf("expected: 0 lines, got: %d", len(result)) + } +}