diff --git a/CHANGELOG.md b/CHANGELOG.md index b4b7f3f9..af33b618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/misc/helpers.h b/src/misc/helpers.h index 2d7214fc..c030e8b7 100644 --- a/src/misc/helpers.h +++ b/src/misc/helpers.h @@ -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) diff --git a/src/window_manager.c b/src/window_manager.c index e16b9c8d..6e930fbe 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -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; } }