aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPeter Geissler <peter@haxx.in>2026-09-20 19:49:09 +0200
committerPeter Geissler <peter@haxx.in>2026-09-20 19:49:09 +0200
commit1b70e64fdb82344edb9b56a7374263539ce3fa0d (patch)
treee07a90c051f110303d4c6f71a38033e95b9e97d0 /src
parenttabs: air before the new-tab button, and the option to do without it (diff)
downloadslosh-1b70e64fdb82344edb9b56a7374263539ce3fa0d.tar.gz
slosh-1b70e64fdb82344edb9b56a7374263539ce3fa0d.tar.xz
slosh-1b70e64fdb82344edb9b56a7374263539ce3fa0d.zip
sidebar: a talkative pane can no longer cost you a tab
Rows were spent in tab order until they ran out, so statuses from early tabs simply consumed the rows later tabs needed for their labels. Four tabs of three announcing panes in a sixteen-row terminal left the fourth tab with no row and no hit anywhere on the sidebar -- not clipped, unreachable -- and the new-tab button went with it. The list is the navigation and a status is an annotation on it, so the annotation is what gives way. Before drawing a tab's statuses, reserve a row for each tab still to come and one for the button, and allow the statuses only what is left over. The "and more" ellipsis is measured against that allowance rather than the cap, so a list cut short by the height still says it was cut, the same as one cut short by the setting. The pad is deliberately not reserved: it already gives itself up a cell at a time, which is the same order of surrender -- spacing, then extra statuses, then statuses, never the thing they are about.
Diffstat (limited to 'src')
-rw-r--r--src/app_draw.c35
1 files changed, 31 insertions, 4 deletions
diff --git a/src/app_draw.c b/src/app_draw.c
index 1dabf98..7312f0d 100644
--- a/src/app_draw.c
+++ b/src/app_draw.c
@@ -2119,18 +2119,45 @@ void draw_tab_sidebar(app_t *a, screen_t *s) {
uint16_t cap = CFG.tab_bar_status;
if (cap > 16) cap = 16; /* the collector keeps no more than that */
+ /* A row the button will want, so the statuses cannot spend it. The pad is
+ * not reserved: it already gives itself up a cell at a time. */
+ uint16_t btn_rows =
+ (CFG.newtab_button && cells(CFG.newtab_mark)) ? (uint16_t)1 : (uint16_t)0;
for (size_t i = 0; i < a->ntabs && y < limit; i++) {
draw_tab_cell(a, s, i, cx, y, cw, dragging_pane);
y++;
if (!cap) continue;
+ /* The floor statuses may not dig through: one row for each tab still to
+ * be drawn, plus the button's.
+ *
+ * The list is the navigation and a status is an annotation on it, so a
+ * chatty pane must not be able to cost you a tab. It could: rows were
+ * spent in tab order until they ran out, and four tabs of three talking
+ * panes in a short terminal left the fourth with no row and no hit --
+ * not clipped, *unreachable*, along with the new-tab button. What gives
+ * way instead is the annotation, the same order the strip already uses
+ * when it is short of room: the pad, then the extra statuses, then the
+ * statuses, never the thing they are about. */
+ uint16_t keep = (uint16_t)(a->ntabs - i - 1);
+ uint16_t floor = (uint16_t)(keep + btn_rows) < limit
+ ? (uint16_t)(limit - keep - btn_rows)
+ : 0;
+ uint16_t room = y < floor ? (uint16_t)(floor - y) : 0;
+
struct tabstatus ts = {0};
walk(a->tabs[i].root, tabstatus_cb, &ts);
- size_t show = ts.total > cap ? cap : ts.n;
+ /* Whatever the cap allowed, this tab's share of what is left may be less.
+ * The "and more" row is measured against the smaller of the two, so a
+ * list cut short by the height still says it was cut. */
+ size_t allow = cap < room ? cap : room;
+ size_t show = ts.total > allow ? allow : (ts.n < allow ? ts.n : allow);
for (size_t j = 0; j < show && y < limit; j++, y++) {
- /* The cap's last row is spent on saying there was more, rather than on
- * one more status pretending the list is complete. */
- bool more = ts.total > cap && j == (size_t)(cap - 1);
+ /* The allowance's last row is spent on saying there was more, rather
+ * than on one more status pretending the list is complete. Measured
+ * against `allow`, not the cap: a tab whose statuses were cut by the
+ * height rather than by the setting is just as incomplete. */
+ bool more = ts.total > allow && j == allow - 1;
char text[112];
/* One leading space, which is the label's own -- a status starts in the
* column its tab's name starts in, rather than indented under it.