aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-15 00:20:34 +0200
committeruser <user@clank>2026-08-15 00:20:34 +0200
commit7f38aa2205ff5823693565593d8340c5f199d2fe (patch)
tree705bb42bc8dc207d5d11159018b2fa1cec783dcd /src
parentcontrib: the shadertoy presets, usable in sl0ppty (diff)
downloadslosh-7f38aa2205ff5823693565593d8340c5f199d2fe.tar.gz
slosh-7f38aa2205ff5823693565593d8340c5f199d2fe.tar.xz
slosh-7f38aa2205ff5823693565593d8340c5f199d2fe.zip
sl0ppty: colour the states that mean a pane is not live
The shader machinery has been able to do this since it existed, and shipped an opinion about exactly one state. Now it ships three, and the line between those and the rest is the point: a pane that is *not live* gets a default -- dead, suspended, scrolled. In all three the cells are showing something other than a running program's present: output from something that has exited, a pane that never started, or the past. None of that is discoverable by looking unless something says so, which is the whole argument for the feature. a pane that is merely *not the one you are in* does not -- unfocused, and one being dragged. That is ambient contrast, which is a taste, and a taste shipped as a default is how a tool gets a reputation for fighting you. It is one line of config away for anyone who disagrees, and there is now a test asserting we have not quietly changed our minds. So: suspended is greyed and dimmed (inert, still legible -- its contents are the command it is waiting to run), and scrollback takes a 9% wash of the scrollback indicator's own colour, so a theme moves the indicator and the wash together. Checked against phosphor, amber and paper: each gets its own hue rather than a hardcoded one. The other half is the gap this leaves. A collapsed header never reaches the shader pass -- a header is chrome and shaders colour contents (D13) -- so collapsing a tab used to hide precisely the facts the colour exists to show. The header now carries them in words, in the same order the status line ranks them: `exited: status 3`, `not started`, `▲ 12`. tests/test_states.py asserts the line in both directions, including that an unfocused pane is left exactly as it was, and that a flattened tab still says which of its panes never started.
Diffstat (limited to 'src')
-rw-r--r--src/app.c22
-rw-r--r--src/config.c34
2 files changed, 47 insertions, 9 deletions
diff --git a/src/app.c b/src/app.c
index 97c4067..2a057b6 100644
--- a/src/app.c
+++ b/src/app.c
@@ -2332,16 +2332,28 @@ static void draw_collapsed(app_t *a, screen_t *s, node_t *n) {
const char *title = pane_title(leaf->pane);
const char *status = pane_status(leaf->pane);
- /* A dead pane says so here too, in place of whatever its program last asked
- * us to show — which is stale by definition. This row is the only thing a
- * flattened tab draws of a pane, and "it is over" is the one fact you
- * cannot afford to have to open it to discover. The shader pass does not
- * reach a header, so the words have to carry it. */
+ /* A pane that is not live says so here, in place of whatever its program
+ * last asked us to show — which is stale by definition once the program is
+ * gone or was never started.
+ *
+ * This row is the only thing a flattened tab draws of a pane, and the
+ * shader pass deliberately never reaches it: shaders colour *contents*, and
+ * a header is chrome (D13). So the states that get a colour everywhere else
+ * have to be carried by the words here, in the same order the status line
+ * ranks them. Without this, collapsing a tab hides exactly the facts the
+ * colour exists to show. */
bool dead = !pane_alive(leaf->pane) && !pane_suspended(leaf->pane);
char words[64];
if (dead) {
exit_words(leaf->pane, words, sizeof words);
status = words;
+ } else if (pane_suspended(leaf->pane)) {
+ status = "not started";
+ } else if (pane_scrolled(leaf->pane)) {
+ uint32_t above = 0, total = 0;
+ pane_scroll_pos(leaf->pane, &above, &total);
+ snprintf(words, sizeof words, "\u25b2 %u", total > above ? total - above : 0);
+ status = words;
}
char line[256];
/* A space on each side, always. A title welded to the rule beside it reads
diff --git a/src/config.c b/src/config.c
index e06e25a..9ebb202 100644
--- a/src/config.c
+++ b/src/config.c
@@ -245,15 +245,33 @@ void config_defaults(config_t *c) {
shader_make(&c->state_shaders[PSTATE_DROP_HOVER][1], "dim", (color_t){0},
140);
c->state_n[PSTATE_DROP_HOVER] = 2;
- /* The other state with an opinion, and for the same reason the drag states
- * have one: what is on those cells is no longer live, and colour is the
- * only thing that says so without reading a word. Gentler than the drag
- * greying, because a dead pane is something you still want to read. */
+ /* The states with an opinion, and the line between those and the ones
+ * without it:
+ *
+ * a pane that is *not live* gets one — dead, suspended, scrolled. In all
+ * three the cells are showing something other than a running program's
+ * present: output from something that has exited, a pane that never
+ * started, or the past. You cannot discover any of that by looking unless
+ * something says so, and that is the whole argument for the feature;
+ *
+ * a pane that is merely *not the one you are in* does not — unfocused,
+ * dragging. That is ambient contrast, which is a taste, and shipping a
+ * taste as a default is how a tool gets a reputation for fighting you.
+ *
+ * All three are gentle on purpose. A dead pane, a suspended one and
+ * scrollback are all things you still want to read. */
shader_make(&c->state_shaders[PSTATE_DEAD][0], "grayscale", (color_t){0},
200);
shader_make(&c->state_shaders[PSTATE_DEAD][1], "dim", (color_t){0}, 90);
c->state_n[PSTATE_DEAD] = 2;
+ /* Laid out and never started: inert, and still legible — a suspended pane's
+ * contents are the command it is waiting to run. */
+ shader_make(&c->state_shaders[PSTATE_SUSPENDED][0], "grayscale", (color_t){0},
+ 170);
+ shader_make(&c->state_shaders[PSTATE_SUSPENDED][1], "dim", (color_t){0}, 60);
+ c->state_n[PSTATE_SUSPENDED] = 2;
+
const color_t accent = rgb(0xff, 0x5f, 0xd7);
const color_t ink = rgb(0x14, 0x14, 0x18);
const color_t dim = rgb(0x45, 0x45, 0x4a);
@@ -275,6 +293,14 @@ void config_defaults(config_t *c) {
c->scroll_fg = ink;
c->scroll_bg = accent;
+ /* Looking at the past, said in colour as well as in the ▲ count: a wash of
+ * the same accent the scroll indicator uses, so a theme moves both together
+ * rather than leaving a hardcoded hue nobody can reach. Weak (about 9%) —
+ * it has to survive being read through. Attached here rather than beside
+ * the other states because it is the one that needs the palette. */
+ shader_make(&c->state_shaders[PSTATE_SCROLLED][0], "tint", c->scroll_bg, 22);
+ c->state_n[PSTATE_SCROLLED] = 1;
+
c->header = dim;
c->header_hover = accent;
c->header_hover_title = bright;