Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FARTHEST_DIR_SEL for windows #1167

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion doc/yabai.1
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,11 @@ SIGNAL_SEL := <index> | LABEL

DIR_SEL := north | east | south | west

FARTHEST_DIR_SEL := farthest_north | farthest_east | farthest_south | farthest_west

STACK_SEL := stack.prev | stack.next | stack.first | stack.last | stack.recent

WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | STACK_SEL | DIR_SEL | <window id>
WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | STACK_SEL | DIR_SEL | FARTHEST_DIR_SEL | <window id>

DISPLAY_SEL := prev | next | first | last | recent | mouse | DIR_SEL | <arrangement index (1\-based)>

Expand Down
4 changes: 3 additions & 1 deletion doc/yabai.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,11 @@ SIGNAL_SEL := <index> | LABEL

DIR_SEL := north | east | south | west

FARTHEST_DIR_SEL := farthest_north | farthest_east | farthest_south | farthest_west

STACK_SEL := stack.prev | stack.next | stack.first | stack.last | stack.recent

WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | STACK_SEL | DIR_SEL | <window id>
WINDOW_SEL := prev | next | first | last | recent | mouse | largest | smallest | STACK_SEL | DIR_SEL | FARTHEST_DIR_SEL | <window id>

DISPLAY_SEL := prev | next | first | last | recent | mouse | DIR_SEL | <arrangement index (1-based)>

Expand Down
84 changes: 66 additions & 18 deletions src/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,24 +204,28 @@ extern bool g_verbose;
/* ----------------------------------------------------------------------------- */

/* --------------------------------COMMON ARGUMENTS----------------------------- */
#define ARGUMENT_COMMON_VAL_ON "on"
#define ARGUMENT_COMMON_VAL_OFF "off"
#define ARGUMENT_COMMON_SEL_PREV "prev"
#define ARGUMENT_COMMON_SEL_NEXT "next"
#define ARGUMENT_COMMON_SEL_FIRST "first"
#define ARGUMENT_COMMON_SEL_LAST "last"
#define ARGUMENT_COMMON_SEL_RECENT "recent"
#define ARGUMENT_COMMON_SEL_NORTH "north"
#define ARGUMENT_COMMON_SEL_EAST "east"
#define ARGUMENT_COMMON_SEL_SOUTH "south"
#define ARGUMENT_COMMON_SEL_WEST "west"
#define ARGUMENT_COMMON_SEL_MOUSE "mouse"
#define ARGUMENT_COMMON_SEL_STACK "stack"
#define ARGUMENT_COMMON_SEL_STACK_PREV "stack.prev"
#define ARGUMENT_COMMON_SEL_STACK_NEXT "stack.next"
#define ARGUMENT_COMMON_SEL_STACK_FIRST "stack.first"
#define ARGUMENT_COMMON_SEL_STACK_LAST "stack.last"
#define ARGUMENT_COMMON_SEL_STACK_RECENT "stack.recent"
#define ARGUMENT_COMMON_VAL_ON "on"
#define ARGUMENT_COMMON_VAL_OFF "off"
#define ARGUMENT_COMMON_SEL_PREV "prev"
#define ARGUMENT_COMMON_SEL_NEXT "next"
#define ARGUMENT_COMMON_SEL_FIRST "first"
#define ARGUMENT_COMMON_SEL_LAST "last"
#define ARGUMENT_COMMON_SEL_RECENT "recent"
#define ARGUMENT_COMMON_SEL_NORTH "north"
#define ARGUMENT_COMMON_SEL_EAST "east"
#define ARGUMENT_COMMON_SEL_SOUTH "south"
#define ARGUMENT_COMMON_SEL_WEST "west"
#define ARGUMENT_COMMON_SEL_FARTHEST_NORTH "farthest_north"
#define ARGUMENT_COMMON_SEL_FARTHEST_EAST "farthest_east"
#define ARGUMENT_COMMON_SEL_FARTHEST_SOUTH "farthest_south"
#define ARGUMENT_COMMON_SEL_FARTHEST_WEST "farthest_west"
#define ARGUMENT_COMMON_SEL_MOUSE "mouse"
#define ARGUMENT_COMMON_SEL_STACK "stack"
#define ARGUMENT_COMMON_SEL_STACK_PREV "stack.prev"
#define ARGUMENT_COMMON_SEL_STACK_NEXT "stack.next"
#define ARGUMENT_COMMON_SEL_STACK_FIRST "stack.first"
#define ARGUMENT_COMMON_SEL_STACK_LAST "stack.last"
#define ARGUMENT_COMMON_SEL_STACK_RECENT "stack.recent"
/* ----------------------------------------------------------------------------- */

