From 2ead7745d6e6d911d689a83140c0c16530391b50 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Thu, 23 Aug 2018 22:09:52 +0300 Subject: [PATCH] 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 Authored original code and tests in #3240. I rewrote most of the resizing code and fixed the failing tests. --- docs/userguide | 12 ++--- include/resize.h | 16 +++++++ parser-specs/commands.spec | 2 +- src/commands.c | 41 ++++------------- src/resize.c | 70 ++++++++++++++++++++--------- testcases/t/141-resize.t | 75 +++++++++++++++++++++++++++++++ testcases/t/187-commands-parser.t | 4 +- 7 files changed, 159 insertions(+), 61 deletions(-) diff --git a/docs/userguide b/docs/userguide index 2746f24e..1c85feb6 100644 --- a/docs/userguide +++ b/docs/userguide @@ -2330,12 +2330,12 @@ resize set [width] [px | ppt] [height] [px | ppt] ------------------------------------------------------- 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. The optional pixel argument specifies by -how many pixels a *floating container* should be grown or shrunk (the default -is 10 pixels). The ppt argument means percentage points and specifies by how -many percentage points a *tiling container* should be grown or shrunk (the -default is 10 percentage points). +less specific and use +width+ or +height+, in which case i3 will take/give space +from all the other containers. The optional pixel argument specifies by how many +pixels a container should be grown or shrunk (the default is 10 pixels). The +optional ppt argument means "percentage points", and if specified it indicates +that a *tiling container* should be grown or shrunk by that many points, instead +of by the +px+ value. Notes about +resize set+: a value of 0 for or means "do not resize in this direction", and resizing a tiling container by +px+ is not diff --git a/include/resize.h b/include/resize.h index 234f4b1f..758cb4a1 100644 --- a/include/resize.h +++ b/include/resize.h @@ -14,3 +14,19 @@ 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); + +/** + * 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); diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index c0c32933..6b015188 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -243,7 +243,7 @@ state RESIZE_TILING: 'or' -> RESIZE_TILING_OR end - -> call cmd_resize($way, $direction, &resize_px, 10) + -> call cmd_resize($way, $direction, &resize_px, 0) state RESIZE_TILING_OR: resize_ppt = number diff --git a/src/commands.c b/src/commands.c index 0a2f3424..99c5368e 100644 --- a/src/commands.c +++ b/src/commands.c @@ -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) { - LOG("tiling resize\n"); +static bool cmd_resize_tiling_direction(I3_CMD, Con *current, const char *way, const char *direction, int px, int ppt) { Con *second = NULL; Con *first = current; direction_t search_direction = parse_direction(direction); bool res = resize_find_tiling_participants(&first, &second, search_direction, false); if (!res) { - LOG("No second container in this direction found.\n"); - ysuccess(false); + yerror("No second container found in this direction.\n"); return false; } - /* get the default percentage */ - int children = con_num_children(first->parent); - LOG("ins. %d children\n", children); - double percentage = 1.0 / children; - 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"); + if (ppt) { + /* For backwards compatibility, 'X px or Y ppt' means that ppt is + * preferred. */ + px = 0; } - - return true; + return resize_neighboring_cons(first, second, px, 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; } else { if (!cmd_resize_tiling_direction(current_match, cmd_output, - current->con, way, direction, resize_ppt)) + current->con, way, direction, + resize_px, resize_ppt)) return; } } diff --git a/src/resize.c b/src/resize.c index 1f8ede10..38591b45 100644 --- a/src/resize.c +++ b/src/resize.c @@ -101,6 +101,53 @@ bool resize_find_tiling_participants(Con **current, Con **other, direction_t dir 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) { 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. */ assert(first->percent > 0.0); assert(second->percent > 0.0); - - /* calculate the new percentage for the first container */ - double new_percent, difference; - 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); + const bool result = resize_neighboring_cons(first, second, pixels, 0); + DLOG("Graphical resize %s: first->percent = %f, second->percent = %f.\n", + result ? "successful" : "failed", first->percent, second->percent); } diff --git a/testcases/t/141-resize.t b/testcases/t/141-resize.t index 0e7fd526..7e8249f1 100644 --- a/testcases/t/141-resize.t +++ b/testcases/t/141-resize.t @@ -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->[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 # was set on the container, but no sibling has been opened yet. See #2015. diff --git a/testcases/t/187-commands-parser.t b/testcases/t/187-commands-parser.t index 39da19d8..48f50e8d 100644 --- a/testcases/t/187-commands-parser.t +++ b/testcases/t/187-commands-parser.t @@ -86,9 +86,9 @@ is(parser_calls( 'resize shrink left 25 px or 33 ppt; ' . 'resize shrink left 25'), "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, 10)", + "cmd_resize(shrink, left, 25, 0)", 'simple resize ok'); is(parser_calls('resize shrink left 25 px or 33 ppt,'),