From: Christian Heller Date: Mon, 11 Aug 2025 02:30:32 +0000 (+0200) Subject: Turn status line into its own widget. X-Git-Url: https://plomlompom.com/repos/booking/%7B%7B%20web_path%20%7D%7D/%7B%7Bprefix%7D%7D/ledger?a=commitdiff_plain;h=0a89c2f51948a9ff05eb5f52d8597ba5e6590205;p=ircplom Turn status line into its own widget. --- diff --git a/ircplom/tui_base.py b/ircplom/tui_base.py index 97b5404..998a650 100644 --- a/ircplom/tui_base.py +++ b/ircplom/tui_base.py @@ -50,9 +50,8 @@ class _YX(NamedTuple): x: int -class _Widget(ABC): +class _Widget: - @abstractmethod def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self._tainted = True @@ -67,14 +66,12 @@ class _Widget(ABC): '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: @@ -108,8 +105,7 @@ class _HistoryWidget(_ScrollableWidget): _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 @@ -298,6 +294,30 @@ class PromptWidget(_ScrollableWidget): 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 @@ -309,6 +329,8 @@ class Window(_Widget): 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() @@ -316,11 +338,12 @@ class Window(_Widget): 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 @@ -333,6 +356,7 @@ class Window(_Widget): 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 @@ -356,9 +380,10 @@ class Window(_Widget): 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: @@ -367,13 +392,6 @@ class Window(_Widget): 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.' @@ -414,8 +432,8 @@ class BaseTui(QueueMixin): 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: @@ -423,6 +441,7 @@ class BaseTui(QueueMixin): self._term.calc_geometry() for window in self._windows: window.set_geometry() + self._y_status = self._term.size.y - 2 self.redraw_affected() @property