home · contact · privacy
9f28a04c542ba586ad3650679c43c70b3fd24734
[plomrogue2] / plomrogue_client / tui.py
1 #!/usr/bin/env python3
2 import curses
3
4
5
6 class AbortOnGetkey(Exception):
7     pass
8
9
10
11 class TUI:
12
13     def __init__(self):
14         self._log = []
15         self.do_refresh = True
16         self.store_widechar = False
17         curses.wrapper(self.run_loop)
18
19     def addstr(self, y, x, line, attr=0):
20         if y < self.size.y - 1 or x + len(line) < self.size.x:
21             self.stdscr.addstr(y, x, line, attr)
22         else:  # workaround to <https://stackoverflow.com/q/7063128>
23             cut_i = self.size.x - x - 1
24             cut = line[:cut_i]
25             last_char = line[cut_i]
26             self.stdscr.addstr(y, self.size.x - 2, last_char, attr)
27             self.stdscr.insstr(y, self.size.x - 2, ' ')
28             self.stdscr.addstr(y, x, cut, attr)
29
30     def reset_size(self):
31         from plomrogue.mapping import YX
32         self.size = YX(*self.stdscr.getmaxyx())
33         self.size = self.size - YX(self.size.y % 4, 0)
34         self.size = self.size - YX(0, self.size.x % 4)
35
36     def init_loop(self):
37         curses.curs_set(0)  # hide cursor
38         self.stdscr.timeout(10)
39         self.reset_size()
40
41     def run_loop(self, stdscr):
42         self.stdscr = stdscr
43         self.init_loop()
44         while True:
45             try:
46                 self.loop()
47             except AbortOnGetkey:
48                 continue
49             self.do_refresh = True
50
51     def log(self, msg):
52         self._log += [msg]
53         self.do_refresh = True
54
55     def get_key_and_keycode(self):
56         try:
57             key = self.stdscr.getkey()
58         except curses.error:
59             raise AbortOnGetkey
60         keycode = None
61         if len(key) == 1:
62             keycode = ord(key)
63             # workaround for <https://stackoverflow.com/a/56390915>
64             if self.store_widechar:
65                 self.store_widechar = False
66                 key = bytes([195, keycode]).decode()
67             if keycode == 195:
68                 self.store_widechar = True
69                 raise AbortOnGetkey
70         return key, keycode
71
72
73 def msg_into_lines_of_width(msg, width):
74     chunk = ''
75     lines = []
76     x = 0
77     for i in range(len(msg)):
78         if x >= width or msg[i] == "\n":
79             lines += [chunk]
80             chunk = ''
81             x = 0
82             if msg[i] == "\n":
83                 x -= 1
84         if msg[i] != "\n":
85             chunk += msg[i]
86         x += 1
87     lines += [chunk]
88     return lines