aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-14 13:52:42 +0200
committeruser <user@clank>2026-08-14 13:52:42 +0200
commitc7c8111bb3d20acc926fa28db6bccc6752a5e87e (patch)
tree450cdfc188c1128dd53eb5203a7efe15b89345ba /src/app.c
parentsl0ppty: terminal shaders — a colour pass over pane contents (diff)
downloadslosh-c7c8111bb3d20acc926fa28db6bccc6752a5e87e.tar.gz
slosh-c7c8111bb3d20acc926fa28db6bccc6752a5e87e.tar.xz
slosh-c7c8111bb3d20acc926fa28db6bccc6752a5e87e.zip
sl0ppty: drag greys the other panes, and dim_unfocused
The first two consumers of the shader pass, and between them they settle how a pane gets one. Dragging a pane by its title now greys every *other* pane, so the one that is moving lifts off the page — which is the thing that made a colour pass worth building. It engages only once the pointer has actually moved, because a press on a title that turns out to be a click would otherwise flash the whole session grey on its way to doing nothing. dim_unfocused fades everything but the focused pane and is off by default: an always-on effect is the kind you stop seeing and start fighting, so it should be asked for. Both are derived every frame rather than attached and cleared. Focus and drags move through far too many paths — hover, click, the finder, a close, a layout apply, any keystroke ending a drag — to keep an attachment in sync with, and a pane left grey by the one path that forgot is a bug that only turns up in front of someone. The rect and the collapsed flag are recomputed every pass for exactly this reason; this is the same rule applied to colour. It also makes the awkward cases free: dropping into the margin, a keystroke ending the drag, or the dragged pane being closed under it all un-grey without anywhere having to say so, and there are tests for each because "it turns off again" is the whole risk. The two policies replace rather than stack. Dragging an unfocused pane while dim_unfocused is on would otherwise give it two reasons to be grey, and they compound into one muddy grey that reads as neither; the drag wins, so the dragged pane is at full colour whatever the focus says. shade_apply no longer caps at SHADE_MAX. That cap was right when the only shaders were the ones a pane had attached, and wrong the moment the session derives one on top of them: a full stack would have silently dropped it. The count is the caller's to bound. The chrome check that mattered was already there: test_drop_target_is_visible samples a frame corner mid-drag, and it still passes, because the pass runs between the contents and the chrome drawn over them.
Diffstat (limited to 'src/app.c')
-rw-r--r--src/app.c44
1 files changed, 39 insertions, 5 deletions
diff --git a/src/app.c b/src/app.c
index ca88d58..cf02474 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1448,18 +1448,52 @@ size_t app_shade_count(app_t *a, uint32_t pane_id) {
return n ? n->nshaders : 0;
}
-/* A pane with no shaders costs nothing: no context is built and no cell is
- * visited, so an unshaded session emits the same bytes it did before. */
+/* The session's opinion about this pane at this moment, as a shader.
+ *
+ * Derived every frame rather than attached and remembered. Focus and drags
+ * move through too many paths — hover, click, the finder, a close, a layout —
+ * to keep an attachment in sync with, and a pane left grey by the one path
+ * that forgot to clear it is a bug that only shows up in front of someone.
+ * The rect and the collapsed flag are recomputed every pass for exactly this
+ * reason; this is the same rule applied to colour. */
+static size_t policy_shader(app_t *a, node_t *n, shader_t *out) {
+ /* Only once the pointer has actually moved: a press that turns out to be a
+ * click would otherwise flash the whole session grey on its way to nothing. */
+ if (a->drag.kind == DRAG_TITLE && a->drag.moved) {
+ /* The pane being dragged keeps its colour and everything else goes grey,
+ * so the one that is moving lifts off the page. This replaces the focus
+ * dimming rather than stacking with it: two reasons to be grey compound
+ * into one muddy grey that reads as neither. */
+ if (n->id == a->drag.src || !CFG.drag_grayscale) return 0;
+ return shader_make(out, "grayscale", (color_t){0}, CFG.drag_grayscale) ? 1
+ : 0;
+ }
+ if (CFG.dim_unfocused && n != cur(a)->focus)
+ return shader_make(out, "dim", (color_t){0}, CFG.dim_unfocused) ? 1 : 0;
+ return 0;
+}
+
+/* A pane with nothing to apply costs nothing: no context is built and no cell
+ * is visited, so an unshaded session emits the same bytes it did before. */
static void shade_leaf(app_t *a, screen_t *s, node_t *n) {
- if (!n->nshaders) return;
+ shader_t chain[SHADE_MAX + 1];
+ size_t nc = 0;
+
+ /* Attached first, policy last: a pane's own colour is the thing the
+ * session's opinion of the moment is then applied to. */
+ for (size_t i = 0; i < n->nshaders && nc < SHADE_MAX; i++)
+ chain[nc++] = n->shaders[i];
+ nc += policy_shader(a, n, &chain[nc]);
+
+ if (!nc) return;
shade_ctx_t base = {
.now_ms = now_ms_(),
.focused = n == cur(a)->focus,
.default_fg = CFG.default_fg,
.default_bg = CFG.default_bg,
};
- shade_apply(s, n->shaders, n->nshaders, n->content.x, n->content.y,
- n->content.w, n->content.h, &base);
+ shade_apply(s, chain, nc, n->content.x, n->content.y, n->content.w,
+ n->content.h, &base);
}
static void draw_node(app_t *a, screen_t *s, node_t *n);