Eliminate flicker on arrow key navigation in inventory and menus

Replace clear-then-write pattern with overwrite-in-place: move cursor
up to start of rendered area and write new content directly over old
lines. Only clear trailing lines if the new render is shorter. This
removes the visible blank frame between clear and re-render.
This commit is contained in:
Samuel Bouchet 2026-03-16 19:50:39 +01:00
parent a6eada7473
commit fcd270861c
2 changed files with 14 additions and 7 deletions

View file

@ -615,12 +615,15 @@ public sealed class WebGameHost
} }
string rendered = writer.ToString().Replace("\n", "\r\n"); string rendered = writer.ToString().Replace("\n", "\r\n");
int renderedLines = rendered.Split('\n').Length; int renderedLines = rendered.Count(c => c == '\n');
// Clear previous render and write new one // Overwrite in place: move cursor up, write new content over old lines,
// then clear any leftover lines if new render is shorter. No clear-before-write = no flicker.
if (previousRenderedLines > 0) if (previousRenderedLines > 0)
await _terminal.WriteAsync($"\x1b[{previousRenderedLines}A\x1b[J"); await _terminal.WriteAsync($"\x1b[{previousRenderedLines}A\r");
await _terminal.WriteAsync(rendered); await _terminal.WriteAsync(rendered);
if (previousRenderedLines > renderedLines)
await _terminal.WriteAsync("\x1b[J");
previousRenderedLines = renderedLines; previousRenderedLines = renderedLines;
var key = await _terminal.ReadKeyAsync(); var key = await _terminal.ReadKeyAsync();

View file

@ -235,10 +235,10 @@ public sealed class WebTerminal
{ {
int selected = 0; int selected = 0;
int pageSize = Math.Min(10, options.Count); int pageSize = Math.Min(10, options.Count);
int previousLineCount = 0;
while (true) while (true)
{ {
int scrollOffset = 0; int scrollOffset = 0;
if (selected >= pageSize) if (selected >= pageSize)
scrollOffset = selected - pageSize + 1; scrollOffset = selected - pageSize + 1;
@ -276,7 +276,14 @@ public sealed class WebTerminal
string rendered = writer.ToString().Replace("\n", "\r\n"); string rendered = writer.ToString().Replace("\n", "\r\n");
int lineCount = rendered.Count(c => c == '\n'); int lineCount = rendered.Count(c => c == '\n');
// Overwrite in place: move cursor up, write over old lines (no flicker).
// Only clear leftover lines if the new render is shorter than previous.
if (previousLineCount > 0)
await WriteAsync($"\x1b[{previousLineCount}A\r");
await WriteAsync(rendered); await WriteAsync(rendered);
if (previousLineCount > lineCount)
await WriteAsync("\x1b[J");
previousLineCount = lineCount;
var key = await ReadKeyAsync(); var key = await ReadKeyAsync();
@ -301,9 +308,6 @@ public sealed class WebTerminal
case ConsoleKey.D8 or ConsoleKey.NumPad8: if (options.Count >= 8) { await WriteAsync($"\x1b[{lineCount}A\x1b[J"); return 7; } break; case ConsoleKey.D8 or ConsoleKey.NumPad8: if (options.Count >= 8) { await WriteAsync($"\x1b[{lineCount}A\x1b[J"); return 7; } break;
case ConsoleKey.D9 or ConsoleKey.NumPad9: if (options.Count >= 9) { await WriteAsync($"\x1b[{lineCount}A\x1b[J"); return 8; } break; case ConsoleKey.D9 or ConsoleKey.NumPad9: if (options.Count >= 9) { await WriteAsync($"\x1b[{lineCount}A\x1b[J"); return 8; } break;
} }
// Clear menu before re-rendering
await WriteAsync($"\x1b[{lineCount}A\x1b[J");
} }
} }