Skip to content

Commit 1240288

Browse files
authored
Merge pull request #5739 from Textualize/clear-selection
clear selections
2 parents db3a675 + e9de303 commit 1240288

File tree

6 files changed

+37
-3
lines changed

6 files changed

+37
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1717
- Fixed alignment not being applied when there are min and max limits on dimensions https://github.com/Textualize/textual/pull/5732
1818
- Fixed issues with OptionList scrollbar not updating https://github.com/Textualize/textual/pull/5736
1919
- Fixed allow_focus method not overriding `can_focus()` https://github.com/Textualize/textual/pull/5737
20+
- Fixed overlap of Input / TextArea selection with arbitrary text selection https://github.com/Textualize/textual/pull/5739
2021

2122
### Changed
2223

@@ -29,6 +30,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2930

3031
- Added `TEXTUAL_DIM_FACTOR` env var to set the opacity of the 'dim' ANSI attribute https://github.com/Textualize/textual/pull/5715
3132
- `notify()` now accepts a `markup` parameter to disable rendering the message as markup https://github.com/Textualize/textual/pull/5719
33+
- Added `Screen.text_selection_started_signal` https://github.com/Textualize/textual/pull/5739
34+
- Added `App.clear_selection()` helper method to clear arbitrary text selection of active screen https://github.com/Textualize/textual/pull/5739
3235

3336
## [3.0.1] - 2025-04-01
3437

src/textual/app.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4290,6 +4290,13 @@ def _refresh_notifications(self) -> None:
42904290
# Update the toast rack.
42914291
self.call_later(toast_rack.show, self._notifications)
42924292

4293+
def clear_selection(self) -> None:
4294+
"""Clear text selection on the active screen."""
4295+
try:
4296+
self.screen.clear_selection()
4297+
except NoScreen:
4298+
pass
4299+
42934300
def notify(
42944301
self,
42954302
message: str,

src/textual/screen.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,11 @@ def __init__(
290290
self.bindings_updated_signal: Signal[Screen] = Signal(self, "bindings_updated")
291291
"""A signal published when the bindings have been updated"""
292292

293+
self.text_selection_started_signal: Signal[Screen] = Signal(
294+
self, "selection_started"
295+
)
296+
"""A signal published when text selection has started."""
297+
293298
self._css_update_count = -1
294299
"""Track updates to CSS."""
295300

@@ -1541,6 +1546,7 @@ def _forward_event(self, event: events.Event) -> None:
15411546
):
15421547
self._selecting = True
15431548
if select_widget is not None and select_offset is not None:
1549+
self.text_selection_started_signal.publish(self)
15441550
self._select_start = (
15451551
select_widget,
15461552
event.screen_offset,

src/textual/signal.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
44
DOMNodes can subscribe to a signal, which will invoke a callback when the signal is published.
55
6-
This is experimental for now, for internal use. It may be part of the public API in a future release.
7-
86
"""
97

108
from __future__ import annotations

src/textual/widgets/_input.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from textual import events
1414
from textual.expand_tabs import expand_tabs_inline
15+
from textual.screen import Screen
1516
from textual.scroll_view import ScrollView
1617
from textual.strip import Strip
1718

@@ -476,6 +477,7 @@ def validate_selection(self, selection: Selection) -> Selection:
476477
return Selection(clamp(start, 0, value_length), clamp(end, 0, value_length))
477478

478479
def _watch_selection(self, selection: Selection) -> None:
480+
self.app.clear_selection()
479481
self.app.cursor_position = self.cursor_screen_offset
480482
if not self._initial_value:
481483
self.scroll_to_region(
@@ -665,6 +667,13 @@ def _toggle_cursor(self) -> None:
665667
self._cursor_visible = not self._cursor_visible
666668

667669
def _on_mount(self, event: Mount) -> None:
670+
def text_selection_started(screen: Screen) -> None:
671+
"""Signal callback to unselect when arbitrary text selection starts."""
672+
self.selection = Selection.cursor(self.cursor_position)
673+
674+
self.screen.text_selection_started_signal.subscribe(
675+
self, text_selection_started, immediate=True
676+
)
668677
self._blink_timer = self.set_interval(
669678
0.5,
670679
self._toggle_cursor,

src/textual/widgets/_text_area.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
)
3434
from textual.document._wrapped_document import WrappedDocument
3535
from textual.expand_tabs import expand_tabs_inline, expand_text_tabs_from_widths
36+
from textual.screen import Screen
3637

3738
if TYPE_CHECKING:
3839
from tree_sitter import Language, Query
@@ -657,6 +658,8 @@ def _watch_selection(
657658
if not self.is_mounted:
658659
return
659660

661+
self.app.clear_selection()
662+
660663
cursor_location = selection.end
661664

662665
self.scroll_cursor_visible()
@@ -1569,9 +1572,17 @@ def gutter_width(self) -> int:
15691572
return gutter_width
15701573

15711574
def _on_mount(self, event: events.Mount) -> None:
1575+
1576+
def text_selection_started(screen: Screen) -> None:
1577+
"""Signal callback to unselect when arbitrary text selection starts."""
1578+
self.selection = Selection(self.cursor_location, self.cursor_location)
1579+
1580+
self.screen.text_selection_started_signal.subscribe(
1581+
self, text_selection_started, immediate=True
1582+
)
1583+
15721584
# When `app.theme` reactive is changed, reset the theme to clear cached styles.
15731585
self.watch(self.app, "theme", self._app_theme_changed, init=False)
1574-
15751586
self.blink_timer = self.set_interval(
15761587
0.5,
15771588
self._toggle_cursor_blink_visible,

0 commit comments

Comments
 (0)