Add support for performing a full width split (with splitw -f), rather

than splitting the current cell. From Stephen Kent.
pull/526/head
nicm 2016-09-04 17:37:06 +00:00
parent 2627ab322e
commit fed1e384ad
5 changed files with 245 additions and 48 deletions

View File

@ -125,7 +125,7 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window)
else
size = (dst_wp->sx * percentage) / 100;
}
lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b'));
lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b'), 0);
if (lc == NULL) {
cmdq_error(cmdq, "create pane failed: pane too small");
return (CMD_RETURN_ERROR);

View File

@ -39,8 +39,8 @@ const struct cmd_entry cmd_split_window_entry = {
.name = "split-window",
.alias = "splitw",
.args = { "bc:dF:l:hp:Pt:v", 0, -1 },
.usage = "[-bdhvP] [-c start-directory] [-F format] "
.args = { "bc:dfF:l:hp:Pt:v", 0, -1 },
.usage = "[-bdfhvP] [-c start-directory] [-F format] "
"[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]",
.tflag = CMD_PANE,
@ -131,7 +131,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL;
lc = layout_split_pane(wp, type, size, args_has(args, 'b'));
lc = layout_split_pane(wp, type, size, args_has(args, 'b'),
args_has(args, 'f'));
if (lc == NULL) {
cause = xstrdup("pane too small");
goto error;

275
layout.c
View File

@ -2,6 +2,7 @@
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
* Copyright (c) 2016 Stephen Kent <smkent@smkent.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -39,6 +40,13 @@ static int layout_resize_pane_grow(struct window *, struct layout_cell *,
static int layout_resize_pane_shrink(struct window *, struct layout_cell *,
enum layout_type, int);
static int layout_need_status(struct layout_cell *, int);
static u_int layout_new_pane_size(struct window *, u_int,
struct layout_cell *, enum layout_type, u_int, u_int,
u_int);
static int layout_set_size_check(struct window *, struct layout_cell *,
enum layout_type, int);
static void layout_resize_child_cells(struct window *,
struct layout_cell *);
struct layout_cell *
layout_create_cell(struct layout_cell *lcparent)
@ -267,17 +275,17 @@ u_int
layout_count_cells(struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int n;
u_int count;
switch (lc->type) {
case LAYOUT_WINDOWPANE:
return (1);
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
n = 0;
count = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry)
n += layout_count_cells(lcchild);
return (n);
count += layout_count_cells(lcchild);
return (count);
default:
fatalx("bad layout type");
}
@ -652,18 +660,166 @@ layout_assign_pane(struct layout_cell *lc, struct window_pane *wp)
layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
}
/* Calculate the new pane size for resized parent. */
static u_int
layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc,
enum layout_type type, u_int size, u_int count_left, u_int size_left)
{
u_int new_size, min, max, available;
/* If this is the last cell, it can take all of the remaining size. */
if (count_left == 1)
return (size_left);
/* How much is available in this parent? */
available = layout_resize_check(w, lc, type);
/*
* Work out the minimum size of this cell and the new size
* proportionate to the previous size.
*/
min = (PANE_MINIMUM + 1) * (count_left - 1);
if (type == LAYOUT_LEFTRIGHT) {
if (lc->sx - available > min)
min = lc->sx - available;
new_size = (lc->sx * size) / previous;
} else {
if (lc->sy - available > min)
min = lc->sy - available;
new_size = (lc->sy * size) / previous;
}
/* Check against the maximum and minimum size. */
max = size_left - min;
if (new_size > max)
new_size = max;
if (new_size < PANE_MINIMUM)
new_size = PANE_MINIMUM;
return (new_size);
}
/* Check if the cell and all its children can be resized to a specific size. */
static int
layout_set_size_check(struct window *w, struct layout_cell *lc,
enum layout_type type, int size)
{
struct layout_cell *lcchild;
u_int new_size, available, previous, count, idx;
/* Cells with no children must just be bigger than minimum. */
if (lc->type == LAYOUT_WINDOWPANE)
return (size >= PANE_MINIMUM);
available = size;
/* Count number of children. */
count = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry)
count++;
/* Check new size will work for each child. */
if (lc->type == type) {
if (type == LAYOUT_LEFTRIGHT)
previous = lc->sx;
else
previous = lc->sy;
idx = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
new_size = layout_new_pane_size(w, previous, lcchild,
type, size, count - idx, available);
if (new_size > available)
return (0);
available -= (new_size + 1);
if (!layout_set_size_check(w, lcchild, type, new_size))
return (0);
idx++;
}
} else {
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->type == LAYOUT_WINDOWPANE)
continue;
if (!layout_set_size_check(w, lcchild, type, size))
return (0);
}
}
return (1);
}
/* Resize all child cells to fit within the current cell. */
static void
layout_resize_child_cells(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int previous, available, count, idx;
if (lc->type == LAYOUT_WINDOWPANE)
return;
/* What is the current size used? */
count = 0;
previous = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
count++;
if (lc->type == LAYOUT_LEFTRIGHT)
previous += lcchild->sx;
else if (lc->type == LAYOUT_TOPBOTTOM)
previous += lcchild->sy;
}
previous += (count - 1);
/* And how much is available? */
available = 0;
if (lc->type == LAYOUT_LEFTRIGHT)
available = lc->sx;
else if (lc->type == LAYOUT_TOPBOTTOM)
available = lc->sy;
/* Resize children into the new size. */
idx = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lc->type == LAYOUT_TOPBOTTOM) {
lcchild->sx = lc->sx;
lcchild->xoff = lc->xoff;
} else {
lcchild->sx = layout_new_pane_size(w, previous, lcchild,
lc->type, lc->sx, count - idx, available);
available -= (lcchild->sx + 1);
}
if (lc->type == LAYOUT_LEFTRIGHT)
lcchild->sy = lc->sy;
else {
lcchild->sy = layout_new_pane_size(w, previous, lcchild,
lc->type, lc->sy, count - idx, available);
available -= (lcchild->sy + 1);
}
layout_resize_child_cells(w, lcchild);
idx++;
}
}
/*
* Split a pane into two. size is a hint, or -1 for default half/half
* split. This must be followed by layout_assign_pane before much else happens!
**/
*/
struct layout_cell *
layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
int insert_before)
int insert_before, int full_size)
{
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
u_int sx, sy, xoff, yoff, size1, size2;
u_int new_size, saved_size, resize_first = 0;
lc = wp->layout_cell;
/*
* If full_size is specified, add a new cell at the top of the window
* layout. Otherwise, split the cell for the current pane.
*/
if (full_size)
lc = wp->window->layout_root;
else
lc = wp->layout_cell;
/* Copy the old cell size. */
sx = lc->sx;
@ -685,19 +841,75 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
fatalx("bad layout type");
}
/*
* Calculate new cell sizes. size is the target size or -1 for middle
* split, size1 is the size of the top/left and size2 the bottom/right.
*/
if (type == LAYOUT_LEFTRIGHT)
saved_size = sx;
else
saved_size = sy;
if (size < 0)
size2 = ((saved_size + 1) / 2) - 1;
else if (insert_before)
size2 = saved_size - size - 1;
else
size2 = size;
if (size2 < PANE_MINIMUM)
size2 = PANE_MINIMUM;
else if (size2 > saved_size - 2)
size2 = saved_size - 2;
size1 = saved_size - 1 - size2;
/* Which size are we using? */
if (insert_before)
new_size = size2;
else
new_size = size1;
/* Confirm there is enough space for full size pane. */
if (full_size && !layout_set_size_check(wp->window, lc, type, new_size))
return (NULL);
if (lc->parent != NULL && lc->parent->type == type) {
/*
* If the parent exists and is of the same type as the split,
* create a new cell and insert it after this one.
*/
/* Create the new child cell. */
lcparent = lc->parent;
lcnew = layout_create_cell(lcparent);
if (insert_before)
TAILQ_INSERT_BEFORE(lc, lcnew, entry);
else
TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
} else if (full_size && lc->parent == NULL && lc->type == type) {
/*
* If the new full size pane is the same type as the root
* split, insert the new pane under the existing root cell
* instead of creating a new root cell. The existing layout
* must be resized before inserting the new cell.
*/
if (lc->type == LAYOUT_LEFTRIGHT) {
lc->sx = new_size;
layout_resize_child_cells(wp->window, lc);
lc->sx = saved_size;
} else if (lc->type == LAYOUT_TOPBOTTOM) {
lc->sy = new_size;
layout_resize_child_cells(wp->window, lc);
lc->sy = saved_size;
}
resize_first = 1;
/* Create the new cell. */
lcnew = layout_create_cell(lc);
if (lc->type == LAYOUT_LEFTRIGHT)
layout_set_size(lcnew, new_size, sy, 0, 0);
else if (lc->type == LAYOUT_TOPBOTTOM)
layout_set_size(lcnew, sx, new_size, 0, 0);
if (insert_before)
TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
else
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
} else {
/*
* Otherwise create a new parent and insert it.
@ -731,46 +943,23 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
lc2 = lcnew;
}
/* Set new cell sizes. size is the target size or -1 for middle split,
* size1 is the size of the top/left and size2 the bottom/right.
/*
* Set new cell sizes. size1 is the size of the top/left and size2 the
* bottom/right.
*/
switch (type) {
case LAYOUT_LEFTRIGHT:
if (size < 0)
size2 = ((sx + 1) / 2) - 1;
else if (insert_before)
size2 = sx - size - 1;
else
size2 = size;
if (size2 < PANE_MINIMUM)
size2 = PANE_MINIMUM;
else if (size2 > sx - 2)
size2 = sx - 2;
size1 = sx - 1 - size2;
if (!resize_first && type == LAYOUT_LEFTRIGHT) {
layout_set_size(lc1, size1, sy, xoff, yoff);
layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
break;
case LAYOUT_TOPBOTTOM:
if (size < 0)
size2 = ((sy + 1) / 2) - 1;
else if (insert_before)
size2 = sy - size - 1;
else
size2 = size;
if (size2 < PANE_MINIMUM)
size2 = PANE_MINIMUM;
else if (size2 > sy - 2)
size2 = sy - 2;
size1 = sy - 1 - size2;
} else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
layout_set_size(lc1, sx, size1, xoff, yoff);
layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
break;
default:
fatalx("bad layout type");
}
/* Assign the panes. */
layout_make_leaf(lc, wp);
if (full_size) {
if (!resize_first)
layout_resize_child_cells(wp->window, lc);
layout_fix_offsets(wp->window->layout_root);
} else
layout_make_leaf(lc, wp);
return (lcnew);
}

7
tmux.1
View File

@ -2011,6 +2011,13 @@ The
.Fl b
option causes the new pane to be created to the left of or above
.Ar target-pane .
The
.Fl f
option creates a new pane spanning the full window height (with
.Fl h )
or full window width (with
.Fl v ) ,
instead of splitting the active pane.
All other options have the same meaning as for the
.Ic new-window
command.

2
tmux.h
View File

@ -2205,7 +2205,7 @@ void layout_resize_pane_to(struct window_pane *, enum layout_type,
u_int);
void layout_assign_pane(struct layout_cell *, struct window_pane *);
struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type,
int, int);
int, int, int);
void layout_close_pane(struct window_pane *);
/* layout-custom.c */