home · contact · privacy
Refactoring, primarily of interaction between LogBuffer and TuiLoop. master
authorChristian Heller <c.heller@plomlompom.de>
Sun, 1 Jun 2025 14:46:53 +0000 (16:46 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 1 Jun 2025 14:46:53 +0000 (16:46 +0200)
ircplom.py

index cb1dfb3417c8804e7844990500e25443b592f5f3..c13de196b1e8590f6d36132e6681f9fa8ba5b8f1 100755 (executable)
@@ -371,16 +371,16 @@ class LogBuffer:
     _display_size: YX
     _y_pgscroll: int
 
-    def __init__(self, wrap_func: Callable) -> None:
-        self._wrap = wrap_func
+    def __init__(self, term: Terminal) -> None:
+        self._term = term
         self._history: list[str] = []
         self._wrapped: list[tuple[int, str]] = []
         self._upscroll_history: int = 0
         self._upscroll_wrapped: int = 0
 
-    def apply_geometry(self, display_size: YX) -> None:
-        'Calcs .wrapped conditions based on new display_size, stored scroll.'
-        self._display_size = display_size
+    def apply_geometry(self, limit_y: int) -> None:
+        'Calcs display conditions based on new display_size, stored scroll.'
+        self._display_size = YX(limit_y, self._term.size.x)
         self._y_pgscroll = self._display_size.y // 2
         self._wrapped.clear()
         self._wrapped += [(-1, '')] * self._display_size.y
@@ -396,7 +396,7 @@ class LogBuffer:
         self._upscroll_wrapped = len(self._wrapped) - (idx_last + 1)
 
     def _add_wrapped(self, idx_original, line) -> int:
-        wrapped_lines = self._wrap(line)
+        wrapped_lines = self._term.wrap(line)
         self._wrapped += [(idx_original, line) for line in wrapped_lines]
         return len(wrapped_lines)
 
@@ -408,6 +408,21 @@ class LogBuffer:
             self._upscroll_history += 1
             self._upscroll_wrapped += n_wrapped
 
+    def draw(self) -> None:
+        'Print display_size/scroll-appropriate wrapped selection of lines.'
+        start_idx = len(self._wrapped) - (self._display_size.y
+                                          + self._upscroll_wrapped)
+        to_write = [t[1] for t in
+                    self._wrapped[start_idx:-(self._upscroll_wrapped + 1)]]
+        if self._upscroll_wrapped:
+            scroll_info = f'vvv [{self._upscroll_wrapped}] '
+            scroll_info += 'v' * (self._display_size.x - len(scroll_info))
+            to_write += [scroll_info]
+        else:
+            to_write += [self._wrapped[-1][1]]
+        for i, line in enumerate(to_write):
+            self._term.write_yx(YX(i, 0), line)
+
     def scroll(self, up=True) -> None:
         'Scrolls view down by half of display size.'
         self._upscroll_wrapped = (
@@ -419,19 +434,6 @@ class LogBuffer:
             idx_lowest = self._wrapped[-(self._upscroll_wrapped + 1)][0]
             self._upscroll_history = len(self._history) - (idx_lowest + 1)
 
-    @property
-    def wrapped(self) -> list[str]:
-        'Returns display_size/scroll-appropriately wrapped selection of lines.'
-        start_idx = len(self._wrapped) - (self._display_size.y
-                                          + self._upscroll_wrapped)
-        to_return = [t[1] for t in
-                     self._wrapped[start_idx:-(self._upscroll_wrapped + 1)]]
-        if self._upscroll_wrapped:
-            scroll_info = f'vvv [{self._upscroll_wrapped}] '
-            scroll_info += 'v' * (self._display_size.x - len(scroll_info))
-            return to_return + [scroll_info]
-        return to_return + [self._wrapped[-1][1]]
-
 
 class TuiPrompt:
     'Keyboard-controlled command input field.'
@@ -475,7 +477,7 @@ class TuiLoop(Loop):
     def __init__(self, term: Terminal, *args, **kwargs) -> None:
         self._term = term
         self._prompt = TuiPrompt(self._term)
-        self._logs = [LogBuffer(self._term.wrap) for i in range(2)]
+        self._logs = [LogBuffer(self._term) for i in range(2)]
         self._log_selected = 0
         self._upscroll = 0
         self._calc_and_draw_all()
@@ -491,7 +493,7 @@ class TuiLoop(Loop):
                 self._logs[1].append(f'<- {event.payload.raw}')
             elif event.type_ == 'SEND':
                 self._logs[1].append(f'-> {event.payload.raw}')
-            self._draw_log()
+            self._log.draw()
         elif event.type_ == 'KEYBINDING':
             getattr(self, f'_cmd__{event.payload[0]}')(*event.payload[1:])
         elif event.type_ == 'INPUT_CHAR':
@@ -503,7 +505,7 @@ class TuiLoop(Loop):
         #     self._log+= [
         #         f'DEBUG {line}' for line
         #         in '\n'.join(format_exception(event.payload)).split('\n')]
-        #     self._draw_log()
+        #     self._log.draw()
         else:
             return True
         self._term.flush()
@@ -513,6 +515,18 @@ class TuiLoop(Loop):
     def _log(self) -> LogBuffer:
         return self._logs[self._log_selected]
 
+    def _calc_and_draw_all(self) -> None:
+        self._term.clear()
+        self._term.calc_geometry()
+        self._prompt.start_y = self._term.size.y - 1
+        self._y_separator = self._term.size.y - 2
+        for log in self._logs:
+            log.apply_geometry(limit_y=self._y_separator)
+        self._term.write_yx(YX(self._y_separator, 0), '=' * self._term.size.x)
+        self._term.write_yx(YX(self._prompt.start_y, 0), INPUT_PROMPT)
+        self._log.draw()
+        self._prompt.draw()
+
     def _cmd__prompt_backspace(self) -> None:
         self._prompt.backspace()
 
@@ -550,7 +564,7 @@ class TuiLoop(Loop):
 
     def _cmd__scroll(self, direction: str) -> None:
         self._log.scroll(up=direction == 'up')
-        self._draw_log()
+        self._log.draw()
 
     def _cmd__disconnect(self, quit_msg: str = 'ircplom says bye') -> None:
         self.broadcast('SEND', IrcMessage('QUIT', [quit_msg]))
@@ -565,28 +579,9 @@ class TuiLoop(Loop):
         if buffer_idx < 0 or buffer_idx > len(self._logs):
             return f'unavailable buffer idx: {buffer_idx}'
         self._log_selected = buffer_idx
-        self._draw_log()
+        self._log.draw()
         return None
 
-    def _calc_and_draw_all(self) -> None:
-        self._term.clear()
-        self._term.calc_geometry()
-        self._prompt.start_y = self._term.size.y - 1
-        self._y_separator = self._term.size.y - 2
-        for log in self._logs:
-            log.apply_geometry(YX(self._y_separator, self._term.size.x))
-        self._draw_frame()
-        self._draw_log()
-        self._prompt.draw()
-
-    def _draw_frame(self) -> None:
-        self._term.write_yx(YX(self._y_separator, 0), '=' * self._term.size.x)
-        self._term.write_yx(YX(self._prompt.start_y, 0), INPUT_PROMPT)
-
-    def _draw_log(self) -> None:
-        for i, line in enumerate(self._log.wrapped):
-            self._term.write_yx(YX(i, 0), line)
-
 
 class SocketRecvLoop(Loop):
     'Loop receiving and translating socket messages towards main loop.'