aboutsummaryrefslogtreecommitdiffstats
path: root/src/app_session.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-29 14:26:28 +0200
committeruser <user@clank>2026-09-29 14:26:28 +0200
commitca1b5bdf5bf7b5ff01ab515846802b3487599d3a (patch)
treea047fc09bccbe077b98acd0bf2648c2e02616b04 /src/app_session.c
parentpanes: read and write a pane by id, without taking focus (diff)
downloadslosh-ca1b5bdf5bf7b5ff01ab515846802b3487599d3a.tar.gz
slosh-ca1b5bdf5bf7b5ff01ab515846802b3487599d3a.tar.xz
slosh-ca1b5bdf5bf7b5ff01ab515846802b3487599d3a.zip
layouts, moves: build panes without pulling the view to them
Applying a layout landed on the first tab it made, and moving a pane into a tab took focus there. For a program building a pane in the background that is two failures at once: it yanks the view off the human mid-sentence, and it leaves the *next* caller to read focus as "where I am" and put its work in a stranger's tab. `apply-layout focus:false` builds the tabs and keeps looking at whatever was on screen; a layout naming an `active` tab still wins, because that is a statement about where to be. `move-pane` gains `beside` (which pane in the destination to split, rather than whatever it has focused) and `focus:false` -- the two things a caller placing a pane next to *itself* needs. That uncovered a sizing bug that was unreachable before: a pane's size comes from a layout pass, and a tab nobody has looked at never had one, so a pane built in the background stayed 1x1 and its program drew into a single cell. The jump is what used to size it. layout_tab() gives a background tab a real pass, borrowing the tab cursor for its length -- nothing composes, reads input or draws a frame in between.
Diffstat (limited to 'src/app_session.c')
-rw-r--r--src/app_session.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/src/app_session.c b/src/app_session.c
index 716b5d1..8900088 100644
--- a/src/app_session.c
+++ b/src/app_session.c
@@ -147,6 +147,10 @@ static node_t *build_pane(app_t *a, const kdl_node_t *node, const char *cwd,
bool app_apply_layout(app_t *a, const kdl_node_t *root, bool replace,
const char *base, char *err, size_t errcap) {
+ /* Taken and cleared up front, so one request's "keep my view" cannot leak
+ * into the next layout applied for any other reason. */
+ bool keep_view = a->layout_keep_view;
+ a->layout_keep_view = false;
a->restore_tab = (size_t)-1;
const kdl_node_t *lay = kdl_child(root, "layout");
if (!lay) lay = root; /* allow a bare list of tabs */
@@ -227,13 +231,24 @@ bool app_apply_layout(app_t *a, const kdl_node_t *root, bool replace,
a->restore_tab -= before;
if (a->restore_tab != (size_t)-1 && a->restore_tab < a->ntabs)
a->cur = a->restore_tab;
- else
+ else if (!keep_view)
a->cur = replace ? 0 : before;
+ else if (a->cur >= a->ntabs && a->ntabs)
+ a->cur = a->ntabs - 1; /* the view we kept is gone (replace): clamp */
a->restore_tab = (size_t)-1;
layout(a);
+ /* Every tab this layout built gets a pass, on screen or not: a pane's size
+ * comes from a layout pass, and `focus:false` means nobody is going to look
+ * at these and give them one. Without it a background pane runs at 1x1 --
+ * which used to be impossible to hit only because applying a layout always
+ * jumped the view to it. */
+ if (keep_view && !replace)
+ for (size_t i = before; i < a->ntabs; i++) layout_tab(a, i);
return true;
}
+void app_layout_keep_view(app_t *a) { a->layout_keep_view = true; }
+
bool app_apply_layout_text_at(app_t *a, const char *text, bool replace,
const char *base, char *err, size_t errcap) {
kdl_node_t *root = kdl_parse(text, err, errcap);