diff --git a/CHANGELOG.md b/CHANGELOG.md index f129a59d70..9acada7d98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased + ### Fixed - Fixed `Pilot.click` not working with `times` parameter https://github.com/Textualize/textual/pull/5398 @@ -18,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - The content of an `Input` will now only be automatically selected when the widget is focused by the user, not when the app itself has regained focus (similar to web browsers). https://github.com/Textualize/textual/pull/5379 +- Updated `TextArea` and `Input` behavior when there is a selection and the user presses left or right https://github.com/Textualize/textual/pull/5400 ## [1.0.0] - 2024-12-12 diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index 78e501ffe4..463f9e13c8 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -761,11 +761,14 @@ def action_cursor_left(self, select: bool = False) -> None: Args: select: If `True`, select the text to the left of the cursor. """ + start, end = self.selection if select: - start, end = self.selection self.selection = Selection(start, end - 1) else: - self.cursor_position -= 1 + if self.selection.is_empty: + self.cursor_position -= 1 + else: + self.cursor_position = min(start, end) def action_cursor_right(self, select: bool = False) -> None: """Accept an auto-completion or move the cursor one position to the right. @@ -773,15 +776,18 @@ def action_cursor_right(self, select: bool = False) -> None: Args: select: If `True`, select the text to the right of the cursor. """ + start, end = self.selection if select: - start, end = self.selection self.selection = Selection(start, end + 1) else: if self._cursor_at_end and self._suggestion: self.value = self._suggestion self.cursor_position = len(self.value) else: - self.cursor_position += 1 + if self.selection.is_empty: + self.cursor_position += 1 + else: + self.cursor_position = max(start, end) def action_home(self, select: bool = False) -> None: """Move the cursor to the start of the input. diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index b011d8d8ae..687ef8107d 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -1835,10 +1835,16 @@ def action_cursor_left(self, select: bool = False) -> None: If the cursor is at the left edge of the document, try to move it to the end of the previous line. + If text is selected, move the cursor to the start of the selection. + Args: select: If True, select the text while moving. """ - target = self.get_cursor_left_location() + target = ( + self.get_cursor_left_location() + if select or self.selection.is_empty + else min(*self.selection) + ) self.move_cursor(target, select=select) def get_cursor_left_location(self) -> Location: @@ -1854,10 +1860,16 @@ def action_cursor_right(self, select: bool = False) -> None: If the cursor is at the end of a line, attempt to go to the start of the next line. + If text is selected, move the cursor to the end of the selection. + Args: select: If True, select the text while moving. """ - target = self.get_cursor_right_location() + target = ( + self.get_cursor_right_location() + if select or self.selection.is_empty + else max(*self.selection) + ) self.move_cursor(target, select=select) def get_cursor_right_location(self) -> Location: diff --git a/tests/input/test_input_terminal_cursor.py b/tests/input/test_input_terminal_cursor.py index b956a29846..31f1770181 100644 --- a/tests/input/test_input_terminal_cursor.py +++ b/tests/input/test_input_terminal_cursor.py @@ -8,7 +8,9 @@ class InputApp(App): CSS = "Input { padding: 4 8 }" def compose(self) -> ComposeResult: - yield Input("こんにちは!") + # We don't want to select the text on focus, as selected text + # has different interactions with the cursor_left action. + yield Input("こんにちは!", select_on_focus=False) async def test_initial_terminal_cursor_position():