Make cmd_resize_tiling_direction work with pixels

Introduces resize_neighboring_cons in resize.c which is also used by
resize_graphical_handler.

Co-authored-by: Andrew Laucius <andrewla@gmail.com>
Authored original code and tests in #3240. I rewrote most of the
resizing code and fixed the failing tests.
This commit is contained in:
Orestis Floros 2018-08-23 22:09:52 +03:00
parent ea43507bed
commit 2ead7745d6
No known key found for this signature in database
GPG Key ID: E9AD9F32E401E38F
7 changed files with 159 additions and 61 deletions

View File

@ -2330,12 +2330,12 @@ resize set [width] <width> [px | ppt] [height] <height> [px | ppt]
------------------------------------------------------- -------------------------------------------------------
Direction can either be one of +up+, +down+, +left+ or +right+. Or you can be 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 less specific and use +width+ or +height+, in which case i3 will take/give space
space from all the other containers. The optional pixel argument specifies by from all the other containers. The optional pixel argument specifies by how many
how many pixels a *floating container* should be grown or shrunk (the default pixels a container should be grown or shrunk (the default is 10 pixels). The
is 10 pixels). The ppt argument means percentage points and specifies by how optional ppt argument means "percentage points", and if specified it indicates
many percentage points a *tiling container* should be grown or shrunk (the that a *tiling container* should be grown or shrunk by that many points, instead
default is 10 percentage points). of by the +px+ value.
Notes about +resize set+: a value of 0 for <width> or <height> means "do Notes about +resize set+: a value of 0 for <width> or <height> means "do
not resize in this direction", and resizing a tiling container by +px+ is not not resize in this direction", and resizing a tiling container by +px+ is not

View File

@ -14,3 +14,19 @@
bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides); bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides);
void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event); void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event);
/**
* Resize the two given containers using the given amount of pixels or
* percentage points. One of the two needs to be 0. A positive amount means
* growing the first container while a negative means shrinking it.
* Returns false when the resize would result in one of the two containers
* having less than 1 pixel of size.
*
*/
bool resize_neighboring_cons(Con *first, Con *second, int px, int ppt);
/**
* Calculate the given container's new percent given a change in pixels.
*
*/
double px_resize_to_percent(Con *con, int px_diff);

View File

@ -243,7 +243,7 @@ state RESIZE_TILING:
'or' 'or'
-> RESIZE_TILING_OR -> RESIZE_TILING_OR
end end
-> call cmd_resize($way, $direction, &resize_px, 10) -> call cmd_resize($way, $direction, &resize_px, 0)
state RESIZE_TILING_OR: state RESIZE_TILING_OR:
resize_ppt = number resize_ppt = number

View File

@ -497,47 +497,23 @@ static void cmd_resize_floating(I3_CMD, const char *way, const char *direction_s
} }
} }
static bool cmd_resize_tiling_direction(I3_CMD, Con *current, const char *way, const char *direction, int ppt) { static bool cmd_resize_tiling_direction(I3_CMD, Con *current, const char *way, const char *direction, int px, int ppt) {
LOG("tiling resize\n");
Con *second = NULL; Con *second = NULL;
Con *first = current; Con *first = current;
direction_t search_direction = parse_direction(direction); direction_t search_direction = parse_direction(direction);
bool res = resize_find_tiling_participants(&first, &second, search_direction, false); bool res = resize_find_tiling_participants(&first, &second, search_direction, false);
if (!res) { if (!res) {
LOG("No second container in this direction found.\n"); yerror("No second container found in this direction.\n");
ysuccess(false);
return false; return false;
} }
/* get the default percentage */ if (ppt) {
int children = con_num_children(first->parent); /* For backwards compatibility, 'X px or Y ppt' means that ppt is
LOG("ins. %d children\n", children); * preferred. */
double percentage = 1.0 / children; px = 0;
LOG("default percentage = %f\n", percentage);
/* resize */
LOG("first->percent before = %f\n", first->percent);
LOG("second->percent before = %f\n", second->percent);
if (first->percent == 0.0)
first->percent = percentage;
if (second->percent == 0.0)
second->percent = percentage;
double new_first_percent = first->percent + ((double)ppt / 100.0);
double new_second_percent = second->percent - ((double)ppt / 100.0);
LOG("new_first_percent = %f\n", new_first_percent);
LOG("new_second_percent = %f\n", new_second_percent);
/* Ensure that the new percentages are positive. */
if (new_first_percent > 0.0 && new_second_percent > 0.0) {
first->percent = new_first_percent;
second->percent = new_second_percent;
LOG("first->percent after = %f\n", first->percent);
LOG("second->percent after = %f\n", second->percent);
} else {
LOG("Not resizing, already at minimum size\n");
} }
return resize_neighboring_cons(first, second, px, ppt);
return true;
} }
static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, const char *way, const char *direction, int ppt) { static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, const char *way, const char *direction, int ppt) {
@ -631,7 +607,8 @@ void cmd_resize(I3_CMD, const char *way, const char *direction, long resize_px,
return; return;
} else { } else {
if (!cmd_resize_tiling_direction(current_match, cmd_output, if (!cmd_resize_tiling_direction(current_match, cmd_output,
current->con, way, direction, resize_ppt)) current->con, way, direction,
resize_px, resize_ppt))
return; return;
} }
} }

