diff options
| author | Ingmar Steen <iksteen@gmail.com> | 2026-09-06 20:34:26 +0200 |
|---|---|---|
| committer | Ingmar Steen <iksteen@gmail.com> | 2026-09-06 20:34:26 +0200 |
| commit | 79a2812808061129bf1449d9f4c3356b36bf0061 (patch) | |
| tree | 773ca521e7432af6e271bfb7f69ff2ab5047f1c9 /src | |
| parent | graphics: place what a float leaves of an image, not nothing (diff) | |
| download | slosh-79a2812808061129bf1449d9f4c3356b36bf0061.tar.gz slosh-79a2812808061129bf1449d9f4c3356b36bf0061.tar.xz slosh-79a2812808061129bf1449d9f4c3356b36bf0061.zip | |
app: cut the modals, toasts and splash out of the images under them
Overlays are painted over the panes and the compositor needs no more
than that: painted later is on top. A placement is not painted, though
-- the client's terminal draws the image over the cells, in whatever
order they arrived -- so an overlay that is only drawn is an overlay an
image sits on top of. The cheatsheet came up under a picture and was
unreadable through it, in the terminal and in the mirror both.
So a frame now records where its overlays landed, and the graphics pass
takes them out of a placement exactly as it takes out a float: the
modal's box (the cheatsheet and the picker wear the same frame), each
toast, and the splash. The list is rebuilt every compose, so a rect that
is not registered again is one that is no longer on screen.
Only the opaque things are registered. The scrim behind a modal tints
what is behind it rather than hiding it, and an image cannot be tinted
by us at all -- so a picture beside the cheatsheet stays bright while
the text around it is pushed back, which is wrong-looking but a great
deal better than the image disappearing while a modal is up.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRz3zwZJvBvzsR9k4cj6dd
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.c | 10 | ||||
| -rw-r--r-- | src/app_draw.c | 13 | ||||
| -rw-r--r-- | src/app_ui.c | 7 |
3 files changed, 27 insertions, 3 deletions
@@ -668,6 +668,15 @@ void draw_status_line(app_t *a, screen_t *s) { screen_text(s, (uint16_t)from, y, middle, middle_fg, NO_COLOR, 0); } +/* Register something painted over the panes, so the graphics pass can cut it + * out of any image underneath it. Opaque things only: a scrim tints what is + * behind it and an image under one should stay on screen, undimmed and wrong + * rather than gone. */ +void app_overlay(app_t *a, rect_t r) { + if (!a || !r.w || !r.h || a->noverlays >= APP_MAX_OVERLAYS) return; + a->overlays[a->noverlays++] = r; +} + void draw_toasts(app_t *a, screen_t *s) { toasts_expire(a); if (!a->ntoasts) return; @@ -706,6 +715,7 @@ void draw_toasts(app_t *a, screen_t *s) { char clipped[160]; snprintf(clipped, sizeof clipped, "%.*s", (int)w, line); screen_text(s, x, y, clipped, TOAST_FG, TOAST_BG, ATTR_BOLD); + app_overlay(a, (rect_t){x, y, (uint16_t)cells(clipped), 1}); } } diff --git a/src/app_draw.c b/src/app_draw.c index c47a253..495add6 100644 --- a/src/app_draw.c +++ b/src/app_draw.c @@ -2520,11 +2520,16 @@ static void gfx_leaf_cb(node_t *n, void *ud) { if (n->hidden || n->collapsed) return; /* not drawn: images included */ /* Above this leaf: every float for a tiled pane, the higher-raised for a * float — the same order draw_floats paints in, so an image is clipped by - * exactly what its cells are covered by. */ - rect_t occ[64]; + * exactly what its cells are covered by. Then the frame's overlays, which + * are above the floats too: the cheatsheet, a toast, the splash. They are + * drawn as cells and a placement is not, so without this the terminal puts + * the picture back over the top of them -- the cheatsheet appearing under + * an image is what asked for this. */ + rect_t occ[64 + APP_MAX_OVERLAYS]; size_t k = 0; for (size_t i = 0; i < w->n; i++) if (!n->floating || w->raised[i] > n->raised) occ[k++] = w->occ[i]; + for (size_t i = 0; i < w->a->noverlays; i++) occ[k++] = w->a->overlays[i]; struct gfx_ctx ctx = {.a = w->a, .out = w->out, .leaf = n, @@ -2817,6 +2822,10 @@ void draw_splash(app_t *a, screen_t *s) { uint16_t x0 = (uint16_t)((s->cols - bw) / 2); uint16_t y0 = (uint16_t)((s->rows - bh) / 2); + /* A cleared backdrop, so the logo reads over whatever a pane put there -- + * including an image, which is cleared by being cut out of its placement + * rather than by writing cells over it. */ + app_overlay(a, (rect_t){x0, y0, bw, bh}); /* A cleared backdrop, so the logo reads over whatever a pane put there. */ char blank[512]; size_t nb = bw < sizeof blank - 1 ? bw : sizeof blank - 1; diff --git a/src/app_ui.c b/src/app_ui.c index 8daf8f1..21b3529 100644 --- a/src/app_ui.c +++ b/src/app_ui.c @@ -189,10 +189,14 @@ static rect_t modal_frame(app_t *a, screen_t *s, uint16_t w, uint16_t h, uint16_t x1 = (uint16_t)(x + w - 1), y1 = (uint16_t)(y + h - 1); /* Opaque, or the panes underneath read through it: a modal you can see a - * shell prompt through is a modal nobody trusts. */ + * shell prompt through is a modal nobody trusts. Opaque for the cells is + * this loop; opaque for an image under it is the registration, without + * which the terminal draws the picture over the modal and the cheatsheet + * reads through a screenshot. */ for (uint16_t yy = y; yy < y + h; yy++) for (uint16_t xx = x; xx < x + w; xx++) screen_text(s, xx, yy, " ", NO_COLOR, MODAL_BG, 0); + app_overlay(a, (rect_t){x, y, w, h}); const char *tl = CFG.rounded ? "\u256d" : "\u250c", *tr = CFG.rounded ? "\u256e" : "\u2510"; @@ -993,6 +997,7 @@ static void drop_render_cb(node_t *n, void *ud) { void app_compose(app_t *a, screen_t *s) { screen_clear(s); /* every frame starts blank: no ghosts in the gap ring */ hit_reset(&s->hits); + a->noverlays = 0; /* re-registered by whatever draws over the panes below */ s->cursor_visible = false; a->painted = s; /* Re-derived by the shader passes below, every frame, from what they ran. */ |
