Make split-window inside a floating pane work more nicely and not

immediately create an overlapping pane, from Dane Jensen.
This commit is contained in:
nicm
2026-07-13 10:03:27 +00:00
committed by tmux update bot
parent 6d227a8479
commit 2b091d91d3
5 changed files with 194 additions and 36 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.69 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD: cmd-join-pane.c,v 1.70 2026/07/13 10:03:27 nicm Exp $ */
/* /*
* Copyright (c) 2011 George Nachman <tmux@georgester.com> * Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -459,6 +459,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (args_has(args, 'h'))
flags |= SPAWN_HORIZONTAL;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause); lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "size or position %s", cause); cmdq_error(item, "size or position %s", cause);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-split-window.c,v 1.141 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD: cmd-split-window.c,v 1.142 2026/07/13 10:03:27 nicm Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -62,8 +62,8 @@ const struct cmd_entry cmd_split_window_entry = {
.usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] " .usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] " "[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] " "[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " " "[-R inactive-border-style] [-T title] "
"[shell-command [argument ...]]", CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -96,14 +96,23 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_new_pane_entry) if (cmd_get_entry(self) == &cmd_new_pane_entry)
is_floating = !args_has(args, 'L'); is_floating = !args_has(args, 'L');
else else {
is_floating = window_pane_is_floating(wp); is_floating = window_pane_is_floating(wp);
flags |= SPAWN_SPLIT;
}
flags = is_floating ? SPAWN_FLOATING : 0; if (is_floating)
flags |= SPAWN_FLOATING;
if (args_has(args, 'h'))
flags |= SPAWN_HORIZONTAL;
if (args_has(args, 'b')) if (args_has(args, 'b'))
flags |= SPAWN_BEFORE; flags |= SPAWN_BEFORE;
if (args_has(args, 'f')) if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE; flags |= SPAWN_FULLSIZE;
if (args_has(args, 'd'))
flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
flags |= SPAWN_ZOOM;
input = args_has(args, 'I'); input = args_has(args, 'I');
if (input || (count == 1 && *args_string(args, 0) == '\0')) if (input || (count == 1 && *args_string(args, 0) == '\0'))
@@ -131,9 +140,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
} }
} }
if (is_floating) if (flags & SPAWN_FLOATING) {
lc = layout_get_floating_cell(item, args, lines, w, wp, &cause); lc = layout_get_floating_cell(item, args, lines, w, wp, flags,
else &cause);
} else
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause); lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
@@ -159,12 +169,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.idx = -1; sc.idx = -1;
sc.cwd = args_get(args, 'c'); sc.cwd = args_get(args, 'c');
sc.flags = flags; sc.flags = flags;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
sc.flags |= SPAWN_ZOOM;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause); cmdq_error(item, "create pane failed: %s", cause);
@@ -240,10 +245,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
break; break;
} }
} }
if (!args_has(args, 'd')) if (~flags & SPAWN_DETACHED)
cmd_find_from_winlink_pane(current, wl, new_wp, 0); cmd_find_from_winlink_pane(current, wl, new_wp, 0);
if (!is_floating) { if (~flags & SPAWN_FLOATING) {
window_pop_zoom(wp->window); window_pop_zoom(wp->window);
server_redraw_window(wp->window); server_redraw_window(wp->window);
} }

158
layout.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: layout.c,v 1.93 2026/07/10 13:38:45 nicm Exp $ */ /* $OpenBSD: layout.c,v 1.94 2026/07/13 10:03:27 nicm Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1598,7 +1598,7 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
struct window *w, struct window_pane *wp, int flags, char **cause) struct window *w, struct window_pane *wp, int flags, char **cause)
{ {
struct layout_cell *lc; struct layout_cell *lc;
enum layout_type type; enum layout_type type = LAYOUT_TOPBOTTOM;
u_int curval; u_int curval;
int size = -1; int size = -1;
char *error = NULL; char *error = NULL;
@@ -1608,12 +1608,11 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
return (NULL); return (NULL);
} }
type = LAYOUT_TOPBOTTOM; if (flags & SPAWN_HORIZONTAL)
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT; type = LAYOUT_LEFTRIGHT;
if (args_has(args, 'l') || args_has(args, 'p')) { if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) { if (flags & SPAWN_FULLSIZE) {
if (type == LAYOUT_TOPBOTTOM) if (type == LAYOUT_TOPBOTTOM)
curval = w->sy; curval = w->sy;
else else
@@ -1641,12 +1640,7 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
return (NULL); return (NULL);
} }
if (args_has(args, 'b')) window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
lc = layout_split_pane(wp, type, size, flags); lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL) if (lc == NULL)
*cause = xstrdup("no space for a new pane"); *cause = xstrdup("no space for a new pane");
@@ -1656,17 +1650,24 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
struct layout_cell * struct layout_cell *
layout_get_floating_cell(struct cmdq_item *item, struct args *args, layout_get_floating_cell(struct cmdq_item *item, struct args *args,
enum pane_lines lines, struct window *w, struct window_pane *wp, enum pane_lines lines, struct window *w, struct window_pane *wp, int flags,
char **cause) char **cause)
{ {
struct layout_cell *lcnew; struct layout_cell *lcnew, *lc = wp->layout_cell;
struct layout_geometry fg; struct layout_geometry fg;
layout_geometry_init(&fg); layout_geometry_init(&fg);
if (layout_floating_args_parse(item, args, lines, w, &fg, cause) != 0) if (flags & SPAWN_SPLIT) {
return (NULL); if (layout_split_floating_cell(lc, w, &fg, lines, flags, cause)
!= 0)
return (NULL);
} else {
if (layout_floating_args_parse(item, args, lines, w, &fg, cause)
!= 0)
return (NULL);
}
window_push_zoom(wp->window, 1, args_has(args, 'Z')); window_push_zoom(wp->window, 1, (flags & SPAWN_ZOOM));
lcnew = layout_floating_pane(w, wp, &fg); lcnew = layout_floating_pane(w, wp, &fg);
return (lcnew); return (lcnew);
} }
@@ -1766,6 +1767,131 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
return (0); return (0);
} }
int
layout_split_floating_cell(struct layout_cell *lc, struct window *w,
struct layout_geometry *out, enum pane_lines lines, int flags,
char **cause)
{
struct layout_geometry old, new;
int tborder = 1, bborder = w->sy - 1;
int lborder = 3, rborder = w->sx - 3;
int border = lines != PANE_LINES_NONE ? 1 : 0;
int size, space;
/* First, move the target cell in-bounds. */
memcpy(&old, &lc->g, sizeof old);
if (lborder > old.xoff - border)
old.xoff = lborder + border;
if (rborder < old.xoff + (int)old.sx + border)
old.xoff = rborder - (int)old.sx - border;
if (tborder > old.yoff - border)
old.yoff = tborder + border;
if (bborder < old.yoff + (int)old.sy + border)
old.yoff = bborder - (int)old.sy - border;
/* Move the new cell to its ideal position. */
memcpy(&new, &old, sizeof new);
if (flags & SPAWN_HORIZONTAL) {
if (flags & SPAWN_BEFORE)
new.xoff -= old.sx + 2 * border;
else
new.xoff += old.sx + 2 * border;
} else {
if (flags & SPAWN_BEFORE)
new.yoff -= old.sy + 2 * border;
else
new.yoff += old.sy + 2 * border;
}
/*
* The position of the new cell is checked to see if it is in bounds.
* If it isn't, the availible space is split and equally given to both
* cells. Only one border is check because the target cell is in bounds
* already.
*/
if (lborder > new.xoff - border) {
/*
* The space for both panes is calculated. Since the offsets are
* associated to where pane contents start, we remove pane
* borders from the space. '1' is added in case the space is
* odd.
*/
space = old.xoff + old.sx - lborder - 3 * border + 1;
size = space / 2;
new.sx = size;
old.sx = size;
new.xoff = lborder + border;
old.xoff = new.xoff + new.sx + 2 * border;
/*
* If the original space was to be odd (now even), subtract 1
* from the rightmost cell
*/
if (space % 2 == 0)
old.sx -= 1;
} else if (rborder < new.xoff + (int)new.sx + border) {
space = rborder - old.xoff - 3 * border + 1;
size = space / 2;
new.sx = size;
old.sx = size;
new.xoff = old.xoff + old.sx + 2 * border;
if (space % 2 == 0)
new.sx -= 1;
} else if (tborder > new.yoff - border) {
space = old.sy + old.yoff - tborder - 3 * border + 1;
size = space / 2;
new.sy = size;
old.sy = size;
new.yoff = tborder + border;
old.yoff = new.yoff + new.sy + 2 * border;
if (space % 2 == 0)
old.sy -= 1;
} else if (bborder < new.yoff + (int)new.sy + border) {
space = bborder - old.yoff - 3 * border + 1;
size = space / 2;
new.sy = size;
old.sy = size;
new.yoff = old.yoff + old.sy + 2 * border;
if (space % 2 == 0)
new.sy -= 1;
}
/*
* Expand the cell to occupy the whole availible space where it was
* spawned.
*/
if (flags & SPAWN_FULLSIZE) {
if (flags & SPAWN_HORIZONTAL) {
new.yoff = tborder + border;
new.sy = bborder - tborder - 2 * border;
if (flags & SPAWN_BEFORE) {
new.xoff = lborder + border;
new.sx = old.xoff - new.xoff - 2 * border;
} else {
new.sx = rborder - new.xoff - border;
}
} else {
new.xoff = lborder + border;
new.sx = rborder - lborder - 2 * border;
if (flags & SPAWN_BEFORE) {
new.yoff = tborder + border;
new.sy = old.yoff - new.yoff - 2 * border;
} else {
new.sy = bborder - new.yoff - border;
}
}
}
if (new.sx < PANE_MINIMUM || new.sy < PANE_MINIMUM ||
old.sx < PANE_MINIMUM || old.sy < PANE_MINIMUM) {
*cause = xstrdup("no space for a new pane");
return (-1);
}
layout_set_size(lc, old.sx, old.sy, old.xoff, old.yoff);
memcpy(out, &new, sizeof *out);
return (0);
}
/* /*
* Removes a cell from the tiled layout by giving the cell's space to the * Removes a cell from the tiled layout by giving the cell's space to the
* nearest neighbour. * nearest neighbour.

19
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1132 2026/07/10 15:20:06 nicm Exp $ .\" $OpenBSD: tmux.1,v 1.1133 2026/07/13 10:03:27 nicm Exp $
.\" .\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\" .\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd $Mdocdate: July 10 2026 $ .Dd $Mdocdate: July 13 2026 $
.Dt TMUX 1 .Dt TMUX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -4075,6 +4075,21 @@ or full window width (with
.Fl v ) , .Fl v ) ,
instead of splitting the active pane. instead of splitting the active pane.
.Pp .Pp
If
.Ar target\-pane
is floating, a new floating pane is created adjacent to
.Ar target\-pane
with equal sizes.
The position is determined by the
.Fl h ,
.Fl v ,
and
.Fl b
flags.
If any of the new pane would not be visible on the screen,
.Ar target\-pane
and the new pane are resized to fit.
.Pp
.Fl k .Fl k
keeps the pane open after the optional keeps the pane open after the optional
.Ar shell\-command .Ar shell\-command

9
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1396 2026/07/10 15:45:11 nicm Exp $ */ /* $OpenBSD: tmux.h,v 1.1397 2026/07/13 10:03:27 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2455,6 +2455,8 @@ struct spawn_context {
#define SPAWN_EMPTY 0x40 #define SPAWN_EMPTY 0x40
#define SPAWN_ZOOM 0x80 #define SPAWN_ZOOM 0x80
#define SPAWN_FLOATING 0x100 #define SPAWN_FLOATING 0x100
#define SPAWN_HORIZONTAL 0x200
#define SPAWN_SPLIT 0x400
}; };
/* Paste buffer. */ /* Paste buffer. */
@@ -3790,10 +3792,13 @@ struct layout_cell *layout_get_tiled_cell(struct cmdq_item *, struct args *,
struct window *, struct window_pane *, int, char **); struct window *, struct window_pane *, int, char **);
struct layout_cell *layout_get_floating_cell(struct cmdq_item *, struct args *, struct layout_cell *layout_get_floating_cell(struct cmdq_item *, struct args *,
enum pane_lines, struct window *, struct window_pane *, enum pane_lines, struct window *, struct window_pane *,
char **); int, char **);
int layout_floating_args_parse(struct cmdq_item *, struct args *, int layout_floating_args_parse(struct cmdq_item *, struct args *,
enum pane_lines, struct window *, struct layout_geometry *, enum pane_lines, struct window *, struct layout_geometry *,
char **); char **);
int layout_split_floating_cell(struct layout_cell *,
struct window *, struct layout_geometry *, enum pane_lines,
int, char **);
int layout_remove_tile(struct window *, struct layout_cell *); int layout_remove_tile(struct window *, struct layout_cell *);
int layout_insert_tile(struct window *, struct layout_cell *); int layout_insert_tile(struct window *, struct layout_cell *);