Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [6.2.1] - 2025-10-01

- Fix inability to copy text outside of an input/textarea when it was focused https://github.com/Textualize/textual/pull/6148
- Fix issue when copying text after a double click https://github.com/Textualize/textual/pull/6148

## [6.2.0] - 2025-09-30

### Changed
Expand Down Expand Up @@ -3129,6 +3134,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040
- New handler system for messages that doesn't require inheritance
- Improved traceback handling

[6.2.1]: https://github.com/Textualize/textual/compare/v6.2.0...v6.2.1
[6.2.0]: https://github.com/Textualize/textual/compare/v6.1.0...v6.2.0
[6.1.0]: https://github.com/Textualize/textual/compare/v6.0.0...v6.1.0
[6.0.0]: https://github.com/Textualize/textual/compare/v5.3.0...v6.0.0
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "textual"
version = "6.2.0"
version = "6.2.1"
homepage = "https://github.com/Textualize/textual"
repository = "https://github.com/Textualize/textual"
documentation = "https://textual.textualize.io/"
Expand Down
2 changes: 1 addition & 1 deletion src/textual/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ def get_selected_text(self) -> str | None:
if selected_text_in_widget is not None:
widget_text.extend(selected_text_in_widget)

selected_text = "".join(widget_text)
selected_text = "".join(widget_text).rstrip("\n")
return selected_text

def action_copy_text(self) -> None:
Expand Down
4 changes: 2 additions & 2 deletions src/textual/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ def extract(self, text: str) -> str:
start_line, start_offset = self.start.transpose

if self.end is None:
end_line = len(lines) - 1
end_offset = len(lines[end_line])
end_line = len(lines)
end_offset = len(lines[-1])
else:
end_line, end_offset = self.end.transpose
end_line = min(len(lines), end_line)
Expand Down
7 changes: 6 additions & 1 deletion src/textual/widgets/_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing_extensions import Literal

from textual import events
from textual.actions import SkipAction
from textual.expand_tabs import expand_tabs_inline
from textual.screen import Screen
from textual.scroll_view import ScrollView
Expand Down Expand Up @@ -1106,7 +1107,11 @@ def action_cut(self) -> None:

def action_copy(self) -> None:
"""Copy the current selection to the clipboard."""
self.app.copy_to_clipboard(self.selected_text)
selected_text = self.selected_text
if selected_text:
self.app.copy_to_clipboard(selected_text)
else:
raise SkipAction()

def action_paste(self) -> None:
"""Paste from the local clipboard."""
Expand Down
3 changes: 3 additions & 0 deletions src/textual/widgets/_text_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from textual._text_area_theme import TextAreaTheme
from textual._tree_sitter import TREE_SITTER, get_language
from textual.actions import SkipAction
from textual.cache import LRUCache
from textual.color import Color
from textual.content import Content
Expand Down Expand Up @@ -2513,6 +2514,8 @@ def action_copy(self) -> None:
selected_text = self.selected_text
if selected_text:
self.app.copy_to_clipboard(selected_text)
else:
raise SkipAction()

def action_paste(self) -> None:
"""Paste from local clipboard."""
Expand Down
22 changes: 22 additions & 0 deletions tests/test_selection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest

from textual.geometry import Offset
from textual.selection import Selection


@pytest.mark.parametrize(
"text,selection,expected",
[
("Hello", Selection(None, None), "Hello"),
("Hello\nWorld", Selection(None, None), "Hello\nWorld"),
("Hello\nWorld", Selection(Offset(0, 1), None), "World"),
("Hello\nWorld", Selection(None, Offset(5, 0)), "Hello"),
("Foo", Selection(Offset(0, 0), Offset(1, 0)), "F"),
("Foo", Selection(Offset(1, 0), Offset(2, 0)), "o"),
("Foo", Selection(Offset(0, 0), Offset(2, 0)), "Fo"),
("Foo", Selection(Offset(0, 0), None), "Foo"),
],
)
def test_extract(text: str, selection: Selection, expected: str) -> None:
"""Test Selection.extract"""
assert selection.extract(text) == expected
Loading