diff options
author | Hubert Hirtz <hubert@hirtzfr.eu> | 2020-06-12 11:24:35 +0200 |
---|---|---|
committer | Hubert Hirtz <hubert@hirtzfr.eu> | 2020-06-12 11:24:35 +0200 |
commit | bff8eb4f41daede6b2057804b222aed5dda114bb (patch) | |
tree | 4b44518383f96515325a1303f1394a61313a43cd | |
parent | Split lines between words (diff) |
wip
Diffstat (limited to '')
-rw-r--r-- | cmd/irc/main.go | 6 | ||||
-rw-r--r-- | ui/buffers.go | 40 | ||||
-rw-r--r-- | ui/buffers_test.go | 87 | ||||
-rw-r--r-- | ui/ui.go | 49 |
4 files changed, 146 insertions, 36 deletions
diff --git a/cmd/irc/main.go b/cmd/irc/main.go index b11f34d..7b63d3c 100644 --- a/cmd/irc/main.go +++ b/cmd/irc/main.go @@ -84,6 +84,10 @@ func main() { app.Exit() case tcell.KeyCtrlL: app.Resize() + case tcell.KeyCtrlU: + app.ScrollUp() + case tcell.KeyCtrlD: + app.ScrollDown() case tcell.KeyCtrlN: app.NextBuffer() case tcell.KeyCtrlP: @@ -202,7 +206,7 @@ func color(nick string) (c [3]rune) { h := fnv.New32() _, _ = h.Write([]byte(nick)) - sum := h.Sum32() % 14 + sum := h.Sum32() % 96 if 1 <= sum { sum++ diff --git a/ui/buffers.go b/ui/buffers.go index 891b12f..c717691 100644 --- a/ui/buffers.go +++ b/ui/buffers.go @@ -22,6 +22,8 @@ func IsSplitRune(c rune) bool { type Point struct { X int I int + + Split bool } type Line struct { @@ -62,30 +64,43 @@ func (line *Line) RenderedHeight(screenWidth int) (height int) { } func (line *Line) computeRenderedHeight(screenWidth int) { + var lastSP Point line.renderedHeight = 1 - residualWidth := 0 + x := 0 + fmt.Printf("\n%d %q\n", screenWidth, line.Content) for _, sp := range line.SplitPoints { - w := sp.X + residualWidth - - if line.renderedHeight*screenWidth < w { - line.renderedHeight += 1 - residualWidth = w % screenWidth + l := sp.X - lastSP.X + + if !sp.Split && x == 0 { + // Don't add space at the beginning of a row + } else if screenWidth < l { + line.renderedHeight += (x + l) / screenWidth + x = (x + l) % screenWidth + } else if screenWidth < x+l { + line.renderedHeight++ + x = l % screenWidth + } else { + x = (x + l) % screenWidth } + + fmt.Printf("%d %d %t occupied by %q\n", line.renderedHeight, x, sp.Split, line.Content[:sp.I]) + lastSP = sp } } func (line *Line) computeSplitPoints() { var wb widthBuffer - lastWasSplit := true + lastWasSplit := false for i, r := range line.Content { curIsSplit := IsSplitRune(r) - if !lastWasSplit && curIsSplit { + if lastWasSplit != curIsSplit { line.SplitPoints = append(line.SplitPoints, Point{ - X: wb.Width(), - I: i, + X: wb.Width(), + I: i, + Split: curIsSplit, }) } @@ -95,8 +110,9 @@ func (line *Line) computeSplitPoints() { if !lastWasSplit { line.SplitPoints = append(line.SplitPoints, Point{ - X: wb.Width(), - I: len(line.Content), + X: wb.Width(), + I: len(line.Content), + Split: true, }) } } diff --git a/ui/buffers_test.go b/ui/buffers_test.go index fdcbece..ca71864 100644 --- a/ui/buffers_test.go +++ b/ui/buffers_test.go @@ -8,6 +8,7 @@ func assertSplitPoints(t *testing.T, line string, expected []Point) { if len(l.SplitPoints) != len(expected) { t.Errorf("%q: expected %d split points got %d", line, len(expected), len(l.SplitPoints)) + return } for i := 0; i < len(expected); i++ { @@ -20,23 +21,31 @@ func assertSplitPoints(t *testing.T, line string, expected []Point) { if e.I != a.I { t.Errorf("%q, point #%d: expected I=%d got %d", line, i, e.I, a.I) } + if e.Split != a.Split { + t.Errorf("%q, point #%d: expected Split=%t got %t", line, i, e.Split, a.Split) + } } } func TestLineSplitPoints(t *testing.T) { assertSplitPoints(t, "hello", []Point{ - {X: 5, I: 5}, + {X: 5, I: 5, Split: true}, }) assertSplitPoints(t, "hello world", []Point{ - {X: 5, I: 5}, - {X: 11, I: 11}, + {X: 5, I: 5, Split: true}, + {X: 6, I: 6, Split: false}, + {X: 11, I: 11, Split: true}, }) assertSplitPoints(t, "lorem ipsum dolor shit amet", []Point{ - {X: 5, I: 5}, - {X: 11, I: 11}, - {X: 17, I: 17}, - {X: 22, I: 22}, - {X: 27, I: 27}, + {X: 5, I: 5, Split: true}, + {X: 6, I: 6, Split: false}, + {X: 11, I: 11, Split: true}, + {X: 12, I: 12, Split: false}, + {X: 17, I: 17, Split: true}, + {X: 18, I: 18, Split: false}, + {X: 22, I: 22, Split: true}, + {X: 23, I: 23, Split: false}, + {X: 27, I: 27, Split: true}, }) } @@ -48,16 +57,64 @@ func assertRenderedHeight(t *testing.T, line string, width int, expected int) { actual := l.RenderedHeight(width) if actual != expected { - t.Errorf("%q (width=%d) expected to take %d lines, takes %d", line, width, expected, actual) + t.Errorf("%q with width=%d expected to take %d lines, takes %d", line, width, expected, actual) } } func TestRenderedHeight(t *testing.T) { - assertRenderedHeight(t, "hello world", 100, 1) - assertRenderedHeight(t, "hello world", 10, 2) + assertRenderedHeight(t, "0123456789", 1, 10) + assertRenderedHeight(t, "0123456789", 2, 5) + assertRenderedHeight(t, "0123456789", 3, 4) + assertRenderedHeight(t, "0123456789", 4, 3) + assertRenderedHeight(t, "0123456789", 5, 2) + assertRenderedHeight(t, "0123456789", 6, 2) + assertRenderedHeight(t, "0123456789", 7, 2) + assertRenderedHeight(t, "0123456789", 8, 2) + assertRenderedHeight(t, "0123456789", 9, 2) + assertRenderedHeight(t, "0123456789", 10, 1) + assertRenderedHeight(t, "0123456789", 11, 1) + + // LEN=9, WIDTH=9 + assertRenderedHeight(t, "take care", 1, 8) // |t|a|k|e|c|a|r|e| + assertRenderedHeight(t, "take care", 2, 4) // |ta|ke|ca|re| + assertRenderedHeight(t, "take care", 3, 3) // |tak|e c|are| + assertRenderedHeight(t, "take care", 4, 2) // |take|care| + assertRenderedHeight(t, "take care", 5, 2) // |take |care | + assertRenderedHeight(t, "take care", 6, 2) // |take |care | + assertRenderedHeight(t, "take care", 7, 2) // |take |care | + assertRenderedHeight(t, "take care", 8, 2) // |take |care | + assertRenderedHeight(t, "take care", 9, 1) // |take care| + assertRenderedHeight(t, "take care", 10, 1) // |take care | + + // LEN=10, WIDTH=10 + assertRenderedHeight(t, "take care", 1, 8) // |t|a|k|e|c|a|r|e| + assertRenderedHeight(t, "take care", 2, 4) // |ta|ke|ca|re| + assertRenderedHeight(t, "take care", 3, 4) // |tak|e |car|e | + assertRenderedHeight(t, "take care", 4, 2) // |take|care| + assertRenderedHeight(t, "take care", 5, 2) // |take |care | + assertRenderedHeight(t, "take care", 6, 2) // |take |care | + assertRenderedHeight(t, "take care", 7, 2) // |take |care | + assertRenderedHeight(t, "take care", 8, 2) // |take |care | + assertRenderedHeight(t, "take care", 9, 2) // |take |care | + assertRenderedHeight(t, "take care", 10, 1) // |take care| + assertRenderedHeight(t, "take care", 11, 1) // |take care | - assertRenderedHeight(t, "have a good day!", 100, 1) - assertRenderedHeight(t, "have a good day!", 10, 2) - assertRenderedHeight(t, "have a good day!", 6, 3) - assertRenderedHeight(t, "have a good day!", 4, 4) + // LEN=16, WIDTH=16 + assertRenderedHeight(t, "have a good day!", 1, 13) // |h|a|v|e|a|g|o|o|d|d|a|y|!| + assertRenderedHeight(t, "have a good day!", 2, 7) // |ha|ve|a |go|od|da|y!| + assertRenderedHeight(t, "have a good day!", 3, 5) // |hav|e a|goo|d d|ay!| + assertRenderedHeight(t, "have a good day!", 4, 4) // |have|a |good|day!| + assertRenderedHeight(t, "have a good day!", 5, 3) // |have |a good|day! | + assertRenderedHeight(t, "have a good day!", 6, 3) // |have a|good |day! | + assertRenderedHeight(t, "have a good day!", 7, 3) // |have a |good |day! | + assertRenderedHeight(t, "have a good day!", 8, 3) // |have a |good |day! | + assertRenderedHeight(t, "have a good day!", 9, 2) // |have a |good day!| + assertRenderedHeight(t, "have a good day!", 10, 2) // |have a |good day! | + assertRenderedHeight(t, "have a good day!", 11, 2) // |have a good|day! | + assertRenderedHeight(t, "have a good day!", 12, 2) // |have a good |day! | + assertRenderedHeight(t, "have a good day!", 13, 2) // |have a good |day! | + assertRenderedHeight(t, "have a good day!", 14, 2) // |have a good |day! | + assertRenderedHeight(t, "have a good day!", 15, 2) // |have a good |day! | + assertRenderedHeight(t, "have a good day!", 16, 1) // |have a good day!| + assertRenderedHeight(t, "have a good day!", 17, 1) // |have a good day! | } @@ -13,6 +13,8 @@ type UI struct { exit atomic.Value // bool bufferList BufferList + scrollAmt int + textInput []rune textCursor int } @@ -80,6 +82,7 @@ func (ui *UI) CurrentBuffer() (title string) { func (ui *UI) NextBuffer() { ok := ui.bufferList.Next() if ok { + ui.scrollAmt = 0 ui.drawBuffer() ui.drawStatus() } @@ -88,11 +91,32 @@ func (ui *UI) NextBuffer() { func (ui *UI) PreviousBuffer() { ok := ui.bufferList.Previous() if ok { + ui.scrollAmt = 0 ui.drawBuffer() ui.drawStatus() } } +func (ui *UI) ScrollUp() { + w, _ := ui.screen.Size() + ui.scrollAmt += w / 2 + ui.drawBuffer() +} + +func (ui *UI) ScrollDown() { + if ui.scrollAmt == 0 { + return + } + + w, _ := ui.screen.Size() + ui.scrollAmt -= w / 2 + if ui.scrollAmt < 0 { + ui.scrollAmt = 0 + } + + ui.drawBuffer() +} + func (ui *UI) AddBuffer(title string) { _, ok := ui.bufferList.Add(title) if ok { @@ -118,7 +142,11 @@ func (ui *UI) AddLine(buffer string, line string, t time.Time, isStatus bool) { ui.bufferList.AddLine(idx, line, t, isStatus) if idx == ui.bufferList.Current { - ui.drawBuffer() + if 0 < ui.scrollAmt { + ui.scrollAmt++ + } else { + ui.drawBuffer() + } } } @@ -176,6 +204,7 @@ func (ui *UI) InputEnter() (content string) { func (ui *UI) Resize() { ui.bufferList.Invalidate() + ui.scrollAmt = 0 ui.draw() } @@ -242,17 +271,25 @@ func (ui *UI) drawBuffer() { var colorState int var fgColor, bgColor int - y0 := h - 2 + yEnd := h - 2 + y0 := ui.scrollAmt + h - 2 for i := len(b.Content) - 1; 0 <= i; i-- { line := &b.Content[i] + if y0 < 0 { + break + } + lineHeight := line.RenderedHeight(w) y0 -= lineHeight - y := y0 + if yEnd <= y0 { + continue + } rs := []rune(line.Content) x := 0 + y := y0 spIdx := 0 hasLineHadSplit := false @@ -267,7 +304,7 @@ func (ui *UI) drawBuffer() { hasLineHadSplit = false } - if IsSplitRune(r) { + if line.SplitPoints[spIdx].Split { if i == line.SplitPoints[spIdx].I { spIdx++ } @@ -370,10 +407,6 @@ func (ui *UI) drawBuffer() { x += runewidth.RuneWidth(r) } - if y0 < 0 { - break - } - st = tcell.StyleDefault bold = false italic = false |