diff --git a/CHANGELOG.md b/CHANGELOG.md index ef2e5b6086..de0ab579ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +### Fixed + +- Fix crash when using Input in a DEFAULT_MODE screen https://github.com/Textualize/textual/pull/6492 + ### Added - Added `DOM.update_classes` https://github.com/Textualize/textual/pull/6478 diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index ef8be70c2d..a2a01d43b1 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -252,7 +252,7 @@ def cursor_position(self, position: int) -> None: """Set the current position of the cursor.""" self.selection = Selection.cursor(position) - selection: Reactive[Selection] = reactive(Selection.cursor(0)) + selection: Reactive[Selection] = reactive(Selection.cursor(0), init=False) """The currently selected range of text.""" placeholder = reactive("") @@ -505,6 +505,9 @@ def validate_selection(self, selection: Selection) -> Selection: return Selection(clamp(start, 0, value_length), clamp(end, 0, value_length)) def _watch_selection(self, selection: Selection) -> None: + if not self.is_mounted: + return + self.app.clear_selection() self.app.cursor_position = self.cursor_screen_offset if not self._initial_value: diff --git a/tests/input/test_input_default_mode.py b/tests/input/test_input_default_mode.py new file mode 100644 index 0000000000..495585ce6d --- /dev/null +++ b/tests/input/test_input_default_mode.py @@ -0,0 +1,34 @@ +import asyncio + +import pytest + +from textual.app import App, ComposeResult +from textual.screen import Screen +from textual.widgets import Input + + +class DefaultScreen(Screen): + def compose(self) -> ComposeResult: + yield Input() + yield Input("Hello, world!") + + +class InputApp(App): + DEFAULT_MODE = "default" + MODES = {"default": DefaultScreen} + + +@pytest.mark.skipif( + not hasattr(asyncio, "eager_task_factory"), reason="only occurs with eager tasks" +) +async def test_input_default_mode(): + """Test that Input in a DEFAULT_MODE Screen doesn't crash. + + Regression test for https://github.com/textualize/textual/issues/6444. + + """ + # changing this will not affect other tests since pytest-asyncio runs every function + # in a new event loop by default + asyncio.get_running_loop().set_task_factory(asyncio.eager_task_factory) + async with InputApp().run_test(): + pass