commands/resize: make resize consider all siblings

Fixes a compatibility issue with i3 where resizing works as described
hereafter:
> Direction can either be one of up, down, left or right. Or you can be
> less specific and use width or height, in which case i3 will take/give
> space from all the other containers.

Sway previously considered only the direct neighbours not all siblings.

Fixes #5936
This commit is contained in:
Tim Hallmann 2022-02-03 19:35:55 +01:00
parent ee7668c1f2
commit c47b8e4a85
2 changed files with 47 additions and 56 deletions

View file

@ -76,61 +76,46 @@ void container_resize_tiled(struct sway_container *con,
} }
// For HORIZONTAL or VERTICAL, we are growing in two directions so select // For HORIZONTAL or VERTICAL, we are growing in two directions so select
// both adjacent siblings. For RIGHT or DOWN, just select the next sibling. // all adjacent siblings. For RIGHT or DOWN or LEFT or UP select just the
// For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to // previous or next sibling.
// the previous sibling. list_t *resize = create_list();
struct sway_container *prev = NULL;
struct sway_container *next = NULL;
list_t *siblings = container_get_siblings(con); list_t *siblings = container_get_siblings(con);
int index = container_sibling_index(con); int index = container_sibling_index(con);
if (axis == AXIS_HORIZONTAL || axis == AXIS_VERTICAL) { if (axis == AXIS_HORIZONTAL || axis == AXIS_VERTICAL) {
if (index == 0) { list_cat(resize, siblings);
next = siblings->items[1];
} else if (index == siblings->length - 1) {
// Convert edge to top/left
next = con;
con = siblings->items[index - 1];
amount = -amount;
} else {
prev = siblings->items[index - 1];
next = siblings->items[index + 1];
}
} else if (axis == WLR_EDGE_TOP || axis == WLR_EDGE_LEFT) { } else if (axis == WLR_EDGE_TOP || axis == WLR_EDGE_LEFT) {
if (!sway_assert(index > 0, "Didn't expect first child")) { if (!sway_assert(index > 0, "Didn't expect first child")) {
return; goto cleanup;
} }
next = con; list_add(resize, siblings->items[index - 1]);
con = siblings->items[index - 1]; list_add(resize, con);
amount = -amount;
} else { } else {
if (!sway_assert(index < siblings->length - 1, if (!sway_assert(index < siblings->length - 1,
"Didn't expect last child")) { "Didn't expect last child")) {
return; goto cleanup;
} }
next = siblings->items[index + 1]; list_add(resize, con);
list_add(resize, siblings->items[index + 1]);
} }
// Apply new dimensions // Apply new dimensions
int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; int sibling_amount = ceil((double)amount / (double)(resize->length - 1));
if (is_horizontal(axis)) { if (is_horizontal(axis)) {
if (con->pending.width + amount < MIN_SANE_W) { for(int i = 0; i < resize->length; i++) {
return; struct sway_container *sibling = resize->items[i];
} double change = sibling == con ? amount : -sibling_amount;
if (next->pending.width - sibling_amount < MIN_SANE_W) { if(sibling->pending.width + change < MIN_SANE_W) {
return; goto cleanup;
} }
if (prev && prev->pending.width - sibling_amount < MIN_SANE_W) {
return;
} }
if (con->child_total_width <= 0) { if (con->child_total_width <= 0) {
return; goto cleanup;
} }
// We're going to resize so snap all the width fractions to full pixels // We're going to resize so snap all the width fractions to full pixels
// to avoid rounding issues // to avoid rounding issues
list_t *siblings = container_get_siblings(con);
for (int i = 0; i < siblings->length; ++i) { for (int i = 0; i < siblings->length; ++i) {
struct sway_container *con = siblings->items[i]; struct sway_container *con = siblings->items[i];
con->width_fraction = con->pending.width / con->child_total_width; con->width_fraction = con->pending.width / con->child_total_width;
@ -138,30 +123,27 @@ void container_resize_tiled(struct sway_container *con,
double amount_fraction = (double)amount / con->child_total_width; double amount_fraction = (double)amount / con->child_total_width;
double sibling_amount_fraction = double sibling_amount_fraction =
prev ? amount_fraction / 2.0 : amount_fraction; amount_fraction / (double)(resize->length - 1);
con->width_fraction += amount_fraction; for(int i = 0; i < resize->length; i++) {
next->width_fraction -= sibling_amount_fraction; struct sway_container *sibling = resize->items[i];
if (prev) { sibling->width_fraction +=
prev->width_fraction -= sibling_amount_fraction; sibling == con ? amount_fraction : -sibling_amount_fraction;
} }
} else { } else {
if (con->pending.height + amount < MIN_SANE_H) { for(int i = 0; i < resize->length; i++) {
return; struct sway_container *sibling = resize->items[i];
} double change = sibling == con ? amount : -sibling_amount;
if (next->pending.height - sibling_amount < MIN_SANE_H) { if(sibling->pending.height + change < MIN_SANE_H) {
return; goto cleanup;
} }
if (prev && prev->pending.height - sibling_amount < MIN_SANE_H) {
return;
} }
if (con->child_total_height <= 0) { if (con->child_total_height <= 0) {
return; goto cleanup;
} }
// We're going to resize so snap all the height fractions to full pixels // We're going to resize so snap all the height fractions to full pixels
// to avoid rounding issues // to avoid rounding issues
list_t *siblings = container_get_siblings(con);
for (int i = 0; i < siblings->length; ++i) { for (int i = 0; i < siblings->length; ++i) {
struct sway_container *con = siblings->items[i]; struct sway_container *con = siblings->items[i];
con->height_fraction = con->pending.height / con->child_total_height; con->height_fraction = con->pending.height / con->child_total_height;
@ -169,12 +151,12 @@ void container_resize_tiled(struct sway_container *con,
double amount_fraction = (double)amount / con->child_total_height; double amount_fraction = (double)amount / con->child_total_height;
double sibling_amount_fraction = double sibling_amount_fraction =
prev ? amount_fraction / 2.0 : amount_fraction; amount_fraction / (double)(resize->length - 1);
con->height_fraction += amount_fraction; for(int i = 0; i < resize->length; i++) {
next->height_fraction -= sibling_amount_fraction; struct sway_container *sibling = resize->items[i];
if (prev) { sibling->height_fraction +=
prev->height_fraction -= sibling_amount_fraction; sibling == con ? amount_fraction : -sibling_amount_fraction;
} }
} }
@ -183,6 +165,9 @@ void container_resize_tiled(struct sway_container *con,
} else { } else {
arrange_workspace(con->pending.workspace); arrange_workspace(con->pending.workspace);
} }
cleanup:
list_free(resize);
} }
/** /**

View file

@ -281,28 +281,34 @@ set|plus|minus|toggle <amount>
*rename* workspace [<old_name>] to <new_name> *rename* workspace [<old_name>] to <new_name>
Rename either <old_name> or the focused workspace to the <new_name> Rename either <old_name> or the focused workspace to the <new_name>
*resize* shrink|grow width|height [<amount> [px|ppt]] *resize* shrink|grow up|right|down|left|width|height [<amount> [px|ppt]]
Resizes the currently focused container by _amount_, specified in pixels or Resizes the currently focused container by _amount_, specified in pixels or
percentage points. If the units are omitted, floating containers are resized percentage points. If the units are omitted, floating containers are resized
in px and tiled containers by ppt. _amount_ will default to 10 if omitted. in px and tiled containers by ppt. _amount_ will default to 10 if omitted.
For tiling containers, space is taken/given from the container in the
specified direction. If _width_ or _height_ is specified, space will be
taken/given from all other containers.
*resize* set height <height> [px|ppt] *resize* set height <height> [px|ppt]
Sets the height of the container to _height_, specified in pixels or Sets the height of the container to _height_, specified in pixels or
percentage points. If the units are omitted, floating containers are percentage points. If the units are omitted, floating containers are
resized in px and tiled containers by ppt. If _height_ is 0, the container resized in px and tiled containers by ppt. If _height_ is 0, the container
will not be resized. will not be resized. For tiling containers, space is taken/given from all
other containers.
*resize* set [width] <width> [px|ppt] *resize* set [width] <width> [px|ppt]
Sets the width of the container to _width_, specified in pixels or Sets the width of the container to _width_, specified in pixels or
percentage points. If the units are omitted, floating containers are percentage points. If the units are omitted, floating containers are
resized in px and tiled containers by ppt. If _width_ is 0, the container resized in px and tiled containers by ppt. If _width_ is 0, the container
will not be resized. will not be resized. For tiling containers, space is taken/given from all
other containers.
*resize* set [width] <width> [px|ppt] [height] <height> [px|ppt] *resize* set [width] <width> [px|ppt] [height] <height> [px|ppt]
Sets the width and height of the container to _width_ and _height_, Sets the width and height of the container to _width_ and _height_,
specified in pixels or percentage points. If the units are omitted, specified in pixels or percentage points. If the units are omitted,
floating containers are resized in px and tiled containers by ppt. If floating containers are resized in px and tiled containers by ppt. If
_width_ or _height_ is 0, the container will not be resized on that axis. _width_ or _height_ is 0, the container will not be resized on that axis.
For tiling containers, space is taken/given from all other containers.
*scratchpad* show *scratchpad* show
Shows a window from the scratchpad. Repeatedly using this command will Shows a window from the scratchpad. Repeatedly using this command will