From d1b7ae346e13f77a72926173a3092d1e83663a9d Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Tue, 1 Jun 2021 01:37:23 +0200 Subject: [PATCH] Refactor client curses code. --- plomrogue_client/tui.py | 51 +++++++++++++++++++++++++++ rogue_chat_curses.py | 77 +++++++++++++---------------------------- 2 files changed, 76 insertions(+), 52 deletions(-) create mode 100644 plomrogue_client/tui.py diff --git a/plomrogue_client/tui.py b/plomrogue_client/tui.py new file mode 100644 index 0000000..ec04304 --- /dev/null +++ b/plomrogue_client/tui.py @@ -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 + 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 diff --git a/rogue_chat_curses.py b/rogue_chat_curses.py index b4ac8e5..11fe948 100755 --- a/rogue_chat_curses.py +++ b/rogue_chat_curses.py @@ -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 - 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: -- 2.30.2