From: Christian Heller Date: Wed, 26 Nov 2025 05:26:32 +0000 (+0100) Subject: To avoid crashes on too long status line window lists, turn those into horizontal... X-Git-Url: https://plomlompom.com/repos/%7B%7Bprefix%7D%7D/%7B%7Bdb.prefix%7D%7D/calendar?a=commitdiff_plain;h=9993c39fa8f1518d0537c7beb91108d6da274171;p=ircplom To avoid crashes on too long status line window lists, turn those into horizontal scrollable. --- diff --git a/src/ircplom/tui_base.py b/src/ircplom/tui_base.py index 8a9f6e5..cfd2e61 100644 --- a/src/ircplom/tui_base.py +++ b/src/ircplom/tui_base.py @@ -31,8 +31,8 @@ _OSC52_PREFIX = b']52;c;' _PASTE_DELIMITER = '\007' _PROMPT_TEMPLATE = '> ' -_PROMPT_ELL_IN = '<…' -_PROMPT_ELL_OUT = '…>' +_ELL_IN = '<…' +_ELL_OUT = '…>' _CHAR_RESIZE = chr(12) _KEYBINDINGS = { @@ -204,6 +204,26 @@ class _Widget(ABC): self.taint() self._sizes = sizes + def make_x_scroll(self, padchar: str, cursor_x: int, left: str, right: str + ) -> tuple[int, str]: + 'Build line of static left, right scrolled with cursor_x if too large.' + to_write = left + offset = 0 + width_gap = self._sizes.x - len(left) - len(right) + if width_gap < 0: + half_width = (self._sizes.x - len(left)) // 2 + if cursor_x > half_width: + to_write += _ELL_IN + offset = len(_ELL_IN) + min(-width_gap, cursor_x - half_width) + to_write += right[offset:] + if len(to_write) > self._sizes.x: + to_write = to_write[:self._sizes.x - len(_ELL_OUT)] + _ELL_OUT + else: + to_write += (right if width_gap == 0 + else ((padchar * width_gap + right) if padchar == '=' + else (right + padchar * width_gap))) + return len(left) - offset + cursor_x, to_write + def draw(self) -> None: 'Print widget\'s content in shape appropriate to set geometry.' if self._drawable: @@ -419,25 +439,14 @@ class PromptWidget(_ScrollableWidget): content = self.input_buffer if self._cursor_x == len(self.input_buffer): content += ' ' - half_width = (self._sizes.x - len(prefix)) // 2 - offset = 0 - if len(prefix) + len(content) > self._sizes.x\ - and self._cursor_x > half_width: - prefix += _PROMPT_ELL_IN - offset = min(len(prefix) + len(content) - self._sizes.x, - self._cursor_x - half_width + len(_PROMPT_ELL_IN)) - cursor_x_to_write = len(prefix) + self._cursor_x - offset - to_write = f'{prefix}{content[offset:]}' - if len(to_write) > self._sizes.x: - to_write = (to_write[:self._sizes.x-len(_PROMPT_ELL_OUT)] - + _PROMPT_ELL_OUT) - if len(to_write) < self._sizes.x: - to_write += ' ' + x_cursor, to_write = self.make_x_scroll(padchar=' ', + cursor_x=self._cursor_x, + left=prefix, + right=content) self._write(self._sizes.y, - FormattingString(to_write[:cursor_x_to_write]) - + FormattingString(to_write[cursor_x_to_write] - ).attrd('reverse') - + FormattingString(to_write[cursor_x_to_write + 1:])) + FormattingString(to_write[:x_cursor]) + + FormattingString(to_write[x_cursor]).attrd('reverse') + + FormattingString(to_write[x_cursor + 1:])) def _archive_prompt(self) -> None: self.append(self.input_buffer) @@ -509,9 +518,11 @@ class _StatusLine(_Widget): self._write = write def _draw(self) -> None: - listed = [] + cursor_x = 0 focused = None + win_listing = '' for w in self._windows: + win_listing += ' ' if w.idx > 0 else '(' item = str(w.idx) if (n := w.history.n_lines_unread): item = f'({item}:{n})' @@ -520,12 +531,13 @@ class _StatusLine(_Widget): if w.idx == self.idx_focus: focused = w item = f'[{item}]' - listed += [item] + cursor_x = len(win_listing) + (len(item) // 2) + win_listing += item assert isinstance(focused, Window) - left = f'{focused.title})' - right = f'({" ".join(listed)}' - width_gap = max(1, (self._sizes.x - len(left) - len(right))) - self._write(self._sizes.y, left + '=' * width_gap + right) + self._write(self._sizes.y, self.make_x_scroll(padchar='=', + cursor_x=cursor_x, + left=focused.title + ')', + right=win_listing)[1]) class Window: diff --git a/src/tests/lib/connect-to-connected b/src/tests/lib/connect-to-connected index cf45c6d..3dc0eee 100644 --- a/src/tests/lib/connect-to-connected +++ b/src/tests/lib/connect-to-connected @@ -4,5 +4,5 @@ insert ./lib/conn # for: connect × connect-to-connected -insert connect : +1 -insert attempting-to-connected : +1 WIN_IDS :2 +insert connect +insert attempting-to-connected : + WIN_IDS :2 diff --git a/src/tests/test.test b/src/tests/test.test index d14e8a1..88ad5cc 100644 --- a/src/tests/test.test +++ b/src/tests/test.test @@ -242,17 +242,6 @@ insert isupport-clear : +8 insert disconnect1 1:-1 +8 WIN_IDS :2 log 8 $ nickname refused for bad format, giving up -# zero unread-lines counts in status line so it won't explode simulated screen -> /window 2 -> /window 3 -> /window 4 -> /window 5 -> /window 6 -> /window 7 -> /window 8 -> /window 9 -> /window 1 - # test failing third connection insert connect : +10 foo.bar.baz :baz.baz.baz insert attempting-to-connected : +10 WIN_IDS=2 foo.bar.baz :baz.baz.baz @@ -263,20 +252,8 @@ log 10 $ will retry connecting in 1 seconds > /window 10 insert disconnect-to-stop-auto-reconnect : +10 -# zero unread-lines counts in status line so it won't explode simulated screen -> /window 2 -> /window 3 -> /window 4 -> /window 5 -> /window 6 -> /window 7 -> /window 8 -> /window 9 -> /window 10 -> /window 11 -> /window 1 - # check that (save TUI tests assuming start on window 0, and no 4 yet) on reconnect, all the same effects can be expected +> /window 1 > /reconnect insert attempting :-1 log 2 $ - password: bar diff --git a/src/tests/tui_status_line_scrolling.test b/src/tests/tui_status_line_scrolling.test new file mode 100644 index 0000000..96064e6 --- /dev/null +++ b/src/tests/tui_status_line_scrolling.test @@ -0,0 +1,62 @@ +insert ./lib/connect-to-connected +insert ./lib/caps-neg-empty +insert ./lib/001-to-usermode + +× new-hi +insert servermsglogged : +0 MSG ::winWIN_ID!~winWIN_ID@bar.bar PRIVMSG foo :hi there +log WIN_ID < [winWIN_ID] hi there + +× ×--------------------------------- + +insert connect-to-connected +insert caps-neg-empty +insert 001-to-usermode +insert new-hi : + WIN_ID :3 +insert new-hi : + WIN_ID :4 +insert new-hi : + WIN_ID :5 +insert new-hi : + WIN_ID :6 +insert new-hi : + WIN_ID :7 +insert new-hi : + WIN_ID :8 +insert new-hi : + WIN_ID :9 +insert new-hi : + WIN_ID :10 +line 22 on_black,bright_white :start)=======([0] (1:32) (2:7) (3:2) (4:2) (5:2) (6:2) (7:2) (8:2) (9:2) (10:2)§§ + +# grow windows list to maximum before ellipsis necessary +insert new-hi : + WIN_ID :11 +line 22 on_black,bright_white :start)([0] (1:33) (2:7) (3:2) (4:2) (5:2) (6:2) (7:2) (8:2) (9:2) (10:2) (11:2)§§ + +# grow list beyond, with focus on left force ellipsis to the right +insert new-hi : + WIN_ID :12 +line 22 on_black,bright_white :start)([0] (1:34) (2:7) (3:2) (4:2) (5:2) (6:2) (7:2) (8:2) (9:2) (10:2) (11:…>§§ + +# shrink (uncut) listing but grow title to the left more, forcing ellipsis still to cut earlier +> /window 1 +line 22 on_black,bright_white foo.bar.baz:debug)(0 [1] (2:7) (3:2) (4:2) (5:2) (6:2) (7:2) (8:2) (9:2) (10:2…>§§ + +# further shrink uncut listing until ellipsis gone again; with focus not moving beyond left half, don't scroll yet +> /window 3 +line 22 on_black,bright_white foo.bar.baz/win3)(0 1 (2:7) [3] (4:2) (5:2) (6:2) (7:2) (8:2) (9:2) (10:2) (11…>§§ +> /window 4 +line 22 on_black,bright_white foo.bar.baz/win4)(0 1 (2:7) 3 [4] (5:2) (6:2) (7:2) (8:2) (9:2) (10:2) (11:2) …>§§ +> /window 5 +line 22 on_black,bright_white foo.bar.baz/win5)(0 1 (2:7) 3 4 [5] (6:2) (7:2) (8:2) (9:2) (10:2) (11:2) (12:2)§§ + +# grow uncut listing again, re-establishing ellipsis to the right +insert new-hi : + WIN_ID :13 +line 22 on_black,bright_white foo.bar.baz/win5)(0 (1:1) (2:7) 3 4 [5] (6:2) (7:2) (8:2) (9:2) (10:2) (11:2) …>§§ + +# move focus into middle of listing, scrolling so that ellipsis on both side +> /window 8 +line 22 on_black,bright_white foo.bar.baz/win8)<…:1) (2:7) 3 4 5 (6:2) (7:2) [8] (9:2) (10:2) (11:2) (12:2) …>§§ + +# move focus further to the right, so that only ellipsis on the left +> /window 9 +line 22 on_black,bright_white foo.bar.baz/win9)<…:1) (2:7) 3 4 5 (6:2) (7:2) 8 [9] (10:2) (11:2) (12:2) (13:2)§§ + +# shrink uncut listing to return to full view again (no ellipses) +> /window 13 +line 22 on_black,bright_white foo.bar.baz/win13)(0 (1:1) (2:7) 3 4 5 (6:2) (7:2) 8 9 (10:2) (11:2) (12:2) [13]§§ + +# add new window, re-establishing ellipsis to the left, with focus remaining quite to the right +insert new-hi : + WIN_ID :14 +line 22 on_black,bright_white foo.bar.baz/win13)<…(2:7) 3 4 5 (6:2) (7:2) 8 9 (10:2) (11:2) (12:2) [13] (14:2)§§