diff --git a/CHANGELOG.md b/CHANGELOG.md index 072f92175e..86d28a6776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed `VERTICAL_BREAKPOINTS` doesn't work https://github.com/Textualize/textual/pull/5785 - Fixed `Button` allowing text selection https://github.com/Textualize/textual/pull/5770 +- Fixed `Input` invalid cursor position after updating the value https://github.com/Textualize/textual/issues/5811 ## [3.2.0] - 2025-05-02 diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index e70c694073..a9e80c696b 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -536,6 +536,10 @@ def _watch_value(self, value: str) -> None: if self._initial_value: self.cursor_position = len(self.value) self._initial_value = False + else: + # Force a re-validation of the selection to ensure it accounts for + # the length of the new value + self.selection = self.selection def _watch_valid_empty(self) -> None: """Repeat validation when valid_empty changes.""" diff --git a/tests/input/test_input_properties.py b/tests/input/test_input_properties.py index ef518ba69d..7792549327 100644 --- a/tests/input/test_input_properties.py +++ b/tests/input/test_input_properties.py @@ -93,3 +93,16 @@ async def test_input_selection_deleted_programmatically(): input_widget.selection = Selection(4, 0) input_widget.delete_selection() assert input_widget.value == "o, world!" + + +async def test_input_selection_is_valid_after_updating_value(): + """Regression test for https://github.com/Textualize/textual/issues/5811""" + app = InputApp() + async with app.run_test() as pilot: + input_widget = pilot.app.query_one(Input) + # Sanity check (by default focusing the input selects all text) + assert input_widget.selection == (0, len(input_widget.value)) + + input_widget.value = "foo" + + assert input_widget.selection == (0, len(input_widget.value))