diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 93d1effef..99c7e61e5 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -14,6 +14,42 @@ #include "stringop.h" #include "util.h" +static bool get_direction_from_next_prev(struct sway_container *container, + struct sway_seat *seat, const char *name, enum wlr_direction *out) { + enum sway_container_layout parent_layout = container_parent_layout(container); + if (strcasecmp(name, "prev") == 0) { + switch (parent_layout) { + case L_HORIZ: + case L_TABBED: + *out = WLR_DIRECTION_LEFT; + break; + case L_VERT: + case L_STACKED: + *out = WLR_DIRECTION_UP; + break; + default: + return false; + } + } else if (strcasecmp(name, "next") == 0) { + switch (parent_layout) { + case L_HORIZ: + case L_TABBED: + *out = WLR_DIRECTION_RIGHT; + break; + case L_VERT: + case L_STACKED: + *out = WLR_DIRECTION_DOWN; + break; + default: + return false; + } + } else { + return false; + } + + return true; +} + static bool parse_direction(const char *name, enum wlr_direction *out) { if (strcasecmp(name, "left") == 0) { @@ -93,7 +129,7 @@ static struct sway_node *get_node_in_output_direction( static struct sway_node *node_get_in_direction_tiling( struct sway_container *container, struct sway_seat *seat, - enum wlr_direction dir) { + enum wlr_direction dir, bool descend) { struct sway_container *wrap_candidate = NULL; struct sway_container *current = container; while (current) { @@ -148,9 +184,13 @@ static struct sway_node *node_get_in_direction_tiling( } } else { struct sway_container *desired_con = siblings->items[desired]; - struct sway_container *c = seat_get_focus_inactive_view( - seat, &desired_con->node); - return &c->node; + if (!descend) { + return &desired_con->node; + } else { + struct sway_container *c = seat_get_focus_inactive_view( + seat, &desired_con->node); + return &c->node; + } } } @@ -348,10 +388,15 @@ struct cmd_results *cmd_focus(int argc, char **argv) { } enum wlr_direction direction = 0; + bool descend = true; if (!parse_direction(argv[0], &direction)) { - return cmd_results_new(CMD_INVALID, - "Expected 'focus ' " - "or 'focus output '"); + if (!get_direction_from_next_prev(container, seat, argv[0], &direction)) { + return cmd_results_new(CMD_INVALID, + "Expected 'focus ' " + "or 'focus output '"); + } else if (argc == 2 && strcasecmp(argv[1], "sibling") == 0) { + descend = false; + } } if (node->type == N_WORKSPACE) { @@ -373,7 +418,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) { if (container_is_floating(container)) { next_focus = node_get_in_direction_floating(container, seat, direction); } else { - next_focus = node_get_in_direction_tiling(container, seat, direction); + next_focus = node_get_in_direction_tiling(container, seat, direction, descend); } if (next_focus) { seat_set_focus(seat, next_focus); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 63b5465ad..8315f8a1d 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -120,6 +120,11 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *focus* up|right|down|left Moves focus to the next container in the specified direction. +*focus* prev|next [sibling] + Moves focus to the previous or next container in the current layout. By default, + the last active child of the newly focused container will be focused. The _sibling_ + option indicates not to immediately focus a child of the container. + *focus* child Moves focus to the last-focused child of the focused container.