summaryrefslogtreecommitdiffstats
path: root/idatui/app.py
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-07 02:16:16 +0200
committeruser <user@clank>2026-08-07 02:16:16 +0200
commitf8fb9b7c7b6abfd495ed3471b24c8ccd51187197 (patch)
treee3ba35c084ffef7a50ac271075b8756044031109 /idatui/app.py
parentTwo hot-path fixes found by profiling the plain-line builder: the opcode-byte... (diff)
downloadida-tui-f8fb9b7c7b6abfd495ed3471b24c8ccd51187197.tar.gz
ida-tui-f8fb9b7c7b6abfd495ed3471b24c8ccd51187197.tar.xz
ida-tui-f8fb9b7c7b6abfd495ed3471b24c8ccd51187197.zip
Only re-apply a scroll after the next refresh when it actually clamped. Both _apply_scroll implementations unconditionally scheduled a deferred scroll_to + refresh(layout=True) — a whole-screen re-arrange on every scroll — as a workaround for scrolling before the view's size is computed. Now the deferred pass runs only when scroll_offset didn't reach the target.
Result: {"status":"keep","total_ms":19005.8,"lg_boot_ms":788.9,"lg_decomp_ms":2752.5,"lg_graph_ms":899,"lg_hex_ms":565.2,"lg_index_ms":71.9,"lg_listing_cold_ms":416.1,"lg_listing_warm_ms":402.3,"lg_nav_ms":6756.3,"lg_palette_ms":4.7,"lg_render_ms":217.1,"lg_search_ms":2335.8,"pure_graph_ms":235.3,"sm_boot_ms":537.4,"sm_decomp_ms":600.9,"sm_graph_ms":693.1,"sm_hex_ms":549.9,"sm_index_ms":0,"sm_listing_cold_ms":280.9,"sm_listing_warm_ms":261.2,"sm_nav_ms":309,"sm_palette_ms":0.3,"sm_render_ms":249.3,"sm_search_ms":78.7,"fails":0}
Diffstat (limited to 'idatui/app.py')
-rw-r--r--idatui/app.py23
1 files changed, 18 insertions, 5 deletions
diff --git a/idatui/app.py b/idatui/app.py
index d07a188..1cdb8a8 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -592,13 +592,22 @@ class ColumnCursor:
self._page_scroll(direction * (self._visible_height() // 2))
def _apply_scroll(self, y: int, x: int = 0) -> None:
- """Set the scroll offset reliably after a (re)load. Applied now and again
- after the next refresh — when the view was just shown its size isn't
- computed yet, so an immediate scroll_to clamps to 0; the deferred pass
- re-applies it and forces a repaint so the pane never shows a stale frame.
+ """Set the scroll offset reliably after a (re)load. Applied now and, if
+ that didn't take, again after the next refresh — when the view was just
+ shown its size isn't computed yet, so an immediate scroll_to clamps to 0;
+ the deferred pass re-applies it and forces a repaint so the pane never
+ shows a stale frame.
+
+ The deferred pass is only scheduled when the scroll actually clamped.
+ ``refresh(layout=True)`` re-arranges the whole screen, and paying that on
+ every scroll that already landed cost ~25% of the time it takes to move
+ through a view.
"""
y = max(0, y)
self.scroll_to(x=x, y=y, animate=False)
+ off = self.scroll_offset
+ if round(off.y) == y and round(off.x) == x:
+ return # max_scroll was current, the offset is already where we want
def _fix(yy: int = y, xx: int = x) -> None:
self.scroll_to(x=xx, y=yy, animate=False)
@@ -1812,7 +1821,11 @@ class HexView(ScrollView, can_focus=True):
self.refresh(layout=True)
self.scroll_to(y=y, animate=False)
- self.call_after_refresh(_fix)
+ # Only re-apply when the scroll clamped (the view's size wasn't computed
+ # yet). See ColumnCursor._apply_scroll: the deferred pass drags a full
+ # layout with it, which is far too expensive to do on every scroll.
+ if round(self.scroll_offset.y) != y:
+ self.call_after_refresh(_fix)
def _scroll_to_cursor(self, center: bool = False) -> None:
height = self._visible_height()