Skip to content

Commit 20a2db9

Browse files
committed
trap focus
1 parent 3427dba commit 20a2db9

File tree

2 files changed

+21
-1
lines changed

2 files changed

+21
-1
lines changed

src/textual/dom.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ def __init__(
228228
) = None
229229
self._pruning = False
230230
self._query_one_cache: LRUCache[QueryOneCacheKey, DOMNode] = LRUCache(1024)
231+
self._trap_focus = False
231232

232233
super().__init__()
233234

@@ -475,6 +476,16 @@ def workers(self) -> WorkerManager:
475476
"""The app's worker manager. Shortcut for `self.app.workers`."""
476477
return self.app.workers
477478

479+
def trap_focus(self, trap_focus: bool = True) -> None:
480+
"""Trap the focus.
481+
482+
When applied to a container, pressing tab to change focus will be restricted to that container.
483+
484+
Args:
485+
trap_focus: `True` to trap focus. `False` to restore default behavior.
486+
"""
487+
self._trap_focus = trap_focus
488+
478489
def run_worker(
479490
self,
480491
work: WorkType[ResultType],

src/textual/screen.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,9 +743,18 @@ def focus_chain(self) -> list[Widget]:
743743
# Additionally, we manually keep track of the visibility of the DOM
744744
# instead of relying on the property `.visible` to save on DOM traversals.
745745
# node_stack: list[tuple[iterator over node children, node visibility]]
746+
747+
root_node = self.screen
748+
749+
if (focused := self.focused) is not None:
750+
for node in focused.ancestors_with_self:
751+
if node._trap_focus:
752+
root_node = node
753+
break
754+
746755
node_stack: list[tuple[Iterator[Widget], bool]] = [
747756
(
748-
iter(sorted(self.displayed_children, key=focus_sorter)),
757+
iter(sorted(root_node.displayed_children, key=focus_sorter)),
749758
self.visible,
750759
)
751760
]

0 commit comments

Comments
 (0)