mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #6148 from Textualize/fix-copy-focus
skip copy in input/textarea if there is nothing to copy
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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/"
|
||||
|
||||
@@ -924,7 +924,7 @@ class Screen(Generic[ScreenResultType], Widget):
|
||||
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:
|
||||
|
||||
@@ -46,8 +46,8 @@ class Selection(NamedTuple):
|
||||
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)
|
||||
|
||||
@@ -11,6 +11,7 @@ from rich.text import Text
|
||||
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
|
||||
@@ -1106,7 +1107,11 @@ class Input(ScrollView):
|
||||
|
||||
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."""
|
||||
|
||||
@@ -16,6 +16,7 @@ from typing_extensions import Literal
|
||||
|
||||
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
|
||||
@@ -2513,6 +2514,8 @@ TextArea {
|
||||
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."""
|
||||
|
||||
22
tests/test_selection.py
Normal file
22
tests/test_selection.py
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user