Skip to content

Improve focus navigation performance by caching Screen.focus_chain between layout changes#6608

Open
RajanChavada wants to merge 1 commit into
Textualize:mainfrom
RajanChavada:fix/focus-chain-cache
Open

Improve focus navigation performance by caching Screen.focus_chain between layout changes#6608
RajanChavada wants to merge 1 commit into
Textualize:mainfrom
RajanChavada:fix/focus-chain-cache

Conversation

@RajanChavada

Copy link
Copy Markdown

Link to issue or discussion

Addresses the acknowledged TODO at screen.py:774:

TODO: Calculating a focus chain is moderately expensive. Suspect we can move focus without calculating the entire thing again.

What this PR does

Screen.focus_chain traverses the entire DOM on every call — sorting children by focus order, walking visibility, and checking disabled state. It's called on every Tab/Shift+Tab keypress and in several internal focus-management paths.

This PR caches the result in _focus_chain_cache and clears it in the three places that make it stale:

Invalidation point Why
Screen._on_layout Covers mount, unmount, show/hide (display: none), and disabled state changes — all of which post a Layout message
Screen.set_focus The root node of the traversal depends on self.focused when _trap_focus is active
DOMNode.trap_focus Directly toggles _trap_focus, changing which node acts as the traversal root — no layout message is posted

_add_children (test-only helper that bypasses the mount pipeline) also clears the cache so existing tests stay green.

Testing

  • All 53 existing focus/screen tests pass (test_focus.py, test_app_focus_blur.py, test_screens.py, test_screen_modes.py)
  • Full non-snapshot suite: 1431 passed, 1 pre-existing failure in test_features.py unrelated to this change

…nges

Recalculating the focus chain on every access traverses the entire DOM
sorted by focus order, which is moderately expensive. Cache the result and
clear it in the three places that make it stale: _on_layout (mount/unmount/
visibility/disabled changes), set_focus (root node depends on focused widget
when _trap_focus is active), and trap_focus() (directly changes which node
acts as root).
@RajanChavada RajanChavada changed the title Cache focus_chain result and invalidate on layout/focus/trap changes Improve focus navigation performance by caching Screen.focus_chain between layout changes Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant