aboutsummaryrefslogtreecommitdiffstats
path: root/docs/TEXTUAL_NOTES.md
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-09 21:27:24 +0200
committerblasty <blasty@local>2026-07-09 21:27:24 +0200
commit584af7e8edf6224c0f3affb2bb98230078a9e836 (patch)
tree0f6b107020a5e0008666bc08851af4e04f053a8b /docs/TEXTUAL_NOTES.md
parentdecompiler: xref jump within the current function moves the cursor (diff)
downloadida-tui-584af7e8edf6224c0f3affb2bb98230078a9e836.tar.gz
ida-tui-584af7e8edf6224c0f3affb2bb98230078a9e836.tar.xz
ida-tui-584af7e8edf6224c0f3affb2bb98230078a9e836.zip
decompiler: fix stale scroll frame on same-function jump
DecompView.goto derived its scroll with _scroll_cursor_into_view() -> a plain scroll_to, which updates scroll_offset but doesn't repaint at the new offset (Textual only repaints on a rounded scroll change, and there's no layout pass since the pane isn't reloaded). So a same-function xref jump landed the cursor correctly but painted the old scroll until the next cursor move nudged a repaint. Route goto's scroll (derived or explicit) through _apply_scroll, which re-applies after the next refresh with layout=True -- the same pattern show() uses. The cross-function path already worked because reloading + clearing the loading cover forces a fresh paint. Document the gotcha in TEXTUAL_NOTES. full suite 64/64.
Diffstat (limited to 'docs/TEXTUAL_NOTES.md')
-rw-r--r--docs/TEXTUAL_NOTES.md12
1 files changed, 11 insertions, 1 deletions
diff --git a/docs/TEXTUAL_NOTES.md b/docs/TEXTUAL_NOTES.md
index ed2e696..eee40fc 100644
--- a/docs/TEXTUAL_NOTES.md
+++ b/docs/TEXTUAL_NOTES.md
@@ -16,10 +16,20 @@ Hard-won Textual behaviour and the patterns this app relies on. Pairs with
`call_after_refresh` with `refresh(layout=True)`; don't zero `virtual_size` on
load (snaps scroll to 0 → visible flash). See `ColumnCursor._apply_scroll`,
`DisasmView._on_primed`, `DecompView.show`.
+- **Scrolling on already-laid-out content (no reload) leaves a stale frame.** A
+ plain `scroll_to` moves `scroll_offset` but Textual only repaints on a
+ *rounded* scroll change, and with no virtual_size/content change there's no
+ layout pass to force a paint — so the pane shows the old frame until the next
+ interaction (moving the cursor auto-corrects it). Route these through
+ `_apply_scroll` too (immediate scroll + deferred `refresh(layout=True)`). Bit
+ us on same-function decompiler jumps (`DecompView.goto`); the reload path
+ (`DecompView.show`) hides it because clearing the loading cover repaints.
- **Pilot lays out synchronously**, so programmatic scroll always has a valid
range in tests — it MASKS the real-terminal clamp/no-repaint bug. Assert the
**render** (trace `render_line`'s scroll at paint), not just `scroll_offset.y`.
- (`test_tui.py`: "pane is repainted at the restored scroll".)
+ (`test_tui.py`: "pane is repainted at the restored scroll".) Note even a render
+ trace can't catch the `goto` stale-frame case (no layout pass to observe) — it
+ only shows in a real terminal.
- **`widget.loading = True`** covers the widget via `_cover_widget` (NOT a normal
child — `query()` won't find it; check `widget._cover_widget`) and **blurs
focus** (focus → None), so `Tab` falls back to focus-navigation. Fix: make the