diff --git a/docs/userguide b/docs/userguide index ad7c6195..73db645a 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1365,11 +1365,13 @@ If you want to resize containers/windows using your keyboard, you can use the resize [ px] [or ppt] --------------------------------------------------------- -Direction can be one of +up+, +down+, +left+ or +right+. 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). +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). I recommend using the resize command inside a so called +mode+: @@ -1378,21 +1380,20 @@ I recommend using the resize command inside a so called +mode+: mode "resize" { # These bindings trigger as soon as you enter the resize mode - # They resize the border in the direction you pressed, e.g. - # when pressing left, the window is resized so that it has - # more space on its left + # Pressing left will shrink the window’s width. + # Pressing right will grow the window’s width. + # Pressing up will shrink the window’s height. + # Pressing down will grow the window’s height. + bindsym j resize shrink width 10 px or 10 ppt + bindsym k resize grow height 10 px or 10 ppt + bindsym l resize shrink height 10 px or 10 ppt + bindsym semicolon resize grow width 10 px or 10 ppt - bindsym j resize shrink left - bindsym Shift+j resize grow left - - bindsym k resize grow down - bindsym Shift+k resize shrink down - - bindsym l resize shrink up - bindsym Shift+l resize grow up - - bindsym semicolon resize grow right - bindsym Shift+semicolon resize shrink right + # same bindings, but for the arrow keys + bindsym Left resize shrink width 10 px or 10 ppt + bindsym Down resize grow height 10 px or 10 ppt + bindsym Up resize shrink height 10 px or 10 ppt + bindsym Right resize grow width 10 px or 10 ppt # back to normal: Enter or Escape bindsym Return mode "default" diff --git a/i3.config b/i3.config index 4ee61d4b..1a457fca 100644 --- a/i3.config +++ b/i3.config @@ -109,34 +109,20 @@ bindsym Mod1+Shift+e exit mode "resize" { # These bindings trigger as soon as you enter the resize mode - # They resize the border in the direction you pressed, e.g. - # when pressing left, the window is resized so that it has - # more space on its left - - bindsym j resize shrink left 10 px or 10 ppt - bindsym Shift+j resize grow left 10 px or 10 ppt - - bindsym k resize shrink down 10 px or 10 ppt - bindsym Shift+k resize grow down 10 px or 10 ppt - - bindsym l resize shrink up 10 px or 10 ppt - bindsym Shift+l resize grow up 10 px or 10 ppt - - bindsym semicolon resize shrink right 10 px or 10 ppt - bindsym Shift+semicolon resize grow right 10 px or 10 ppt + # Pressing left will shrink the window’s width. + # Pressing right will grow the window’s width. + # Pressing up will shrink the window’s height. + # Pressing down will grow the window’s height. + bindsym j resize shrink width 10 px or 10 ppt + bindsym k resize grow height 10 px or 10 ppt + bindsym l resize shrink height 10 px or 10 ppt + bindsym semicolon resize grow width 10 px or 10 ppt # same bindings, but for the arrow keys - bindsym Left resize shrink left 10 px or 10 ppt - bindsym Shift+Left resize grow left 10 px or 10 ppt - - bindsym Down resize shrink down 10 px or 10 ppt - bindsym Shift+Down resize grow down 10 px or 10 ppt - - bindsym Up resize shrink up 10 px or 10 ppt - bindsym Shift+Up resize grow up 10 px or 10 ppt - - bindsym Right resize shrink right 10 px or 10 ppt - bindsym Shift+Right resize grow right 10 px or 10 ppt + bindsym Left resize shrink width 10 px or 10 ppt + bindsym Down resize grow height 10 px or 10 ppt + bindsym Up resize shrink height 10 px or 10 ppt + bindsym Right resize grow width 10 px or 10 ppt # back to normal: Enter or Escape bindsym Return mode "default" diff --git a/i3.config.keycodes b/i3.config.keycodes index af081de0..8f170514 100644 --- a/i3.config.keycodes +++ b/i3.config.keycodes @@ -110,34 +110,20 @@ bindcode $mod+Shift+26 exit mode "resize" { # These bindings trigger as soon as you enter the resize mode - # They resize the border in the direction you pressed, e.g. - # when pressing left, the window is resized so that it has - # more space on its left - - bindcode 44 resize shrink left 10 px or 10 ppt - bindcode Shift+44 resize grow left 10 px or 10 ppt - - bindcode 45 resize shrink down 10 px or 10 ppt - bindcode Shift+45 resize grow down 10 px or 10 ppt - - bindcode 46 resize shrink up 10 px or 10 ppt - bindcode Shift+46 resize grow up 10 px or 10 ppt - - bindcode 47 resize shrink right 10 px or 10 ppt - bindcode Shift+47 resize grow right 10 px or 10 ppt + # Pressing left will shrink the window’s width. + # Pressing right will grow the window’s width. + # Pressing up will shrink the window’s height. + # Pressing down will grow the window’s height. + bindcode 44 resize shrink width 10 px or 10 ppt + bindcode 45 resize grow height 10 px or 10 ppt + bindcode 46 resize shrink height 10 px or 10 ppt + bindcode 47 resize grow width 10 px or 10 ppt # same bindings, but for the arrow keys - bindcode 113 resize shrink left 10 px or 10 ppt - bindcode Shift+113 resize grow left 10 px or 10 ppt - - bindcode 116 resize shrink down 10 px or 10 ppt - bindcode Shift+116 resize grow down 10 px or 10 ppt - - bindcode 111 resize shrink up 10 px or 10 ppt - bindcode Shift+111 resize grow up 10 px or 10 ppt - - bindcode 114 resize shrink right 10 px or 10 ppt - bindcode Shift+114 resize grow right 10 px or 10 ppt + bindsym 113 resize shrink width 10 px or 10 ppt + bindsym 116 resize grow height 10 px or 10 ppt + bindsym 111 resize shrink height 10 px or 10 ppt + bindsym 114 resize grow width 10 px or 10 ppt # back to normal: Enter or Escape bindcode 36 mode "default" diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index 490e4972..1a3f99d9 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -141,7 +141,7 @@ state RESIZE: -> RESIZE_DIRECTION state RESIZE_DIRECTION: - direction = 'up', 'down', 'left', 'right' + direction = 'up', 'down', 'left', 'right', 'width', 'height' -> RESIZE_PX state RESIZE_PX: diff --git a/src/commands.c b/src/commands.c index 2df26f6c..0a5abde5 100644 --- a/src/commands.c +++ b/src/commands.c @@ -391,6 +391,171 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { cmd_output->json_output = sstrdup("{\"success\": true}"); } +static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) { + LOG("floating resize\n"); + if (strcmp(direction, "up") == 0) { + floating_con->rect.y -= px; + floating_con->rect.height += px; + } else if (strcmp(direction, "down") == 0) { + floating_con->rect.height += px; + } else if (strcmp(direction, "left") == 0) { + floating_con->rect.x -= px; + floating_con->rect.width += px; + } else { + floating_con->rect.width += px; + } +} + +static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int ppt) { + LOG("tiling resize\n"); + /* get the appropriate current container (skip stacked/tabbed cons) */ + Con *current = focused; + while (current->parent->layout == L_STACKED || + current->parent->layout == L_TABBED) + current = current->parent; + + /* Then further go up until we find one with the matching orientation. */ + orientation_t search_orientation = + (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT); + + while (current->type != CT_WORKSPACE && + current->type != CT_FLOATING_CON && + current->parent->orientation != search_orientation) + current = current->parent; + + /* get the default percentage */ + int children = con_num_children(current->parent); + Con *other; + LOG("ins. %d children\n", children); + double percentage = 1.0 / children; + LOG("default percentage = %f\n", percentage); + + orientation_t orientation = current->parent->orientation; + + if ((orientation == HORIZ && + (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) || + (orientation == VERT && + (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) { + LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", + (orientation == HORIZ ? "horizontal" : "vertical")); + cmd_output->json_output = sstrdup("{\"sucess\": false}"); + return; + } + + if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) { + other = TAILQ_PREV(current, nodes_head, nodes); + } else { + other = TAILQ_NEXT(current, nodes); + } + if (other == TAILQ_END(workspaces)) { + LOG("No other container in this direction found, cannot resize.\n"); + cmd_output->json_output = sstrdup("{\"sucess\": false}"); + return; + } + LOG("other->percent = %f\n", other->percent); + LOG("current->percent before = %f\n", current->percent); + if (current->percent == 0.0) + current->percent = percentage; + if (other->percent == 0.0) + other->percent = percentage; + double new_current_percent = current->percent + ((double)ppt / 100.0); + double new_other_percent = other->percent - ((double)ppt / 100.0); + LOG("new_current_percent = %f\n", new_current_percent); + LOG("new_other_percent = %f\n", new_other_percent); + /* Ensure that the new percentages are positive and greater than + * 0.05 to have a reasonable minimum size. */ + if (definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON) && + definitelyGreaterThan(new_other_percent, 0.05, DBL_EPSILON)) { + current->percent += ((double)ppt / 100.0); + other->percent -= ((double)ppt / 100.0); + LOG("current->percent after = %f\n", current->percent); + LOG("other->percent after = %f\n", other->percent); + } else { + LOG("Not resizing, already at minimum size\n"); + } +} + +static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, int ppt) { + LOG("width/height resize\n"); + /* get the appropriate current container (skip stacked/tabbed cons) */ + Con *current = focused; + while (current->parent->layout == L_STACKED || + current->parent->layout == L_TABBED) + current = current->parent; + + /* Then further go up until we find one with the matching orientation. */ + orientation_t search_orientation = + (strcmp(direction, "width") == 0 ? HORIZ : VERT); + + while (current->type != CT_WORKSPACE && + current->type != CT_FLOATING_CON && + current->parent->orientation != search_orientation) + current = current->parent; + + /* get the default percentage */ + int children = con_num_children(current->parent); + LOG("ins. %d children\n", children); + double percentage = 1.0 / children; + LOG("default percentage = %f\n", percentage); + + orientation_t orientation = current->parent->orientation; + + if ((orientation == HORIZ && + strcmp(direction, "height") == 0) || + (orientation == VERT && + strcmp(direction, "width") == 0)) { + LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", + (orientation == HORIZ ? "horizontal" : "vertical")); + cmd_output->json_output = sstrdup("{\"sucess\": false}"); + return; + } + + if (children == 1) { + LOG("This is the only container, cannot resize.\n"); + cmd_output->json_output = sstrdup("{\"sucess\": false}"); + return; + } + + /* Ensure all the other children have a percentage set. */ + Con *child; + TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + LOG("child->percent = %f (child %p)\n", child->percent, child); + if (child->percent == 0.0) + child->percent = percentage; + } + + double new_current_percent = current->percent + ((double)ppt / 100.0); + double subtract_percent = ((double)ppt / 100.0) / (children - 1); + LOG("new_current_percent = %f\n", new_current_percent); + LOG("subtract_percent = %f\n", subtract_percent); + /* Ensure that the new percentages are positive and greater than + * 0.05 to have a reasonable minimum size. */ + TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + if (child == current) + continue; + if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) { + LOG("Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->percent - subtract_percent); + cmd_output->json_output = sstrdup("{\"sucess\": false}"); + return; + } + } + if (!definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON)) { + LOG("Not resizing, already at minimum size\n"); + cmd_output->json_output = sstrdup("{\"sucess\": false}"); + return; + } + + current->percent += ((double)ppt / 100.0); + LOG("current->percent after = %f\n", current->percent); + + TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + if (child == current) + continue; + child->percent -= subtract_percent; + LOG("child->percent after (%p) = %f\n", child, child->percent); + } +} + /* * Implementation of 'resize grow|shrink [ px] [or ppt]'. * @@ -408,85 +573,12 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz Con *floating_con; if ((floating_con = con_inside_floating(focused))) { - printf("floating resize\n"); - if (strcmp(direction, "up") == 0) { - floating_con->rect.y -= px; - floating_con->rect.height += px; - } else if (strcmp(direction, "down") == 0) { - floating_con->rect.height += px; - } else if (strcmp(direction, "left") == 0) { - floating_con->rect.x -= px; - floating_con->rect.width += px; - } else { - floating_con->rect.width += px; - } + cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px); } else { - LOG("tiling resize\n"); - /* get the appropriate current container (skip stacked/tabbed cons) */ - Con *current = focused; - while (current->parent->layout == L_STACKED || - current->parent->layout == L_TABBED) - current = current->parent; - - /* Then further go up until we find one with the matching orientation. */ - orientation_t search_orientation = - (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT); - - while (current->type != CT_WORKSPACE && - current->type != CT_FLOATING_CON && - current->parent->orientation != search_orientation) - current = current->parent; - - /* get the default percentage */ - int children = con_num_children(current->parent); - Con *other; - LOG("ins. %d children\n", children); - double percentage = 1.0 / children; - LOG("default percentage = %f\n", percentage); - - orientation_t orientation = current->parent->orientation; - - if ((orientation == HORIZ && - (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) || - (orientation == VERT && - (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) { - LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", - (orientation == HORIZ ? "horizontal" : "vertical")); - cmd_output->json_output = sstrdup("{\"sucess\": false}"); - return; - } - - if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) { - other = TAILQ_PREV(current, nodes_head, nodes); - } else { - other = TAILQ_NEXT(current, nodes); - } - if (other == TAILQ_END(workspaces)) { - LOG("No other container in this direction found, cannot resize.\n"); - cmd_output->json_output = sstrdup("{\"sucess\": false}"); - return; - } - LOG("other->percent = %f\n", other->percent); - LOG("current->percent before = %f\n", current->percent); - if (current->percent == 0.0) - current->percent = percentage; - if (other->percent == 0.0) - other->percent = percentage; - double new_current_percent = current->percent + ((double)ppt / 100.0); - double new_other_percent = other->percent - ((double)ppt / 100.0); - LOG("new_current_percent = %f\n", new_current_percent); - LOG("new_other_percent = %f\n", new_other_percent); - /* Ensure that the new percentages are positive and greater than - * 0.05 to have a reasonable minimum size. */ - if (definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON) && - definitelyGreaterThan(new_other_percent, 0.05, DBL_EPSILON)) { - current->percent += ((double)ppt / 100.0); - other->percent -= ((double)ppt / 100.0); - LOG("current->percent after = %f\n", current->percent); - LOG("other->percent after = %f\n", other->percent); - } else { - LOG("Not resizing, already at minimum size\n"); - } + if (strcmp(direction, "width") == 0 || + strcmp(direction, "height") == 0) + cmd_resize_tiling_width_height(current_match, cmd_output, way, direction, ppt); + else cmd_resize_tiling_direction(current_match, cmd_output, way, direction, ppt); } cmd_output->needs_tree_render = true; diff --git a/testcases/t/141-resize.t b/testcases/t/141-resize.t index c2038060..91aeca50 100644 --- a/testcases/t/141-resize.t +++ b/testcases/t/141-resize.t @@ -88,6 +88,45 @@ cmd 'resize grow left 10 px or 25 ppt'; is($nodes->[0]->{percent}, 0.25, 'left window got 25%'); is($nodes->[1]->{percent}, 0.75, 'right window got 75%'); +################################################################################ +# Check that the resize grow/shrink width/height syntax works. +################################################################################ + +# Use two windows +$tmp = fresh_workspace; + +$left = open_window; +$right = open_window; + +cmd 'resize grow width 10 px or 25 ppt'; + +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[0]->{percent}, 0.25, 'left window got 25%'); +is($nodes->[1]->{percent}, 0.75, 'right window got 75%'); + +# Now test it with four windows +$tmp = fresh_workspace; + +open_window for (1..4); + +cmd 'resize grow width 10 px or 25 ppt'; + +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%'); +is($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%'); +is($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%'); +is($nodes->[3]->{percent}, 0.50, 'fourth window got 50%'); + +# height should be a no-op in this situation +cmd 'resize grow height 10 px or 25 ppt'; + +($nodes, $focus) = get_ws_content($tmp); +is($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%'); +is($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%'); +is($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%'); +is($nodes->[3]->{percent}, 0.50, 'fourth window got 50%'); + + ############################################################ # checks that resizing floating windows works ############################################################