Skip to content

Commit acbc319

Browse files
authored
Merge branch 'main' into tomnewton/avoid_circular_dependency
2 parents 1c7b849 + b801c15 commit acbc319

File tree

11 files changed

+74
-34
lines changed

11 files changed

+74
-34
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1111

1212
- Fixed type hint aliasing for App under TYPE_CHECKING https://github.com/Textualize/textual/pull/6152
1313
- Fixed circular dependency effecting `bazel` users https://github.com/Textualize/textual/pull/6163
14+
- Fixed for text selection with double width characters https://github.com/Textualize/textual/pull/6186
15+
16+
### Changed
17+
18+
- Simplified system commands (command palette) to a single word https://github.com/Textualize/textual/pull/6183
1419

1520
## [6.3.0] - 2025-10-11
1621

src/textual/_compositor.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -927,8 +927,10 @@ def get_widget_and_offset_at(
927927
offset_x = 0
928928
offset_x2 = 0
929929

930+
from rich.cells import get_character_cell_size
931+
930932
for segment in line:
931-
end += len(segment.text)
933+
end += segment.cell_length
932934
style = segment.style
933935
if style is not None and style._meta is not None:
934936
meta = style.meta
@@ -937,11 +939,14 @@ def get_widget_and_offset_at(
937939
offset_x2 = offset_x + len(segment.text)
938940

939941
if x < end and x >= start:
940-
if x == end - 1:
941-
segment_offset = len(segment.text)
942-
else:
943-
first, _ = segment.split_cells(x - start)
944-
segment_offset = len(first.text)
942+
segment_cell_length = 0
943+
cell_cut = x - start
944+
segment_offset = 0
945+
for character in segment.text:
946+
if segment_cell_length >= cell_cut:
947+
break
948+
segment_cell_length += get_character_cell_size(character)
949+
segment_offset += 1
945950
return widget, (
946951
None
947952
if offset_y is None

src/textual/app.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ class MyApp(App[None]):
518518
"inline": lambda app: app.is_inline,
519519
"ansi": lambda app: app.ansi_color,
520520
"nocolor": lambda app: app.no_color,
521-
} # type: ignore[assignment]
521+
}
522522

523523
title: Reactive[str] = Reactive("", compute=False)
524524
"""The title of the app, displayed in the header."""
@@ -1256,25 +1256,25 @@ def get_system_commands(self, screen: Screen) -> Iterable[SystemCommand]:
12561256
"""
12571257
if not self.ansi_color:
12581258
yield SystemCommand(
1259-
"Change theme",
1259+
"Theme",
12601260
"Change the current theme",
12611261
self.action_change_theme,
12621262
)
12631263
yield SystemCommand(
1264-
"Quit the application",
1264+
"Quit",
12651265
"Quit the application as soon as possible",
12661266
self.action_quit,
12671267
)
12681268

12691269
if screen.query("HelpPanel"):
12701270
yield SystemCommand(
1271-
"Hide keys and help panel",
1271+
"Keys",
12721272
"Hide the keys and widget help panel",
12731273
self.action_hide_help_panel,
12741274
)
12751275
else:
12761276
yield SystemCommand(
1277-
"Show keys and help panel",
1277+
"Keys",
12781278
"Show help for the focused widget and a summary of available keys",
12791279
self.action_show_help_panel,
12801280
)
@@ -1291,7 +1291,7 @@ def get_system_commands(self, screen: Screen) -> Iterable[SystemCommand]:
12911291
)
12921292

12931293
yield SystemCommand(
1294-
"Save screenshot",
1294+
"Screenshot",
12951295
"Save an SVG 'screenshot' of the current screen",
12961296
lambda: self.set_timer(0.1, self.deliver_screenshot),
12971297
)

src/textual/dom.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class DOMNode(MessagePump):
179179
# Names of potential computed reactives
180180
_computes: ClassVar[frozenset[str]]
181181

182-
_PSEUDO_CLASSES: ClassVar[dict[str, Callable[[object], bool]]] = {}
182+
_PSEUDO_CLASSES: ClassVar[dict[str, Callable[[App[Any]], bool]]] = {}
183183
"""Pseudo class checks."""
184184

185185
def __init__(

src/textual/screen.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,14 +1652,22 @@ def _forward_event(self, event: events.Event) -> None:
16521652
):
16531653
end_widget = self._select_end[0]
16541654
select_offset = end_widget.content_region.bottom_right_inclusive
1655-
self._select_end = (end_widget, event.offset, select_offset)
1655+
self._select_end = (
1656+
end_widget,
1657+
event.screen_offset,
1658+
select_offset,
1659+
)
16561660

16571661
elif (
16581662
select_widget is not None
16591663
and select_widget.allow_select
16601664
and select_offset is not None
16611665
):
1662-
self._select_end = (select_widget, event.offset, select_offset)
1666+
self._select_end = (
1667+
select_widget,
1668+
event.screen_offset,
1669+
select_offset,
1670+
)
16631671

16641672
elif isinstance(event, events.MouseEvent):
16651673
if isinstance(event, events.MouseUp):
@@ -1730,7 +1738,6 @@ def _watch__select_end(
17301738
Args:
17311739
select_end: The end selection.
17321740
"""
1733-
17341741
if select_end is None or self._select_start is None:
17351742
# Nothing to select
17361743
return

src/textual/selection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ def extract(self, text: str) -> str:
5656
return lines[start_line][start_offset:end_offset]
5757

5858
selection: list[str] = []
59-
selected_lines = lines[start_line:end_line]
59+
selected_lines = lines[start_line : end_line + 1]
6060
if len(selected_lines) >= 2:
6161
first_line, *mid_lines, last_line = selected_lines
6262
selection.append(first_line[start_offset:])
6363
selection.extend(mid_lines)
64-
selection.append(last_line[: end_offset + 1])
64+
selection.append(last_line[:end_offset])
6565
else:
6666
return lines[start_line][start_offset:end_offset]
6767
return "\n".join(selection)

src/textual/widgets/_markdown.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939

4040
class MarkdownStream:
41-
"""An object to manager streaming markdown.
41+
"""An object to manage streaming markdown.
4242
4343
This will accumulate markdown fragments if they can't be rendered fast enough.
4444

src/textual/worker_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
@rich.repr.auto(angular=True)
2424
class WorkerManager:
25-
"""An object to manager a number of workers.
25+
"""An object to manage a number of workers.
2626
2727
You will not have to construct this class manually, as widgets, screens, and apps
2828
have a worker manager accessibly via a `workers` attribute.

0 commit comments

Comments
 (0)