_PASTE_DELIMITER = '\007'
_PROMPT_TEMPLATE = '> '
-_PROMPT_ELL_IN = '<…'
-_PROMPT_ELL_OUT = '…>'
+_ELL_IN = '<…'
+_ELL_OUT = '…>'
_CHAR_RESIZE = chr(12)
_KEYBINDINGS = {
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:
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)
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})'
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:
# 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
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
> /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
--- /dev/null
+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)§§