diff options
Diffstat (limited to 'app/ui/tui.py')
| -rw-r--r-- | app/ui/tui.py | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/app/ui/tui.py b/app/ui/tui.py index b22a186..60e2dda 100644 --- a/app/ui/tui.py +++ b/app/ui/tui.py @@ -37,6 +37,7 @@ import contextlib import os import re import textwrap +import unicodedata from pathlib import Path from typing import Callable, List, Optional, Sequence, Tuple, Union @@ -255,13 +256,31 @@ def _hline(scr, y: int, x: int, n: int, attr: int = 0) -> None: pass +def _char_width(ch: str) -> int: + """Terminal cells CH occupies (East Asian Wide/Fullwidth count 2).""" + return 2 if unicodedata.east_asian_width(ch) in ("W", "F") else 1 + + +def _disp_width(text: str) -> int: + """Terminal cells TEXT occupies (East Asian Wide/Fullwidth count 2).""" + return sum(_char_width(ch) for ch in text) + + def _fit(text: str, width: int) -> str: """Truncate TEXT to WIDTH columns, appending '~' when cut.""" if width < 1: return "" - if len(text) <= width: + if _disp_width(text) <= width: return text - return text[: max(0, width - 1)] + "~" + out: List[str] = [] + used = 0 + for ch in text: + w = _char_width(ch) + if used + w > width - 1: + break + out.append(ch) + used += w + return "".join(out) + "~" def _wrap_segments(segments: Sequence[Tuple[str, int]], width: int @@ -283,13 +302,13 @@ def _wrap_segments(segments: Sequence[Tuple[str, int]], width: int current: List[Tuple[str, int]] = [] used = 0 for text, attr in tokens: - if current and used + len(text) > width: + if current and used + _disp_width(text) > width: lines.append(current) current, used = [], 0 if text.isspace(): continue # the break eats the whitespace it happens at current.append((text, attr)) - used += len(text) + used += _disp_width(text) if current: lines.append(current) return lines or [[]] |
