diff options
| author | Peter Geissler <peter@haxx.in> | 2026-09-20 19:49:09 +0200 |
|---|---|---|
| committer | Peter Geissler <peter@haxx.in> | 2026-09-20 19:49:09 +0200 |
| commit | 1b70e64fdb82344edb9b56a7374263539ce3fa0d (patch) | |
| tree | e07a90c051f110303d4c6f71a38033e95b9e97d0 /src | |
| parent | tabs: air before the new-tab button, and the option to do without it (diff) | |
| download | slosh-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.c | 35 |
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. |
