From b148ab2aa9012c60632a6e0aaf2db5465b7a9b34 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Fri, 23 May 2025 14:52:31 +0100 Subject: [PATCH] fix(input): fix invalid cursor position after updating value Validate the `Input` selection when the value changes, to ensure it accounts for the length of the new value. Fixes #5811 --- CHANGELOG.md | 1 + src/textual/widgets/_input.py | 4 ++++ tests/input/test_input_properties.py | 13 +++++++++++++ 3 files changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 072f92175..86d28a677 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 e70c69407..a9e80c696 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -536,6 +536,10 @@ class Input(ScrollView): 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 ef518ba69..779254932 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))