home · contact · privacy
Refactor client curses code.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 31 May 2021 23:37:23 +0000 (01:37 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 31 May 2021 23:37:23 +0000 (01:37 +0200)
plomrogue_client/tui.py [new file with mode: 0644]
rogue_chat_curses.py

diff --git a/plomrogue_client/tui.py b/plomrogue_client/tui.py
new file mode 100644 (file)
index 0000000..ec04304
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+import curses
+
+
+
+class CursesScreen:
+
+    def wrap_loop(self, loop):
+        curses.wrapper(self.start_loop, loop)
+
+    def safe_addstr(self, y, x, line, attr=0):
+        if y < self.size.y - 1 or x + len(line) < self.size.x:
+            self.stdscr.addstr(y, x, line, attr)
+        else:  # workaround to <https://stackoverflow.com/q/7063128>
+            cut_i = self.size.x - x - 1
+            cut = line[:cut_i]
+            last_char = line[cut_i]
+            self.stdscr.addstr(y, self.size.x - 2, last_char, attr)
+            self.stdscr.insstr(y, self.size.x - 2, ' ')
+            self.stdscr.addstr(y, x, cut, attr)
+
+    def reset_size(self):
+        from plomrogue.mapping import YX
+        self.size = YX(*self.stdscr.getmaxyx())
+        self.size = self.size - YX(self.size.y % 4, 0)
+        self.size = self.size - YX(0, self.size.x % 4)
+
+    def start_loop(self, stdscr, loop):
+        self.stdscr = stdscr
+        curses.curs_set(0)  # hide cursor
+        stdscr.timeout(10)
+        loop()
+
+
+
+def msg_into_lines_of_width(msg, width):
+    chunk = ''
+    lines = []
+    x = 0
+    for i in range(len(msg)):
+        if x >= width or msg[i] == "\n":
+            lines += [chunk]
+            chunk = ''
+            x = 0
+            if msg[i] == "\n":
+                x -= 1
+        if msg[i] != "\n":
+            chunk += msg[i]
+        x += 1
+    lines += [chunk]
+    return lines
index b4ac8e50302dbabc4ff94bbd676a5d635790740a..11fe94868c72ee8891877eec4a487c123ac41368 100755 (executable)
@@ -8,6 +8,7 @@ from plomrogue.things import ThingBase
 from plomrogue.misc import quote
 from plomrogue.errors import ArgError
 from plomrogue_client.socket import ClientSocket
+from plomrogue_client.tui import msg_into_lines_of_width, CursesScreen
 
 
 
@@ -557,7 +558,8 @@ class TUI:
         self.ascii_draw_stage = 0
         self.full_ascii_draw = ''
         self.offset = YX(0,0)
-        curses.wrapper(self.loop)
+        self.screen = CursesScreen()
+        self.screen.wrap_loop(self.loop)
 
     def update_on_connect(self):
         self.socket.send('TASKS')
@@ -832,18 +834,10 @@ class TUI:
             info += ')'
         return info
 
-    def loop(self, stdscr):
+    def loop(self):
 
         def safe_addstr(y, x, line):
-            if y < self.size.y - 1 or x + len(line) < self.size.x:
-                stdscr.addstr(y, x, line, curses.color_pair(1))
-            else:  # workaround to <https://stackoverflow.com/q/7063128>
-                cut_i = self.size.x - x - 1
-                cut = line[:cut_i]
-                last_char = line[cut_i]
-                stdscr.addstr(y, self.size.x - 2, last_char, curses.color_pair(1))
-                stdscr.insstr(y, self.size.x - 2, ' ')
-                stdscr.addstr(y, x, cut, curses.color_pair(1))
+            self.screen.safe_addstr(y, x, line, curses.color_pair(1))
 
         def handle_input(msg):
             command, args = self.parser.parse(msg)
@@ -852,29 +846,10 @@ class TUI:
         def task_action_on(action):
             return action_tasks[action] in self.game.tasks
 
-        def msg_into_lines_of_width(msg, width):
-            chunk = ''
-            lines = []
-            x = 0
-            for i in range(len(msg)):
-                if x >= width or msg[i] == "\n":
-                    lines += [chunk]
-                    chunk = ''
-                    x = 0
-                    if msg[i] == "\n":
-                        x -= 1
-                if msg[i] != "\n":
-                    chunk += msg[i]
-                x += 1
-            lines += [chunk]
-            return lines
-
         def reset_screen_size():
-            self.size = YX(*stdscr.getmaxyx())
-            self.size = self.size - YX(self.size.y % 4, 0)
-            self.size = self.size - YX(0, self.size.x % 4)
-            self.left_window_width = min(52, int(self.size.x / 2))
-            self.right_window_width = self.size.x - self.left_window_width
+            self.screen.reset_size()
+            self.left_window_width = min(52, int(self.screen.size.x / 2))
+            self.right_window_width = self.screen.size.x - self.left_window_width
 
         def recalc_input_lines():
             if not self.mode.has_input_prompt:
@@ -900,7 +875,7 @@ class TUI:
                 lines += msg_into_lines_of_width(line, self.right_window_width)
             lines.reverse()
             height_header = 2
-            max_y = self.size.y - len(self.input_lines)
+            max_y = self.screen.size.y - len(self.input_lines)
             for i in range(len(lines)):
                 if (i >= max_y - height_header):
                     break
@@ -912,12 +887,12 @@ class TUI:
             height_header = 2
             for i in range(len(lines)):
                 y = height_header + i
-                if y >= self.size.y - len(self.input_lines):
+                if y >= self.screen.size.y - len(self.input_lines):
                     break
                 safe_addstr(y, self.left_window_width, lines[i])
 
         def draw_input():
-            y = self.size.y - len(self.input_lines)
+            y = self.screen.size.y - len(self.input_lines)
             for i in range(len(self.input_lines)):
                 safe_addstr(y, self.left_window_width, self.input_lines[i])
                 y += 1
@@ -987,7 +962,7 @@ class TUI:
                 else:
                     for line in map_lines_split:
                         self.map_lines += [''.join(line)]
-                window_center = YX(int(self.size.y / 2),
+                window_center = YX(int(self.screen.size.y / 2),
                                    int(self.left_window_width / 2))
                 center = self.game.player.position
                 if self.mode.shows_info or self.mode.name == 'control_tile_draw':
@@ -1000,7 +975,7 @@ class TUI:
             term_x = max(0, -self.offset.x)
             map_y = max(0, self.offset.y)
             map_x = max(0, self.offset.x)
-            while term_y < self.size.y and map_y < len(self.map_lines):
+            while term_y < self.screen.size.y and map_y < len(self.map_lines):
                 to_draw = self.map_lines[map_y][map_x:self.left_window_width + self.offset.x]
                 safe_addstr(term_y, term_x, to_draw)
                 term_y += 1
@@ -1010,7 +985,7 @@ class TUI:
             players = [t for t in self.game.things if t.type_ == 'Player']
             players.sort(key=lambda t: len(t.name))
             players.reverse()
-            shrink_offset = max(0, (self.size.y - self.left_window_width // 2) // 2)
+            shrink_offset = max(0, (self.screen.size.y - self.left_window_width // 2) // 2)
             y = 0
             for t in players:
                 offset_y = y - shrink_offset
@@ -1020,7 +995,7 @@ class TUI:
                     name = name[:max_len - 1] + '…'
                 safe_addstr(y, 0, '@%s:%s' % (t.thing_char, name))
                 y += 1
-                if y >= self.size.y:
+                if y >= self.screen.size.y:
                     break
 
         def draw_face_popup():
@@ -1037,14 +1012,14 @@ class TUI:
                 safe_addstr(end_y, start_x, '| ' + body_part[12:18] + ' |')
 
             if hasattr(t, 'face'):
-                draw_body_part(t.face, self.size.y - 3)
+                draw_body_part(t.face, self.screen.size.y - 3)
             if hasattr(t, 'hat'):
-                draw_body_part(t.hat, self.size.y - 6)
-            safe_addstr(self.size.y - 2, start_x, '----------')
+                draw_body_part(t.hat, self.screen.size.y - 6)
+            safe_addstr(self.screen.size.y - 2, start_x, '----------')
             name = t.name[:]
             if len(name) > 7:
                 name = name[:6 - 1] + '…'
-            safe_addstr(self.size.y - 1, start_x,
+            safe_addstr(self.screen.size.y - 1, start_x,
                         '@%s:%s' % (t.thing_char, name))
 
         def draw_help():
@@ -1065,7 +1040,7 @@ class TUI:
                     content += '[%s] – %s\n' % (key, action_descriptions[action])
                 content += '\n'
             content += self.mode.list_available_modes(self)
-            for i in range(self.size.y):
+            for i in range(self.screen.size.y):
                 safe_addstr(i,
                             self.left_window_width * (not self.mode.has_input_prompt),
                             ' ' * self.left_window_width)
@@ -1073,15 +1048,15 @@ class TUI:
             for line in content.split('\n'):
                 lines += msg_into_lines_of_width(line, self.right_window_width)
             for i in range(len(lines)):
-                if i >= self.size.y:
+                if i >= self.screen.size.y:
                     break
                 safe_addstr(i,
                             self.left_window_width * (not self.mode.has_input_prompt),
                             lines[i])
 
         def draw_screen():
-            stdscr.clear()
-            stdscr.bkgd(' ', curses.color_pair(1))
+            self.screen.stdscr.clear()
+            self.screen.stdscr.bkgd(' ', curses.color_pair(1))
             recalc_input_lines()
             if self.mode.has_input_prompt:
                 draw_input()
@@ -1189,7 +1164,6 @@ class TUI:
             'dance': 'DANCE',
         }
 
-        curses.curs_set(0)  # hide cursor
         curses.start_color()
         self.set_default_colors()
         curses.init_pair(1, 7, 0)
@@ -1197,7 +1171,6 @@ class TUI:
             self.log_msg('@ unfortunately, your terminal does not seem to '
                          'support re-definition of colors; you might miss out '
                          'on some color effects')
-        stdscr.timeout(10)
         reset_screen_size()
         self.explorer = YX(0, 0)
         self.input_ = ''
@@ -1217,7 +1190,7 @@ class TUI:
             for msg in self.socket.get_message():
                 handle_input(msg)
             try:
-                key = stdscr.getkey()
+                key = self.screen.stdscr.getkey()
                 self.do_refresh = True
             except curses.error:
                 continue
@@ -1252,7 +1225,7 @@ class TUI:
                 self.restore_input_values()
             elif self.mode.has_input_prompt and key != '\n':  # Return key
                 self.input_ += key
-                max_length = self.right_window_width * self.size.y - len(input_prompt) - 1
+                max_length = self.right_window_width * self.screen.size.y - len(input_prompt) - 1
                 if len(self.input_) > max_length:
                     self.input_ = self.input_[:max_length]
             elif key == self.keys['help'] and not self.mode.is_single_char_entry: