Hide the zoomed panes when a covered tiled pane is activated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Michael Grant
2026-10-01 17:56:43 +01:00
parent 42f6f3ca26
commit 0ce3f20a2d
10 changed files with 164 additions and 32 deletions

View File

@@ -77,8 +77,18 @@ cmd_resize_pane_desktop(struct window *w)
shown = 1;
}
}
if (shown)
if (shown) {
/* Do not leave the active pane covered by a pane just shown. */
if (!window_pane_is_visible(w->active)) {
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (window_pane_is_visible(wp))
break;
}
if (wp != NULL)
window_set_active_pane(w, wp, 1);
}
return;
}
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == w->modal || (wp->flags & PANE_HIDDEN))

View File

@@ -121,10 +121,10 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
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);
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
}
redraw_invalidate_scene(w);
server_redraw_window(w);

View File

@@ -193,14 +193,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
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 (zwp != NULL) {
window_zoom_move(zwp, lastwp);
server_redraw_window(w);
}
if (window_set_active_pane(w, lastwp, 1) ||
zwp != NULL) {
cmd_find_from_winlink(current, wl, 0);
cmd_select_pane_redraw(w);
}
}
return (CMD_RETURN_NORMAL);
}
@@ -273,14 +274,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
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 (zwp != NULL) {
window_zoom_move(zwp, wp);
server_redraw_window(w);
}
if (window_set_active_pane(w, wp, 1) || zwp != NULL)
cmd_find_from_winlink_pane(current, wl, wp, 0);
cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w);
return (CMD_RETURN_NORMAL);
}

View File

