AUTO_FOCUS targets first focusable widget.

Related issues: #2578.
This commit is contained in:
Rodrigo Girão Serrão
2023-05-16 11:27:24 +01:00
parent 6147c28dbf
commit b592ac077a
3 changed files with 24 additions and 5 deletions

View File

@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fixed `TreeNode.toggle` and `TreeNode.toggle_all` not posting a `Tree.NodeExpanded` or `Tree.NodeCollapsed` message https://github.com/Textualize/textual/issues/2535 - Fixed `TreeNode.toggle` and `TreeNode.toggle_all` not posting a `Tree.NodeExpanded` or `Tree.NodeCollapsed` message https://github.com/Textualize/textual/issues/2535
- `footer--description` component class was being ignored https://github.com/Textualize/textual/issues/2544 - `footer--description` component class was being ignored https://github.com/Textualize/textual/issues/2544
- Pasting empty selection in `Input` would raise an exception https://github.com/Textualize/textual/issues/2563 - Pasting empty selection in `Input` would raise an exception https://github.com/Textualize/textual/issues/2563
- `Screen.AUTO_FOCUS` now focuses the first _focusable_ widget that matches the selector https://github.com/Textualize/textual/issues/2578
### Added ### Added

View File

@@ -668,11 +668,14 @@ class Screen(Generic[ScreenResultType], Widget):
size = self.app.size size = self.app.size
if self.AUTO_FOCUS is not None and self.focused is None: if self.AUTO_FOCUS is not None and self.focused is None:
try: try:
to_focus = self.query(self.AUTO_FOCUS).first() focus_candidates = self.query(self.AUTO_FOCUS)
except NoMatches: except NoMatches:
pass pass
else: else:
self.set_focus(to_focus) for widget in focus_candidates:
if widget.focusable:
self.set_focus(widget)
break
self._refresh_layout(size, full=True) self._refresh_layout(size, full=True)
self.refresh() self.refresh()

View File

@@ -6,7 +6,7 @@ import pytest
from textual.app import App, ScreenStackError from textual.app import App, ScreenStackError
from textual.screen import Screen from textual.screen import Screen
from textual.widgets import Button, Input from textual.widgets import Button, Input, Label
skip_py310 = pytest.mark.skipif( skip_py310 = pytest.mark.skipif(
sys.version_info.minor == 10 and sys.version_info.major == 3, sys.version_info.minor == 10 and sys.version_info.major == 3,
@@ -155,8 +155,7 @@ async def test_screens():
async def test_auto_focus(): async def test_auto_focus():
class MyScreen(Screen[None]): class MyScreen(Screen[None]):
def compose(self) -> None: def compose(self):
print("composing")
yield Button() yield Button()
yield Input(id="one") yield Input(id="one")
yield Input(id="two") yield Input(id="two")
@@ -192,3 +191,19 @@ async def test_auto_focus():
assert app.focused is None assert app.focused is None
app.pop_screen() app.pop_screen()
assert app.focused.id == "two" assert app.focused.id == "two"
async def test_auto_focus_skips_non_focusable_widgets():
class MyScreen(Screen[None]):
def compose(self):
yield Label()
yield Button()
class MyApp(App[None]):
def on_mount(self):
self.push_screen(MyScreen())
app = MyApp()
async with app.run_test():
assert app.focused is not None
assert isinstance(app.focused, Button)