aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-10 16:10:33 +0200
committeruser <user@clank>2026-09-10 16:10:33 +0200
commitfca50377124202217c9cf5071d346f1bb30907bb (patch)
tree5c3530974568e88ae4794387e5f7d9f88f9fe6ee /src
parentdocs: the tab bar's new shapes, written down (diff)
downloadslosh-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.c71
-rw-r--r--src/config.c6
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 =