home · contact · privacy
Add _ClientWindowsManager for better control of Client-TUI relations.
authorChristian Heller <c.heller@plomlompom.de>
Wed, 6 Aug 2025 21:52:33 +0000 (23:52 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Wed, 6 Aug 2025 21:52:33 +0000 (23:52 +0200)
ircplom/client_tui.py

index 6a48a1e108535b8fe09544631ff6d0600c6a7baa..5b7feda01b0fb76f5033158148a3e2000e2dea75 100644 (file)
@@ -1,7 +1,8 @@
 'TUI adaptions to Client.'
 # built-ins
 from getpass import getuser
-from typing import Any, Optional
+from inspect import signature
+from typing import Callable, Optional
 # ourselves
 from ircplom.tui_base import (BaseTui, PromptWidget, TuiEvent, Window,
                               CMD_SHORTCUTS)
@@ -76,69 +77,77 @@ class _PrivmsgWindow(_ClientWindow):
         self.cmd__privmsg(self.stream, msg)
 
 
-class ClientTui(BaseTui):
-    'TUI expanded towards Client features.'
+class _ClientWindowsManager:
+    nick_confirmed: bool = False
+    nickname: str = '?'
+    windows: list[_ClientWindow] = []
 
-    def __init__(self, **kwargs) -> None:
-        super().__init__(**kwargs)
-        self._clients_data: dict[str, dict[str, Any]] = {}
+    def __init__(self, new_window: Callable) -> None:
+        self._tui_new_window = new_window
+        self.windows += [self._new_window(stream)
+                         for stream in (STREAM_SERVER, STREAM_RAW)]
 
-    def _new_client_window(self, client_id: str, stream: str) -> _ClientWindow:
-        new_idx = len(self._windows)
+    def _new_window(self, stream: str) -> _ClientWindow:
         win_class = (_PrivmsgWindow if stream[0:1] != STREAM_PREFIX_META
                      else _ClientWindow)
-        win = win_class(idx=new_idx, term=self._term, _q_out=self._q_out,
-                        client_id=client_id, stream=stream)
+        win = self._tui_new_window(win_class, stream=stream)
         if isinstance(win, _PrivmsgWindow):
-            win.prompt.update_prefix(**{k: self._clients_data[client_id][k]
-                                        for k in _PROMPT_UPDATE_TRIGGER_KEYS})
-        self._windows += [win]
-        self._switch_window(new_idx)
+            win.prompt.update_prefix(
+                **{k: getattr(self, k) for k in _PROMPT_UPDATE_TRIGGER_KEYS})
+        self.windows += [win]
         return win
 
-    @property
-    def _all_client_wins(self) -> list[_ClientWindow]:
-        return [win for win in self._windows if isinstance(win, _ClientWindow)]
-
-    def client_wins(self, client_id: str = '') -> list[_ClientWindow]:
-        'All _ClientWindows matching client_id; if none, create basics.'
-        wins = [w for w in self._all_client_wins if client_id == w.client_id]
-        if not wins:
-            for stream in (STREAM_SERVER, STREAM_RAW):
-                wins += [self._new_client_window(client_id, stream)]
-        return wins
-
-    def client_win(self, client_id: str, stream: str) -> _ClientWindow:
-        'That _ClientWindow matching client_id and stream; if none, create.'
-        client_wins = self.client_wins(client_id)
-        candidates = [win for win in client_wins if win.stream == stream]
-        if candidates:
-            return candidates[0]
-        win = self._new_client_window(client_id=client_id, stream=stream)
-        return win
+    def window(self, stream: str) -> _ClientWindow:
+        'Return client window of stream.'
+        for win in [w for w in self.windows if w.stream == stream]:
+            return win
+        return self._new_window(stream)
 
-    def log_for_client(self, client_id: str, stream: str, msg: str) -> None:
-        'To stream of client_id, log msg.'
+    def log(self, msg: str, stream: str) -> None:
+        'Log msg to stram.'
         if stream == STREAM_GLOB:
-            for win in self.client_wins(client_id):
+            for win in self.windows:
                 win.log.append(msg)
         else:
-            self.client_win(client_id, stream).log.append(msg)
-        self.redraw_affected()
-
-    def update_client_data(self, client_id: str, **kwargs) -> None:
-        'Apply kwargs settings to client data, update representation.'
-        if client_id not in self._clients_data:
-            self._clients_data[client_id] = {}
-        clients_data = self._clients_data[client_id]
+            self.window(stream).log.append(msg)
+
+    def update(self, **kwargs) -> bool:
+        'Apply settings in kwargs, follow represntation update triggres.'
         for k, v in kwargs.items():
-            clients_data[k] = v
+            setattr(self, k, v)
         if _PROMPT_UPDATE_TRIGGER_KEYS & set(kwargs.keys()):
-            for prompt in [w.prompt for w in self.client_wins(client_id)
+            for prompt in [w.prompt for w in self.windows
                            if isinstance(w.prompt, _PrivmsgPromptWidget)]:
-                assert isinstance(prompt, _PrivmsgPromptWidget)
-                prompt.update_prefix(**{k: clients_data[k]
+                prompt.update_prefix(**{k: getattr(self, k)
                                         for k in _PROMPT_UPDATE_TRIGGER_KEYS})
+            return True
+        return False
+
+
+class ClientTui(BaseTui):
+    'TUI expanded towards Client features.'
+
+    def __init__(self, **kwargs) -> None:
+        super().__init__(**kwargs)
+        self._client_mngrs: dict[str, _ClientWindowsManager] = {}
+
+    def _new_window(self, win_class, **kwargs) -> Window:
+        new_idx = len(self._windows)
+        win = win_class(idx=new_idx, term=self._term, _q_out=self._q_out,
+                        **kwargs)
+        self._windows += [win]
+        self._switch_window(new_idx)
+        return win
+
+    def for_client_do(self, client_id: str, todo: str, **kwargs) -> None:
+        'Forward todo to appropriate ClientTuiMgr.'
+        if client_id not in self._client_mngrs:
+            self._client_mngrs[client_id] = _ClientWindowsManager(
+                new_window=lambda cls, **kwargs: self._new_window(
+                    cls, client_id=client_id, **kwargs))
+        client_data = self._client_mngrs[client_id]
+        method = getattr(client_data, todo)
+        if method(**kwargs) or signature(method).return_annotation is None:
             self.redraw_affected()
 
     def cmd__connect(self,
@@ -149,7 +158,7 @@ class ClientTui(BaseTui):
         'Create Client and pass it via NewClientEvent.'
         split = host_port.split(':', maxsplit=1)
         hostname = split[0]
-        if hostname in self._clients_data:
+        if hostname in self._client_mngrs:
             return f'already set up connection to {hostname}'
         port = -1
         if len(split) > 1:
@@ -178,9 +187,10 @@ class _ClientKnowingTui(Client):
                                                  ).kw(**kwargs))
 
     def _log(self, msg: str, stream: str = STREAM_SERVER) -> None:
-        self._ctput('log_for_client', stream=stream, msg=msg)
+        self._ctput('for_client_do', todo='log', stream=stream, msg=msg)
 
     def update_login(self, nick_confirmed: bool, nickname: str = '') -> None:
         super().update_login(nick_confirmed, nickname)
-        self._ctput('update_client_data', nick_confirmed=self.nick_confirmed,
+        self._ctput('for_client_do', todo='update',
+                    nick_confirmed=self.nick_confirmed,
                     nickname=self.conn_setup.nickname)