diff options
| author | user <user@clank> | 2026-09-10 16:20:47 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-10 16:20:47 +0200 |
| commit | a573c2b72486f88853bfc59ac0249578bd3aa548 (patch) | |
| tree | 9b2298090e175eaa9775a1810e3ea6c72649a294 | |
| parent | sidebar: tab_bar_pad back to 0, now that the frame is the separation (diff) | |
| download | slosh-a573c2b72486f88853bfc59ac0249578bd3aa548.tar.gz slosh-a573c2b72486f88853bfc59ac0249578bd3aa548.tar.xz slosh-a573c2b72486f88853bfc59ac0249578bd3aa548.zip | |
sidebar: the new-tab button says what it is, given a row to say it on
The mark went bare because in the strip it sits at the end of a row of
tabs, where the company it keeps says what it does and columns are
scarce. A sidebar row is neither: it is a row of its own with columns
to spare, and a lone glyph on it reads as decoration -- the hint under
the pointer cannot help, since you have to suspect a thing is a button
before you hover it.
So with the room it reads '+ new tab', in the word hint_for already
gives this action rather than a second name for the same thing, and
falls back to the bare mark when the sidebar is too narrow. The strip
is untouched. The row fills and hits like a tab's, so hover and the
drop-target fill cover the row rather than the word on it.
| -rw-r--r-- | docs/panes.md | 3 | ||||
| -rw-r--r-- | src/app_draw.c | 53 | ||||
| -rw-r--r-- | tests/test_tab_bar.py | 45 |
3 files changed, 84 insertions, 17 deletions
diff --git a/docs/panes.md b/docs/panes.md index 329e017..ab9bd08 100644 --- a/docs/panes.md +++ b/docs/panes.md @@ -62,7 +62,8 @@ tab_bar_chrome true // frame it like a pane A **sidebar lists one tab per row** down the chosen edge, and the panes trade the strip's row for its columns. Everything the strip does travels with it: click to switch, double-click to rename in place, drag a pane onto a tab or -onto the `+`, drag tabs to reorder. The pane count and the prefix badge move +onto the `+` — which, given a row of its own and the columns to spare, spells +itself out as `+ new tab`. The pane count and the prefix badge move to the sidebar's bottom rows; the [status line](config.md) along the bottom keeps the full width. A terminal too narrow to give up the columns falls back to the top row until it grows. diff --git a/src/app_draw.c b/src/app_draw.c index 7614582..54b8950 100644 --- a/src/app_draw.c +++ b/src/app_draw.c @@ -1903,29 +1903,50 @@ static uint16_t draw_tab_cell(app_t *a, screen_t *s, size_t i, uint16_t x, return hit_w; } -/* A bare mark, spaced like the frame's own buttons rather than spelled out. - * It used to read "+tab", because a pane frame carried a "+" for splitting - * and two verbs that look identical is a UI bug the fork shipped. That "+" - * went when the border became the button, so the collision it was avoiding - * no longer exists and the word was left explaining itself to nobody. +/* In the strip, a bare mark, spaced like the frame's own buttons rather than + * spelled out. It used to read "+tab", because a pane frame carried a "+" for + * splitting and two verbs that look identical is a UI bug the fork shipped. + * That "+" went when the border became the button, so the collision it was + * avoiding no longer exists and the word was left explaining itself to + * nobody: in the strip the mark sits at the end of a row of tabs, where what + * it does is plain from the company it keeps. * - * Padded to three cells for the same reason the frame's buttons are: a - * one-cell target is a thing you miss with a mouse. What it does is said by - * the hint under the pointer, which is where every other one-character - * affordance here says it. */ + * A sidebar row is not that. It is a row of its own with columns to spare, + * and a lone glyph on it reads as decoration rather than as a button -- the + * hint under the pointer cannot rescue it either, because you have to suspect + * a thing is a button before you hover it. So given the room it says the + * word, and the word is the one `hint_for` already gives this action rather + * than a second name for the same thing. + * + * `max_w` is the row to fill; 0 is the strip's natural width. Three cells at + * minimum for the same reason the frame's buttons are: a one-cell target is a + * thing you miss with a mouse. */ static void draw_newtab_button(app_t *a, screen_t *s, uint16_t x, uint16_t y, - bool dragging_pane) { - char btn[24]; - snprintf(btn, sizeof btn, " %s ", CFG.newtab_mark); + uint16_t max_w, bool dragging_pane) { + char btn[64]; + if (max_w && (uint16_t)(cells(CFG.newtab_mark) + 10) <= max_w) + snprintf(btn, sizeof btn, " %s new tab ", CFG.newtab_mark); + else + snprintf(btn, sizeof btn, " %s ", CFG.newtab_mark); + /* Padded out on a sidebar row for the same reason a tab's label is: the + * hover and the drop fill are the row, not the word sitting on it. */ + if (max_w) { + size_t len = strlen(btn); + while (cells(btn) < max_w && len + 1 < sizeof btn) { + btn[len++] = ' '; + btn[len] = 0; + } + } uint16_t w = screen_text(s, x, y, btn, TAB_IDLE, NO_COLOR, 0); - if (ptr_on(a, x, y, w, 1)) + uint16_t hit_w = max_w ? max_w : w; + if (ptr_on(a, x, y, hit_w, 1)) screen_text(s, x, y, btn, TAB_HOVER, NO_COLOR, ATTR_BOLD); /* The button that makes a tab is also a place to drop a pane into one. */ if (dragging_pane) screen_text(s, x, y, btn, a->drag.new_tab_target ? TAB_ACTIVE_HOVER_FG : DROP_C, a->drag.new_tab_target ? DROP_C : NO_COLOR, ATTR_BOLD); - hit_add(&s->hits, x, y, w, 1, "newtab"); + hit_add(&s->hits, x, y, hit_w, 1, "newtab"); } void draw_tab_strip(app_t *a, screen_t *s) { @@ -1968,7 +1989,7 @@ void draw_tab_strip(app_t *a, screen_t *s) { { uint16_t bw = (uint16_t)(cells(CFG.newtab_mark) + 2); if (bw > 2 && x + bw <= right) - draw_newtab_button(a, s, x, y, dragging_pane); + draw_newtab_button(a, s, x, y, 0, dragging_pane); } } @@ -2126,7 +2147,7 @@ void draw_tab_sidebar(app_t *a, screen_t *s) { } if (cells(CFG.newtab_mark) && y < limit) - draw_newtab_button(a, s, cx, y, dragging_pane); + draw_newtab_button(a, s, cx, y, cw, dragging_pane); } /* The space between two of a split's children, or false if they are flush. diff --git a/tests/test_tab_bar.py b/tests/test_tab_bar.py index eab817a..ce3ad9b 100644 --- a/tests/test_tab_bar.py +++ b/tests/test_tab_bar.py @@ -303,6 +303,48 @@ def test_chrome_false_gives_the_columns_back(): ) +def test_the_sidebar_button_says_what_it_does(): + with Session(SH, cols=90, rows=20, config=LEFT) as s: + s.settle(40) + snap = s.snapshot() + row = _row(snap, Y0 + 1) + check("a row of its own spells the verb out", "new tab" in row, repr(row)) + check( + "and the whole row is the target, like a tab's", + snap.hit_at(CX + CW - 1, Y0 + 1) == "newtab", + str(snap.hit_at(CX + CW - 1, Y0 + 1)), + ) + + +def test_a_narrow_sidebar_keeps_the_bare_mark(): + narrow = _cfg('tab_bar_side "left"\ntab_bar_width 10\n') + with Session(SH, cols=90, rows=20, config=narrow) as s: + s.settle(40) + snap = s.snapshot() + row = snap.line(Y0 + 1)[CX : CX + 8] + check( + "no room for the word, so it is not said", "new tab" not in row, repr(row) + ) + check( + "...but the button is still there", + snap.hit_at(CX, Y0 + 1) == "newtab", + str(snap.hit_at(CX, Y0 + 1)), + ) + + +def test_the_strip_keeps_its_bare_mark(): + """The word is a sidebar's affordance, not a new spelling everywhere: in + the strip the mark sits at the end of a row of tabs, where columns are + scarce and the company it keeps says what it is.""" + with Session(SH, cols=90, rows=20) as s: + s.settle(40) + check( + "the strip is unchanged", + "new tab" not in s.snapshot().line(1), + repr(s.snapshot().line(1)), + ) + + def test_narrow_terminal_falls_back_to_top(): # 40 < width + min_pane cols + 4: the sidebar would leave no room for the # pane it is chrome for, so the strip goes back to the top row. @@ -347,6 +389,9 @@ if __name__ == "__main__": test_the_sidebar_is_framed_like_a_pane() test_compact_shares_its_lines_with_the_ring() test_chrome_false_gives_the_columns_back() + test_the_sidebar_button_says_what_it_does() + test_a_narrow_sidebar_keeps_the_bare_mark() + test_the_strip_keeps_its_bare_mark() test_narrow_terminal_falls_back_to_top() test_growing_back_restores_the_sidebar() sys.exit(report()) |
