summaryrefslogtreecommitdiffstats
path: root/layout.c
diff options
context:
space:
mode:
authornicm <nicm>2016-09-04 17:37:06 +0000
committernicm <nicm>2016-09-04 17:37:06 +0000
commitfed1e384ad7eb88cc6203e49d4efa8e5ed4edb59 (patch)
tree7ec149e5a721c1e0746a0c61b3bddb19d52b63c3 /layout.c
parent2627ab322e0e8dffbf86b1c2eb969139a8062174 (diff)
Add support for performing a full width split (with splitw -f), rather
than splitting the current cell. From Stephen Kent.
Diffstat (limited to 'layout.c')
-rw-r--r--layout.c275
1 files changed, 232 insertions, 43 deletions
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 <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);
}