|
4 | 4 | from curses import wrapper, error |
5 | 5 | import curses |
6 | 6 | import logging |
7 | | -from .utils import CharacterScroller, IS_WINDOWS, init_utils |
| 7 | +from .utils import CharacterScroller, IS_WINDOWS, TermSize |
8 | 8 |
|
9 | 9 |
|
10 | 10 | QUIT_CHARACTERS = ["\x1B", "Q", "q"] |
|
13 | 13 | log = logging.getLogger(__name__) |
14 | 14 |
|
15 | 15 |
|
16 | | -def curses_scroller(win, write_config): |
| 16 | +def curses_scroller(win, cfg): |
17 | 17 | """ |
18 | 18 | Curses-main: render a text in a side-scrolling manner, using curses. |
19 | 19 |
|
20 | 20 | :param win: Internal curses based object |
21 | 21 | :type win: curses._window |
22 | | - :param write_config: Write initial config |
23 | | - :type: bool |
| 22 | + :param cfg: Config object |
| 23 | + :type: configparser.ConfigParser |
24 | 24 | """ |
25 | | - winsize = win.getmaxyx() |
26 | | - log.debug("win dimensions: (columns, rows) (%d, %d)", winsize[1], winsize[0]) |
27 | | - |
28 | | - cfg = init_utils(write_config) |
29 | | - box = cfg["cursestext"].getboolean("box") |
30 | | - # try: |
31 | | - if box: |
| 25 | + if cfg["cursestext"].getboolean("box"): |
32 | 26 | win.box() |
33 | 27 | if not IS_WINDOWS: |
34 | 28 | curses.curs_set(0) # Hide the cursor |
35 | 29 |
|
| 30 | + term_size = TermSize(0, 0) |
| 31 | + update_term_size(win, cfg["cursestext"].getboolean("box"), term_size) |
36 | 32 | argv = {} |
37 | | - argv["term_rows"] = winsize[0] - (2 if box else 1) |
38 | | - argv["term_columns"] = winsize[1] - (2 if box else 0) |
39 | 33 | argv["min_scroll_line"] = 3 |
40 | | - scroller = CharacterScroller(cfg, **argv) |
| 34 | + scroller = CharacterScroller(cfg, term_size, **argv) |
| 35 | + draw_items(win, cfg["cursestext"].getboolean("box"), |
| 36 | + argv["min_scroll_line"], scroller, term_size) |
41 | 37 |
|
42 | | - win.addstr(1, 10, "Scroll-Text") |
43 | | - add_quit_text(win, box, scroller.line, argv["term_rows"]) |
44 | 38 | win.timeout(100) |
45 | 39 | try: |
46 | | - do_textloop(win, box, term_size, scroller, term_size.get_rows()) |
| 40 | + do_textloop(win, cfg, term_size, scroller, argv["min_scroll_line"]) |
47 | 41 | except KeyboardInterrupt: |
48 | 42 | pass |
49 | 43 |
|
50 | 44 |
|
51 | | -def do_textloop(win, box, scroller, visibile_height): |
| 45 | +def do_textloop(win, cfg, term_size, scroller, min_scroll_line): |
52 | 46 | """ |
53 | 47 | This method loops over the scrolled text |
54 | 48 | """ |
| 49 | + box = cfg["cursestext"].getboolean("box") |
| 50 | + term_too_small_printed = False |
55 | 51 | for text in scroller: |
56 | 52 | win_text = text |
57 | | - if not box and scroller.line == visibile_height: |
| 53 | + # hack: When writing to the last line we prevent adding an immediate newline and thus |
| 54 | + # moving the text upwards, by removing the last character of the visibile text. |
| 55 | + if not box and scroller.line == term_size.get_rows(): |
58 | 56 | win_text = text[:-1] |
59 | | - win.addstr(scroller.line, (1 if box else 0), win_text) |
60 | | - win.redrawwin() |
61 | | - if check_quit(win): |
| 57 | + if scroller.line >= min_scroll_line: |
| 58 | + _addstr_wrapper(win, scroller.line, (1 if box else 0), win_text) |
| 59 | + term_too_small_printed = False |
| 60 | + win.redrawwin() |
| 61 | + else: |
| 62 | + if not term_too_small_printed: |
| 63 | + log.debug("Terminal is too small cols: %d rows: %d", |
| 64 | + term_size.get_cols(), term_size.get_rows()) |
| 65 | + term_too_small_printed = True |
| 66 | + character = get_char(win) |
| 67 | + if character == curses.KEY_EXIT: |
62 | 68 | return |
| 69 | + if character == curses.KEY_RESIZE: |
| 70 | + update_term_size(win, box, term_size) |
| 71 | + draw_items(win, box, min_scroll_line, scroller, term_size) |
63 | 72 |
|
64 | 73 |
|
65 | | -def add_quit_text(win, box, line, visibile_height): |
| 74 | +def add_quit_text(win, box, line, term_size): |
66 | 75 | """ |
67 | 76 | Adds a hint message to win. |
68 | | - TODO: visibile_height, winsize, ...?? |
69 | 77 | """ |
70 | | - if not box and line == visibile_height: |
71 | | - win.addstr(visibile_height - 2, 0, " You can quit with 'q' or 'Q'.") |
| 78 | + if term_size.get_cols() < len(" You can quit with 'q' or 'Q'.") + 2: |
| 79 | + return |
| 80 | + if not box and line == term_size.get_rows(): |
| 81 | + _addstr_wrapper(win, term_size.get_rows() - 2, 0, " You can quit with 'q' or 'Q'.") |
72 | 82 | else: |
73 | | - win.addstr(visibile_height, (2 if box else 0), " You can quit with 'q' or 'Q'.") |
| 83 | + _addstr_wrapper(win, term_size.get_rows(), |
| 84 | + (2 if box else 0), " You can quit with 'q' or 'Q'.") |
74 | 85 |
|
75 | 86 |
|
76 | | -def check_quit(win): |
| 87 | +def get_char(win): |
77 | 88 | """ |
78 | | - :returns: True, if a quit character is entered, False, otherwise. |
79 | | - :rtype: Boolean |
| 89 | + :returns: curses.KEY_EXIT, if a quit character is entered, the current character, otherwise. |
| 90 | + :rtype: int |
80 | 91 | """ |
81 | | - character = win.getch(4, 0) |
| 92 | + log.debug("getch") |
| 93 | + character = None |
| 94 | + character = win.getch(0, 0) |
82 | 95 | if character != -1: |
83 | 96 | log.debug("got key (%d) type %s", character, type(character)) |
84 | 97 | if character != -1 and (chr(character) in QUIT_CHARACTERS): |
85 | | - return True |
86 | | - return False |
| 98 | + return curses.KEY_EXIT |
| 99 | + return character |
| 100 | + |
| 101 | + |
| 102 | +def update_term_size(win, box, term_size): |
| 103 | + """ |
| 104 | + Updates TermSize object. |
| 105 | + """ |
| 106 | + winsize = win.getmaxyx() |
| 107 | + log.debug("win dimensions: (columns, rows) (%d, %d)", winsize[1], winsize[0]) |
| 108 | + available_rows = winsize[0] - (2 if box else 1) |
| 109 | + available_columns = winsize[1] - (2 if box else 0) |
| 110 | + term_size.set_size(available_columns, available_rows) |
| 111 | + |
| 112 | + |
| 113 | +def draw_items(win, box, min_scroll_line, scroller, term_size): |
| 114 | + """ |
| 115 | + Add strings to the curses window. |
| 116 | + """ |
| 117 | + # clear the window contents |
| 118 | + win.clear() |
| 119 | + |
| 120 | + # and redraw screen |
| 121 | + if scroller.line != 1 and term_size.get_cols() > len("Scroll-Text"): |
| 122 | + _addstr_wrapper(win, 1, 10, "Scroll-Text") |
| 123 | + if term_size.get_rows() > min_scroll_line: |
| 124 | + add_quit_text(win, box, scroller.line, term_size) |
| 125 | + |
| 126 | + |
| 127 | +def _addstr_wrapper(win, row, column, text): |
| 128 | + log.debug("addstr to line %d", row) |
| 129 | + try: |
| 130 | + win.addstr(row, column, text) |
| 131 | + except: # pylint: disable=W0702 (bare-except) |
| 132 | + log.exception("Error in addstr") |
87 | 133 |
|
88 | 134 |
|
89 | | -def work(write_config): |
| 135 | +def work(cfg): |
90 | 136 | """Main usese curses.wrapper. See curses doc for details. |
91 | 137 | """ |
92 | 138 | try: # noqa: C901 ignoring 'TryExcept 42' is too complex - fix later |
93 | | - wrapper(curses_scroller, write_config) |
| 139 | + wrapper(curses_scroller, cfg) |
94 | 140 | except error as ex: |
95 | 141 | log.exception(ex) |
96 | 142 | finally: |
|
0 commit comments