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]:
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
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:
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
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)
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)
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):