View File

@ -101,6 +101,53 @@ bool resize_find_tiling_participants(Con **current, Con **other, direction_t dir
return true; return true;
} }
/*
* Calculate the given container's new percent given a change in pixels.
*
*/
double px_resize_to_percent(Con *con, int px_diff) {
Con *parent = con->parent;
const orientation_t o = con_orientation(parent);
const int total = (o == HORIZ ? parent->rect.width : parent->rect.height);
/* deco_rect.height is subtracted from each child in render_con_split */
const int target = px_diff + (o == HORIZ ? con->rect.width : con->rect.height + con->deco_rect.height);
return ((double)target / (double)total);
}
/*
* Resize the two given containers using the given amount of pixels or
* percentage points. One of the two needs to be 0. A positive amount means
* growing the first container while a negative means shrinking it.
* Returns false when the resize would result in one of the two containers
* having less than 1 pixel of size.
*
*/
bool resize_neighboring_cons(Con *first, Con *second, int px, int ppt) {
assert(px * ppt == 0);
Con *parent = first->parent;
orientation_t orientation = con_orientation(parent);
const int total = (orientation == HORIZ ? parent->rect.width : parent->rect.height);
double new_first_percent;
double new_second_percent;
if (ppt) {
new_first_percent = first->percent + ((double)ppt / 100.0);
new_second_percent = second->percent - ((double)ppt / 100.0);
} else {
new_first_percent = px_resize_to_percent(first, px);
new_second_percent = second->percent + first->percent - new_first_percent;
}
/* Ensure that no container will be less than 1 pixel in the resizing
* direction. */
if (lround(new_first_percent * total) <= 0 || lround(new_second_percent * total) <= 0) {
return false;
}
first->percent = new_first_percent;
second->percent = new_second_percent;
con_fix_percent(parent);
return true;
}
void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event) { void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event) {
Con *output = con_get_output(first); Con *output = con_get_output(first);
@ -179,24 +226,7 @@ void resize_graphical_handler(Con *first, Con *second, orientation_t orientation
/* if we got thus far, the containers must have valid percentages. */ /* if we got thus far, the containers must have valid percentages. */
assert(first->percent > 0.0); assert(first->percent > 0.0);
assert(second->percent > 0.0); assert(second->percent > 0.0);
const bool result = resize_neighboring_cons(first, second, pixels, 0);
/* calculate the new percentage for the first container */ DLOG("Graphical resize %s: first->percent = %f, second->percent = %f.\n",
double new_percent, difference; result ? "successful" : "failed", first->percent, second->percent);
double percent = first->percent;
DLOG("percent = %f\n", percent);
int original = (orientation == HORIZ ? first->rect.width : first->rect.height);
DLOG("original = %d\n", original);
new_percent = (original + pixels) * (percent / original);
difference = percent - new_percent;
DLOG("difference = %f\n", difference);
DLOG("new percent = %f\n", new_percent);
first->percent = new_percent;
/* calculate the new percentage for the second container */
double s_percent = second->percent;
second->percent = s_percent + difference;
DLOG("second->percent = %f\n", second->percent);
/* now we must make sure that the sum of the percentages remain 1.0 */
con_fix_percent(first->parent);
} }

