diff options
| author | blasty <blasty@local> | 2026-07-09 16:12:28 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-09 16:12:28 +0200 |
| commit | 2451641f5edf59b08069fe11fbbb5b8a48fcb168 (patch) | |
| tree | cd46f6c343128a65a79255b4093f8a4e015831ce | |
| parent | stop piling up idalib workers; raise max-workers headroom (diff) | |
| download | ida-tui-2451641f5edf59b08069fe11fbbb5b8a48fcb168.tar.gz ida-tui-2451641f5edf59b08069fe11fbbb5b8a48fcb168.tar.xz ida-tui-2451641f5edf59b08069fe11fbbb5b8a48fcb168.zip | |
smoother scroll restore on jump-back; non-degenerate test
The restored scroll was correct in the final state but load() zeroed
virtual_size (snapping scroll to 0) and only a deferred call_after_refresh
jumped to the target -> a visible flash to the top before settling. Now: don't
zero virtual_size on load, and apply the target scroll synchronously in
_on_primed (plus the deferred call as a fallback for un-sized regions).
Also hardened the scroll test to use a MID-viewport cursor (rel>0). The prior
test had the cursor at the viewport top (rel=0), where scroll-into-view derives
the same scroll -> it would have passed even if scroll restore were broken.
pilot suite 52/52.
| -rw-r--r-- | idatui/app.py | 8 | ||||
| -rw-r--r-- | tests/test_tui.py | 19 |
2 files changed, 20 insertions, 7 deletions
diff --git a/idatui/app.py b/idatui/app.py index 7dc59b4..a5be0f7 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -561,7 +561,8 @@ class DisasmView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=True self.cursor = cursor self.cursor_x = cursor_x self._pending_scroll_y = scroll_y - self.virtual_size = Size(0, 0) + # NB: don't zero virtual_size here — that snaps the scroll to 0 and + # causes a visible jump before _on_primed restores the target scroll. self._matches = [] self._ranges = {} self._search_texts = None @@ -624,8 +625,9 @@ class DisasmView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=True self._clamp_x() # cursor line is now cached; keep the column in range if self._pending_scroll_y is not None and self._pending_scroll_y >= 0: target = min(self._pending_scroll_y, max(total - 1, 0)) - # Defer until after layout recomputes max_scroll_y, else scroll_to - # clamps to 0 (virtual_size was only just set). + # Apply now (works when the region is already sized) and again after + # refresh (covers the case where max_scroll_y isn't computed yet). + self.scroll_to(y=target, animate=False) self.call_after_refresh(lambda t=target: self.scroll_to(y=t, animate=False)) else: self._scroll_cursor_into_view() diff --git a/tests/test_tui.py b/tests/test_tui.py index 18194bf..51c9f57 100644 --- a/tests/test_tui.py +++ b/tests/test_tui.py @@ -495,16 +495,27 @@ async def run(db): for _ in range(4): await pilot.press("ctrl+d") await pilot.pause(0.2) + # Put the cursor in the MIDDLE of the viewport so the test is + # non-degenerate: with rel>0, restoring only the cursor (scroll-into-view) + # would derive a different scroll, so this actually checks scroll restore. + base = round(dis.scroll_offset.y) + mid = base + min(dis.size.height // 2, max(dis.total - base - 1, 0)) + dis.cursor, dis.cursor_x = mid, 0 + dis.refresh() + dis._after_cursor_move() + await pilot.pause(0.1) want_sy, want_cur = round(dis.scroll_offset.y), dis.cursor + want_rel = want_cur - want_sy await goto_name(fb.name) await wait_until(pilot, lambda: app._cur.ea == fb.addr, timeout=20) 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) - await pilot.pause(0.4) - check("disasm scroll + cursor restored on back", - round(dis.scroll_offset.y) == want_sy and dis.cursor == want_cur, - f"scroll={round(dis.scroll_offset.y)} (want {want_sy}) cursor={dis.cursor}") + await pilot.pause(0.5) + check("disasm scroll + cursor restored on back (mid-viewport)", + 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}") # PageUp/Down keep the cursor at the same viewport-relative row. await goto_name(fa.name) |