struct token
Expand Down Expand Up @@ -798,6 +802,50 @@ static struct selector parse_window_selector(FILE *rsp, char **message, struct w
} else {
daemon_fail(rsp, "could not locate the selected window.\n");
}
} else if (token_equals(result.token, ARGUMENT_COMMON_SEL_FARTHEST_NORTH)) {
if (acting_window) {
struct window *closest_window = window_manager_find_farthest_managed_window_in_direction(&g_window_manager, acting_window, DIR_NORTH);
if (closest_window) {
result.window = closest_window;
} else {
daemon_fail(rsp, "could not locate a farthest northward managed window.\n");
}
} else {
daemon_fail(rsp, "could not locate the selected window.\n");
}
} else if (token_equals(result.token, ARGUMENT_COMMON_SEL_FARTHEST_EAST)) {
if (acting_window) {
struct window *closest_window = window_manager_find_farthest_managed_window_in_direction(&g_window_manager, acting_window, DIR_EAST);
if (closest_window) {
result.window = closest_window;
} else {
daemon_fail(rsp, "could not locate a farthest eastward managed window.\n");
}
} else {
daemon_fail(rsp, "could not locate the selected window.\n");
}
} else if (token_equals(result.token, ARGUMENT_COMMON_SEL_FARTHEST_SOUTH)) {
if (acting_window) {
struct window *closest_window = window_manager_find_farthest_managed_window_in_direction(&g_window_manager, acting_window, DIR_SOUTH);
if (closest_window) {
result.window = closest_window;
} else {
daemon_fail(rsp, "could not locate a farthest southward managed window.\n");
}
} else {
daemon_fail(rsp, "could not locate the selected window.\n");
}
} else if (token_equals(result.token, ARGUMENT_COMMON_SEL_FARTHEST_WEST)) {
if (acting_window) {
struct window *closest_window = window_manager_find_farthest_managed_window_in_direction(&g_window_manager, acting_window, DIR_WEST);
if (closest_window) {
result.window = closest_window;
} else {
daemon_fail(rsp, "could not locate a farthest westward managed window.\n");
}
} else {
daemon_fail(rsp, "could not locate the selected window.\n");
}
} else if (token_equals(result.token, ARGUMENT_COMMON_SEL_MOUSE)) {
struct window *mouse_window = window_manager_find_window_below_cursor(&g_window_manager);
if (mouse_window) {
Expand Down
85 changes: 85 additions & 0 deletions src/view.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,91 @@ struct window_node *view_find_window_node_in_direction(struct view *view, struct
return best_node;
}

struct window_node *view_find_farthest_window_node_in_direction(struct view *view, struct window_node *source, int direction)
{
// Maximize parallel part of direction and minimize orthogonal part of direction
// measured from center of source window (source_point) to center of
// - west border of target for DIR_WEST
// - east border of target for DIR_EAST
// - north border of target for DIR_NORTH
// - south border of target for DIR_SOUTH
// Note coordinate origin: (x=0 (vertical),y=0 (horizontal)) is at top, left corner of screen.

int best_distance_parallel = 0, best_distance_orthogonal = INT_MAX;
CGPoint source_point = area_center(source->area);

struct window_node *best_node = NULL;
struct window_node *target = window_node_find_first_leaf(view->root);

while (target) {
CGPoint target_point = area_center(target->area);

int distance_parallel=0, distance_orthogonal=0;
switch (direction) {
case DIR_EAST: {
distance_parallel = (source_point.x-(target->area.x+target->area.w))*(source_point.x-(target->area.x+target->area.w));
distance_orthogonal = (source_point.y-target_point.y)*(source_point.y-target_point.y);
} break;
case DIR_WEST: {
distance_parallel = (source_point.x-target->area.x)*(source_point.x-target->area.x);
distance_orthogonal = (source_point.y-target_point.y)*(source_point.y-target_point.y);
} break;
case DIR_NORTH: {
distance_parallel = (source_point.y-target->area.y)*(source_point.y-target->area.y);
distance_orthogonal = (source_point.x-target_point.x)*(source_point.x-target_point.x);
} break;
case DIR_SOUTH: {
distance_parallel = (source_point.y-(target->area.y+target->area.h))*(source_point.y-(target->area.y+target->area.h));
distance_orthogonal = (source_point.x-target_point.x)*(source_point.x-target_point.x);
} break;
}

// Skip if parallel dir is not farther and orthogonal dir is not closer
if (distance_parallel <= best_distance_parallel &&
distance_orthogonal >= best_distance_orthogonal) goto next;

switch (direction) {
case DIR_EAST: {
// Accept only if target is actually in the east: compare left of target with right of source
if (target->area.x >= source->area.x + source->area.w) {
best_node = target;
best_distance_parallel = distance_parallel;
best_distance_orthogonal = distance_orthogonal;
}
} break;
case DIR_SOUTH: {
// Accept only if target is actually in the south: compare top of target with bottom of source
if (target->area.y >= source->area.y + source->area.h) {
best_node = target;
best_distance_parallel = distance_parallel;
best_distance_orthogonal = distance_orthogonal;
}
} break;
case DIR_WEST: {
// Accept only if target is actually in the west: compare right of target with left of source
if (target->area.x + target->area.w <= source->area.x) {
best_node = target;
best_distance_parallel = distance_parallel;
best_distance_orthogonal = distance_orthogonal;
}
} break;
case DIR_NORTH: {
// Accept only if target is actually in the north: compare bottom of target with top of source
if (target->area.y + target->area.h <= source->area.y) {
best_node = target;
best_distance_parallel = distance_parallel;
best_distance_orthogonal = distance_orthogonal;
}
} break;
}

next:
target = window_node_find_next_leaf(target);
}

return best_node;
}

struct window_node *view_find_window_node(struct view *view, uint32_t window_id)
{
struct window_node *node = window_node_find_first_leaf(view->root);
Expand Down
1 change: 1 addition & 0 deletions src/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ struct window_node *window_node_find_prev_leaf(struct window_node *node);
struct window_node *window_node_find_next_leaf(struct window_node *node);

struct window_node *view_find_window_node_in_direction(struct view *view, struct window_node *source, int direction);
struct window_node *view_find_farthest_window_node_in_direction(struct view *view, struct window_node *source, int direction);
struct window_node *view_find_window_node(struct view *view, uint32_t window_id);
void view_stack_window_node(struct view *view, struct window_node *node, struct window *window);
void view_add_window_node(struct view *view, struct window *window);
Expand Down
14 changes: 14 additions & 0 deletions src/window_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,20 @@ struct window *window_manager_find_closest_managed_window_in_direction(struct wi
return window_manager_find_window(wm, closest->window_order[0]);
}

struct window *window_manager_find_farthest_managed_window_in_direction(struct window_manager *wm, struct window *window, int direction)
{
struct view *view = window_manager_find_managed_window(wm, window);
if (!view) return NULL;

struct window_node *node = view_find_window_node(view, window->id);
if (!node) return NULL;

struct window_node *farthest = view_find_farthest_window_node_in_direction(view, node, direction);
if (!farthest) return NULL;

return window_manager_find_window(wm, farthest->window_order[0]);
}

struct window *window_manager_find_prev_managed_window(struct space_manager *sm, struct window_manager *wm, struct window *window)
{
struct view *view = space_manager_find_view(sm, space_manager_active_space());
Expand Down
1 change: 1 addition & 0 deletions src/window_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ struct window *window_manager_find_window_at_point_filtering_window(struct windo
struct window *window_manager_find_window_at_point(struct window_manager *wm, CGPoint point);
struct window *window_manager_find_window_below_cursor(struct window_manager *wm);
struct window *window_manager_find_closest_managed_window_in_direction(struct window_manager *wm, struct window *window, int direction);
struct window *window_manager_find_farthest_managed_window_in_direction(struct window_manager *wm, struct window *window, int direction);
struct window *window_manager_find_prev_managed_window(struct space_manager *sm, struct window_manager *wm, struct window *window);
struct window *window_manager_find_next_managed_window(struct space_manager *sm, struct window_manager *wm, struct window *window);
struct window *window_manager_find_first_managed_window(struct space_manager *sm, struct window_manager *wm);
Expand Down