@@ -147,11 +147,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
if (!visible && Zflag)
zwp = window_zoomed_pane(w);
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
if (zwp != NULL) {
window_zoom_move(zwp, wp);
server_redraw_window(w);
}
window_set_active_pane(w, wp, 1);
}
if (wl != NULL) {
session_set_current(s, wl);

View File

@@ -144,17 +144,32 @@ zoomed "$Y" 1
zoomed "$A" 1
window_zoomed "$X" 1
# Activating a tiled pane that is not zoomed unzooms every pane but the
# floats stay floats.
# Activating a tiled pane that is not zoomed hides the zoomed panes, as the
# desktop does, and shows the tiles. They stay zoomed, and floats that are not
# zoomed stay where they are.
run select-pane -t "$B"
check "$B" '#{pane_active}' 1
for p in "$A" "$B" "$X" "$Y"; do
for p in "$A" "$Y"; do
zoomed "$p" 1
check "$p" '#{pane_hidden_flag}' 1
done
for p in "$B" "$X"; do
zoomed "$p" 0
check "$p" '#{pane_hidden_flag}' 0
done
window_zoomed "$B" 0
check "$X" '#{pane_floating_flag}' 1
check "$Y" '#{pane_floating_flag}' 1
# The desktop shows them again.
run resize-pane -a -H -t "$A"
for p in "$A" "$Y"; do
zoomed "$p" 1
check "$p" '#{pane_hidden_flag}' 0
done
window_zoomed "$B" 1
run resize-pane -a -Z -t "$A"
# Activating a tiled pane that is itself zoomed does not unzoom it.
run resize-pane -Z -t "$A"
run select-pane -t "$A"

View File

@@ -237,6 +237,48 @@ run resize-pane -a -H -t "$A"
hidden "$A" 0
hidden "$X" 0
# Activating a tiled pane that is covered by zoomed panes hides them, as the
# desktop does: they stay zoomed, the tiles are shown and the pane is active.
reset
C=$(run split-window -dPF '#{pane_id}') || exit 1
run resize-pane -Z -t "$A"
run resize-pane -Z -t "$B"
run select-pane -t "$C"
check "$C" '#{pane_active}' 1
for p in "$A" "$B"; do
hidden "$p" 1
check "$p" '#{pane_zoomed_flag}' 1
done
hidden "$C" 0
check "$C" '#{window_zoomed_flag}' 0
# The last pane brings back the one that was zoomed.
run select-pane -l
check "$B" '#{pane_active}:#{pane_hidden_flag}:#{window_zoomed_flag}' '1:0:1'
# The desktop shows the others, and the active pane is not left covered.
run select-pane -t "$C"
run resize-pane -a -H -t "$A"
for p in "$A" "$B"; do
hidden "$p" 0
done
check "$A" '#{window_zoomed_flag}' 1
visible=$(run list-panes -F '#{pane_active}#{pane_zoomed_flag}' | grep -c '^11$')
[ "$visible" = 1 ] || fail "active pane is covered after the desktop"
# select-pane -Z moves the zoom to a covered tiled pane without hiding the old
# one, and so do last-pane -Z and rotate-window -Z.
reset
run resize-pane -Z -t "$A"
run select-pane -Z -t "$B"
check "$B" '#{pane_zoomed_flag}:#{pane_active}' '1:1'
check "$A" '#{pane_zoomed_flag}:#{pane_hidden_flag}' '0:0'
run select-pane -Z -t "$A"
run select-pane -Z -l
check "$B" '#{pane_zoomed_flag}:#{pane_hidden_flag}' '1:0'
check "$A" '#{pane_zoomed_flag}:#{pane_hidden_flag}' '0:0'
run rotate-window -Z
check "$A" '#{pane_hidden_flag}' 0
check "$B" '#{pane_hidden_flag}' 0
# Floating and tiling a hidden pane keeps it hidden.
reset
run resize-pane -H -t "$B"

View File

@@ -320,7 +320,7 @@ check_ok resize-pane -Z -t "$p0"
check_fmt "$p0" '#{window_zoomed_flag}' '0'
# Splitting while zoomed with -d leaves the zoom alone; without -d the new
# pane is active and covered, so selecting it unzooms.
# pane is active and covered, so selecting it hides the zoomed pane.
check_ok resize-pane -Z -t "$p0"
check_fmt 'P:0' '#{window_zoomed_flag}' '1'
check_ok split-window -d -v -t "$p0"
@@ -334,16 +334,25 @@ 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'
check_fmt "$p0" '#{pane_zoomed_flag}:#{pane_hidden_flag}' '1:1'
p6=$($TMUX display-message -p -t P:0 '#{pane_id}')
check_ok kill-pane -t "$p6"
check_ok resize-pane -a -H -t "$p0"
check_ok resize-pane -a -Z -t "$p0"
# Zoom and unzoom preserve the exact tiled layout. Selecting another pane
# without -Z unzooms, while -Z transfers zoom to the selected pane.
# without -Z hides the zoomed pane until the desktop shows it, while -Z
# transfers zoom to the selected pane.
layout=$(pane_layout P:0)
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
check_ok select-pane -t "$p2"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
check_fmt "$p0" '#{pane_zoomed_flag}:#{pane_hidden_flag}' '1:1'
check_ok resize-pane -a -H -t "$p0"
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok resize-pane -a -Z -t "$p0"
check_layout P:0 "$layout"
check_ok select-pane -t "$p0"
@@ -355,7 +364,7 @@ check_ok resize-pane -Z -t "$p2"
check_layout P:0 "$layout"
# Directional selection temporarily restores the full layout to find its
# neighbour, then follows the same unzoom or -Z transfer rules.
# neighbour, then follows the same hide or -Z transfer rules.
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
check_ok select-pane -t "$p0"
@@ -363,6 +372,9 @@ check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok select-pane -D -t "$p0"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
check_fmt "$p0" '#{pane_zoomed_flag}:#{pane_hidden_flag}' '1:1'
check_ok resize-pane -a -H -t "$p0"
check_ok resize-pane -a -Z -t "$p0"
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
@@ -379,6 +391,9 @@ check_ok resize-pane -Z -t "$p2"
check_ok select-pane -l -t P:0
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'0:0:1'
check_fmt "$p2" '#{pane_zoomed_flag}:#{pane_hidden_flag}' '1:1'
check_ok resize-pane -a -H -t "$p0"
check_ok resize-pane -a -Z -t "$p0"
check_ok select-pane -t "$p0"
check_ok select-pane -t "$p2"

View File

@@ -93,6 +93,21 @@ function noZoom( i)
fail("pane " aid[i] " is still zoomed")
}
}
function hideZoomed(t, i, id, want)
{
sameZ("")
for (i = 1; i <= nb; i++) {
id = bid[i]
if (id == t || !(id in ina))
continue
want = (bH[id] || bZ[id]) ? 1 : 0
if (aH[id] != want)
fail("pane " id " zoomed " bZ[id] " hidden " bH[id] \
" became hidden " aH[id])
}
if (aH[t])
fail("pane " t " is still hidden")
}
function covered(t, i, q)
{
if (bF[t] || bZ[t])
@@ -177,8 +192,9 @@ END {
if (op == "select") {
if (aA[T] != 1) fail("not active")
if (aH[T]) fail("still hidden")
if (covered(T)) noZoom(); else sameZ("")
sameF(""); sameH(T)
if (covered(T)) hideZoomed(T)
else { sameZ(""); sameH(T) }
sameF("")
} else if (op == "zoom") {
sameF("")
if (bZ[T]) {
@@ -195,12 +211,12 @@ END {
if (bH[T]) {
if (aH[T]) fail("not shown")
if (aA[T] != 1) fail("not active when shown")
if (covered(T)) noZoom(); else sameZ("")
if (covered(T)) hideZoomed(T)
else { sameZ(""); sameH(T) }
} else {
if (!aH[T]) fail("not hidden")
sameZ("")
sameZ(""); sameH(T)
}
sameH(T)
} else if (op == "desktop") {
sameF(""); sameZ("")
for (i = 1; i <= nb; i++) {
@@ -267,11 +283,12 @@ END {
sameF(""); sameH(""); noZoom()
} else {
# Moving around or changing layout: floating panes stay floating
# and nothing is hidden.
# and only a zoomed pane is hidden, when a covered pane is made
# active.
sameF("")
for (i = 1; i <= nb; i++) {
id = bid[i]
if (id in ina && aH[id] > bH[id])
if (id in ina && aH[id] > bH[id] && !bZ[id])
fail("pane " id " was hidden by this")
}
}

View File

@@ -337,14 +337,20 @@ check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p1"
check_ok rotate-window -D -t R:0
check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p0"
# Rotation without -Z unzooms. With -Z it preserves zoom and transfers it to
# the pane which arrives at the active position.
# Rotation without -Z hides the zoomed pane until the desktop shows it. With -Z
# it preserves zoom and transfers it to the pane which arrives at the active
# position.
layout=$($TMUX display-message -p -t R:0 '#{window_layout}')
check_ok resize-pane -Z -t "$p0"
check_ok rotate-window -U -t R:0
check_fmt "$p1" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'0:0:1'
check_fmt "$p0" '#{pane_zoomed_flag}:#{pane_hidden_flag}' '1:1'
check_ok resize-pane -a -H -t "$p0"
check_ok resize-pane -a -Z -t "$p0"
check_ok rotate-window -D -t R:0
check_ok select-pane -t "$p1"
check_ok select-pane -t "$p0"
check_fmt 'R:0' '#{window_layout}' "$layout"
check_ok resize-pane -Z -t "$p0"

View File

@@ -75,6 +75,8 @@ static void window_pane_free(struct window_pane *);
static void window_pane_scrollbar_timer(int, short, void *);
static int window_activate_pane(struct window *, struct window_pane *, int,
int);
static void window_hide_one(struct window_pane *, int);
static void window_hide_zoomed(struct window *);
static void window_pane_full_size_offset(struct window_pane *, int *, int *,
u_int *, u_int *);
@@ -775,14 +777,14 @@ window_activate_pane(struct window *w, struct window_pane *wp, int notify,
return (0);
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.
* The pane is covered by a zoomed pane. Raise it above the zoom
* if it can be, otherwise (a tiled pane that is not zoomed) hide
* the zoomed panes, as the desktop does, to show the tiles.
*/
if (window_pane_is_raised(wp))
window_raise_pane(wp);
else
window_unzoom(w, 1);
window_hide_zoomed(w);
} else if (raise) {
/* Raise the pane if the option says to. */
switch (options_get_number(w->options, "pane-raise-on-focus")) {
@@ -1041,6 +1043,30 @@ window_count_tiled_siblings(struct layout_cell *lc)
*/
void
window_hide_pane(struct window_pane *wp)
{
window_hide_one(wp, 1);
}
/*
* Hide every zoomed pane that is not hidden, marking them as the desktop does so
* that it shows them again. The focus is left for the caller to move.
*/
static void
window_hide_zoomed(struct window *w)
{
struct window_pane *wp;
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((wp->flags & PANE_ZOOMED) && (~wp->flags & PANE_HIDDEN)) {
window_hide_one(wp, 0);
wp->flags |= PANE_HIDDENALL;
}
}
}
/* Hide a pane, moving the focus away from it if it is active and asked to. */
static void
window_hide_one(struct window_pane *wp, int refocus)
{
struct window *w = wp->window;
struct layout_cell *lc = wp->layout_cell;
@@ -1068,7 +1094,7 @@ window_hide_pane(struct window_pane *wp)
layout_fix_panes(w, NULL);
/* Move the focus to the last used pane or the top one that is seen. */
if (wp == w->active) {
if (refocus && wp == w->active) {
TAILQ_FOREACH(wp1, &w->last_panes, sentry) {
if (wp1 != wp && window_pane_is_visible(wp1))
break;