From 544a0bb5427a159a41dfe41e539093f7f47d2fe8 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 6 Apr 2024 15:40:05 +0900 Subject: Use synchronized update mode to simplify and enhance rendering --- CHANGELOG.md | 5 +++++ src/terminal.go | 39 +++++++++------------------------------ src/tui/light.go | 6 +----- src/tui/tcell.go | 5 ----- src/tui/tui.go | 1 - 5 files changed, 15 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d6c9fb1..6f710099 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +0.50.0 +------ +- Eliminated any flickering of the screen and simplified the code using [synchronized update mode](https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036) + - TODO: Not all terminals support this mode. We can detect if the current terminal supports this. But it means we can't simplify the code using the mode. + 0.49.0 ------ - Ingestion performance improved by around 40% (more or less depending on options) diff --git a/src/terminal.go b/src/terminal.go index 2289a7f9..c2de7234 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -130,7 +130,6 @@ type previewed struct { offset int filled bool image bool - wipe bool wireframe bool } @@ -145,8 +144,6 @@ type itemLine struct { selected bool label string queryLen int - width int - bar bool result Result } @@ -768,7 +765,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { initialPreviewOpts: opts.Preview, previewOpts: opts.Preview, previewer: previewer{0, []string{}, 0, false, true, disabledState, "", []bool{}}, - previewed: previewed{0, 0, 0, false, false, false, false}, + previewed: previewed{0, 0, 0, false, false, false}, previewBox: previewBox, eventBox: eventBox, mutex: sync.Mutex{}, @@ -1836,12 +1833,11 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool, bar b // Avoid unnecessary redraw newLine := itemLine{offset: line, current: current, selected: selected, label: label, - result: result, queryLen: len(t.input), width: 0, bar: bar} + result: result, queryLen: len(t.input)} prevLine := t.prevLines[i] forceRedraw := prevLine.offset != newLine.offset printBar := func() { - if len(t.scrollbar) > 0 && (bar != prevLine.bar || forceRedraw) { - t.prevLines[i].bar = bar + if len(t.scrollbar) > 0 { t.move(line, t.window.Width()-1, true) if bar { t.window.CPrint(tui.ColScrollbar, t.scrollbar) @@ -1859,7 +1855,7 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool, bar b return } - t.move(line, 0, forceRedraw) + t.move(line, 0, true) if current { if len(label) == 0 { t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty) @@ -1871,7 +1867,7 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool, bar b } else { t.window.CPrint(tui.ColCurrentSelectedEmpty, t.markerEmpty) } - newLine.width = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true) + t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true) } else { if len(label) == 0 { t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty) @@ -1883,11 +1879,7 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool, bar b } else { t.window.Print(t.markerEmpty) } - newLine.width = t.printHighlighted(result, tui.ColNormal, tui.ColMatch, false, true) - } - fillSpaces := prevLine.width - newLine.width - if fillSpaces > 0 { - t.window.Print(strings.Repeat(" ", fillSpaces)) + t.printHighlighted(result, tui.ColNormal, tui.ColMatch, false, true) } printBar() t.prevLines[i] = newLine @@ -1931,7 +1923,7 @@ func (t *Terminal) overflow(runes []rune, max int) bool { return t.displayWidthWithLimit(runes, 0, max) > max } -func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool) int { +func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool) { item := result.item // Overflow @@ -2009,11 +2001,9 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat offsets[idx].offset[1] = util.Min32(offset.offset[1], int32(maxWidth)) } } - displayWidth = t.displayWidthWithLimit(text, 0, displayWidth) } t.printColoredString(t.window, text, offsets, colBase) - return displayWidth } func (t *Terminal) printColoredString(window tui.Window, text []rune, offsets []colorOffset, colBase tui.ColorPair) { @@ -2072,21 +2062,11 @@ func (t *Terminal) renderPreviewSpinner() { } func (t *Terminal) renderPreviewArea(unchanged bool) { - if t.previewed.wipe && t.previewed.version != t.previewer.version { - t.previewed.wipe = false - t.pwindow.Erase() - } else if unchanged { + if unchanged { t.pwindow.MoveAndClear(0, 0) // Clear scroll offset display } else { t.previewed.filled = false - // We don't erase the window here to avoid flickering during scroll. - // However, tcell renderer uses double-buffering technique and there's no - // flickering. So we just erase the window and make the rest of the code - // simpler. - if !t.pwindow.EraseMaybe() { - t.pwindow.DrawBorder() - t.pwindow.Move(0, 0) - } + t.pwindow.Erase() } height := t.pwindow.Height() @@ -2173,7 +2153,6 @@ Loop: isItermImage := strings.HasPrefix(passThrough, "\x1b]1337;") isImage := isSixel || isItermImage if isImage { - t.previewed.wipe = true // NOTE: We don't have a good way to get the height of an iTerm image, // so we assume that it requires the full height of the preview // window. diff --git a/src/tui/light.go b/src/tui/light.go index 216c4c36..90cd310b 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -71,7 +71,7 @@ func (r *LightRenderer) csi(code string) string { func (r *LightRenderer) flush() { if r.queued.Len() > 0 { - fmt.Fprint(os.Stderr, "\x1b[?25l"+r.queued.String()+"\x1b[?25h") + fmt.Fprint(os.Stderr, "\x1b[?2026h"+r.queued.String()+"\x1b[?2026l") r.queued.Reset() } } @@ -1128,7 +1128,3 @@ func (w *LightWindow) Erase() { w.FinishFill() w.Move(0, 0) } - -func (w *LightWindow) EraseMaybe() bool { - return false -} diff --git a/src/tui/tcell.go b/src/tui/tcell.go index 0ca8aee7..bc6e13ee 100644 --- a/src/tui/tcell.go +++ b/src/tui/tcell.go @@ -565,11 +565,6 @@ func (w *TcellWindow) Erase() { w.drawBorder(false) } -func (w *TcellWindow) EraseMaybe() bool { - w.Erase() - return true -} - func (w *TcellWindow) Enclose(y int, x int) bool { return x >= w.left && x < (w.left+w.width) && y >= w.top && y < (w.top+w.height) diff --git a/src/tui/tui.go b/src/tui/tui.go index 022fae34..eacf6a65 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -530,7 +530,6 @@ type Window interface { Fill(text string) FillReturn CFill(fg Color, bg Color, attr Attr, text string) FillReturn Erase() - EraseMaybe() bool } type FullscreenRenderer struct { -- cgit v1.2.3