diff options
| author | user <user@clank> | 2026-09-10 16:10:33 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-10 16:10:33 +0200 |
| commit | fca50377124202217c9cf5071d346f1bb30907bb (patch) | |
| tree | 5c3530974568e88ae4794387e5f7d9f88f9fe6ee /src | |
| parent | docs: the tab bar's new shapes, written down (diff) | |
| download | slosh-fca50377124202217c9cf5071d346f1bb30907bb.tar.gz slosh-fca50377124202217c9cf5071d346f1bb30907bb.tar.xz slosh-fca50377124202217c9cf5071d346f1bb30907bb.zip | |
sidebar: framed like a pane, and in compact mode framed *with* it (tab_bar_chrome)
A list of tabs sitting in bare columns beside a framed layout reads as
text that wandered in from outside. So the sidebar gets a frame of its
own -- and in compact mode not its own: its inner vertical lands on the
tab area's ring column, and because the lines are strokes that union
with whatever is already in the cell, the corners become tees without
anyone computing a junction. The sidebar and the layout come out as one
figure, which is the whole point.
Classic mode gets a free-standing frame in the idle frame colour,
rounded or square with everything else. The frame costs a column each
side and a row top and bottom, taken from the sidebar's own budget --
and dropped entirely, before the list is, when the sidebar is too small
to afford it: chrome must not displace what it is chrome for.
The content box (cx/cw) now feeds the labels, the status rows, their
hits and the truncation budget alike, so the frame cannot disagree with
where the clicks land.
Diffstat (limited to 'src')
| -rw-r--r-- | src/app_draw.c | 71 | ||||
| -rw-r--r-- | src/config.c | 6 |
2 files changed, 57 insertions, 20 deletions
diff --git a/src/app_draw.c b/src/app_draw.c index b61d330..7614582 100644 --- a/src/app_draw.c +++ b/src/app_draw.c @@ -2026,35 +2026,66 @@ static void fit_status(char *text, size_t cap, uint16_t budget) { void draw_tab_sidebar(app_t *a, screen_t *s) { bool dragging_pane = a->drag.kind == DRAG_TITLE && a->drag.moved; + bool left = app_tab_bar_side(a) == TAB_BAR_LEFT; uint16_t sw = app_tab_bar_cols(); - uint16_t x = app_tab_bar_side(a) == TAB_BAR_LEFT - ? 0 - : (uint16_t)(s->cols > sw ? s->cols - sw : 0); - /* The strip's row rule, plus the configured air above the list. The pad is - * where the drawing starts, not a layout fact: the panes' rect is the same - * with pad 0 and pad 5, so tuning it repaints without reflowing anyone. */ - uint16_t y = (uint16_t)((CFG.compact ? 0 : CFG.gap) + CFG.tab_bar_pad); + uint16_t x = left ? 0 : (uint16_t)(s->cols > sw ? s->cols - sw : 0); + uint16_t top = CFG.compact ? 0 : CFG.gap; /* the strip's own row rule */ uint16_t limit = (uint16_t)(s->rows > (CFG.status_line ? 1 : 0) ? s->rows - (CFG.status_line ? 1 : 0) : 0); + /* The frame, when asked for: the sidebar framed the way panes are framed, + * so it reads as part of the chrome rather than as text floating beside + * it. Strokes, not glyphs picked by hand -- in compact mode the inner + * vertical lands on the tab area's own ring column and the union makes + * the junctions, so the sidebar and the ring come out as one figure. Too + * few rows or columns to afford the lines, and the frame is dropped + * before the list is: chrome must not displace what it is chrome for. */ + uint16_t cx = x, cw = sw; + bool chrome = CFG.tab_bar_chrome && limit > (uint16_t)(top + 3) && sw >= 6; + if (chrome) { + rect_t in; + in.y = (uint16_t)(top + 1); + in.h = (uint16_t)(limit - top - 2); + if (CFG.compact) { + /* One vertical of our own (the screen edge); the other is the ring's. */ + in.x = left ? 1 : x; + in.w = (uint16_t)(sw - 1); + cx = in.x; + cw = (uint16_t)(sw - 1); + } else { + in.x = (uint16_t)(x + 1); + in.w = (uint16_t)(sw - 2); + cx = in.x; + cw = (uint16_t)(sw - 2); + } + stroke_ring(s, in, FRAME_IDLE, 0, true); + limit--; /* the bottom line's row */ + } + + /* The strip's row rule, plus the frame's top line, plus the configured air + * above the list. The pad is where the drawing starts, not a layout fact: + * the panes' rect is the same with pad 0 and pad 5, so tuning it repaints + * without reflowing anyone. */ + uint16_t y = (uint16_t)(top + (chrome ? 1 : 0) + CFG.tab_bar_pad); + /* Bottom rows first: what the horizontal strip keeps on its right. Indented - * one cell to sit under the labels' own leading space -- at x exactly, the - * count reads flush against the screen edge on the left and against the - * compact ring's corner on the right. */ + * one cell to sit under the labels' own leading space -- at cx exactly, the + * count reads flush against whatever the column starts with: the screen + * edge, the compact ring's corner, or the frame's own line. */ char info[64]; size_t np = app_pane_count(a); snprintf(info, sizeof info, "%zu pane%s", np, np == 1 ? "" : "s"); - if (limit > y && (uint16_t)(strlen(info) + 1) <= sw) { + if (limit > y && (uint16_t)(strlen(info) + 1) <= cw) { limit--; - screen_text(s, (uint16_t)(x + 1), limit, info, TAB_COUNT, NO_COLOR, 0); + screen_text(s, (uint16_t)(cx + 1), limit, info, TAB_COUNT, NO_COLOR, 0); } if (a->prefix) { /* the prefix is a mode: say so, and say which key */ char pfx[24]; config_chord_name(CFG.prefix_key, CFG.prefix_mods, pfx, sizeof pfx); - if (limit > y && (uint16_t)(cells(pfx) + 1) <= sw) { + if (limit > y && (uint16_t)(cells(pfx) + 1) <= cw) { limit--; - screen_text(s, (uint16_t)(x + 1), limit, pfx, PREFIX_FG, PREFIX_BG, + screen_text(s, (uint16_t)(cx + 1), limit, pfx, PREFIX_FG, PREFIX_BG, ATTR_BOLD); } } @@ -2062,7 +2093,7 @@ 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 */ for (size_t i = 0; i < a->ntabs && y < limit; i++) { - draw_tab_cell(a, s, i, x, y, sw, dragging_pane); + draw_tab_cell(a, s, i, cx, y, cw, dragging_pane); y++; if (!cap) continue; @@ -2078,24 +2109,24 @@ void draw_tab_sidebar(app_t *a, screen_t *s) { snprintf(text, sizeof text, " \u2026"); } else { snprintf(text, sizeof text, " %s", ts.row[j].text); - fit_status(text, sizeof text, sw); + fit_status(text, sizeof text, cw); } /* Dim like the pane count: these are ambience until pointed at. The * hover brightening doubles as the affordance that the row is a door; * the hint under the pointer says where it leads. */ - bool hot = !more && ptr_on(a, x, y, sw, 1); - screen_text(s, x, y, text, hot ? TAB_HOVER : TAB_COUNT, NO_COLOR, + bool hot = !more && ptr_on(a, cx, y, cw, 1); + screen_text(s, cx, y, text, hot ? TAB_HOVER : TAB_COUNT, NO_COLOR, hot ? ATTR_BOLD : 0); if (!more) { char act[24]; snprintf(act, sizeof act, "find:%u", ts.row[j].pane); - hit_add(&s->hits, x, y, sw, 1, act); + hit_add(&s->hits, cx, y, cw, 1, act); } } } if (cells(CFG.newtab_mark) && y < limit) - draw_newtab_button(a, s, x, y, dragging_pane); + draw_newtab_button(a, s, cx, y, dragging_pane); } /* The space between two of a split's children, or false if they are flush. diff --git a/src/config.c b/src/config.c index 48442e0..b5cdcc3 100644 --- a/src/config.c +++ b/src/config.c @@ -930,6 +930,7 @@ void config_defaults(config_t *c) { /* Three is enough for "the build, the tests, and one more thing" without a * chatty tab pushing the list off the bottom. */ c->tab_bar_status = 3; + c->tab_bar_chrome = true; c->status_line = true; /* Deliberately wider than the panes' own margin (gap * gap_aspect = 2), so * the strip and the line read as chrome sitting outside the layout rather @@ -1637,6 +1638,8 @@ char *config_render(const config_t *c) { cb_add(&b, "tab_bar_status %u // pane status rows under each tab, 0 = off\n", c->tab_bar_status); + cb_add(&b, "tab_bar_chrome %s // frame the sidebar like a pane\n", + yesno(c->tab_bar_chrome)); cb_add(&b, "status_line %s // the line along the bottom\n", yesno(c->status_line)); cb_add(&b, "status_pad %u\n", c->status_pad); @@ -1945,6 +1948,7 @@ static const char *const KNOWN_TOP[] = { "status_bar", "status_line", "status_pad", + "tab_bar_chrome", "tab_bar_pad", "tab_bar_side", "tab_bar_status", @@ -2163,6 +2167,8 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, (uint16_t)kdl_arg_int(kdl_child(root, "tab_bar_pad"), 0, c->tab_bar_pad); c->tab_bar_status = (uint16_t)kdl_arg_int(kdl_child(root, "tab_bar_status"), 0, c->tab_bar_status); + c->tab_bar_chrome = + kdl_arg_bool(kdl_child(root, "tab_bar_chrome"), 0, c->tab_bar_chrome); c->status_line = kdl_arg_bool(kdl_child(root, "status_line"), 0, c->status_line); c->status_pad = |
