Support focus <direction> for floating containers

This kind of worked before in that focus would change, but it wasn't
intentionally supported and had side effects such as not raising
the container, and being unable to cycle through all floaters depending
on the direction used.

This commit makes it properly supported. The new focus is chosen based
on the distance to the center point of each floating container in the
workspace, and the container is raised.

In a multi output setup, if both visible workspaces have floating
containers, focus will NOT cross into the other output. It is assumed
the user will use a workspace binding in this case.

If two floating containers occupy the exact same center point and you
try to focus in a direction, the behaviour is undefined.
This commit is contained in:
Ryan Dwyer 2019-03-20 22:16:53 +10:00 committed by Drew DeVault
parent bdb402404c
commit cdcc2a5bb5

View file

@ -1,3 +1,4 @@
#include <float.h>
#include <strings.h>
#include <wlr/types/wlr_output_layout.h>
#include "log.h"
@ -90,8 +91,9 @@ static struct sway_node *get_node_in_output_direction(
return &ws->node;
}
static struct sway_node *node_get_in_direction(struct sway_container *container,
struct sway_seat *seat, enum wlr_direction dir) {
static struct sway_node *node_get_in_direction_tiling(
struct sway_container *container, struct sway_seat *seat,
enum wlr_direction dir) {
struct sway_container *wrap_candidate = NULL;
struct sway_container *current = container;
while (current) {
@ -172,6 +174,37 @@ static struct sway_node *node_get_in_direction(struct sway_container *container,
return NULL;
}
static struct sway_node *node_get_in_direction_floating(
struct sway_container *con, struct sway_seat *seat,
enum wlr_direction dir) {
double ref_lx = con->x + con->width / 2;
double ref_ly = con->y + con->height / 2;
double closest_distance = DBL_MAX;
struct sway_container *closest_con = NULL;
for (int i = 0; i < con->workspace->floating->length; i++) {
struct sway_container *floater = con->workspace->floating->items[i];
if (floater == con) {
continue;
}
float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT
? (floater->x + floater->width / 2) - ref_lx
: (floater->y + floater->height / 2) - ref_ly;
if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) {
distance = -distance;
}
if (distance < 0) {
continue;
}
if (distance < closest_distance) {
closest_distance = distance;
closest_con = floater;
}
}
return closest_con ? &closest_con->node : NULL;
}
static struct cmd_results *focus_mode(struct sway_workspace *ws,
struct sway_seat *seat, bool floating) {
struct sway_container *new_focus = NULL;
@ -330,11 +363,19 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
return cmd_results_new(CMD_SUCCESS, NULL);
}
struct sway_node *next_focus =
node_get_in_direction(container, seat, direction);
struct sway_node *next_focus = NULL;
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);
}
if (next_focus) {
seat_set_focus(seat, next_focus);
seat_consider_warp_to_focus(seat);
if (next_focus->type == N_CONTAINER) {
container_raise_floating(next_focus->sway_container);
}
}
return cmd_results_new(CMD_SUCCESS, NULL);