View File

@ -141,6 +141,81 @@ cmp_float($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%');
cmp_float($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%'); cmp_float($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%');
cmp_float($nodes->[3]->{percent}, 0.50, 'fourth window got 50%'); cmp_float($nodes->[3]->{percent}, 0.50, 'fourth window got 50%');
################################################################################
# Check that we can grow tiled windows by pixels
################################################################################
$tmp = fresh_workspace;
$left = open_window;
$right = open_window;
($nodes, $focus) = get_ws_content($tmp);
cmp_float($nodes->[0]->{rect}->{width}, 640, 'left window is 640px');
cmp_float($nodes->[1]->{rect}->{width}, 640, 'right window is 640px');
cmd 'resize grow left 10px';
($nodes, $focus) = get_ws_content($tmp);
cmp_float($nodes->[0]->{rect}->{width}, 630, 'left window is 630px');
cmp_float($nodes->[1]->{rect}->{width}, 650, 'right window is 650px');
################################################################################
# Check that we can shrink tiled windows by pixels
################################################################################
$tmp = fresh_workspace;
$left = open_window;
$right = open_window;
($nodes, $focus) = get_ws_content($tmp);
cmp_float($nodes->[0]->{rect}->{width}, 640, 'left window is 640px');
cmp_float($nodes->[1]->{rect}->{width}, 640, 'right window is 640px');
cmd 'resize shrink left 10px';
($nodes, $focus) = get_ws_content($tmp);
cmp_float($nodes->[0]->{rect}->{width}, 650, 'left window is 650px');
cmp_float($nodes->[1]->{rect}->{width}, 630, 'right window is 630px');
################################################################################
# Check that we can shrink vertical tiled windows by pixels
################################################################################
$tmp = fresh_workspace;
cmd 'split v';
$top = open_window;
$bottom = open_window;
($nodes, $focus) = get_ws_content($tmp);
my @heights = ($nodes->[0]->{rect}->{height}, $nodes->[1]->{rect}->{height});
cmd 'resize grow up 10px';
($nodes, $focus) = get_ws_content($tmp);
cmp_float($nodes->[0]->{rect}->{height}, $heights[0] - 10, 'top window is 10px larger');
cmp_float($nodes->[1]->{rect}->{height}, $heights[1] + 10, 'bottom window is 10px smaller');
################################################################################
# Check that we can shrink vertical tiled windows by pixels
################################################################################
$tmp = fresh_workspace;
cmd 'split v';
$top = open_window;
$bottom = open_window;
($nodes, $focus) = get_ws_content($tmp);
my @heights = ($nodes->[0]->{rect}->{height}, $nodes->[1]->{rect}->{height});
cmd 'resize shrink up 10px';
($nodes, $focus) = get_ws_content($tmp);
cmp_float($nodes->[0]->{rect}->{height}, $heights[0] + 10, 'top window is 10px smaller');
cmp_float($nodes->[1]->{rect}->{height}, $heights[1] - 10, 'bottom window is 10px larger');
################################################################################ ################################################################################
# Check that the resize grow/shrink width/height syntax works if a nested split # Check that the resize grow/shrink width/height syntax works if a nested split
# was set on the container, but no sibling has been opened yet. See #2015. # was set on the container, but no sibling has been opened yet. See #2015.

View File

@ -86,9 +86,9 @@ is(parser_calls(
'resize shrink left 25 px or 33 ppt; ' . 'resize shrink left 25 px or 33 ppt; ' .
'resize shrink left 25'), 'resize shrink left 25'),
"cmd_resize(shrink, left, 10, 10)\n" . "cmd_resize(shrink, left, 10, 10)\n" .
"cmd_resize(shrink, left, 25, 10)\n" . "cmd_resize(shrink, left, 25, 0)\n" .
"cmd_resize(shrink, left, 25, 33)\n" . "cmd_resize(shrink, left, 25, 33)\n" .
"cmd_resize(shrink, left, 25, 10)", "cmd_resize(shrink, left, 25, 0)",
'simple resize ok'); 'simple resize ok');
is(parser_calls('resize shrink left 25 px or 33 ppt,'), is(parser_calls('resize shrink left 25 px or 33 ppt,'),