diff options
| -rw-r--r-- | idatui/app.py | 5 | ||||
| -rw-r--r-- | tests/test_tui.py | 13 |
2 files changed, 17 insertions, 1 deletions
diff --git a/idatui/app.py b/idatui/app.py index a5be0f7..b76e6c5 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -627,8 +627,11 @@ class DisasmView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=True target = min(self._pending_scroll_y, max(total - 1, 0)) # Apply now (works when the region is already sized) and again after # refresh (covers the case where max_scroll_y isn't computed yet). + # The deferred call also forces a repaint, so the pane is never left + # showing a stale frame at the top with scroll_offset already moved. self.scroll_to(y=target, animate=False) - self.call_after_refresh(lambda t=target: self.scroll_to(y=t, animate=False)) + self.call_after_refresh( + lambda t=target: (self.scroll_to(y=t, animate=False), self.refresh())) else: self._scroll_cursor_into_view() self._pending_scroll_y = None diff --git a/tests/test_tui.py b/tests/test_tui.py index 51c9f57..884b1c3 100644 --- a/tests/test_tui.py +++ b/tests/test_tui.py @@ -508,6 +508,15 @@ async def run(db): want_rel = want_cur - want_sy await goto_name(fb.name) await wait_until(pilot, lambda: app._cur.ea == fb.addr, timeout=20) + # Record the scroll at each repaint so we catch the "scroll_offset moved + # but the pane wasn't repainted" bug (which scroll_offset checks miss). + renders: list[int] = [] + _orig_rl = dis.render_line + def _traced(y, _o=_orig_rl): + if y == 0: + renders.append(round(dis.scroll_offset.y)) + return _o(y) + dis.render_line = _traced await pilot.press("escape") await wait_until(pilot, lambda: app._cur.ea == fa.addr, timeout=20) await wait_until(pilot, lambda: dis.total > 40, timeout=20) @@ -516,6 +525,10 @@ async def run(db): round(dis.scroll_offset.y) == want_sy and dis.cursor == want_cur and want_rel > 0, f"scroll={round(dis.scroll_offset.y)} (want {want_sy}) " f"cursor={dis.cursor} (want {want_cur}) rel_before={want_rel}") + check("pane is repainted at the restored scroll (no stale top frame)", + bool(renders) and renders[-1] == want_sy, + f"last repaint scroll={renders[-1] if renders else None} (want {want_sy})") + dis.render_line = _orig_rl # PageUp/Down keep the cursor at the same viewport-relative row. await goto_name(fa.name) |
