diff options
| author | blasty <blasty@local> | 2026-07-25 10:47:41 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-25 10:47:41 +0200 |
| commit | 82f8d3d800d47c444b8b2f5a08918ede38954e46 (patch) | |
| tree | d0a1d055fcb390ed23d010d4d2288382511ab958 /idatui | |
| parent | docs: README covers the split view, strings browser and command palette (diff) | |
| download | ida-tui-82f8d3d800d47c444b8b2f5a08918ede38954e46.tar.gz ida-tui-82f8d3d800d47c444b8b2f5a08918ede38954e46.tar.xz ida-tui-82f8d3d800d47c444b8b2f5a08918ede38954e46.zip | |
split: keep the companion pane level with the driver's cursor
Scrolling either pane left the other one wherever it happened to be: the
companion only scrolled when the linked row went off-screen (reveal()), so the
link could sit at the bottom edge while the driver's cursor was mid-viewport —
visually incoherent, your eye had to hunt for it.
Add ListingView/DecompView.align(row, screen_row): scroll so the linked row lands
at the SAME viewport offset as the driver's cursor, and use it in _sync_split for
both directions. The two panes now track each other line-for-line, so the eye
reads straight across. Best-effort at the ends (can't scroll above line 0, nor
past the end when the pseudocode is shorter than the viewport).
Pilot split_view: deterministic alignment check — park the listing cursor at a
known viewport offset deep in the function, sync, and assert the decomp's scroll
top is exactly the aligned value (accounting for both clamps). 19/19; full suite
166/2-flake.
Diffstat (limited to 'idatui')
| -rw-r--r-- | idatui/app.py | 21 |
1 files changed, 19 insertions, 2 deletions
diff --git a/idatui/app.py b/idatui/app.py index 2bfda88..30e9b9b 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -1284,6 +1284,14 @@ class ListingView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=Tru if row < top or row >= top + height: self.scroll_to(y=max(row - height // 3, 0), animate=False) + def align(self, row: int, screen_row: int) -> None: + """Scroll so ``row`` sits at viewport offset ``screen_row`` — keeps this + (companion) pane visually level with the driver's cursor in split view. + Clamps at the ends, so alignment is best-effort near the edges.""" + top = max(0, min(row - max(screen_row, 0), max(self.total - 1, 0))) + if top != round(self.scroll_offset.y): + self.scroll_to(y=top, animate=False) + # -- navigation -------------------------------------------------------- # def _visible_height(self) -> int: return max(self.size.height, 1) @@ -1597,6 +1605,13 @@ class DecompView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=True if line < top or line >= top + height: self.scroll_to(y=max(line - height // 3, 0), animate=False) + def align(self, line: int, screen_row: int) -> None: + """Scroll so ``line`` sits at viewport offset ``screen_row`` — keeps this + (companion) pane visually level with the driver's cursor in split view.""" + top = max(0, min(line - max(screen_row, 0), max(len(self._strips) - 1, 0))) + if top != round(self.scroll_offset.y): + self.scroll_to(y=top, animate=False) + def line_for_ea(self, ea: int) -> int | None: """The pseudocode line whose marker ea is the largest <= ``ea`` (the C line that best covers an instruction address).""" @@ -4741,7 +4756,9 @@ class IdaTui(App): rows.add(r) lst.set_link(rows) if rows: - lst.reveal(min(rows)) + # keep the linked region level with the driver's cursor row so + # the eye tracks straight across the two panes + lst.align(min(rows), dec.cursor - round(dec.scroll_offset.y)) else: # the listing drives lst.set_link(set()) ea = lst._cursor_ea() @@ -4759,7 +4776,7 @@ class IdaTui(App): line = dec.line_for_ea(ea) if line is not None: dec.set_link(line) - dec.reveal(line) + dec.align(line, lst.cursor - round(lst.scroll_offset.y)) else: dec.set_link(None) |
