From: Christian Heller Date: Sat, 7 Jun 2025 15:58:48 +0000 (+0200) Subject: Refactor widget sizing, drawing. X-Git-Url: https://plomlompom.com/repos/%22https:/validator.w3.org/%7B%7Bprefix%7D%7D/blog?a=commitdiff_plain;h=78097196f73a1f8aa866af0db03be71adf608e1e;p=ircplom Refactor widget sizing, drawing. --- diff --git a/ircplom.py b/ircplom.py index e63a1e2..b754b82 100755 --- a/ircplom.py +++ b/ircplom.py @@ -74,7 +74,7 @@ class Terminal: @contextmanager def context(self, q_to_main: EventQueue) -> Generator: 'Combine multiple contexts into one.' - signal(SIGWINCH, lambda *_: q_to_main.eput('SIGWINCH')) + signal(SIGWINCH, lambda *_: q_to_main.eput('SET_SCREEN')) self._blessed = BlessedTerminal() with (self._blessed.raw(), self._blessed.fullscreen(), @@ -412,7 +412,7 @@ class Widget(ABC): 'Defines most basic TUI object API.' @abstractmethod - def set_geometry(self, measurements: tuple[int | YX, ...]) -> None: + def set_geometry(self, measurements: YX) -> None: 'Update widget\'s measurements, re-generate content where necessary.' @abstractmethod @@ -451,9 +451,7 @@ class PromptWidget(ScrollableWidget): self._input_buffer = '' self._history_idx = 0 - def set_geometry(self, measurements: tuple[int | YX, ...]) -> None: - assert len(measurements) == 1 - assert isinstance(measurements[0], int) + def set_geometry(self, measurements: YX) -> None: self._start_y = measurements[0] def append(self, to_append: str) -> None: @@ -462,8 +460,8 @@ class PromptWidget(ScrollableWidget): self.draw() def draw(self) -> None: - self._write_yx(YX(self._start_y, len(INPUT_PROMPT)), - f'{self._input_buffer}_') + self._write_yx(YX(self._start_y, 0), + f'{INPUT_PROMPT} {self._input_buffer}_') def _scroll(self, up: bool = True) -> None: if up and -(self._history_idx) < len(self._history): @@ -520,10 +518,8 @@ class LogWidget(ScrollableWidget): self._wrapped += [(idx_original, line) for line in wrapped_lines] return len(wrapped_lines) - def set_geometry(self, measurements: tuple[int | YX, ...]) -> None: - assert len(measurements) == 1 - assert isinstance(measurements[0], YX) - self._view_size = measurements[0] + def set_geometry(self, measurements: YX) -> None: + self._view_size = measurements self._y_pgscroll = self._view_size.y // 2 self._wrapped.clear() self._wrapped += [(None, '')] * self._view_size.y @@ -571,23 +567,25 @@ class LogWidget(ScrollableWidget): class Window(Widget): 'Collects a log and a prompt meant for the same content stream.' + _y_separator: int def __init__(self, term: Terminal) -> None: self._term = term self.log = LogWidget(self._term.wrap, self._term.write_yx) self.prompt = PromptWidget(self._term.write_yx) + if hasattr(self._term, 'size'): + self.set_geometry() - def set_geometry(self, measurements: tuple[int | YX, ...]) -> None: - assert len(measurements) == 2 - assert isinstance(measurements[0], int) - assert isinstance(measurements[1], int) - log_height = measurements[0] - start_y_prompt = measurements[1] - self.log.set_geometry((YX(log_height, self._term.size.x),)) - self.prompt.set_geometry((start_y_prompt,)) + def set_geometry(self, _=None) -> None: + assert _ is None + self._y_separator = self._term.size.y - 2 + self.log.set_geometry(YX(self._y_separator, self._term.size.x)) + self.prompt.set_geometry(YX(self._term.size.y - 1, self._term.size.x)) def draw(self) -> None: + self._term.clear() self.log.draw() + self._term.write_yx(YX(self._y_separator, 0), '=' * self._term.size.x) self.prompt.draw() @@ -599,9 +597,8 @@ class TuiLoop(Loop): self._windows = [Window(self._term)] self._conn_windows: list[Window] = [] self._window_idx = 0 - self._calc_and_draw_all() - self._term.flush() super().__init__(*args, **kwargs) + self.put(Event('SET_SCREEN')) def _cmd_name_to_cmd(self, cmd_name: str) -> Optional[Callable]: cmd_parent = self @@ -621,9 +618,13 @@ class TuiLoop(Loop): def process_main(self, event: Event) -> bool: if not super().process_main(event): return False - if event.type_ == 'CONNECTION_WINDOW': + if event.type_ == 'SET_SCREEN': + self._term.calc_geometry() + for window in self._windows: + window.set_geometry() + self.window.draw() + elif event.type_ == 'CONNECTION_WINDOW': conn_win = Window(self._term) - conn_win.set_geometry((self._y_separator, self._y_prompt)) self._windows += [conn_win] self._conn_windows += [conn_win] elif event.type_ == 'ALERT': @@ -645,8 +646,6 @@ class TuiLoop(Loop): cmd(*event.payload[1:]) elif event.type_ == 'INPUT_CHAR': self.window.prompt.append(event.payload) - elif event.type_ == 'SIGWINCH': - self._calc_and_draw_all() # elif event.type_ == 'DEBUG': # from traceback import format_exception # for line in '\n'.join(format_exception(event.payload) @@ -663,17 +662,6 @@ class TuiLoop(Loop): 'Currently selected Window.' return self._windows[self._window_idx] - def _calc_and_draw_all(self) -> None: - self._term.clear() - self._term.calc_geometry() - self._y_prompt = self._term.size.y - 1 - self._y_separator = self._term.size.y - 2 - self._term.write_yx(YX(self._y_separator, 0), '=' * self._term.size.x) - self._term.write_yx(YX(self._y_prompt, 0), INPUT_PROMPT) - for window in self._windows: - window.set_geometry((self._y_separator, self._y_prompt)) - self.window.draw() - def cmd__connect(self, hostname: str, username: str,