From 640e98fd6c3d1419c8b18736d97b83febdfbeb0d Mon Sep 17 00:00:00 2001 From: koekeishiya Date: Mon, 12 Dec 2022 15:16:54 +0100 Subject: [PATCH] #1435 change how window placement is decided when warping windows within the same space --- CHANGELOG.md | 1 + src/view.c | 65 ++++++++++++++++++++++++-------------------- src/window_manager.c | 33 ++++++++++++++++++++++ 3 files changed, 69 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c580d207..881af5fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Updated scripting-addition to support macOS Ventura 13.0.0 [#1297](https://github.com/koekeishiya/yabai/issues/1297) - Properly escape application name when returned in window queries [#1489](https://github.com/koekeishiya/yabai/issues/1489) - Remove window tags used for debugging purposes from result of window query because it could cause a crash under certain conditions when a window closes [#1475](https://github.com/koekeishiya/yabai/issues/1475) +- Change window placement of warp command to be more natural when warping windows within the same space [#1435](https://github.com/koekeishiya/yabai/issues/1435) ## [5.0.1] - 2022-09-26 ### Changed diff --git a/src/view.c b/src/view.c index 3f14e989..27354cea 100644 --- a/src/view.c +++ b/src/view.c @@ -136,41 +136,46 @@ static inline float window_node_get_gap(struct view *view) } #define area_ax_truncate(a) \ - a.x = (int)(a.x + 0.5f); \ - a.y = (int)(a.y + 0.5f); \ - a.w = (int)(a.w + 0.5f); \ - a.h = (int)(a.h + 0.5f); + a->x = (int)(a->x + 0.5f); \ + a->y = (int)(a->y + 0.5f); \ + a->w = (int)(a->w + 0.5f); \ + a->h = (int)(a->h + 0.5f); -static void area_make_pair(struct view *view, struct window_node *node) +static void area_make_pair(enum window_node_split split, float gap, float ratio, struct area *parent_area, struct area *left_area, struct area *right_area) { - enum window_node_split split = window_node_get_split(node); - float ratio = window_node_get_ratio(node); - float gap = window_node_get_gap(view); - if (split == SPLIT_Y) { - node->left->area = node->area; - node->left->area.w *= ratio; - node->left->area.w -= gap; - - node->right->area = node->area; - node->right->area.x += (node->area.w * ratio); - node->right->area.w *= (1 - ratio); - node->right->area.x += gap; - node->right->area.w -= gap; + *left_area = *parent_area; + left_area->w *= ratio; + left_area->w -= gap; + + *right_area = *parent_area; + right_area->x += (parent_area->w * ratio); + right_area->w *= (1 - ratio); + right_area->x += gap; + right_area->w -= gap; } else { - node->left->area = node->area; - node->left->area.h *= ratio; - node->left->area.h -= gap; + *left_area = *parent_area; + left_area->h *= ratio; + left_area->h -= gap; - node->right->area = node->area; - node->right->area.y += (node->area.h * ratio); - node->right->area.h *= (1 - ratio); - node->right->area.y += gap; - node->right->area.h -= gap; + *right_area = *parent_area; + right_area->y += (parent_area->h * ratio); + right_area->h *= (1 - ratio); + right_area->y += gap; + right_area->h -= gap; } - area_ax_truncate(node->left->area); - area_ax_truncate(node->right->area); + area_ax_truncate(left_area); + area_ax_truncate(right_area); +} + +static void area_make_pair_for_node(struct view *view, struct window_node *node) +{ + enum window_node_split split = window_node_get_split(node); + float ratio = window_node_get_ratio(node); + float gap = window_node_get_gap(view); + + area_make_pair(split, gap, ratio, &node->area, &node->left->area, &node->right->area); node->split = split; node->ratio = ratio; @@ -283,13 +288,13 @@ static void window_node_split(struct view *view, struct window_node *node, struc node->right = right; node->zoom = NULL; - area_make_pair(view, node); + area_make_pair_for_node(view, node); } void window_node_update(struct view *view, struct window_node *node) { if (window_node_is_intermediate(node)) { - area_make_pair(view, node->parent); + area_make_pair_for_node(view, node->parent); } if (window_node_is_leaf(node)) { diff --git a/src/window_manager.c b/src/window_manager.c index 6b9c2e92..6c70c2d2 100644 --- a/src/window_manager.c +++ b/src/window_manager.c @@ -1680,6 +1680,31 @@ enum window_op_error window_manager_warp_window(struct space_manager *sm, struct } } else { if (a_view->sid == b_view->sid) { + + // + // :NaturalWarp + // + // NOTE(koekeishiya): Precalculate both target areas and select the one that has the closest distance to the source area. + // This allows the warp to feel more natural in terms of where the window is placed on screen, however, this is only utilized + // for warp operations where both operands belong to the same space. There may be a better system to handle this if/when multiple + // monitors should be supported. + // + + struct area cf, cs; + area_make_pair(window_node_get_split(b_node), window_node_get_gap(b_view), window_node_get_ratio(b_node), &b_node->area, &cf, &cs); + + CGPoint ca = { (int)(0.5f + a_node->area.x + a_node->area.w / 2.0f), (int)(0.5f + a_node->area.y + a_node->area.h / 2.0f) }; + float dcf = powf((ca.x - (int)(0.5f + cf.x + cf.w / 2.0f)), 2.0f) + powf((ca.y - (int)(0.5f + cf.y + cf.h / 2.0f)), 2.0f); + float dcs = powf((ca.x - (int)(0.5f + cs.x + cs.w / 2.0f)), 2.0f) + powf((ca.y - (int)(0.5f + cs.y + cs.h / 2.0f)), 2.0f); + + if (dcf < dcs) { + b_node->child = CHILD_FIRST; + } else if (dcf > dcs) { + b_node->child = CHILD_SECOND; + } else { + b_node->child = window_node_is_left_child(a_node) ? CHILD_FIRST : CHILD_SECOND; + } + struct window_node *a_node_rm = view_remove_window_node(a_view, a); struct window_node *a_node_add = view_add_window_node_with_insertion_point(b_view, a, b->id); @@ -1703,6 +1728,14 @@ enum window_op_error window_manager_warp_window(struct space_manager *sm, struct } } + // + // :NaturalWarp + // + // TODO(koekeishiya): Warp operations with operands that belong to different monitors does not yet implement a heuristic to select + // the target area that feels the most natural in terms of where the window is placed on screen. Is it possible to do better when + // warping between spaces that belong to the same monitor as well?? + // + space_manager_untile_window(sm, a_view, a); window_manager_remove_managed_window(wm, a->id); window_manager_add_managed_window(wm, a, b_view);