From: Christian Heller Date: Tue, 21 Oct 2025 00:57:28 +0000 (+0200) Subject: Fix off-by-one, too-early-enabling scroll calculations. X-Git-Url: https://plomlompom.com/repos/%7B%7B%20web_path%20%7D%7D/templates?a=commitdiff_plain;h=554e4b1205cd23a4f24d9b184c3515ce65412e7e;p=ircplom Fix off-by-one, too-early-enabling scroll calculations. --- diff --git a/src/ircplom/tui_base.py b/src/ircplom/tui_base.py index fca16f0..e57f698 100644 --- a/src/ircplom/tui_base.py +++ b/src/ircplom/tui_base.py @@ -115,21 +115,21 @@ class _ScrollableWidget(_Widget): class _HistoryWidget(_ScrollableWidget): - _history_idx_neg = -1 - _wrapped_idx_neg: int = -1 - _history_offset: int = 0 - _UNSET_HISTORY_IDX_POS: int = -1 + _y_pgscroll: int + _UNSET_IDX_NEG: int = 0 + _UNSET_IDX_POS: int = -1 _COMPARAND_HISTORY_IDX_POS: int = -2 _BOOKMARK_HISTORY_IDX_POS: int = -3 _PADDING_HISTORY_IDX_POS: int = -4 - _newest_read_history_idx_pos: int - _y_pgscroll: int def __init__(self, wrap: Callable[[str], list[str]], **kwargs) -> None: super().__init__(**kwargs) self._wrap = wrap self._wrapped: list[tuple[int, str]] = [] - self._newest_read_history_idx_pos = self._UNSET_HISTORY_IDX_POS + self._newest_read_history_idx_pos = self._UNSET_IDX_POS + self._history_offset = 0 + self._history_idx_neg = self._UNSET_IDX_NEG + self._wrapped_idx_neg = self._UNSET_IDX_NEG def _add_wrapped(self, history_idx_pos: int, line: str) -> int: wrapped_lines = self._wrap(line) @@ -151,7 +151,7 @@ class _HistoryWidget(_ScrollableWidget): # ensure that of the full line identified by ._history_idx_neg, # ._wrapped_idx_neg point to the lowest of its wrap parts self._wrapped_idx_neg = ( - -1 if (not self._wrapped) + self._UNSET_IDX_NEG if (not self._wrapped) else (-len(self._wrapped) + self._last_wrapped_idx_pos_for_hist_idx_pos( self._len_full_history + max(self._history_idx_neg, @@ -161,11 +161,11 @@ class _HistoryWidget(_ScrollableWidget): def append(self, to_append: str) -> None: super().append(to_append) self.taint() - if self._history_idx_neg < -1: + if not self._UNSET_IDX_NEG != self._history_idx_neg >= -1: self._history_idx_neg -= 1 if self._drawable: n_wrapped = self._add_wrapped(len(self._history) - 1, to_append) - if self._wrapped_idx_neg < -1: + if not self._UNSET_IDX_NEG != self._wrapped_idx_neg >= -1: self._wrapped_idx_neg -= n_wrapped if len(self._history) > _LOG_MAX_LEN: self._history = self._history[1:] @@ -201,7 +201,7 @@ class _HistoryWidget(_ScrollableWidget): to_write_w_attrs += [(attrs, line)] if add_scroll_info: - scroll_info = f'vvv [{(-1) * self._history_idx_neg}] ' + scroll_info = f'vvv [{(-1) * self._history_idx_neg - 1}] ' scroll_info += 'v' * (self._sizes.x - len(scroll_info)) to_write_w_attrs += [(['reverse'], scroll_info)] @@ -256,7 +256,7 @@ class _HistoryWidget(_ScrollableWidget): def _scroll(self, up: bool = True) -> None: super()._scroll(up) if self._drawable and self._wrapped: - if up: + if up and len(self._wrapped) > 2: self._wrapped_idx_neg = max( -len(self._wrapped), self._wrapped_idx_neg - self._y_pgscroll) diff --git a/src/tests/tui_draw.test b/src/tests/tui_draw.test index 38a5df7..5abf93f 100644 --- a/src/tests/tui_draw.test +++ b/src/tests/tui_draw.test @@ -32,9 +32,16 @@ line 23 on_black,bright_white,reverse §§ § > repeat empty-init +# check scrolling on empty history does nothing +> /window.history.scroll up +repeat empty-init +> /window.history.scroll down +repeat empty-init + # non-empty command input starts log at bottom, with date above it > foo log 0 #!. invalid prompt command: not prefixed by / +| with-only-2-lines-history-in repeat lines-empty-16 +0 repeat lines-empty-4 +16 | history-lines-0:0:2-in @@ -44,44 +51,57 @@ line 20 on_black,bright_white 20§§-§§-§§ §§ line 21 on_black,bright_red,bold #!. §§:§§:§§ invalid prompt command: not prefixed by / §§ | history-lines-0:0:2-out repeat status-prompt-empty +| with-only-2-lines-history-out + +# check scrolling on history merely 2 lines long won't do anything either +> /window.history.scroll up +repeat with-only-2-lines-history +> /window.history.scroll down +repeat with-only-2-lines-history # further inputs grow log upwards > /foo log 0 #!. invalid prompt command: /foo unknown -> /bar -log 0 #!. invalid prompt command: /bar unknown -| before-1st-scroll-in +| before-first-scroll-in repeat lines-empty-16 +0 repeat lines-empty-2 +16 -| history-lines-0:0:4-in -repeat history-lines-0:0:2 +18 -| history-lines-0:2:4-in -line 20 on_black,bright_red,bold #!. §§:§§:§§ invalid prompt command: /foo unknown §§ -line 21 on_black,bright_red,bold #!. §§:§§:§§ invalid prompt command: /bar unknown §§ -| history-lines-0:2:4-out -| history-lines-0:0:4-out +repeat lines-empty-1 +18 +repeat history-lines-0:0:2 +19 +| history-lines-0:2:3-in +line 21 on_black,bright_red,bold #!. §§:§§:§§ invalid prompt command: /foo unknown §§ +| history-lines-0:2:3-out repeat status-prompt-empty -| before-1st-scroll-out +| before-first-scroll-out -# check scrolling up on short history +# check scrolling up does something on history 3 lines long > /window.history.scroll up -| after-1st-scroll-in | topmost-scroll-in repeat lines-empty-16 +0 repeat lines-empty-4 +16 repeat history-lines-0:0:1 +20 | topmost-scroll-out -line 21 on_black,bright_white,reverse vvv [4] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [2] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty -| after-1st-scroll-out -# check scrolling down on short history +# check scrolling down on 3-lines history > /window.history.scroll down -repeat before-1st-scroll +repeat before-first-scroll + +> /bar +log 0 #!. invalid prompt command: /bar unknown +| history-lines-0:0:4-in +repeat history-lines-0:0:2 +18 +| history-lines-0:2:4-in +repeat history-lines-0:2:3 +20 +line 21 on_black,bright_red,bold #!. §§:§§:§§ invalid prompt command: /bar unknown §§ +| history-lines-0:2:4-out +| history-lines-0:0:4-out # check history growing below up-scroll > /window.history.scroll up -repeat after-1st-scroll +repeat topmost-scroll +0 +line 21 on_black,bright_white,reverse vvv [3] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +repeat status-prompt-empty > /help log 0 #.. commands available in this window: log 0 #.. /connect HOST_PORT [NICKNAME_PW] [REALNAME_USERNAME] @@ -97,7 +117,7 @@ log 0 #.. /window.prompt.move_cursor DIRECTION log 0 #.. /window.prompt.scroll DIRECTION | before-scrolldown-not-to-bottom-in repeat topmost-scroll -line 21 on_black,bright_white,reverse vvv [16] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [15] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty | before-scrolldown-not-to-bottom-out @@ -129,7 +149,7 @@ line 19 on_black,bright_cyan #.. §§:§§:§§ /window TOWARDS§§ line 20 on_black,bright_cyan #.. §§:§§:§§ /window.history.scroll DIRECTION§§ | history-lines-0:11:12-out | history-lines-0:8:12-out -line 21 on_black,bright_white,reverse vvv [5] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [4] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty | after-scrolldown-not-to-bottom-out @@ -200,13 +220,17 @@ line 21 on_black,bright_red,bold #!. §§:§§:§§ invalid prompt command: /7 u repeat status-prompt-empty | at-bottom-before-wrapped-out +# check scroll-down even with long history does nothing +> /window.history.scroll down +repeat at-bottom-before-wrapped + # quick look one scroll up to check single-scroll increase of below-scroll count (when up-scroll not limited, and all lines un-wrapped) > /window.history.scroll up repeat lines-empty-8 repeat history-lines-0:0:8 +8 repeat history-lines-0:8:12 +16 repeat history-lines-0:12:13 +20 -line 21 on_black,bright_white,reverse vvv [12] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [11] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty > /window.history.scroll down repeat at-bottom-before-wrapped @@ -241,7 +265,7 @@ repeat lines-empty-4 +0 repeat lines-empty-1 +4 repeat history-lines-0:0:16 +5 | scrollup-after-wrapped-out -line 21 on_black,bright_white,reverse vvv [11] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [10] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty # check scroll-down fully reversible even if over wrapped @@ -249,14 +273,14 @@ repeat status-prompt-empty repeat at-bottom-after-wrapped > /window.history.scroll up repeat scrollup-after-wrapped -line 21 on_black,bright_white,reverse vvv [11] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [10] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty # check wrapped input only increases below-scroll count by one > /bar_0123456789_0123456789_012345678 log 0 #!. invalid prompt command: /bar_0123456789_0123456789_012345678 unknown repeat scrollup-after-wrapped -line 21 on_black,bright_white,reverse vvv [12] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [11] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty # check scroll-down over wrapped will snap down to bottom of wrapped @@ -266,23 +290,23 @@ repeat history-lines-0:8:16 +2 repeat history-lines-0:16:24 +10 repeat history-lines-0:24:26 +18 repeat history-lines-0:26:27 +20 -line 21 on_black,bright_white,reverse vvv [2] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [1] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty # # check scrolls-up over longer history until top > /window.history.scroll up repeat scrollup-after-wrapped -line 21 on_black,bright_white,reverse vvv [12] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [11] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty > /window.history.scroll up repeat lines-empty-16 +0 repeat history-lines-0:0:4 +16 repeat history-lines-0:4:5 +20 -line 21 on_black,bright_white,reverse vvv [23] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [22] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty > /window.history.scroll up repeat topmost-scroll -line 21 on_black,bright_white,reverse vvv [27] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [26] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-empty # check that triggering creation of new window with new lines adds it to status, with unread-lines count @@ -308,7 +332,7 @@ log 1 >.. CAP LS :302 log 1 >.. USER baz 0 * :bar log 1 >.. NICK :foo repeat topmost-scroll -line 21 on_black,bright_white,reverse vvv [27] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [26] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ line 22 on_black,bright_white :start)==========================================================([(0:1)] (1:19)§§ repeat prompt-empty @@ -367,7 +391,7 @@ repeat prompt-empty # check switch-back to window 0, retaining clearing of window 1's unread-lines count > /window 0 repeat topmost-scroll -line 21 on_black,bright_white,reverse vvv [27] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [26] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-after-first-return | status-prompt-after-first-return-in line 22 on_black,bright_white :start)===============================================================([(0:1)] 1§§ @@ -380,7 +404,7 @@ repeat lines-empty-8 +0 repeat lines-empty-1 +8 repeat history-lines-0:0:8 +9 repeat history-lines-0:8:12 +17 -line 21 on_black,bright_white,reverse vvv [16] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [15] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-after-first-return > /window.history.scroll down | before-first-sight-of-bookmark-in @@ -390,7 +414,7 @@ repeat history-lines-0:8:16 +6 repeat history-lines-0:16:20 +14 repeat history-lines-0:20:22 +18 repeat history-lines-0:22:23 +20 -line 21 on_black,bright_white,reverse vvv [5] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [4] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-after-first-return | before-first-sight-of-bookmark-out > /window.history.scroll down @@ -417,7 +441,7 @@ repeat lines-empty-2 +0 repeat history-lines-0:0:16 +2 repeat history-lines-0:16:18 +18 repeat history-lines-0:18:19 +20 -line 21 on_black,bright_white,reverse vvv [9] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [8] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-after-first-return > /window.history.scroll down repeat first-sight-of-bookmark @@ -513,7 +537,7 @@ repeat history-lines-1:8:12 +14 repeat history-lines-1:12:14 +18 repeat history-lines-1:14:15 +20 | early-win1-upscroll-out -line 21 on_black,bright_white,reverse vvv [11] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [10] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-both-empty > /window.history.scroll down repeat history-lines-1:5:6 +0 @@ -525,7 +549,7 @@ repeat bookmark +14 repeat history-lines-1:19:20 +15 repeat history-lines-1:20:24 +16 repeat history-lines-1:24:25 +20 -line 21 on_black,bright_white,reverse vvv [3] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [2] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-both-empty # check that with new lines left unread, switch away and back into window moves bookmark below newest read line, counts unread lines in status @@ -556,7 +580,7 @@ repeat history-lines-1:20:24 +15 repeat history-lines-1:24:25 +19 repeat bookmark +20 | keep-bookmark-on-outside-growth-test-out -line 21 on_black,bright_white,reverse vvv [3] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [2] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ line 22 on_black,bright_white foo.bar.baz:debug)====================================================(0 [(1:2)]§§ repeat prompt-empty @@ -569,7 +593,7 @@ line 22 on_black,bright_white :start)=========================================== repeat prompt-empty > /window 1 repeat keep-bookmark-on-outside-growth-test -line 21 on_black,bright_white,reverse vvv [5] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [4] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ | status-prompt-on-win1-4-unread-in line 22 on_black,bright_white foo.bar.baz:debug)====================================================(0 [(1:4)]§§ repeat prompt-empty @@ -578,7 +602,7 @@ repeat prompt-empty # check same applies when leaving affected window with bookmark out-of-sight > /window.history.scroll up repeat early-win1-upscroll -line 21 on_black,bright_white,reverse vvv [13] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [12] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ repeat status-prompt-on-win1-4-unread > /window 0 loggedservermsg 0 1 <.. PING :baz @@ -588,7 +612,7 @@ line 22 on_black,bright_white :start)=========================================== repeat prompt-empty > /window 1 repeat early-win1-upscroll -line 21 on_black,bright_white,reverse vvv [15] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [14] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ line 22 on_black,bright_white foo.bar.baz:debug)====================================================(0 [(1:6)]§§ repeat prompt-empty > /window.history.scroll down @@ -598,7 +622,7 @@ repeat history-lines-1:8:16 +3 repeat history-lines-1:16:24 +11 repeat history-lines-1:24:25 +19 repeat bookmark +20 -line 21 on_black,bright_white,reverse vvv [7] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ +line 21 on_black,bright_white,reverse vvv [6] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv§§ line 22 on_black,bright_white foo.bar.baz:debug)====================================================(0 [(1:6)]§§ repeat prompt-empty > /window.history.scroll down