From 7810f8abf3e2dae2ff59126e088936cad5a3852e Mon Sep 17 00:00:00 2001 From: Michael Grant Date: Wed, 30 Sep 2026 23:10:40 +0100 Subject: [PATCH] Zoomed panes are now like full screen floating panes. --- cmd-break-pane.c | 9 +- cmd-display-menu.c | 7 +- cmd-join-pane.c | 145 ++++++----- cmd-kill-pane.c | 1 - cmd-resize-pane.c | 15 +- cmd-rotate-window.c | 144 ++++++----- cmd-select-pane.c | 35 ++- cmd-split-window.c | 21 -- cmd-swap-pane.c | 29 ++- cmd-switch-client.c | 10 +- format.c | 71 +----- layout-custom.c | 66 +++-- layout.c | 129 ++++++---- mode-tree.c | 6 +- options-table.c | 13 + regress/floating-pane-zoom.sh | 18 +- regress/modal-pane.sh | 25 +- regress/pane-ops.sh | 31 ++- regress/window-ops.sh | 18 ++ resize.c | 12 +- screen-redraw.c | 4 +- server-client.c | 4 +- server-fn.c | 7 +- spawn.c | 13 +- tmux.h | 22 +- window-panes.c | 45 +--- window-switch.c | 7 +- window-visible.c | 5 +- window.c | 461 ++++++++++++++++++++-------------- 29 files changed, 752 insertions(+), 621 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index f2f1aff25..d0b60cfe3 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -59,10 +59,6 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args, cmdq_error(item, "pane is already floating"); return (CMD_RETURN_ERROR); } - if (w->flags & WINDOW_ZOOMED) { - cmdq_error(item, "can't float a pane while window is zoomed"); - return (CMD_RETURN_ERROR); - } if (layout_floating_args_parse(item, args, lines, w, fg, &cause) != 0) { cmdq_error(item, "failed to float pane: %s", cause); @@ -73,8 +69,7 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args, layout_set_size(lc, fg->sx, fg->sy, fg->xoff, fg->yoff); lc->flags |= LAYOUT_CELL_FLOATING; - TAILQ_REMOVE(&w->z_index, wp, zentry); - TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); + window_raise_pane(wp); if (!args_has(args, 'd')) window_set_active_pane(w, wp, 1); @@ -125,8 +120,6 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) if (idx == -1) return (CMD_RETURN_ERROR); } - server_unzoom_window(w); - if (window_count_panes(w, 1) == 1) { if (server_link_window(src_s, wl, dst_s, idx, 0, !args_has(args, 'd'), &cause) != 0) { diff --git a/cmd-display-menu.c b/cmd-display-menu.c index f9627ca67..ff4e4310d 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -470,12 +470,9 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) lg.yoff++; } - window_push_zoom(w, 0, 1); lc = layout_floating_pane(w, wp, &lg); - if (lc == NULL) { - window_pop_zoom(w); + if (lc == NULL) goto out; - } sc.item = item; sc.s = s; @@ -501,10 +498,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "create pane failed: %s", cause); free(cause); cause = NULL; - window_pop_zoom(w); goto fail; } - window_pop_zoom(w); new_wp->flags |= PANE_CAPTUREALLKEYS; if (!args_has(args, 'E')) new_wp->flags |= PANE_CLOSEONCANCEL; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 44d79cafb..d9bcaee8b 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -64,6 +64,59 @@ const struct cmd_entry cmd_move_pane_entry = { .exec = cmd_join_pane_exec }; +/* Does a position name change the stacking order rather than the geometry? */ +static int +cmd_join_pane_is_stacking(const char *position) +{ + return (strcmp(position, "front") == 0 || + strcmp(position, "back") == 0 || + strcmp(position, "forward") == 0 || + strcmp(position, "backward") == 0 || + strcmp(position, "forward-loop") == 0 || + strcmp(position, "backward-loop") == 0); +} + +/* Get the stacking group of a pane: modal, always on top, or other. */ +static int +cmd_join_pane_group(struct window_pane *wp) +{ + if (wp == wp->window->modal) + return (0); + if (wp->flags & PANE_FLOATOVERZOOM) + return (1); + return (2); +} + +/* Get the visible pane just in front of a pane in its group, if any. */ +static struct window_pane * +cmd_join_pane_forward(struct window_pane *wp) +{ + struct window_pane *owp = wp; + + do { + owp = TAILQ_PREV(owp, window_panes_zindex, zentry); + } while (owp != NULL && !window_pane_is_visible(owp)); + if (owp == NULL || cmd_join_pane_group(owp) != cmd_join_pane_group(wp)) + return (NULL); + return (owp); +} + +/* Get the visible pane just behind a pane in its group, if any. */ +static struct window_pane * +cmd_join_pane_backward(struct window_pane *wp) +{ + struct window_pane *owp = wp; + + do { + owp = TAILQ_NEXT(owp, zentry); + } while (owp != NULL && !window_pane_is_visible(owp)); + if (owp == NULL || + !window_pane_is_raised(owp) || + cmd_join_pane_group(owp) != cmd_join_pane_group(wp)) + return (NULL); + return (owp); +} + static enum cmd_retval cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl, struct window_pane *wp, const char *position) @@ -128,62 +181,35 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl, xoff = (3 * wx) / 4 - px / 2; yoff = (3 * wy) / 4 - py / 2; } else if (strcmp(position, "front") == 0) { - TAILQ_REMOVE(&w->z_index, wp, zentry); - TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); + window_raise_pane(wp); } else if (strcmp(position, "back") == 0) { - TAILQ_REMOVE(&w->z_index, wp, zentry); - TAILQ_FOREACH(owp, &w->z_index, zentry) { - if (!window_pane_is_floating_with_hidden(owp)) - break; - } - if (owp != NULL) - TAILQ_INSERT_BEFORE(owp, wp, zentry); - else - TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); + window_lower_pane(wp); } else if (strcmp(position, "forward") == 0) { - owp = TAILQ_PREV(wp, window_panes_zindex, zentry); - while (owp != NULL && owp->layout_cell == NULL) - owp = TAILQ_PREV(owp, window_panes_zindex, zentry); + owp = cmd_join_pane_forward(wp); if (owp != NULL) { TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_INSERT_BEFORE(owp, wp, zentry); } } else if (strcmp(position, "backward") == 0) { - owp = TAILQ_NEXT(wp, zentry); - while (owp != NULL && owp->layout_cell == NULL) - owp = TAILQ_NEXT(owp, zentry); - if (owp != NULL && window_pane_is_floating(owp)) { + owp = cmd_join_pane_backward(wp); + if (owp != NULL) { TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry); } } else if (strcmp(position, "forward-loop") == 0) { - owp = TAILQ_PREV(wp, window_panes_zindex, zentry); - while (owp != NULL && owp->layout_cell == NULL) - owp = TAILQ_PREV(owp, window_panes_zindex, zentry); - TAILQ_REMOVE(&w->z_index, wp, zentry); - if (owp != NULL) + owp = cmd_join_pane_forward(wp); + if (owp != NULL) { + TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_INSERT_BEFORE(owp, wp, zentry); - else { - TAILQ_FOREACH(owp, &w->z_index, zentry) { - if (!window_pane_is_floating_with_hidden(owp)) - break; - } - if (owp != NULL) - TAILQ_INSERT_BEFORE(owp, wp, zentry); - else - TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); - } + } else + window_lower_pane(wp); } else if (strcmp(position, "backward-loop") == 0) { - owp = TAILQ_NEXT(wp, zentry); - while (owp != NULL && owp->layout_cell == NULL) - owp = TAILQ_NEXT(owp, zentry); - if (owp != NULL && window_pane_is_floating(owp)) { + owp = cmd_join_pane_backward(wp); + if (owp != NULL) { TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry); - } else { - TAILQ_REMOVE(&w->z_index, wp, zentry); - TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); - } + } else + window_raise_pane(wp); } else { cmdq_error(item, "unknown position: %s", position); return (CMD_RETURN_ERROR); @@ -345,6 +371,7 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl, struct window_pane *owp; const char *errstr; u_int n, z; + int group; z = strtonum(s, 0, UINT_MAX, &errstr); if (errstr != NULL) { @@ -353,11 +380,16 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl, } TAILQ_REMOVE(&w->z_index, wp, zentry); + /* The pane stays in front of or behind panes of the other groups. */ + group = cmd_join_pane_group(wp); n = 0; TAILQ_FOREACH(owp, &w->z_index, zentry) { - if (!window_pane_is_floating_with_hidden(owp)) + if (cmd_join_pane_group(owp) < group) + continue; + if (cmd_join_pane_group(owp) > group || + !window_pane_is_raised(owp)) break; - if (owp->layout_cell == NULL) + if (!window_pane_is_visible(owp)) continue; if (n >= z) break; @@ -386,10 +418,6 @@ cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w, cmdq_error(item, "pane is not floating"); return (CMD_RETURN_ERROR); } - if (w->flags & WINDOW_ZOOMED) { - cmdq_error(item, "can't tile a pane while window is zoomed"); - return (CMD_RETURN_ERROR); - } lc->fg.sx = lc->g.sx; lc->fg.sy = lc->g.sy; @@ -402,8 +430,10 @@ cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w, } lc->flags &= ~LAYOUT_CELL_FLOATING; - TAILQ_REMOVE(&w->z_index, wp, zentry); - TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); + if (~wp->flags & PANE_ZOOMED) { + TAILQ_REMOVE(&w->z_index, wp, zentry); + TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); + } if (!args_has(args, 'd')) window_set_active_pane(w, wp, 1); @@ -453,6 +483,12 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "pane is not floating"); return (CMD_RETURN_ERROR); } + /* Moving unzooms a zoomed pane; reordering does not. */ + s = args_get(args, 'P'); + if ((dst_wp->flags & PANE_ZOOMED) && + !args_has(args, 'z') && + (s == NULL || !cmd_join_pane_is_stacking(s))) + window_unzoom_pane(dst_wp, 1); if ((s = args_get(args, 'P')) != NULL) return (cmd_join_pane_place(item, dst_wl, dst_wp, s)); if ((s = args_get(args, 'z')) != NULL) @@ -470,9 +506,6 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - server_unzoom_window(dst_w); - server_unzoom_window(src_w); - if (src_wp == dst_wp) { if (window_pane_is_floating(src_wp)) return (cmd_join_pane_tile(item, args, src_w, src_wp)); @@ -504,13 +537,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) src_wp->window = dst_w; options_set_parent(src_wp->options, dst_w->options); src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); - if (flags & SPAWN_BEFORE) { + if (flags & SPAWN_BEFORE) TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry); - TAILQ_INSERT_BEFORE(dst_wp, src_wp, zentry); - } else { + else TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); - TAILQ_INSERT_AFTER(&dst_w->z_index, dst_wp, src_wp, zentry); - } + TAILQ_INSERT_TAIL(&dst_w->z_index, src_wp, zentry); layout_assign_pane(lc, src_wp, 0); colour_palette_from_option(&src_wp->palette, src_wp->options); diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index f634a7cd7..7f0d04f94 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -78,7 +78,6 @@ cmd_kill_pane_all(struct cmdq_item *item, const char *filter) struct window_pane *wp = target->wp; struct window_pane *loopwp, *tmpwp; - server_unzoom_window(wl->window); TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { if (loopwp == wp) continue; diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index c644dd783..fb760b3fd 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -41,8 +41,8 @@ const struct cmd_entry cmd_resize_pane_entry = { .name = "resize-pane", .alias = "resizep", - .args = { "D::L::MR::Tt:U::x:y:Z", 0, 1, NULL }, - .usage = "[-MTZ] [-D lines] [-L columns] [-R columns] [-U lines] " + .args = { "aD::L::MR::Tt:U::x:y:Z", 0, 1, NULL }, + .usage = "[-aMTZ] [-D lines] [-L columns] [-R columns] [-U lines] " "[-x width] [-y height] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -84,16 +84,17 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) return (cmd_resize_pane_mouse_update(self, item)); if (args_has(args, 'Z')) { - if (w->flags & WINDOW_ZOOMED) + if (args_has(args, 'a')) window_unzoom(w, 1); + else if (wp->flags & PANE_ZOOMED) + window_unzoom_pane(wp, 1); else window_zoom(wp); server_redraw_window(w); return (CMD_RETURN_NORMAL); } - if (!window_pane_is_floating(wp)) - server_unzoom_window(w); - lc = wp->layout_cell; /* may have been replaced by unzoom */ + if (wp->flags & PANE_ZOOMED) + window_unzoom_pane(wp, 1); if (args_has(args, 'x')) { x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause); @@ -207,6 +208,8 @@ cmd_resize_pane_mouse_update(__unused struct cmd *self, struct cmdq_item *item) if (wp == NULL || c == NULL || c->session != s) return (CMD_RETURN_NORMAL); + if (wp->flags & PANE_ZOOMED) + return (CMD_RETURN_NORMAL); if (!window_pane_is_floating(wp)) { c->tty.mouse_drag_update = cmd_resize_pane_mouse_resize_tiled; cmd_resize_pane_mouse_resize_tiled(c, &event->m); diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index d62fd0fa4..3e112bbf2 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -40,77 +40,99 @@ const struct cmd_entry cmd_rotate_window_entry = { .exec = cmd_rotate_window_exec }; +struct cmd_rotate_window_slot { + struct layout_cell *lc; + int xoff; + int yoff; + u_int sx; + u_int sy; +}; + static enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct cmd_find_state *current = cmdq_get_current(item); - struct cmd_find_state *target = cmdq_get_target(item); - struct winlink *wl = target->wl; - struct window *w = wl->window; - struct window_pane *wp, *wp2; - struct layout_cell *lc; - u_int sx, sy, xoff, yoff; + struct args *args = cmd_get_args(self); + struct cmd_find_state *current = cmdq_get_current(item); + struct cmd_find_state *target = cmdq_get_target(item); + struct winlink *wl = target->wl; + struct window *w = wl->window; + struct window_pane *wp, *zwp = NULL, **all, **tiled; + struct window_pane **rotated; + struct cmd_rotate_window_slot *slots; + u_int i, j, n = 0, nt = 0; + int active = -1; - window_push_zoom(w, 0, args_has(args, 'Z')); + if (args_has(args, 'Z')) + zwp = window_zoomed_pane(w); - if (args_has(args, 'D')) { - wp = TAILQ_LAST(&w->panes, window_panes); - TAILQ_REMOVE(&w->panes, wp, entry); - TAILQ_INSERT_HEAD(&w->panes, wp, entry); + /* + * Only tiled panes rotate. Floating panes keep their place in the + * list and their cell. + */ + TAILQ_FOREACH(wp, &w->panes, entry) + n++; + all = xcalloc(n, sizeof *all); + tiled = xcalloc(n, sizeof *tiled); + rotated = xcalloc(n, sizeof *rotated); + slots = xcalloc(n, sizeof *slots); - lc = wp->layout_cell; - xoff = wp->xoff; yoff = wp->yoff; - sx = wp->sx; sy = wp->sy; - TAILQ_FOREACH(wp, &w->panes, entry) { - if ((wp2 = TAILQ_NEXT(wp, entry)) == NULL) - break; - wp->layout_cell = wp2->layout_cell; - if (wp->layout_cell != NULL) - wp->layout_cell->wp = wp; - wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; - window_pane_resize(wp, wp2->sx, wp2->sy); - } - wp->layout_cell = lc; - if (wp->layout_cell != NULL) - wp->layout_cell->wp = wp; - wp->xoff = xoff; wp->yoff = yoff; - window_pane_resize(wp, sx, sy); - - if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) - wp = TAILQ_LAST(&w->panes, window_panes); - } else { - wp = TAILQ_FIRST(&w->panes); - TAILQ_REMOVE(&w->panes, wp, entry); - TAILQ_INSERT_TAIL(&w->panes, wp, entry); - - lc = wp->layout_cell; - xoff = wp->xoff; yoff = wp->yoff; - sx = wp->sx; sy = wp->sy; - TAILQ_FOREACH_REVERSE(wp, &w->panes, window_panes, entry) { - if ((wp2 = TAILQ_PREV(wp, window_panes, entry)) == NULL) - break; - wp->layout_cell = wp2->layout_cell; - if (wp->layout_cell != NULL) - wp->layout_cell->wp = wp; - wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; - window_pane_resize(wp, wp2->sx, wp2->sy); - } - wp->layout_cell = lc; - if (wp->layout_cell != NULL) - wp->layout_cell->wp = wp; - wp->xoff = xoff; wp->yoff = yoff; - window_pane_resize(wp, sx, sy); - - if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) - wp = TAILQ_FIRST(&w->panes); + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + all[n++] = wp; + if (!layout_cell_is_tiled(wp->layout_cell)) + continue; + tiled[nt] = wp; + slots[nt].lc = wp->layout_cell; + slots[nt].xoff = wp->xoff; + slots[nt].yoff = wp->yoff; + slots[nt].sx = wp->sx; + slots[nt].sy = wp->sy; + if (wp == w->active) + active = nt; + nt++; + } + for (i = 0; i < nt; i++) { + if (args_has(args, 'D')) + rotated[i] = tiled[(i + nt - 1) % nt]; + else + rotated[i] = tiled[(i + 1) % nt]; } - window_set_active_pane(w, wp, 1); - cmd_find_from_winlink_pane(current, wl, wp, 0); - window_pop_zoom(w); + /* Put the rotated panes back into the tiled places in the list. */ + for (i = 0; i < n; i++) + TAILQ_REMOVE(&w->panes, all[i], entry); + for (i = j = 0; i < n; i++) { + if (layout_cell_is_tiled(all[i]->layout_cell)) + wp = rotated[j++]; + else + wp = all[i]; + TAILQ_INSERT_TAIL(&w->panes, wp, entry); + } + + /* Each rotated pane takes over the cell of the place it moved to. */ + for (i = 0; i < nt; i++) { + wp = rotated[i]; + wp->layout_cell = slots[i].lc; + wp->layout_cell->wp = wp; + wp->xoff = slots[i].xoff; + wp->yoff = slots[i].yoff; + window_pane_resize(wp, slots[i].sx, slots[i].sy); + } + + if (active != -1) { + wp = rotated[active]; + window_set_active_pane(w, wp, 1); + cmd_find_from_winlink_pane(current, wl, wp, 0); + if (zwp != NULL) + window_zoom_move(zwp, wp); + } redraw_invalidate_scene(w); server_redraw_window(w); + free(all); + free(tiled); + free(rotated); + free(slots); + return (CMD_RETURN_NORMAL); } diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e23ca0adc..847b42c0d 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -155,7 +155,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct winlink *wl = target->wl; struct window *w = wl->window; struct session *s = target->s; - struct window_pane *wp = target->wp, *lastwp; + struct window_pane *wp = target->wp, *lastwp, *zwp = NULL; struct options *oo = wp->options; char *title; const char *style; @@ -190,15 +190,17 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) visible = 1; else visible = window_pane_is_visible(lastwp); - if (!visible && window_push_zoom(w, 0, Zflag)) - server_redraw_window(w); + if (!visible && Zflag) + zwp = window_zoomed_pane(w); window_redraw_active_switch(w, lastwp); if (window_set_active_pane(w, lastwp, 1)) { cmd_find_from_winlink(current, wl, 0); cmd_select_pane_redraw(w); } - if (!visible && window_pop_zoom(w)) + if (zwp != NULL) { + window_zoom_move(zwp, lastwp); server_redraw_window(w); + } } return (CMD_RETURN_NORMAL); } @@ -221,23 +223,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'L')) { - window_push_zoom(w, 0, 1); + if (args_has(args, 'L')) wp = window_pane_find_left(wp); - window_pop_zoom(w); - } else if (args_has(args, 'R')) { - window_push_zoom(w, 0, 1); + else if (args_has(args, 'R')) wp = window_pane_find_right(wp); - window_pop_zoom(w); - } else if (args_has(args, 'U')) { - window_push_zoom(w, 0, 1); + else if (args_has(args, 'U')) wp = window_pane_find_up(wp); - window_pop_zoom(w); - } else if (args_has(args, 'D')) { - window_push_zoom(w, 0, 1); + else if (args_has(args, 'D')) wp = window_pane_find_down(wp); - window_pop_zoom(w); - } if (wp == NULL) return (CMD_RETURN_NORMAL); @@ -277,15 +270,17 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) visible = 1; else visible = window_pane_is_visible(wp); - if (!visible && window_push_zoom(w, 0, Zflag)) - server_redraw_window(w); + if (!visible && Zflag) + zwp = window_zoomed_pane(w); window_redraw_active_switch(w, wp); if (window_set_active_pane(w, wp, 1)) cmd_find_from_winlink_pane(current, wl, wp, 0); cmdq_insert_hook(s, item, current, "after-select-pane"); cmd_select_pane_redraw(w); - if (!visible && window_pop_zoom(w)) + if (zwp != NULL) { + window_zoom_move(zwp, wp); server_redraw_window(w); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 641792e2c..7fafaadf9 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -89,7 +89,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state fs; struct key_event *event = cmdq_get_event(item); int input, empty, is_floating, flags = 0; - int restore_zoom = 0; const char *template, *style, *value; char *cause = NULL, *cp, *title; const struct options_table_entry *oe; @@ -97,16 +96,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) enum pane_lines lines; u_int count = args_count(args); - if (window_active_pane_is_over_zoom(w)) - restore_zoom = 1; - if (cmd_get_entry(self) == &cmd_new_pane_entry) is_floating = !args_has(args, 'L'); else { - if (!window_pane_is_visible(wp)) - restore_zoom = 0; - if (!restore_zoom) - window_unzoom(w, 1); is_floating = window_pane_is_floating(wp); flags |= SPAWN_SPLIT; } @@ -143,8 +135,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) flags |= SPAWN_MODAL|SPAWN_FLOATOVERZOOM; if (is_floating && args_has(args, 'A')) flags |= SPAWN_FLOATOVERZOOM; - if ((w->flags & WINDOW_ZOOMED) && (flags & SPAWN_FLOATOVERZOOM)) - restore_zoom = 1; input = args_has(args, 'I'); if (input || (count == 1 && *args_string(args, 0) == '\0')) @@ -180,8 +170,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (cause != NULL) { cmdq_error(item, "%s", cause); free(cause); - if (restore_zoom) - window_pop_zoom(w); return (CMD_RETURN_ERROR); } @@ -288,13 +276,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (~flags & SPAWN_DETACHED) cmd_find_from_winlink_pane(current, wl, new_wp, 0); - if (restore_zoom) { - window_pop_zoom(wp->window); - server_redraw_window(wp->window); - } else if ((~flags & SPAWN_FLOATING) && !args_has(args, 'O')) { - window_pop_zoom(wp->window); - server_redraw_window(wp->window); - } server_redraw_session(s); if (args_has(args, 'M') && is_floating) { @@ -343,8 +324,6 @@ fail: layout_close_pane(new_wp); window_remove_pane(wp->window, new_wp); } - if (restore_zoom || (~flags & SPAWN_FLOATING)) - window_pop_zoom(wp->window); if (sc.argv != NULL) cmd_free_argv(sc.argc, sc.argv); environ_free(sc.environ); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 04e1cc7a9..8ff2aa0ae 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -68,7 +68,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - int src_idx, dst_idx; + int src_idx, dst_idx, src_zoomed, dst_zoomed; dst_w = target->wl->window; dst_wp = target->wp; @@ -82,8 +82,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - if (window_push_zoom(dst_w, 0, args_has(args, 'Z'))) - server_redraw_window(dst_w); + dst_zoomed = (args_has(args, 'Z') && (dst_w->flags & WINDOW_ZOOMED)); if (args_has(args, 'D')) { if (window_pane_is_floating(dst_wp)) { @@ -111,8 +110,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) } } - if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z'))) - server_redraw_window(src_w); + src_zoomed = (args_has(args, 'Z') && (src_w->flags & WINDOW_ZOOMED)); if (src_wp == dst_wp) goto out; @@ -140,6 +138,12 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) else TAILQ_INSERT_AFTER(&dst_w->z_index, tmp_wp, src_wp, zentry); + /* Zoom belongs to the position in the window, like the cell. */ + if ((src_wp->flags ^ dst_wp->flags) & PANE_ZOOMED) { + src_wp->flags ^= PANE_ZOOMED; + dst_wp->flags ^= PANE_ZOOMED; + } + src_lc = src_wp->layout_cell; dst_lc = dst_wp->layout_cell; src_lc->wp = dst_wp; @@ -197,9 +201,16 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) events_fire_window("window-layout-changed", dst_w); out: - if (window_pop_zoom(src_w)) - server_redraw_window(src_w); - if (src_w != dst_w && window_pop_zoom(dst_w)) - server_redraw_window(dst_w); + /* With -Z, leave the active pane zoomed if the window was. */ + if (src_zoomed) { + window_unzoom(src_w, 1); + if (src_w->active != NULL) + window_zoom(src_w->active); + } + if (src_w != dst_w && dst_zoomed) { + window_unzoom(dst_w, 1); + if (dst_w->active != NULL) + window_zoom(dst_w->active); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 44ea43cbc..eff62bca8 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -59,7 +59,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) struct session *s; struct winlink *wl; struct window *w; - struct window_pane *wp; + struct window_pane *wp, *zwp = NULL; const char *tablename; struct key_table *table; struct sort_criteria sort_crit; @@ -143,12 +143,14 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) visible = 1; else visible = window_pane_is_visible(wp); - if (!visible && window_push_zoom(w, 0, Zflag)) - server_redraw_window(w); + if (!visible && Zflag) + zwp = window_zoomed_pane(w); window_redraw_active_switch(w, wp); window_set_active_pane(w, wp, 1); - if (!visible && window_pop_zoom(w)) + if (zwp != NULL) { + window_zoom_move(zwp, wp); server_redraw_window(w); + } } if (wl != NULL) { session_set_current(s, wl); diff --git a/format.c b/format.c index 97bcc4bfc..51fb02dff 100644 --- a/format.c +++ b/format.c @@ -866,10 +866,7 @@ format_cb_window_layout(struct format_tree *ft) if (w == NULL) return (NULL); - if (w->saved_layout_root != NULL) - lcroot = w->saved_layout_root; - else - lcroot = w->layout_root; + lcroot = w->layout_root; if (c != NULL && (c->flags & CLIENT_CONTROL) && @@ -893,7 +890,7 @@ format_cb_window_visible_layout(struct format_tree *ft) (c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_CONTROL_NEWLAYOUTS)) flags |= LAYOUT_CUSTOM_OLD_FORMAT; - return (layout_dump(w, w->layout_root, flags)); + return (layout_dump_visible(w, flags)); } /* Callback for pane_start_command. */ @@ -2706,36 +2703,12 @@ static void * format_cb_pane_unzoomed_height(struct format_tree *ft) { struct window_pane *wp = ft->wp; - struct window *w; - struct layout_cell *lc, *root; - int status, floating; - u_int sy; + int xoff, yoff; + u_int sx, sy; - if (wp == NULL) + if (wp == NULL || wp->layout_cell == NULL) return (NULL); - w = wp->window; - - lc = wp->saved_layout_cell; - if (lc == NULL) - lc = wp->layout_cell; - if (lc == NULL) - return (NULL); - sy = lc->g.sy; - floating = (lc->flags & LAYOUT_CELL_FLOATING); - - root = w->saved_layout_root; - if (root == NULL) - root = w->layout_root; - if (lc == wp->saved_layout_cell && !floating) - status = window_get_pane_status(w); - else - status = window_pane_get_pane_status(wp); - if (!floating && - root != NULL && - layout_add_horizontal_border(root, lc, status) && - sy > 1) - sy--; - + layout_get_pane_geometry(wp, 1, &xoff, &yoff, &sx, &sy); return (format_printf("%u", sy)); } @@ -2744,36 +2717,12 @@ static void * format_cb_pane_unzoomed_width(struct format_tree *ft) { struct window_pane *wp = ft->wp; - struct layout_cell *lc; - int saved, sb_w, sb_pad; - u_int sx; + int xoff, yoff; + u_int sx, sy; - if (wp == NULL) + if (wp == NULL || wp->layout_cell == NULL) return (NULL); - - lc = wp->saved_layout_cell; - saved = (lc != NULL); - if (lc == NULL) - lc = wp->layout_cell; - if (lc == NULL) - return (NULL); - sx = lc->g.sx; - - if ((saved && !SCREEN_IS_ALTERNATE(&wp->base) && - wp->window->sb == PANE_SCROLLBARS_ALWAYS) || - (!saved && window_pane_scrollbar_reserve(wp))) { - sb_w = wp->scrollbar_style.width; - sb_pad = wp->scrollbar_style.pad; - if (sb_w < 1) - sb_w = 1; - if (sb_pad < 0) - sb_pad = 0; - if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) - sx = PANE_MINIMUM; - else - sx -= sb_w + sb_pad; - } - + layout_get_pane_geometry(wp, 1, &xoff, &yoff, &sx, &sy); return (format_printf("%u", sx)); } diff --git a/layout-custom.c b/layout-custom.c index d7f8f7e9f..f903ddab8 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -307,32 +307,68 @@ bad: return (xstrdup("0000,")); } +/* + * Dump the layout as it is seen while a pane is zoomed: the zoomed pane fills + * the window with the floating panes in front of it. The cells are temporary + * and do not belong to the panes. + */ +char * +layout_dump_visible(struct window *w, int flags) +{ + struct window_pane *zwp = window_zoomed_pane(w), *wp; + struct layout_cell *root, *lc, *lcnext; + char *out; + + if (zwp == NULL) + return (layout_dump(w, w->layout_root, flags)); + + root = layout_create_cell(NULL); + layout_set_size(root, w->sx, w->sy, 0, 0); + root->wp = zwp; + TAILQ_FOREACH(wp, &w->z_index, zentry) { + if (wp == zwp) + break; + if (!window_pane_is_unzoomed_float(wp) || + !window_pane_is_visible(wp)) + continue; + if (root->type == LAYOUT_WINDOWPANE) { + lc = layout_create_cell(root); + layout_set_size(lc, w->sx, w->sy, 0, 0); + lc->wp = zwp; + root->wp = NULL; + root->type = LAYOUT_TOPBOTTOM; + TAILQ_INSERT_TAIL(&root->cells, lc, entry); + } + lc = layout_create_cell(root); + memcpy(&lc->g, &wp->layout_cell->g, sizeof lc->g); + lc->flags |= LAYOUT_CELL_FLOATING; + lc->wp = wp; + TAILQ_INSERT_TAIL(&root->cells, lc, entry); + } + out = layout_dump(w, root, flags); + + if (root->type != LAYOUT_WINDOWPANE) { + TAILQ_FOREACH_SAFE(lc, &root->cells, entry, lcnext) { + TAILQ_REMOVE(&root->cells, lc, entry); + free(lc); + } + } + free(root); + return (out); +} + /* Get a floating pane cell's z-index in the layout being dumped. */ static u_int layout_cell_zindex(struct layout_cell *lc) { struct window_pane *wp = lc->wp, *wq; struct window *w = wp->window; - struct layout_cell *other; - int saved = (lc == wp->saved_layout_cell); u_int i = 0; - if (saved && - w->active != NULL && - (w->active->flags & PANE_ZOOMED) && - (w->active->saved_layout_cell->flags & LAYOUT_CELL_FLOATING)) { - if (wp == w->active) - return (0); - i++; - } TAILQ_FOREACH(wq, &w->z_index, zentry) { if (wq == wp) break; - if (saved) - other = wq->saved_layout_cell; - else - other = wq->layout_cell; - if (other != NULL && (other->flags & LAYOUT_CELL_FLOATING)) + if (window_pane_is_floating(wq)) i++; } return (i); diff --git a/layout.c b/layout.c index 74c3c29bb..639340532 100644 --- a/layout.c +++ b/layout.c @@ -415,18 +415,89 @@ layout_add_horizontal_border(struct layout_cell *root, struct layout_cell *lc, return (0); } +/* + * Work out the offset and size a pane should have from its cell. A zoomed pane + * fills the window unless layout is set, which gives the place it has in the + * layout. + */ +void +layout_get_pane_geometry(struct window_pane *wp, int layout, int *xoff, + int *yoff, u_int *sx, u_int *sy) +{ + struct window *w = wp->window; + struct layout_cell *lc = wp->layout_cell; + int status, sb_w, sb_pad, border, reserve; + + if (layout && !window_pane_is_floating(wp)) + status = window_get_pane_status(w); + else + status = window_pane_get_pane_status(wp); + if ((wp->flags & PANE_ZOOMED) && !layout) { + /* A zoomed pane fills the window, whatever its cell. */ + *xoff = 0; + *yoff = 0; + *sx = w->sx; + *sy = w->sy; + border = (status == PANE_STATUS_TOP || + status == PANE_STATUS_BOTTOM); + } else { + *xoff = lc->g.xoff; + *yoff = lc->g.yoff; + *sx = lc->g.sx; + *sy = lc->g.sy; + border = (!window_pane_is_floating(wp) && + layout_add_horizontal_border(w->layout_root, lc, status)); + } + if (border) { + if (status == PANE_STATUS_TOP) + (*yoff)++; + if (*sy > 1) + (*sy)--; + } + + /* + * The layout ignores anything a mode does to the scrollbars while the + * window is zoomed. + */ + if (layout && (w->flags & WINDOW_ZOOMED)) + reserve = (!SCREEN_IS_ALTERNATE(&wp->base) && + w->sb == PANE_SCROLLBARS_ALWAYS); + else + reserve = window_pane_scrollbar_reserve(wp); + if (reserve) { + sb_w = wp->scrollbar_style.width; + sb_pad = wp->scrollbar_style.pad; + if (sb_w < 1) + sb_w = 1; + if (sb_pad < 0) + sb_pad = 0; + if (w->sb_pos == PANE_SCROLLBARS_LEFT) { + if ((int)*sx - sb_w - sb_pad < PANE_MINIMUM) { + *xoff = *xoff + (int)*sx - PANE_MINIMUM; + *sx = PANE_MINIMUM; + } else { + *sx = *sx - sb_w - sb_pad; + *xoff = *xoff + sb_w + sb_pad; + } + } else /* sb_pos == PANE_SCROLLBARS_RIGHT */ + if ((int)*sx - sb_w - sb_pad < PANE_MINIMUM) + *sx = PANE_MINIMUM; + else + *sx = *sx - sb_w - sb_pad; + } +} + /* Update pane offsets and sizes based on their cells. */ void layout_fix_panes(struct window *w, struct window_pane *skip) { struct window_pane *wp; - struct layout_cell *lc, *root = w->layout_root; - int status, sb_w, sb_pad; int old_xoff, old_yoff, changed = 0; - u_int sx, sy, old_sx, old_sy; + u_int old_sx, old_sy, sx, sy; + int xoff, yoff; TAILQ_FOREACH(wp, &w->panes, entry) { - if ((lc = wp->layout_cell) == NULL || wp == skip) + if (wp->layout_cell == NULL || wp == skip) continue; old_xoff = wp->xoff; @@ -434,43 +505,11 @@ layout_fix_panes(struct window *w, struct window_pane *skip) old_sx = wp->sx; old_sy = wp->sy; - wp->xoff = lc->g.xoff; - wp->yoff = lc->g.yoff; - sx = lc->g.sx; - sy = lc->g.sy; - - status = window_pane_get_pane_status(wp); - if (!window_pane_is_floating(wp) && - layout_add_horizontal_border(root, lc, status)) { - if (status == PANE_STATUS_TOP) - wp->yoff++; - if (sy > 1) - sy--; - } - - if (window_pane_scrollbar_reserve(wp)) { - sb_w = wp->scrollbar_style.width; - sb_pad = wp->scrollbar_style.pad; - if (sb_w < 1) - sb_w = 1; - if (sb_pad < 0) - sb_pad = 0; - if (w->sb_pos == PANE_SCROLLBARS_LEFT) { - if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) { - wp->xoff = wp->xoff + - (int)sx - PANE_MINIMUM; - sx = PANE_MINIMUM; - } else { - sx = sx - sb_w - sb_pad; - wp->xoff = wp->xoff + sb_w + sb_pad; - } - } else /* sb_pos == PANE_SCROLLBARS_RIGHT */ - if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) - sx = PANE_MINIMUM; - else - sx = sx - sb_w - sb_pad; + layout_get_pane_geometry(wp, 0, &xoff, &yoff, &sx, &sy); + wp->xoff = xoff; + wp->yoff = yoff; + if (window_pane_scrollbar_reserve(wp)) wp->flags |= PANE_REDRAWSCROLLBAR; - } window_pane_resize(wp, sx, sy); @@ -1683,10 +1722,6 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args, return (NULL); } - if (window_active_pane_is_over_zoom(w)) - window_push_zoom(w, 0, 1); - else - window_push_zoom(w, 1, (flags & SPAWN_ZOOM)); lc = layout_split_pane(wp, type, size, flags); if (lc == NULL) *cause = xstrdup("no space for a new pane"); @@ -1713,12 +1748,6 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args, return (NULL); } - if (flags & SPAWN_FLOATOVERZOOM) - window_push_zoom(wp->window, 0, 1); - else if (window_active_pane_is_over_zoom(w)) - window_push_zoom(wp->window, 0, 1); - else - window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM)); lcnew = layout_floating_pane(w, wp, &fg); return (lcnew); } diff --git a/mode-tree.c b/mode-tree.c index 222047f61..9088336e2 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -621,7 +621,7 @@ mode_tree_zoom(struct mode_tree_data *mtd, struct args *args) struct window_pane *wp = mtd->wp; if (args_has(args, 'Z')) { - mtd->zoomed = (wp->window->flags & WINDOW_ZOOMED); + mtd->zoomed = (wp->flags & PANE_ZOOMED); if (!mtd->zoomed && window_zoom(wp) == 0) server_redraw_window(wp->window); } else @@ -710,8 +710,8 @@ mode_tree_free(struct mode_tree_data *mtd) { struct window_pane *wp = mtd->wp; - if (mtd->zoomed == 0) - server_unzoom_window(wp->window); + if (mtd->zoomed == 0 && window_unzoom_pane(wp, 1) == 0) + server_redraw_window(wp->window); mode_tree_clear_prompt(mtd); mode_tree_free_items(&mtd->children); diff --git a/options-table.c b/options-table.c index 71ed025bd..9c719dd4a 100644 --- a/options-table.c +++ b/options-table.c @@ -72,6 +72,9 @@ static const char *options_table_pane_scrollbars_position_list[] = { static const char *options_table_pane_status_list[] = { "off", "top", "bottom", "top-floating", "bottom-floating", NULL }; +static const char *options_table_pane_raise_on_focus_list[] = { + "floating", "all", "off", NULL +}; static const char *options_table_pane_border_indicators_list[] = { "off", "colour", "arrows", "both", NULL }; @@ -1631,6 +1634,16 @@ const struct options_table_entry options_table[] = { .text = "The default colour palette for colours zero to 255." }, + { .name = "pane-raise-on-focus", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_pane_raise_on_focus_list, + .default_num = PANE_RAISE_FLOATING, + .text = "Which panes are raised to the front when they are made " + "active: floating panes only, floating and zoomed panes, or " + "none." + }, + { .name = "pane-scrollbars", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, diff --git a/regress/floating-pane-zoom.sh b/regress/floating-pane-zoom.sh index 84e9db00c..bcca64f6a 100644 --- a/regress/floating-pane-zoom.sh +++ b/regress/floating-pane-zoom.sh @@ -56,6 +56,7 @@ for mode in before after detached modal; do -x 20 -y 8 -X 8 -Y 3 '') || exit 1 if [ "$mode" = before ]; then run resize-pane -Z -t "$base" + run select-pane -t "$float" fi run set -gu @unzoomed check_zoom @@ -131,28 +132,29 @@ for z in 1 0 99; do check_zoom done run resize-pane -Z -t "$base" -check "$float" '#{pane_z}' 2 +check "$float" '#{pane_z}' 1 +check "$hidden" '#{pane_z}' 2 check "$base" '#{pane_z}' 4 run kill-pane -t "$hidden" run kill-pane -t "$back" -# Explicit zoom toggling and operations on tiled panes retain their -# existing behaviour. The over-zoom flag alone does not make a pane a float: -# zooming the float itself gives it a tiled cell until unzoom. +# Zooming a float keeps it floating. Resizing a zoomed float unzooms just that +# pane, but operations on other panes leave a zoom alone. run resize-pane -Z -t "$float" -check "$float" '#{pane_zoomed_flag}:#{pane_floating_flag}' '1:0' +check "$float" '#{pane_zoomed_flag}:#{pane_floating_flag}' '1:1' run resize-pane -t "$float" -x 25 check "$float" '#{window_zoomed_flag}:#{pane_floating_flag}:#{pane_width}' \ '0:1:23' run resize-pane -Z -t "$base" run resize-pane -t "$other" -D 1 -check "$base" '#{window_zoomed_flag}' 0 +check "$base" '#{window_zoomed_flag}' 1 +run resize-pane -Z -t "$base" -# A hidden ordinary float must still be unzoomed before resizing it. +# A float covered by a zoom can be resized without unzooming. hidden=$(run new-pane -dPF '#{pane_id}' -t "$base" '') || exit 1 run resize-pane -Z -t "$base" run resize-pane -t "$hidden" -x 25 check "$hidden" '#{window_zoomed_flag}:#{pane_floating_flag}:#{pane_width}' \ - '0:1:23' + '1:1:23' exit 0 diff --git a/regress/modal-pane.sh b/regress/modal-pane.sh index 16718ddc3..82b55d9f4 100644 --- a/regress/modal-pane.sh +++ b/regress/modal-pane.sh @@ -537,8 +537,8 @@ case "$(fmt "$ignored" '#{pane_flags}')" in *) ;; esac -# Existing floating panes are filtered when zoom begins: -A panes remain in the -# visible layout and ordinary floating panes do not. +# Existing floating panes stay floating when zoom begins: -A panes remain above +# the zoom and ordinary floating panes are covered by it. check_ok new-window -d -t modal: -n existing-over-zoom 'cat' base=$(fmt modal:existing-over-zoom '#{pane_id}') check_ok split-window -dh -t "$base" 'cat' @@ -552,10 +552,10 @@ check_ok select-window -t modal:existing-over-zoom check_ok select-pane -t "$base" check_ok resize-pane -Z -t "$base" must_equal "$(fmt "$over" '#{pane_floating_flag}')" 1 -must_equal "$(fmt "$under" '#{pane_floating_flag}')" 0 +must_equal "$(fmt "$under" '#{pane_floating_flag}')" 1 must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1 -# Geometry changed in the visible zoom layout is copied back when unzooming. +# Geometry changed while zoomed is kept when unzooming. check_ok select-pane -t "$over" left=$(fmt "$over" '#{pane_left}') top=$(fmt "$over" '#{pane_top}') @@ -593,8 +593,8 @@ done must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1 # A pane with -A is also above a zoom target which was itself floating. The -# temporary tiled target must sit behind retained floating panes, then return -# to the normal floating z order when unzoomed. +# target must sit behind the -A pane, then return to the normal floating z +# order when unzoomed. check_ok new-window -d -t modal: -n floating-zoom-target 'cat' base=$(fmt modal:floating-zoom-target '#{pane_id}') check_ok split-window -dh -t "$base" 'cat' @@ -606,13 +606,13 @@ over=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \ fail "float-over-zoom pane creation failed" check_ok select-window -t modal:floating-zoom-target check_ok select-pane -t "$target" -must_equal "$(fmt "$target" '#{pane_z}')" 0 -must_equal "$(fmt "$over" '#{pane_z}')" 1 +must_equal "$(fmt "$target" '#{pane_z}')" 1 +must_equal "$(fmt "$over" '#{pane_z}')" 0 check_ok resize-pane -Z -t "$target" must_equal "$(fmt "$target" \ '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_floating_flag}:#{pane_z}')" \ - 1:1:0:2 + 1:1:1:1 must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_z}')" 1:0 check_ok select-pane -t "$over" must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1 @@ -620,13 +620,14 @@ check_ok resize-pane -Z -t "$target" must_equal "$(fmt "$over" '#{pane_active}:#{pane_z}')" 1:0 must_equal "$(fmt "$target" '#{pane_floating_flag}:#{pane_z}')" 1:1 -# If the target remains active, it returns to the front on unzoom. +# A pane with -A stays in front of the target, even when the target is active +# and is zoomed and unzoomed again. check_ok select-pane -t "$target" check_ok resize-pane -Z -t "$target" must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_z}')" 1:0 check_ok resize-pane -Z -t "$target" -must_equal "$(fmt "$target" '#{pane_active}:#{pane_z}')" 1:0 -must_equal "$(fmt "$over" '#{pane_z}')" 1 +must_equal "$(fmt "$target" '#{pane_active}:#{pane_z}')" 1:1 +must_equal "$(fmt "$over" '#{pane_z}')" 0 cleanup exit 0 diff --git a/regress/pane-ops.sh b/regress/pane-ops.sh index 25795c026..60696234a 100644 --- a/regress/pane-ops.sh +++ b/regress/pane-ops.sh @@ -166,7 +166,8 @@ check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1" # break-pane and join-pane. # break-pane moves a pane to a new window; -P -F prints where it went and -n -# names the new window. Moving a pane out of a zoomed window unzooms it first. +# names the new window. Moving another pane out of a zoomed window leaves the +# zoom alone. check_ok resize-pane -Z -t "$p0" out=$($TMUX break-pane -d -P -F '#{window_index}:#{pane_id}' -n broken \ -s "$p1" -t P:) @@ -176,13 +177,14 @@ if [ "$out" != "1:$p1" ]; then fi check_fmt 'P:1' '#{window_name}:#{window_panes}' 'broken:1' check_fmt 'P:0' '#{window_panes}' '3' -check_fmt 'P:0' '#{window_zoomed_flag}' '0' +check_fmt 'P:0' '#{window_zoomed_flag}' '1' # join-pane -v moves it back (the source window, left empty, is destroyed) and -# also unzooms the destination before changing its layout. -check_ok resize-pane -Z -t "$p0" +# also leaves the destination zoomed while changing its layout. check_ok join-pane -d -v -s P:broken.0 -t "$p2" check_fmt 'P:0' '#{window_panes}' '4' +check_fmt 'P:0' '#{window_zoomed_flag}' '1' +check_ok resize-pane -Z -t "$p0" check_fmt 'P:0' '#{window_zoomed_flag}' '0' if $TMUX has-session -t P:broken 2>/dev/null; then echo "Window 'broken' still exists after join-pane." @@ -317,14 +319,23 @@ check_fmt "$p0" '#{window_zoomed_flag}:#{pane_width}x#{pane_height}' \ check_ok resize-pane -Z -t "$p0" check_fmt "$p0" '#{window_zoomed_flag}' '0' -# Splitting while zoomed unzooms first. +# Splitting while zoomed with -d leaves the zoom alone; without -d the new +# pane is active and covered, so selecting it unzooms. check_ok resize-pane -Z -t "$p0" check_fmt 'P:0' '#{window_zoomed_flag}' '1' check_ok split-window -d -v -t "$p0" -check_fmt 'P:0' '#{window_zoomed_flag}' '0' +check_fmt 'P:0' '#{window_zoomed_flag}' '1' check_fmt 'P:0' '#{window_panes}' '5' p6=$($TMUX display-message -p -t P:0.2 '#{pane_id}') check_ok kill-pane -t "$p6" +check_fmt 'P:0' '#{window_zoomed_flag}' '1' +check_ok resize-pane -Z -t "$p0" +check_fmt 'P:0' '#{window_zoomed_flag}' '0' +check_ok resize-pane -Z -t "$p0" +check_ok split-window -v -t "$p0" +check_fmt 'P:0' '#{window_zoomed_flag}' '0' +p6=$($TMUX display-message -p -t P:0 '#{pane_id}') +check_ok kill-pane -t "$p6" # Zoom and unzoom preserve the exact tiled layout. Selecting another pane # without -Z unzooms, while -Z transfers zoom to the selected pane. @@ -378,7 +389,8 @@ check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \ check_ok resize-pane -Z -t "$p0" check_layout P:0 "$layout" -# Killing either a hidden ordinary pane or the zoom target unzooms. +# Killing a hidden ordinary pane leaves the zoom, and killing the zoom target +# shows what was underneath. check_ok new-window -d -t P:12 -n zoom-kill 'cat' zk0=$($TMUX display-message -p -t P:12.0 '#{pane_id}') zk1=$($TMUX split-window -d -P -F '#{pane_id}' -t P:12.0 'cat') @@ -386,8 +398,7 @@ zk2=$($TMUX split-window -d -P -F '#{pane_id}' -t P:12.0 'cat') check_ok resize-pane -Z -t "$zk0" check_ok kill-pane -t "$zk1" check_fmt "$zk0" '#{window_panes}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \ - '2:0:0' -check_ok resize-pane -Z -t "$zk0" + '2:1:1' check_ok kill-pane -t "$zk0" check_fmt "$zk2" '#{window_panes}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \ '1:0:0' @@ -406,7 +417,7 @@ while [ "$($TMUX display-message -p -t P:13 '#{window_panes}')" != 2 ]; do [ $i -gt 50 ] && { echo "Hidden pane did not exit."; exit 1; } sleep 0.1 done -check_fmt "$ze0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0' +check_fmt "$ze0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1' check_ok resize-pane -Z -t "$ze2" check_ok send-keys -t "$ze2" C-d i=0 diff --git a/regress/window-ops.sh b/regress/window-ops.sh index c385a3328..c1bbce674 100644 --- a/regress/window-ops.sh +++ b/regress/window-ops.sh @@ -355,6 +355,24 @@ check_ok resize-pane -Z -t "$p1" check_ok rotate-window -D -t R:0 check_fmt 'R:0' '#{window_layout}' "$layout" +# Floating panes are not rotated: they keep their geometry and stay floating +# while the tiled panes rotate around them. +fp=$($TMUX new-pane -dPF '#{pane_id}' -t R:0 -x 20 -y 8 -X 8 -Y 3 '') +fgeom=$($TMUX display-message -p -t "$fp" \ + '#{pane_floating_flag}:#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}') +check_ok select-pane -t "$p0" +check_ok rotate-window -U -t R:0 +check_fmt "$fp" \ + '#{pane_floating_flag}:#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}' \ + "$fgeom" +check_fmt "$p1" '#{pane_floating_flag}:#{pane_top}:#{pane_active}' '0:0:1' +check_ok rotate-window -D -t R:0 +check_fmt "$fp" \ + '#{pane_floating_flag}:#{pane_left}:#{pane_top}:#{pane_width}:#{pane_height}' \ + "$fgeom" +check_fmt "$p0" '#{pane_floating_flag}:#{pane_top}:#{pane_active}' '0:0:1' +check_ok kill-pane -t "$fp" + # --------------------------------------------------------------------------- # kill-window. diff --git a/resize.c b/resize.c index 67562522d..ca33e0d87 100644 --- a/resize.c +++ b/resize.c @@ -42,7 +42,6 @@ resize_fire_window_resized(struct window *w, u_int old_sx, u_int old_sy) void resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) { - struct window_pane *zwp; u_int old_sx = w->sx, old_sy = w->sy; /* Check size limits. */ @@ -55,11 +54,6 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) if (sy > WINDOW_MAXIMUM) sy = WINDOW_MAXIMUM; - /* If the window is zoomed, unzoom. */ - zwp = window_zoomed_pane(w); - if (zwp != NULL) - window_unzoom(w, 1); - /* Resize the layout first. */ layout_resize(w, sx, sy); @@ -72,9 +66,9 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) log_debug("%s: @%u resized to %ux%u; layout %ux%u", __func__, w->id, sx, sy, w->layout_root->g.sx, w->layout_root->g.sy); - /* Restore the window zoom state. */ - if (zwp != NULL && window_has_pane(w, zwp)) - window_zoom(zwp); + /* Zoomed panes follow the window size. */ + if (w->flags & WINDOW_ZOOMED) + layout_fix_panes(w, NULL); tty_update_window_offset(w); server_redraw_window(w); diff --git a/screen-redraw.c b/screen-redraw.c index 01cdd1177..c65ace871 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -358,7 +358,7 @@ redraw_pane_to_scene(struct redraw_build_ctx *bctx, struct window_pane *wp, int wx = wp->xoff + px, wy = wp->yoff + py; int left, right, top, bottom; - if (window_pane_is_floating(wp)) { + if (window_pane_is_unzoomed_float(wp)) { left = wp->xoff - 1; right = wp->xoff + wp->sx; top = wp->yoff - 1; @@ -676,7 +676,7 @@ redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp, enum pane_lines pane_lines = window_pane_get_pane_lines(wp); int pane_status, left, right, top, bottom, wx, wy; int mark_top, mark_bottom, mark_left, mark_right, mask = 0; - int floating = window_pane_is_floating(wp); + int floating = window_pane_is_unzoomed_float(wp); if (floating && pane_lines == PANE_LINES_NONE) return; diff --git a/server-client.c b/server-client.c index 4a29055c8..7037f2e64 100644 --- a/server-client.c +++ b/server-client.c @@ -675,7 +675,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, return (KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER); } else /* py > sl_bottom */ return (KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN); - } else if (window_pane_is_floating(wp) && + } else if (window_pane_is_unzoomed_float(wp) && window_pane_get_pane_lines(wp) != PANE_LINES_NONE && (px == bdr_left || py == wp->yoff - 1 || @@ -691,7 +691,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, TAILQ_FOREACH(fwp, &w->panes, entry) { if (!window_pane_is_visible(fwp)) continue; - if (window_pane_is_floating(fwp) && + if (window_pane_is_unzoomed_float(fwp) && window_pane_get_pane_lines(fwp) == PANE_LINES_NONE) continue; if (window_pane_scrollbar_reserve(fwp)) { diff --git a/server-fn.c b/server-fn.c index e9db5a887..f63089bfe 100644 --- a/server-fn.c +++ b/server-fn.c @@ -226,11 +226,9 @@ server_kill_pane(struct window_pane *wp) server_kill_window(w, 1); recalculate_sizes(); } else { - window_push_zoom(w, 0, wp->flags & PANE_FLOATOVERZOOM); server_client_remove_pane(wp); layout_close_pane(wp); window_remove_pane(w, wp); - window_pop_zoom(w); server_redraw_window(w); } } @@ -423,17 +421,14 @@ server_destroy_pane(struct window_pane *wp, int notify) if (notify) server_fire_pane_exit("pane-exited", wp); - window_push_zoom(w, 0, wp->flags & PANE_FLOATOVERZOOM); server_client_remove_pane(wp); layout_close_pane(wp); window_remove_pane(w, wp); if (TAILQ_EMPTY(&w->panes)) server_kill_window(w, 1); - else { - window_pop_zoom(w); + else server_redraw_window(w); - } } static void diff --git a/spawn.c b/spawn.c index 801288a99..bf4984c16 100644 --- a/spawn.c +++ b/spawn.c @@ -362,13 +362,6 @@ spawn_pane(struct spawn_context *sc, char **cause) new_wp->layout_cell->flags |= LAYOUT_CELL_FLOATING; if (sc->flags & SPAWN_FLOATOVERZOOM) new_wp->flags |= PANE_FLOATOVERZOOM; - - /* - * If window currently zoomed, window_set_active_pane calls - * window_unzoom which it copies back the saved_layout_cell. - */ - if (w->flags & WINDOW_ZOOMED) - new_wp->saved_layout_cell = new_wp->layout_cell; } /* @@ -593,6 +586,8 @@ complete: if (sc->flags & SPAWN_RESPAWN) return (new_wp); + if (sc->flags & SPAWN_ZOOM) + window_zoom(new_wp); if (sc->flags & SPAWN_MODAL) { w->modal_last = w->active; w->modal = new_wp; @@ -747,10 +742,8 @@ spawn_editor(struct client *c, const char *buf, size_t len, lg.sy = w->sy * 9 / 10; lg.xoff = w->sx / 2 - lg.sx / 2; lg.yoff = w->sy / 2 - lg.sy / 2; - window_push_zoom(w, 0, 1); lc = layout_floating_pane(w, NULL, &lg); if (lc == NULL) { - window_pop_zoom(w); spawn_editor_free(es); return (NULL); } @@ -774,11 +767,9 @@ spawn_editor(struct client *c, const char *buf, size_t len, environ_free(env); if (wp == NULL) { free(cause); - window_pop_zoom(w); spawn_editor_free(es); return (NULL); } - window_pop_zoom(w); options_set_number(wp->options, "remain-on-exit", 0); es->pid = wp->pid; wp->editor = es; diff --git a/tmux.h b/tmux.h index dee768686..bf484217b 100644 --- a/tmux.h +++ b/tmux.h @@ -1156,6 +1156,11 @@ enum pane_lines { PANE_LINES_ROUNDED }; +/* Panes raised when they are made active. */ +#define PANE_RAISE_FLOATING 0 +#define PANE_RAISE_ALL 1 +#define PANE_RAISE_OFF 2 + /* Pane border indicator option. */ #define PANE_BORDER_OFF 0 #define PANE_BORDER_COLOUR 1 @@ -1312,7 +1317,6 @@ struct window_pane { struct options *options; struct layout_cell *layout_cell; - struct layout_cell *saved_layout_cell; u_int sx; u_int sy; @@ -1455,14 +1459,12 @@ struct window { struct window_pane *active; struct window_pane *modal; struct window_pane *modal_last; - struct window_pane *was_zoomed; struct window_panes last_panes; struct window_panes z_index; struct window_panes panes; int lastlayout; struct layout_cell *layout_root; - struct layout_cell *saved_layout_root; char *old_layout; u_int sx; @@ -1496,7 +1498,6 @@ struct window { #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 -#define WINDOW_WASZOOMED 0x10 #define WINDOW_RESIZE 0x20 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) @@ -3719,12 +3720,13 @@ struct window_pane *window_add_pane(struct window *, struct window_pane *, u_int, int); void window_resize(struct window *, u_int, u_int, int, int); void window_pane_send_resize(struct window_pane *, u_int, u_int); +void window_raise_pane(struct window_pane *); +void window_lower_pane(struct window_pane *); int window_zoom(struct window_pane *); +int window_unzoom_pane(struct window_pane *, int); int window_unzoom(struct window *, int); -int window_active_pane_is_over_zoom(struct window *); struct window_pane *window_zoomed_pane(struct window *); -int window_push_zoom(struct window *, int, int); -int window_pop_zoom(struct window *); +void window_zoom_move(struct window_pane *, struct window_pane *); void window_lost_pane(struct window *, struct window_pane *); void window_remove_pane(struct window *, struct window_pane *); struct window_pane *window_pane_at_index(struct window *, u_int); @@ -3815,7 +3817,8 @@ int window_pane_get_pane_status(struct window_pane *); struct style_range *window_pane_status_get_range(struct window_pane *, u_int, u_int); int window_pane_is_floating(struct window_pane *); -int window_pane_is_floating_with_hidden(struct window_pane *); +int window_pane_is_unzoomed_float(struct window_pane *); +int window_pane_is_raised(struct window_pane *); /* window-border.c */ void window_set_fill_cells(struct window *); @@ -3852,6 +3855,8 @@ int layout_cell_has_tiled_child(struct layout_cell *); int layout_add_horizontal_border(struct layout_cell *, struct layout_cell *, int); void layout_fix_offsets(struct window *); +void layout_get_pane_geometry(struct window_pane *, int, int *, + int *, u_int *, u_int *); void layout_fix_panes(struct window *, struct window_pane *); void layout_resize_adjust(struct window *, struct layout_cell *, enum layout_type, int); @@ -3901,6 +3906,7 @@ int layout_insert_tile(struct window *, struct layout_cell *); /* layout-custom.c */ #define LAYOUT_CUSTOM_OLD_FORMAT 0x1 char *layout_dump(struct window *, struct layout_cell *, int); +char *layout_dump_visible(struct window *, int); int layout_parse(struct window *, const char *, char **); /* layout-set.c */ diff --git a/window-panes.c b/window-panes.c index e65e59606..da9db43cf 100644 --- a/window-panes.c +++ b/window-panes.c @@ -150,21 +150,7 @@ window_panes_add_area(struct window_panes_modedata *data, static int window_panes_pane_floating(struct window_pane *wp) { - struct layout_cell *lc = wp->saved_layout_cell; - - if (lc == NULL) - lc = wp->layout_cell; - if (lc == NULL || (~lc->flags & LAYOUT_CELL_FLOATING)) - return (0); - return (1); -} - -static int -window_panes_pane_visible(struct window_pane *wp) -{ - if (wp->saved_layout_cell != NULL) - return (1); - return (window_pane_is_visible(wp)); + return (window_pane_is_floating(wp)); } static int @@ -172,12 +158,10 @@ window_panes_get_geometry(struct window_pane *wp, struct layout_cell *root, u_int osx, u_int osy, u_int dsx, u_int dsy, u_int *xp, u_int *yp, u_int *sxp, u_int *syp) { - struct layout_cell *lc = wp->saved_layout_cell; + struct layout_cell *lc = wp->layout_cell; int status; u_int x, y, sx, sy, x2, y2; - if (lc == NULL) - lc = wp->layout_cell; if (lc == NULL || osx == 0 || osy == 0 || dsx == 0 || dsy == 0) return (0); @@ -375,12 +359,7 @@ window_panes_mark_pane_status_borders(u_char *map, struct window *w, return; TAILQ_FOREACH(wp, &w->panes, entry) { - if (!window_panes_pane_visible(wp)) - continue; - - lc = wp->saved_layout_cell; - if (lc == NULL) - lc = wp->layout_cell; + lc = wp->layout_cell; if (lc == NULL || !layout_add_horizontal_border(root, lc, status)) continue; @@ -404,9 +383,7 @@ window_panes_get_floating_borders(struct window_pane *wp, u_int osx, u_int osy, { struct layout_cell *lc; - lc = wp->saved_layout_cell; - if (lc == NULL) - lc = wp->layout_cell; + lc = wp->layout_cell; if (lc == NULL || (~lc->flags & LAYOUT_CELL_FLOATING)) return (0); @@ -801,8 +778,6 @@ window_panes_draw_pane(struct window_panes_modedata *data, struct screen *s = &wp->base; u_int pane, x, y, sx, sy; - if (!window_panes_pane_visible(wp)) - return; if (!window_panes_get_geometry(wp, root, osx, osy, dsx, dsy, &x, &y, &sx, &sy)) return; @@ -839,9 +814,7 @@ window_panes_draw_screen(struct window_mode_entry *wme) if (!window_panes_get_source(data, NULL, NULL, &w)) return; - root = w->saved_layout_root; - if (root == NULL) - root = w->layout_root; + root = w->layout_root; if (root == NULL) return; @@ -941,7 +914,7 @@ window_panes_init(struct window_mode_entry *wme, struct cmdq_item *item, if (args_has(args, 'Z')) data->zoomed = -1; else { - data->zoomed = (w->flags & WINDOW_ZOOMED); + data->zoomed = (wp->flags & PANE_ZOOMED); if (!data->zoomed) window_panes_set_preview(data); if (!data->zoomed && window_zoom(wp) == 0) @@ -967,8 +940,8 @@ window_panes_free(struct window_mode_entry *wme) evtimer_del(&data->timer); - if (data->zoomed == 0) - server_unzoom_window(w); + if (data->zoomed == 0 && window_unzoom_pane(wme->wp, 1) == 0) + server_redraw_window(w); server_redraw_window(w); server_redraw_window_borders(w); server_status_window(w); @@ -1092,8 +1065,6 @@ window_panes_key(struct window_mode_entry *wme, struct client *c, return; } - if (wp->window->flags & WINDOW_ZOOMED) - window_unzoom(wp->window, 1); window_panes_run_command(data, c, target); window_pane_reset_mode(wp); } diff --git a/window-switch.c b/window-switch.c index 1ff1007bb..8d1517ecf 100644 --- a/window-switch.c +++ b/window-switch.c @@ -380,7 +380,7 @@ window_switch_init(struct window_mode_entry *wme, if (!args_has(args, 'Z')) data->zoomed = -1; else { - data->zoomed = (wp->window->flags & WINDOW_ZOOMED); + data->zoomed = (wp->flags & PANE_ZOOMED); if (!data->zoomed && window_zoom(wp) == 0) server_redraw_window(wp->window); } @@ -398,8 +398,9 @@ window_switch_free(struct window_mode_entry *wme) struct window_switch_modedata *data = wme->data; u_int i; - if (data->zoomed == 0) - server_unzoom_window(wme->wp->window); + if (data->zoomed == 0 && + window_unzoom_pane(wme->wp, 1) == 0) + server_redraw_window(wme->wp->window); for (i = 0; i < data->item_size; i++) window_switch_free_item(data->item_list[i]); diff --git a/window-visible.c b/window-visible.c index a49bfa2ef..fadd02c50 100644 --- a/window-visible.c +++ b/window-visible.c @@ -103,7 +103,7 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width, continue; } - if (window_pane_is_floating(wp) && + if (window_pane_is_unzoomed_float(wp) && window_pane_get_pane_lines(wp) == PANE_LINES_NONE) no_border = 1; else @@ -120,7 +120,8 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width, py < tb || py > bb) continue; - if (!window_pane_is_floating(wp) && (py == tb || py == bb)) + if (!window_pane_is_unzoomed_float(wp) && + (py == tb || py == bb)) continue; sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; diff --git a/window.c b/window.c index 515c2ffc6..4f839ef83 100644 --- a/window.c +++ b/window.c @@ -457,21 +457,11 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) static void window_destroy(struct window *w) { - struct window_pane *wp; - log_debug("window @%u destroyed (%d references)", w->id, w->references); - if (w->flags & WINDOW_ZOOMED) { - w->flags &= ~WINDOW_ZOOMED; - TAILQ_FOREACH(wp, &w->panes, entry) { - wp->flags &= ~PANE_ZOOMED; - wp->saved_layout_cell = NULL; - } - } RB_REMOVE(windows, &windows, w); layout_free_cell(w->layout_root, 0); - layout_free_cell(w->saved_layout_root, 0); free(w->old_layout); menu_destroy(w); @@ -656,7 +646,7 @@ window_pane_contains(struct window_pane *wp, u_int x, u_int y) return (0); window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); - if (!window_pane_is_floating(wp)) { + if (!window_pane_is_unzoomed_float(wp)) { if ((int)x < xoff || x > xoff + sx) return (0); if ((int)y < yoff || y > yoff + sy) @@ -685,7 +675,7 @@ window_pane_floating_overlaps(struct window_pane *fwp, struct window_pane *wp) int fxoff, fyoff, xoff, yoff, border = 0; u_int fsx, fsy, sx, sy; - if (!window_pane_is_floating(fwp)) + if (!window_pane_is_unzoomed_float(fwp) && (~fwp->flags & PANE_ZOOMED)) return (0); window_pane_full_size_offset(fwp, &fxoff, &fyoff, &fsx, &fsy); @@ -761,8 +751,29 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) return (0); if (w->modal != NULL && wp != w->modal) return (0); - if ((w->flags & WINDOW_ZOOMED) && !window_pane_is_visible(wp)) - window_unzoom(w, 1); + if (!window_pane_is_visible(wp)) { + /* + * The pane is covered by a zoomed pane. A tiled pane that is + * not zoomed can only be reached by unzooming (any floats + * stay); anything else can be raised above the zoom. + */ + if (window_pane_is_raised(wp)) + window_raise_pane(wp); + else + window_unzoom(w, 1); + } else { + /* Raise the pane if the option says to. */ + switch (options_get_number(w->options, "pane-raise-on-focus")) { + case PANE_RAISE_FLOATING: + if (window_pane_is_unzoomed_float(wp)) + window_raise_pane(wp); + break; + case PANE_RAISE_ALL: + if (window_pane_is_raised(wp)) + window_raise_pane(wp); + break; + } + } lastwp = w->active; window_pane_stack_remove(&w->last_panes, wp); @@ -830,14 +841,6 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) if (wp == w->active) break; - /* If the pane is floating, move to the front. */ - if (window_pane_is_floating(wp)) { - TAILQ_REMOVE(&w->z_index, wp, zentry); - TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); - wp->flags |= PANE_REDRAW; - redraw_invalidate_scene(w); - } - wp = w->active; if (wp == NULL) break; @@ -860,11 +863,12 @@ window_get_active_at(struct window *w, u_int x, u_int y) } /* - * A floating pane is above every tiled pane, including their status - * lines, so check those first. + * A floating pane or a zoomed pane is above every tiled pane, including + * their status lines, so check those first. They are in stacking order. */ TAILQ_FOREACH(wp, &w->z_index, zentry) { - if (window_pane_is_floating(wp) && + if ((window_pane_is_unzoomed_float(wp) || + (wp->flags & PANE_ZOOMED)) && window_pane_contains(wp, x, y)) return (wp); } @@ -876,7 +880,7 @@ window_get_active_at(struct window *w, u_int x, u_int y) */ TAILQ_FOREACH(wp, &w->z_index, zentry) { if (!window_pane_is_visible(wp) || - window_pane_is_floating(wp)) + window_pane_is_unzoomed_float(wp)) continue; window_pane_full_size_offset(wp, &xoff, &yoff, &sx, @@ -892,7 +896,7 @@ window_get_active_at(struct window *w, u_int x, u_int y) if (!window_pane_is_visible(wp)) continue; window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); - if (!window_pane_is_floating(wp)) { + if (!window_pane_is_unzoomed_float(wp)) { /* * Tiled - to and including the right border, excluding * the bottom border. @@ -966,117 +970,164 @@ window_find_string(struct window *w, const char *s) return (window_get_active_at(w, x, y)); } -int -window_zoom(struct window_pane *wp) +/* + * Move a pane to the front of the panes that are raised above the layout: + * behind the modal pane and any always-on-top pane. + */ +void +window_raise_pane(struct window_pane *wp) { struct window *w = wp->window; struct window_pane *wp1; - struct layout_cell *lc; - struct layout_geometry lg; - if (w->flags & WINDOW_ZOOMED) + TAILQ_REMOVE(&w->z_index, wp, zentry); + TAILQ_FOREACH(wp1, &w->z_index, zentry) { + if (wp == w->modal) + break; + if (wp1 == w->modal) + continue; + if ((wp->flags & PANE_FLOATOVERZOOM) || + (~wp1->flags & PANE_FLOATOVERZOOM)) + break; + } + if (wp1 == NULL) + TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); + else + TAILQ_INSERT_BEFORE(wp1, wp, zentry); + wp->flags |= PANE_REDRAW; + redraw_invalidate_scene(w); +} + +/* Move a pane to the back of the panes it is stacked with. */ +void +window_lower_pane(struct window_pane *wp) +{ + struct window *w = wp->window; + struct window_pane *wp1; + + if (wp == w->modal) + return; + + TAILQ_REMOVE(&w->z_index, wp, zentry); + TAILQ_FOREACH(wp1, &w->z_index, zentry) { + if (wp1 == w->modal) + continue; + if (wp->flags & PANE_FLOATOVERZOOM) { + if (~wp1->flags & PANE_FLOATOVERZOOM) + break; + } else if (!window_pane_is_raised(wp1)) + break; + } + if (wp1 == NULL) + TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); + else + TAILQ_INSERT_BEFORE(wp1, wp, zentry); + wp->flags |= PANE_REDRAW; + redraw_invalidate_scene(w); +} + +/* Set or clear WINDOW_ZOOMED depending on whether any pane is zoomed. */ +static void +window_update_zoomed(struct window *w) +{ + struct window_pane *wp; + + w->flags &= ~WINDOW_ZOOMED; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->flags & PANE_ZOOMED) { + w->flags |= WINDOW_ZOOMED; + break; + } + } +} + +/* Clear the zoomed flag on a pane and put it back behind the floats. */ +static void +window_unzoom_one(struct window_pane *wp) +{ + struct window *w = wp->window; + + wp->flags &= ~PANE_ZOOMED; + if (!window_pane_is_floating(wp)) { + TAILQ_REMOVE(&w->z_index, wp, zentry); + TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); + } + window_update_zoomed(w); +} + +/* Fire the events after panes have been unzoomed. */ +static void +window_unzoomed(struct window *w, int notify) +{ + layout_fix_panes(w, NULL); + if (notify) { + if (~w->flags & WINDOW_ZOOMED) + events_fire_window("window-unzoomed", w); + events_fire_window("window-layout-changed", w); + } + redraw_invalidate_scene(w); +} + +/* + * Zoom a pane. A zoomed pane is shown at the size of the window above the + * layout, and above any panes behind it in the stacking order; other panes can + * be zoomed on top of it. + */ +int +window_zoom(struct window_pane *wp) +{ + struct window *w = wp->window; + int was_zoomed = (w->flags & WINDOW_ZOOMED); + + if (wp->flags & PANE_ZOOMED) return (-1); if (window_count_panes(w, 1) == 1) return (-1); - if (w->active != wp && - (w->active == NULL || - (~w->active->flags & PANE_FLOATOVERZOOM) || - !window_pane_is_floating(w->active))) - window_set_active_pane(w, wp, 1); wp->flags |= PANE_ZOOMED; - - TAILQ_FOREACH(wp1, &w->panes, entry) { - wp1->saved_layout_cell = wp1->layout_cell; - wp1->layout_cell = NULL; - } - - w->saved_layout_root = w->layout_root; - layout_init(w, wp); - TAILQ_FOREACH(wp1, &w->panes, entry) { - lc = wp1->saved_layout_cell; - if (wp1 == wp || - (~wp1->flags & PANE_FLOATOVERZOOM) || - lc == NULL || - (~lc->flags & LAYOUT_CELL_FLOATING)) - continue; - memcpy(&lg, &lc->g, sizeof lg); - lc = layout_floating_pane(w, wp, &lg); - layout_assign_pane(lc, wp1, 0); - } - /* A floating zoom target is now tiled, so put it behind the floats. */ - if (wp->saved_layout_cell->flags & LAYOUT_CELL_FLOATING) { - TAILQ_REMOVE(&w->z_index, wp, zentry); - TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); - } w->flags |= WINDOW_ZOOMED; - events_fire_window("window-zoomed", w); - events_fire_window("window-layout-changed", w); + window_raise_pane(wp); + layout_fix_panes(w, NULL); + if (w->active != wp) + window_set_active_pane(w, wp, 1); + if (!was_zoomed) + events_fire_window("window-zoomed", w); + events_fire_window("window-layout-changed", w); redraw_invalidate_scene(w); return (0); } +/* Unzoom one pane. */ +int +window_unzoom_pane(struct window_pane *wp, int notify) +{ + struct window *w = wp->window; + + if (~wp->flags & PANE_ZOOMED) + return (-1); + window_unzoom_one(wp); + window_unzoomed(w, notify); + return (0); +} + +/* Unzoom every pane. */ int window_unzoom(struct window *w, int notify) { - struct window_pane *wp, *zoomed = NULL; - struct layout_cell *slc; + struct window_pane *wp, *wp1; if (~w->flags & WINDOW_ZOOMED) return (-1); - - TAILQ_FOREACH(wp, &w->panes, entry) { + TAILQ_FOREACH_SAFE(wp, &w->panes, entry, wp1) { if (wp->flags & PANE_ZOOMED) - zoomed = wp; - if (~wp->flags & PANE_FLOATOVERZOOM) - continue; - if (wp->flags & PANE_ZOOMED) - continue; - slc = wp->saved_layout_cell; - if (slc == NULL || wp->layout_cell == NULL) - continue; - memcpy(&slc->g, &wp->layout_cell->g, sizeof slc->g); - memcpy(&slc->fg, &wp->layout_cell->fg, sizeof slc->fg); + window_unzoom_one(wp); } - - w->flags &= ~WINDOW_ZOOMED; - layout_free(w, 0); - w->layout_root = w->saved_layout_root; - w->saved_layout_root = NULL; - - TAILQ_FOREACH(wp, &w->panes, entry) { - wp->layout_cell = wp->saved_layout_cell; - wp->saved_layout_cell = NULL; - wp->flags &= ~PANE_ZOOMED; - } - /* Put a floating zoom target back into the floating part of the list. */ - if (zoomed != NULL && window_pane_is_floating(zoomed)) { - TAILQ_REMOVE(&w->z_index, zoomed, zentry); - if (zoomed == w->active) - TAILQ_INSERT_HEAD(&w->z_index, zoomed, zentry); - else { - TAILQ_FOREACH(wp, &w->z_index, zentry) { - if (!window_pane_is_floating(wp)) - break; - } - if (wp == NULL) - TAILQ_INSERT_TAIL(&w->z_index, zoomed, zentry); - else - TAILQ_INSERT_BEFORE(wp, zoomed, zentry); - } - } - layout_fix_panes(w, NULL); - - if (notify) { - events_fire_window("window-unzoomed", w); - events_fire_window("window-layout-changed", w); - } - - redraw_invalidate_scene(w); + window_unzoomed(w, notify); return (0); } +/* Get the zoomed pane at the front of the stacking order. */ struct window_pane * window_zoomed_pane(struct window *w) { @@ -1084,63 +1135,26 @@ window_zoomed_pane(struct window *w) if (~w->flags & WINDOW_ZOOMED) return (NULL); - TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) { - if (wp->layout_cell != NULL && !window_pane_is_floating(wp)) + TAILQ_FOREACH(wp, &w->z_index, zentry) { + if (wp->flags & PANE_ZOOMED) return (wp); } return (NULL); } -int -window_active_pane_is_over_zoom(struct window *w) +/* + * Move zoom from one pane to another, so a command that made another pane + * active can leave the window looking zoomed. + */ +void +window_zoom_move(struct window_pane *from, struct window_pane *to) { - if (~w->flags & WINDOW_ZOOMED) - return (0); - if (w->active == NULL) - return (0); - if (~w->active->flags & PANE_FLOATOVERZOOM) - return (0); - return (window_pane_is_floating(w->active)); -} - -int -window_push_zoom(struct window *w, int always, int flag) -{ - struct window_pane *wp = window_zoomed_pane(w); - - log_debug("%s: @%u %d", __func__, w->id, - flag && (w->flags & WINDOW_ZOOMED)); - if (flag && (always || (w->flags & WINDOW_ZOOMED))) - w->flags |= WINDOW_WASZOOMED; - else - w->flags &= ~WINDOW_WASZOOMED; - if (w->flags & WINDOW_WASZOOMED) - w->was_zoomed = wp; - else - w->was_zoomed = NULL; - return (window_unzoom(w, 1) == 0); -} - -int -window_pop_zoom(struct window *w) -{ - struct window_pane *wp = w->was_zoomed; - - log_debug("%s: @%u %d", __func__, w->id, - !!(w->flags & WINDOW_WASZOOMED)); - if (w->flags & WINDOW_WASZOOMED) { - w->flags &= ~WINDOW_WASZOOMED; - w->was_zoomed = NULL; - if (w->active != NULL && - ((~w->active->flags & PANE_FLOATOVERZOOM) || - !window_pane_is_floating(w->active))) - wp = w->active; - if (wp == NULL || !window_has_pane(w, wp)) - wp = w->active; - if (wp != NULL) - return (window_zoom(wp) == 0); - } - return (0); + if (from == to) + return; + if (from != NULL) + window_unzoom_pane(from, 1); + if (to != NULL) + window_zoom(to); } struct window_pane * @@ -1171,10 +1185,12 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, } if (~flags & SPAWN_FLOATING) TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); - else if (w->modal != NULL) - TAILQ_INSERT_AFTER(&w->z_index, w->modal, wp, zentry); else { + if (flags & SPAWN_FLOATOVERZOOM) + wp->flags |= PANE_FLOATOVERZOOM; TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); + if (~flags & SPAWN_MODAL) + window_raise_pane(wp); } redraw_invalidate_scene(w); return (wp); @@ -1191,8 +1207,14 @@ window_lost_pane(struct window *w, struct window_pane *wp) server_clear_marked(); if (wp == w->modal_last) w->modal_last = NULL; - if (wp == w->was_zoomed) - w->was_zoomed = NULL; + + /* Remove zoom now so the panes it covered are visible again. */ + if (wp->flags & PANE_ZOOMED) { + wp->flags &= ~PANE_ZOOMED; + window_update_zoomed(w); + if (~w->flags & WINDOW_ZOOMED) + events_fire_window("window-unzoomed", w); + } window_pane_stack_remove(&w->last_panes, wp); if (wp == w->active) { @@ -1206,6 +1228,16 @@ window_lost_pane(struct window *w, struct window_pane *wp) w->active = lastwp; else w->active = TAILQ_FIRST(&w->last_panes); + if (w->active != NULL && !window_pane_is_visible(w->active)) { + /* Use the topmost pane that can be seen. */ + TAILQ_FOREACH(lastwp, &w->z_index, zentry) { + if (lastwp != wp && + window_pane_is_visible(lastwp)) + break; + } + if (lastwp != NULL) + w->active = lastwp; + } if (w->active == NULL) { w->active = TAILQ_PREV(wp, window_panes, entry); if (w->active == NULL) @@ -1231,6 +1263,10 @@ window_remove_pane(struct window *w, struct window_pane *wp) TAILQ_REMOVE(&w->z_index, wp, zentry); redraw_invalidate_scene(w); window_pane_destroy(wp); + + /* A window with one pane left has nothing to zoom. */ + if ((w->flags & WINDOW_ZOOMED) && window_count_panes(w, 1) == 1) + window_unzoom(w, 1); } struct window_pane * @@ -1297,11 +1333,11 @@ window_pane_zindex(struct window_pane *wp, u_int *i) *i = 0; TAILQ_FOREACH(wq, &w->z_index, zentry) { if (wq == wp) { - if (!window_pane_is_floating(wp)) + if (!window_pane_is_raised(wp)) (*i)++; return (0); } - if (window_pane_is_floating(wq)) + if (window_pane_is_raised(wq) && window_pane_is_visible(wq)) (*i)++; } @@ -2107,9 +2143,20 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, int window_pane_is_visible(struct window_pane *wp) { - if (~wp->window->flags & WINDOW_ZOOMED) + struct window *w = wp->window; + struct window_pane *wp1; + + if (~w->flags & WINDOW_ZOOMED) return (1); - return (wp->layout_cell != NULL); + + /* A pane is covered by any zoomed pane in front of it. */ + TAILQ_FOREACH(wp1, &w->z_index, zentry) { + if (wp1 == wp) + return (1); + if (wp1->flags & PANE_ZOOMED) + return (0); + } + return (1); } int @@ -2211,6 +2258,37 @@ window_pane_full_size_offset(struct window_pane *wp, int *xoff, int *yoff, *sy = wp->sy; } +/* + * Get the full size and offset of a pane as if nothing were zoomed, for moving + * between panes. + */ +static void +window_pane_layout_size_offset(struct window_pane *wp, int *xoff, int *yoff, + u_int *sx, u_int *sy) +{ + struct window *w = wp->window; + int pxoff, pyoff; + u_int psx, psy, sb_w; + + if (~wp->flags & PANE_ZOOMED) { + window_pane_full_size_offset(wp, xoff, yoff, sx, sy); + return; + } + layout_get_pane_geometry(wp, 1, &pxoff, &pyoff, &psx, &psy); + + if (window_pane_scrollbar_reserve(wp)) + sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; + else + sb_w = 0; + if (w->sb_pos == PANE_SCROLLBARS_LEFT) + *xoff = pxoff - sb_w; + else + *xoff = pxoff; + *sx = psx + sb_w; + *yoff = pyoff; + *sy = psy; +} + /* * Find the pane directly above another. We build a list of those adjacent to * top edge and then choose the best. @@ -2232,7 +2310,7 @@ window_pane_find_up(struct window_pane *wp) list = NULL; size = 0; - window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); + window_pane_layout_size_offset(wp, &xoff, &yoff, &sx, &sy); edge = yoff; if (status == PANE_STATUS_TOP) { @@ -2250,7 +2328,7 @@ window_pane_find_up(struct window_pane *wp) right = xoff + (int)sx; TAILQ_FOREACH(next, &w->panes, entry) { - window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); + window_pane_layout_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; if (yoff + (int)sy + 1 != edge) @@ -2293,7 +2371,7 @@ window_pane_find_down(struct window_pane *wp) list = NULL; size = 0; - window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); + window_pane_layout_size_offset(wp, &xoff, &yoff, &sx, &sy); edge = yoff + (int)sy + 1; if (status == PANE_STATUS_TOP) { @@ -2311,7 +2389,7 @@ window_pane_find_down(struct window_pane *wp) right = wp->xoff + (int)wp->sx; TAILQ_FOREACH(next, &w->panes, entry) { - window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); + window_pane_layout_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; if (yoff != edge) @@ -2353,7 +2431,7 @@ window_pane_find_left(struct window_pane *wp) list = NULL; size = 0; - window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); + window_pane_layout_size_offset(wp, &xoff, &yoff, &sx, &sy); edge = xoff; if (edge == 0) @@ -2363,7 +2441,7 @@ window_pane_find_left(struct window_pane *wp) bottom = yoff + (int)sy; TAILQ_FOREACH(next, &w->panes, entry) { - window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); + window_pane_layout_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; if (xoff + (int)sx + 1 != edge) @@ -2405,7 +2483,7 @@ window_pane_find_right(struct window_pane *wp) list = NULL; size = 0; - window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy); + window_pane_layout_size_offset(wp, &xoff, &yoff, &sx, &sy); edge = xoff + (int)sx + 1; if (edge >= (int)w->sx) @@ -2415,7 +2493,7 @@ window_pane_find_right(struct window_pane *wp) bottom = wp->yoff + (int)wp->sy; TAILQ_FOREACH(next, &w->panes, entry) { - window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy); + window_pane_layout_size_offset(next, &xoff, &yoff, &sx, &sy); if (next == wp) continue; if (xoff != edge) @@ -2906,7 +2984,7 @@ window_pane_get_pane_lines(struct window_pane *wp) { struct options *oo; - if (!window_pane_is_floating(wp)) + if (!window_pane_is_unzoomed_float(wp)) oo = wp->window->options; else oo = wp->options; @@ -2937,7 +3015,7 @@ window_pane_get_pane_status(struct window_pane *wp) (wp->flags & PANE_ZOOMED)) return (PANE_STATUS_OFF); - if (!window_pane_is_floating(wp)) + if (!window_pane_is_unzoomed_float(wp)) return (window_get_pane_status(wp->window)); if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE) return (PANE_STATUS_OFF); @@ -2960,14 +3038,19 @@ window_pane_is_floating(struct window_pane *wp) return (1); } +/* + * Is the pane floating and drawn as a floating pane? A zoomed pane is a + * floating pane in the layout but fills the window like a tiled one. + */ int -window_pane_is_floating_with_hidden(struct window_pane *wp) +window_pane_is_unzoomed_float(struct window_pane *wp) { - struct layout_cell *lc = wp->layout_cell; - - if (lc == NULL) - lc = wp->saved_layout_cell; - if (lc == NULL || (lc->flags & LAYOUT_CELL_FLOATING) == 0) - return (0); - return (1); + return (window_pane_is_floating(wp) && (~wp->flags & PANE_ZOOMED)); +} + +/* Is the pane a float or a zoomed pane, that is above the tiled panes? */ +int +window_pane_is_raised(struct window_pane *wp) +{ + return (window_pane_is_floating(wp) || (wp->flags & PANE_ZOOMED)); }