x: int
-class _Widget(ABC):
+class _Widget:
- @abstractmethod
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self._tainted = True
'If in need of re-drawing.'
return self._tainted
- @abstractmethod
def set_geometry(self, measurements: _YX) -> bool:
'Update widget\'s measurements, re-generate content where necessary.'
self._tainted = True
self._drawable = len([m for m in measurements if m < 0]) == 0
return self._drawable
- @abstractmethod
def draw(self) -> bool:
'Print widget\'s content in shape appropriate to set geometry.'
if not self._drawable:
_view_size: _YX
_y_pgscroll: int
- def __init__(self, wrap: Callable[[str], list[str]], **kwargs
- ) -> None:
+ def __init__(self, wrap: Callable[[str], list[str]], **kwargs) -> None:
super().__init__(**kwargs)
self._wrap = wrap
self._wrapped_idx = self._history_idx = -1
return to_return
+class _StatusLine(_Widget):
+ _y: int
+ _width: int
+
+ def __init__(self, write: Callable, status_title: str, **kwargs) -> None:
+ super().__init__(**kwargs)
+ self._write = write
+ self._status_title = status_title
+
+ def set_geometry(self, measurements: _YX) -> bool:
+ if not super().set_geometry(measurements):
+ return False
+ self._y, self._width = measurements
+ return True
+
+ def draw(self) -> bool:
+ if not super().draw():
+ return False
+ title_box = f'{self._status_title}]'
+ status_line = title_box + '=' * (self._width - len(title_box))
+ self._write(status_line, self._y)
+ return True
+
+
class Window(_Widget):
'Widget filling entire screen with sub-widgets like .prompt, .history.'
_y_status: int
self._term = term
self.history = _HistoryWidget(wrap=self._term.wrap,
write=self._term.write)
+ self._status_line = _StatusLine(write=self._term.write,
+ status_title=self.status_title)
self.prompt = self.__annotations__['prompt'](write=self._term.write)
if hasattr(self._term, 'size'):
self.set_geometry()
def taint(self) -> None:
super().taint()
self.history.taint()
+ self._status_line.taint()
self.prompt.taint()
@property
def tainted(self) -> bool:
- return super().tainted or self.history.tainted or self.prompt.tainted
+ return self._tainted or self.history.tainted or self.prompt.tainted
def set_geometry(self, _=None) -> bool:
assert _ is None
super().set_geometry(_YX(0, 0))
self._y_status = self._term.size.y - 2
self.history.set_geometry(_YX(self._y_status, self._term.size.x))
+ self._status_line.set_geometry(_YX(self._y_status, self._term.size.x))
self.prompt.set_geometry(_YX(self._term.size.y - 1, self._term.size.x))
return True
for y, line in enumerate(lines):
self._term.write(line, y)
return False
- title_box = f'{self.status_title}]'
- status_line = title_box + '=' * (self._term.size.x - len(title_box))
- self._term.write(status_line, self._y_status)
+ for widget in [
+ w for w in (self.history, self.prompt, self._status_line)
+ if w.tainted]:
+ widget.draw()
return True
def cmd__paste(self) -> None:
self._y_status)
self._tainted = True
- def draw_tainted(self) -> None:
- 'Draw tainted parts of self.'
- for widget in [w for w in (self.history, self.prompt) if w.tainted]:
- widget.draw()
- if self.tainted:
- self.draw()
-
class TuiEvent(AffectiveEvent):
'To affect TUI.'
return win
def redraw_affected(self) -> None:
- 'On focused window call .draw_tainted, then flush screen.'
- self.window.draw_tainted()
+ 'On focused window call .draw, then flush screen.'
+ self.window.draw()
self._term.flush()
def _set_screen(self) -> None:
self._term.calc_geometry()
for window in self._windows:
window.set_geometry()
+ self._y_status = self._term.size.y - 2
self.redraw_affected()
@property