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
This commit is contained in:
TomJGooding
2025-05-23 14:52:31 +01:00
parent e9be1898ca
commit b148ab2aa9
3 changed files with 18 additions and 0 deletions

View File

@@ -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

View File

@@ -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."""

View File

@@ -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))