Skip to content

Commit

Permalink
#301 use euclidean distance to find target window using cardinal dire…
Browse files Browse the repository at this point in the history
…ctions
  • Loading branch information
koekeishiya committed Apr 21, 2020
1 parent 9d2a0e3 commit a763f98
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 47 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added
- New window commands `--minimize` and `--deminimize`. Minimized windows are now reported through window queries and there is a new attribute `minimized` to identify the current state [#379](https://github.com/koekeishiya/yabai/issues/379)

### Changed
- Window commands using cardinal directions use euclidean distance (frame center) to identify best target window [#301](https://github.com/koekeishiya/yabai/issues/301)

## [2.4.3] - 2020-04-14
### Changed
- Changed how *mouse down* events are handled to reduce cycles spent in macOS event tap callback [#376](https://github.com/koekeishiya/yabai/issues/376)
Expand Down
44 changes: 4 additions & 40 deletions src/misc/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,47 +192,11 @@ static inline bool psn_equals(ProcessSerialNumber *a, ProcessSerialNumber *b)
}
#pragma clang diagnostic pop

static bool rect_is_in_direction(CGRect r1, CGRect r2, int direction)
static inline int euclidean_distance(CGPoint p1, CGPoint p2)
{
CGPoint r1_max = { CGRectGetMaxX(r1), CGRectGetMaxY(r1) };
CGPoint r2_max = { CGRectGetMaxX(r2), CGRectGetMaxY(r2) };

switch (direction) {
case DIR_NORTH: if (r2.origin.y > r1_max.y) return false; break;
case DIR_WEST: if (r2.origin.x > r1_max.x) return false; break;
case DIR_SOUTH: if (r2_max.y < r1.origin.y) return false; break;
case DIR_EAST: if (r2_max.x < r1.origin.x) return false; break;
}

switch (direction) {
case DIR_NORTH:
case DIR_SOUTH:
return (r2.origin.x >= r1.origin.x && r2.origin.x <= r1_max.x) ||
(r2_max.x >= r1.origin.x && r2_max.x <= r1_max.x) ||
(r1.origin.x > r2.origin.x && r1.origin.x < r2_max.x);
case DIR_WEST:
case DIR_EAST:
return (r2.origin.y >= r1.origin.y && r2.origin.y <= r1_max.y) ||
(r2_max.y >= r1.origin.y && r2_max.y <= r1_max.y) ||
(r1.origin.y > r2.origin.y && r1_max.y < r2_max.y);
}

return false;
}

static uint32_t rect_distance(CGRect r1, CGRect r2, int direction)
{
CGPoint r1_max = { CGRectGetMaxX(r1), CGRectGetMaxY(r1) };
CGPoint r2_max = { CGRectGetMaxX(r2), CGRectGetMaxY(r2) };

switch (direction) {
case DIR_NORTH: return r2_max.y > r1.origin.y ? r2_max.y - r1.origin.y : r1.origin.y - r2_max.y;
case DIR_WEST: return r2_max.x > r1.origin.x ? r2_max.x - r1.origin.x : r1.origin.x - r2_max.x;
case DIR_SOUTH: return r2.origin.y < r1_max.y ? r1_max.y - r2.origin.y : r2.origin.y - r1_max.y;
case DIR_EAST: return r2.origin.x < r1_max.x ? r1_max.x - r2.origin.x : r2.origin.x - r1_max.x;
}

return UINT32_MAX;
int dx = p1.x - p2.x;
int dy = p1.y - p2.y;
return dx*dx + dy*dy;
}

static bool triangle_contains_point(CGPoint t[3], CGPoint p)
Expand Down
40 changes: 33 additions & 7 deletions src/window_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,21 +678,47 @@ struct window *window_manager_find_window_below_cursor(struct window_manager *wm

static struct window *window_manager_find_closest_window_for_direction_in_window_list(struct window_manager *wm, struct window *source, int direction, uint32_t *window_list, int window_count)
{
CGRect source_frame = window_frame(source);
struct window *best_window = NULL;
uint32_t best_distance = UINT32_MAX;
int best_distance = INT_MAX;

CGRect source_frame = window_frame(source);
CGPoint source_point = { source_frame.origin.x, source_frame.origin.y };

for (int i = 0; i < window_count; ++i) {
struct window *window = window_manager_find_window(wm, window_list[i]);
if (!window || !window_is_standard(window) || window == source) continue;

CGRect frame = window_frame(window);
if (!rect_is_in_direction(source_frame, frame, direction)) continue;
CGPoint point = { frame.origin.x, frame.origin.y };

int distance = euclidean_distance(source_point, point);
if (distance >= best_distance) continue;

uint32_t distance = rect_distance(source_frame, frame, direction);
if (distance < best_distance) {
best_window = window;
best_distance = distance;
switch (direction) {
case DIR_EAST: {
if (point.x > source_point.x) {
best_window = window;
best_distance = distance;
}
} break;
case DIR_SOUTH: {
if (point.y > source_point.y) {
best_window = window;
best_distance = distance;
}
} break;
case DIR_WEST: {
if (point.x < source_point.x) {
best_window = window;
best_distance = distance;
}
} break;
case DIR_NORTH: {
if (point.y < source_point.y) {
best_window = window;
best_distance = distance;
}
} break;
}
}

Expand Down

0 comments on commit a763f98

Please sign in to comment.