diff --git a/layout.c b/layout.c index d5dcf1ccf..279f504af 100644 --- a/layout.c +++ b/layout.c @@ -31,8 +31,9 @@ * a cell which contains a list of cells, and 'leaf' to refer to a cell that * contains a window pane. A leaf is considered to be 'tiled' if it is to be * drawn as a part of the tiled layout. A 'neighbour' is a sibling that is also - * tiled. A cell's 'split' size refers to the side that is shortened when - * splitting it, determined by the parent's type. + * tiled or a node that contains a tiled leaf in a subtree. A cell's 'split' + * size refers to the side that is shortened when splitting it, determined by + * the parent's type. * * Each window has a pointer to the root of its layout tree (containing its * panes), every pane has a pointer back to the cell containing it, and each @@ -296,6 +297,27 @@ layout_cell_is_first_tiled(struct layout_cell *lc) return (lcchild == lc); } +static struct layout_cell * +layout_cell_get_first_tiled(struct layout_cell *lc) +{ + struct layout_cell *lcchild, *lcchild2; + + if (layout_cell_is_tiled(lc)) + return (lc); + if (lc->type == LAYOUT_WINDOWPANE) + return (NULL); + + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (layout_cell_is_tiled(lcchild)) + return (lcchild); + if (lcchild->type != LAYOUT_WINDOWPANE) { + lcchild2 = layout_cell_get_first_tiled(lcchild); + if (lcchild2 != NULL) + return (lcchild2); + } + } + return (NULL); +} /* Fix cell offsets for a child cell. */ static void @@ -609,6 +631,20 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc, } } +/* Resizes a cell to a specified size */ +void +layout_resize_set_size(struct window *w, struct layout_cell *lc, + enum layout_type type, u_int size) +{ + int change; + + if (type == LAYOUT_LEFTRIGHT) + change = size - lc->sx; + else + change = size - lc->sy; + layout_resize_adjust(w, lc, type, change); +} + /* Find and return the nearest neighbour to a cell in a specific direction. */ static struct layout_cell * layout_cell_get_neighbour_direction(struct layout_cell *lc, int direction) @@ -1210,6 +1246,75 @@ layout_replace_with_node(struct window *w, struct layout_cell *lc, return (lcparent); } +/* Checks if there is enough space for two new panes. */ +int +layout_split_check_space(struct window_pane *wp, struct layout_cell *lc, + enum layout_type type) +{ + struct style *sb_style = &wp->scrollbar_style; + u_int minimum, sx = lc->sx, sy = lc->sy; + int scrollbars, status; + + if (lc->flags & LAYOUT_CELL_FLOATING) + fatalx("floating cells cannot be split"); + + status = window_get_pane_status(wp->window); + scrollbars = options_get_number(wp->window->options, "pane-scrollbars"); + + switch (type) { + case LAYOUT_LEFTRIGHT: + if (scrollbars) { + minimum = PANE_MINIMUM * 2 + sb_style->width + + sb_style->pad; + } else + minimum = PANE_MINIMUM * 2 + 1; + if (sx < minimum) + return (0); + break; + case LAYOUT_TOPBOTTOM: + if (layout_add_horizontal_border(wp->window, lc, status)) + minimum = PANE_MINIMUM * 2 + 2; + else + minimum = PANE_MINIMUM * 2 + 1; + if (sy < minimum) + return (0); + break; + default: + fatalx("bad layout type"); + } + + return (1); +} + +/* Calculates the new cell sizes when splitting a pane. */ +void +layout_split_sizes(struct layout_cell *lc, int size, int before, + enum layout_type type, u_int *size1, u_int *size2, u_int *saved_size) +{ + u_int s1, s2, ss; + u_int sx = lc->sx, sy = lc->sy; + + if (type == LAYOUT_LEFTRIGHT) + ss = sx; + else + ss = sy; + if (size < 0) + s2 = ((ss + 1) / 2) - 1; + else if (before) + s2 = ss - size - 1; + else + s2 = size; + if (s2 < PANE_MINIMUM) + s2 = PANE_MINIMUM; + else if (s2 > sx - 2) + s2 = ss - 2; + s1 = ss - 1 - s2; + + *size1 = s1; + *size2 = s2; + *saved_size = ss; +} + /* * 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! @@ -1219,11 +1324,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, int flags) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; - struct style *sb_style = &wp->scrollbar_style; - u_int sx, sy, xoff, yoff, size1, size2, minimum; + u_int sx, sy, xoff, yoff, size1, size2; u_int new_size, saved_size, resize_first = 0; - int full_size = (flags & SPAWN_FULLSIZE), status; - int scrollbars; + int full_size = (flags & SPAWN_FULLSIZE); + int before = (flags & SPAWN_BEFORE); /* * If full_size is specified, add a new cell at the top of the window @@ -1233,8 +1337,6 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, lc = wp->window->layout_root; else lc = wp->layout_cell; - status = window_get_pane_status(wp->window); - scrollbars = options_get_number(wp->window->options, "pane-scrollbars"); /* Copy the old cell size. */ sx = lc->sx; @@ -1243,47 +1345,14 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, yoff = lc->yoff; /* Check there is enough space for the two new panes. */ - switch (type) { - case LAYOUT_LEFTRIGHT: - if (scrollbars) { - minimum = PANE_MINIMUM * 2 + sb_style->width + - sb_style->pad; - } else - minimum = PANE_MINIMUM * 2 + 1; - if (sx < minimum) - return (NULL); - break; - case LAYOUT_TOPBOTTOM: - if (layout_add_horizontal_border(wp->window, lc, status)) - minimum = PANE_MINIMUM * 2 + 2; - else - minimum = PANE_MINIMUM * 2 + 1; - if (sy < minimum) - return (NULL); - break; - default: - fatalx("bad layout type"); - } + if (!layout_split_check_space(wp, lc, type)) + return (NULL); /* * 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 (flags & SPAWN_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; + layout_split_sizes(lc, size, before, type, &size1, &size2, &saved_size); /* Which size are we using? */ if (flags & SPAWN_BEFORE) @@ -1729,3 +1798,67 @@ layout_remove_tile(struct window *w, struct layout_cell *lc) layout_set_size(lc, 0, 0, 0, 0); return (1); } + +/* + * Inserts a cell back into the tiled layout by taking half the space from its + * nearest neighbour. + */ +int +layout_insert_tile(struct window *w, struct layout_cell *lc) +{ + struct layout_cell *lcneighbour, *lctiled, *lcparent; + enum layout_type type; + u_int size1, size2, saved_size; + + if (lc == NULL) + fatalx("layout cell cannot be null when tiling"); + + lcparent = lc->parent; + if (lc->flags & LAYOUT_CELL_FLOATING) + return (1); + + if (lcparent == NULL) { + /* Only pane in the layout. */ + layout_set_size(lc, w->sx, w->sy, 0, 0); + return (1); + } + + type = lcparent->type; + lcneighbour = layout_cell_get_neighbour(lc); + if (lcneighbour == NULL) { + /* + * This will become the only visible cell in the parent. + * Tile the parent, then set the child's 'split' size. + */ + layout_insert_tile(w, lcparent); + if (type == LAYOUT_LEFTRIGHT) + size1 = lcparent->sx; + else + size1 = lcparent->sy; + layout_resize_set_size(w, lc, type, size1); + } else { + /* + * If the neighbour is a node, a tiled child in the subtree of + * the neighbour is needed to check for space. + */ + lctiled = layout_cell_get_first_tiled(lcneighbour); + if (!layout_split_check_space(lctiled->wp, lcneighbour, type)) + return (0); + layout_split_sizes(lcneighbour, -1, 0, type, &size1, &size2, + &saved_size); + layout_resize_set_size(w, lc, type, size1); + layout_resize_set_size(w, lcneighbour, type, size2); + } + + /* Setting opposite of the 'split' size to that of the parent. */ + if (lcparent->type == LAYOUT_LEFTRIGHT) { + size1 = lcparent->sy; + type = LAYOUT_TOPBOTTOM; + } else { + size1 = lcparent->sx; + type = LAYOUT_LEFTRIGHT; + } + layout_resize_set_size(w, lc, type, size1); + + return (1); +} diff --git a/tmux.h b/tmux.h index b03525c7e..869936e68 100644 --- a/tmux.h +++ b/tmux.h @@ -3629,6 +3629,8 @@ void layout_fix_offsets(struct window *); void layout_fix_panes(struct window *, struct window_pane *); void layout_resize_adjust(struct window *, struct layout_cell *, enum layout_type, int); +void layout_resize_set_size(struct window *, struct layout_cell *, + enum layout_type, u_int); struct layout_cell *layout_cell_get_neighbour(struct layout_cell *); void layout_init(struct window *, struct window_pane *); void layout_free(struct window *); @@ -3643,6 +3645,10 @@ int layout_resize_floating_pane_to(struct window_pane *, enum layout_type, u_int, char **); void layout_assign_pane(struct layout_cell *, struct window_pane *, int); +int layout_split_check_space(struct window_pane *, + struct layout_cell *, enum layout_type); +void layout_split_sizes(struct layout_cell *, int, int, + enum layout_type, u_int *, u_int *, u_int *); struct layout_cell *layout_replace_with_node(struct window *, struct layout_cell *, enum layout_type); struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, @@ -3661,6 +3667,7 @@ int layout_floating_args_parse(struct cmdq_item *, struct args *, enum pane_lines, struct window *, u_int *, u_int *, int *, int *, char **); int layout_remove_tile(struct window *, struct layout_cell *); +int layout_insert_tile(struct window *, struct layout_cell *); /* layout-custom.c */ char *layout_dump(struct window *, struct layout_cell *);