mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user