From fed1e384ad7eb88cc6203e49d4efa8e5ed4edb59 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 4 Sep 2016 17:37:06 +0000 Subject: [PATCH] Add support for performing a full width split (with splitw -f), rather than splitting the current cell. From Stephen Kent. --- cmd-join-pane.c | 2 +- cmd-split-window.c | 7 +- layout.c | 275 ++++++++++++++++++++++++++++++++++++++------- tmux.1 | 7 ++ tmux.h | 2 +- 5 files changed, 245 insertions(+), 48 deletions(-) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index aa8c25f1..263e7dfc 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -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); diff --git a/cmd-split-window.c b/cmd-split-window.c index de6827a7..8d24c6c5 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -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; diff --git a/layout.c b/layout.c index bc9ad562..dffa08bd 100644 --- a/layout.c +++ b/layout.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2016 Stephen Kent * * 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); } diff --git a/tmux.1 b/tmux.1 index 1d13d0c7..bd4152b2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -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. diff --git a/tmux.h b/tmux.h index d1879e54..dda7a18e 100644 --- a/tmux.h +++ b/tmux.h @@ -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 */