From: Christian Heller Date: Mon, 1 Dec 2025 20:09:22 +0000 (+0100) Subject: Have WidgetAtom._make_x_scroll work StylingString rather than str. X-Git-Url: https://plomlompom.com/repos/%7B%7Bprefix%7D%7D/static/process_efforts?a=commitdiff_plain;h=d16ac4906ad1013b56e32fbaec10f83289ad70ff;p=ircplom Have WidgetAtom._make_x_scroll work StylingString rather than str. --- diff --git a/src/ircplom/tui_base.py b/src/ircplom/tui_base.py index f2cee7b..ebd68c5 100644 --- a/src/ircplom/tui_base.py +++ b/src/ircplom/tui_base.py @@ -75,29 +75,15 @@ class StylingString: if self._is_brace(c) else c for c in text]) - def __add__(self, other: Self) -> Self: + def __len__(self) -> int: + return len(self.stripped()) + + def __add__(self, other: Self | str) -> Self: return self.__class__(str(self) + str(other), store_raw=True) def __eq__(self, other) -> bool: return isinstance(other, self.__class__) and self._str == other._str - def __str__(self) -> str: - return self._str - - def attrd(self, *attrs) -> Self: - 'Return variant wrapped in mark-up for attrs.' - return self.__class__(''.join((self._BRACE_IN, ','.join(list(attrs)), - self._SEP, self._str, self._BRACE_OUT)), - store_raw=True) - - @classmethod - def _is_brace(cls, c: str) -> bool: - return c in {cls._BRACE_IN, cls._BRACE_OUT} - - def stripped(self) -> str: - 'Return without mark-up.' - return ''.join([text for _, text in self.parts_w_attrs()]) - @classmethod def _parse_for_markups(cls, char: str, in_tag: bool, markups: list[str] ) -> tuple[bool, bool]: @@ -122,8 +108,61 @@ class StylingString: do_print = True return do_print, in_tag + def __getitem__(self, idx: int | slice) -> Self: + if isinstance(idx, slice): + assert idx.step is None + start = idx.start or 0 + stop = (len(self) + idx.stop + if isinstance(idx.stop, int) and idx.stop < 0 + else idx.stop) + else: + start = idx + stop = idx + 1 + raw_str = '' + idx_stripped = -1 + in_tag = False + markups: list[str] = [] + n_applied = 0 + for char in self._str: + do_print, in_tag = self._parse_for_markups(char, in_tag, markups) + while len(markups) < n_applied: + raw_str += self._BRACE_OUT + n_applied -= 1 + if not do_print: + continue + idx_stripped += 1 + if idx_stripped == stop: + break + if idx_stripped < start: + continue + if len(markups) > n_applied: + raw_str += self._BRACE_IN + while len(markups) > n_applied: + raw_str += markups[n_applied] + n_applied += 1 + raw_str += self._SEP + raw_str += char + return self.__class__(raw_str, store_raw=True) + + def __str__(self) -> str: + return self._str + + def attrd(self, *attrs) -> Self: + 'Return variant wrapped in mark-up for attrs.' + return self.__class__(''.join((self._BRACE_IN, ','.join(list(attrs)), + self._SEP, self._str, self._BRACE_OUT)), + store_raw=True) + + @classmethod + def _is_brace(cls, c: str) -> bool: + return c in {cls._BRACE_IN, cls._BRACE_OUT} + + def stripped(self) -> str: + 'Return without mark-up.' + return ''.join([text for _, text in self.parts_w_attrs()]) + def parts_w_attrs(self) -> tuple[tuple[tuple[str, ...], str], ...]: - 'Break into individually formatted parts, with respective attributes.' + 'Break into styled formatted parts, with respective attributes.' next_part = '' markups: list[str] = [] in_tag = False @@ -213,10 +252,14 @@ class _Widget(ABC): class _WidgetAtom(_Widget, ABC): _tainted: bool = True - def _make_x_scroll(self, padchar: str, cursor_x: int, left: str, right: str - ) -> tuple[int, str]: + def _make_x_scroll(self, + padchar: str, + cursor_x: int, + left: StylingString, + right: StylingString + ) -> StylingString: 'Build line of static left, right scrolled with cursor_x if too large.' - to_write = left[:] + to_write = left offset = 0 width_gap = self._size.x - len(left + right) if width_gap < 0: @@ -229,9 +272,10 @@ class _WidgetAtom(_Widget, ABC): to_write = to_write[:self._size.x - len(_ELL_OUT)] + _ELL_OUT else: to_write += (right if width_gap == 0 - else ((padchar * width_gap + right) if padchar == '=' - else (right + padchar * width_gap))) - return len(left) - offset + cursor_x, to_write + else (StylingString(padchar * width_gap) + right + if padchar == '=' + else right + padchar * width_gap)) + return to_write def set_geometry(self, size: _YX) -> None: self.tainted = True @@ -482,17 +526,19 @@ class PromptWidget(_ScrollableWidget): self._input_buffer_unsafe = content[:] def _draw(self) -> None: - content = self.input_buffer if self._cursor_x == len(self.input_buffer): - content += ' ' - x_cursor, to_write = self._make_x_scroll(padchar=' ', - cursor_x=self._cursor_x, - left=self.prefix[:], - right=content) + content = StylingString(self.input_buffer) + content += StylingString(' ').attrd('reverse') + else: + content = StylingString(self.input_buffer[:self._cursor_x]) + content += StylingString(self.input_buffer[self._cursor_x] + ).attrd('reverse') + content += StylingString(self.input_buffer[self._cursor_x + 1:]) self._write(self._size.y, - StylingString(to_write[:x_cursor]) - + StylingString(to_write[x_cursor]).attrd('reverse') - + StylingString(to_write[x_cursor + 1:])) + self._make_x_scroll(padchar=' ', + cursor_x=self._cursor_x, + left=StylingString(self.prefix), + right=content)) def _archive_prompt(self) -> None: self.append(self.input_buffer) @@ -566,7 +612,7 @@ class _StatusLine(_WidgetAtom): def _draw(self) -> None: cursor_x = 0 focus = None - win_listing = '' + win_listing = StylingString('') for w in self._windows: win_listing += ' ' if w.idx > 0 else '(' item = str(w.idx) @@ -580,10 +626,11 @@ class _StatusLine(_WidgetAtom): cursor_x = len(win_listing) + (len(item) // 2) win_listing += item assert isinstance(focus, Window) - self._write(self._size.y, self._make_x_scroll(padchar='=', - cursor_x=cursor_x, - left=focus.title + ')', - right=win_listing)[1]) + self._write(self._size.y, + self._make_x_scroll(padchar='=', + cursor_x=cursor_x, + left=StylingString(focus.title + ')'), + right=win_listing)) class Window(_Widget):