From de57d2d03771d2ec35a4a479a5bf5af8f75cb84c Mon Sep 17 00:00:00 2001 From: Tobias Schaffner Date: Sun, 4 Aug 2024 22:31:31 +0200 Subject: [PATCH] Add hotkey support for (tiling-) window switching Despite the existing hotkey support for window tiling in Cinnamon, an efficient mechanism for navigating between the tiled windows, apart from tabbing through the list of open windows, was missing. This patch adds support for navigation between windows using hotkeys. Signed-off-by: Tobias Schaffner --- debian/libmuffin0.symbols | 1 + src/core/display.c | 122 ++++++++++++++++++++++++++++++++++++++ src/core/keybindings.c | 89 +++++++++++++++++++++++++++ src/meta/display.h | 7 +++ src/meta/prefs.h | 8 +++ 5 files changed, 227 insertions(+) diff --git a/debian/libmuffin0.symbols b/debian/libmuffin0.symbols index 9ccc4eac4..819d48d76 100644 --- a/debian/libmuffin0.symbols +++ b/debian/libmuffin0.symbols @@ -2146,6 +2146,7 @@ libmuffin.so.0 libmuffin0 #MINVER# meta_display_get_tab_current@Base 5.3.0 meta_display_get_tab_list@Base 5.3.0 meta_display_get_tab_next@Base 5.3.0 + meta_display_get_tab_in_direction@Base 6.0.1 meta_display_get_type@Base 5.3.0 meta_display_get_workspace_manager@Base 5.3.0 meta_display_get_x11_display@Base 5.3.0 diff --git a/src/core/display.c b/src/core/display.c index c9e2c1982..6dc855eaf 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2418,6 +2418,94 @@ find_tab_backward (MetaDisplay *display, return NULL; } +static bool +tab_position_nearer(MetaWindow* test, + MetaWindow* current, + MetaWindow* target) +{ + int test_center_x = test->rect.x + test->rect.width / 2; + int test_center_y = test->rect.y + test->rect.height / 2; + int current_center_x = current->rect.x + current->rect.width / 2; + int current_center_y = current->rect.y + current->rect.height / 2; + int target_center_x = target->rect.x + target->rect.width / 2; + int target_center_y = target->rect.y + target->rect.height / 2; + + float test_distance = sqrt(pow(test_center_x - current_center_x, 2) + + pow(test_center_y - current_center_y, 2)); + float target_distance = sqrt(pow(target_center_x - current_center_x, 2) + + pow(target_center_y - current_center_y, 2)); + + return (test_distance < target_distance); +} + +static bool +tab_position_behind(MetaWindow* test, + MetaWindow* current, + MetaDirection direction) +{ + int test_center_x = test->rect.x + test->rect.width / 2; + int test_center_y = test->rect.y + test->rect.height / 2; + int current_center_x = current->rect.x + current->rect.width / 2; + int current_center_y = current->rect.y + current->rect.height / 2; + + return ((direction == META_DIRECTION_RIGHT && + test_center_x > current_center_x) || + (direction == META_DIRECTION_LEFT && + test_center_x < current_center_x) || + (direction == META_DIRECTION_UP && + test_center_y < current_center_y) || + (direction == META_DIRECTION_DOWN && + test_center_y > current_center_y)); +} + +static bool +tab_is_next_in_direction (MetaWindow* test, + MetaWindow* current, + MetaWindow* target, + MetaDirection direction) +{ + return (tab_position_behind(test, current, direction) && + (target == current || tab_position_nearer(test, current, target))); +} + +static MetaWindow* +find_tab_in_direction (MetaDisplay *display, + MetaTabList type, + MetaWorkspace *workspace, + GList *start, + MetaDirection direction) +{ + GList *result; + GList *tmp; + + g_return_val_if_fail (start != NULL, NULL); + g_return_val_if_fail (workspace != NULL, NULL); + + result = start; + tmp = start->next; + + while (tmp != NULL) { + MetaWindow *window = tmp->data; + if (IN_TAB_CHAIN (window, type) && + !window->minimized && + tab_is_next_in_direction(window, start->data, result->data, direction)) + result = tmp; + tmp = tmp->next; + } + + tmp = workspace->mru_list; + while (tmp != start && tmp != NULL) { + MetaWindow *window = tmp->data; + if (IN_TAB_CHAIN (window, type) && + !window->minimized && + tab_is_next_in_direction(window, start->data, result->data, direction)) + result = tmp; + tmp = tmp->next; + } + + return result->data; +} + static int mru_cmp (gconstpointer a, gconstpointer b) @@ -2592,6 +2680,40 @@ meta_display_get_tab_current (MetaDisplay *display, return NULL; } +/** + * meta_display_get_tab_in_direction: + * @display: a #MetaDisplay + * @type: type of tab list + * @workspace: origin workspace + * @window: (nullable): starting window + * @direction: The direction that should be searched. + * + * Determine the nearest window in the given direction. + * + * Returns: (transfer none): The next window in the given direction + * + */ +MetaWindow* +meta_display_get_tab_in_direction (MetaDisplay *display, + MetaTabList type, + MetaWorkspace *workspace, + MetaWindow *window, + MetaDirection direction) +{ + gboolean skip; + GList *tab_list; + MetaWindow *ret; + tab_list = meta_display_get_tab_list (display, type, workspace); + + if (tab_list == NULL) + return NULL; + + ret = find_tab_in_direction (display, type, workspace, tab_list, direction); + + g_list_free (tab_list); + return ret; +} + MetaGravity meta_resize_gravity_from_grab_op (MetaGrabOp op) { diff --git a/src/core/keybindings.c b/src/core/keybindings.c index 5e1665c99..fb27e1664 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -3491,6 +3491,67 @@ handle_cycle (MetaDisplay *display, do_choose_window (display, event_window, event, binding, backwards); } +static void +handle_switch_tile_action (MetaDisplay *display, + MetaWindow *event_window, + ClutterKeyEvent *event, + MetaKeyBinding *binding, + MetaDirection direction) +{ + MetaWorkspaceManager *workspace_manager = display->workspace_manager; + MetaTabList type = binding->handler->data; + MetaWindow *window; + + window = meta_display_get_tab_in_direction (display, + type, + workspace_manager->active_workspace, + NULL, + direction); + + if (window) + meta_window_activate (window, event->time); +} + +static void +handle_switch_tile_action_right (MetaDisplay *display, + MetaWindow *event_window, + ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer dummy) +{ + handle_switch_tile_action (display, event_window, event, binding, META_DIRECTION_RIGHT); +} + +static void +handle_switch_tile_action_left (MetaDisplay *display, + MetaWindow *event_window, + ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer dummy) +{ + handle_switch_tile_action (display, event_window, event, binding, META_DIRECTION_LEFT); +} + +static void +handle_switch_tile_action_up (MetaDisplay *display, + MetaWindow *event_window, + ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer dummy) +{ + handle_switch_tile_action (display, event_window, event, binding, META_DIRECTION_UP); +} + +static void +handle_switch_tile_action_down (MetaDisplay *display, + MetaWindow *event_window, + ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer dummy) +{ + handle_switch_tile_action (display, event_window, event, binding, META_DIRECTION_DOWN); +} + static void handle_toggle_fullscreen (MetaDisplay *display, MetaWindow *window, @@ -4598,6 +4659,34 @@ init_builtin_key_bindings (MetaDisplay *display) META_KEYBINDING_ACTION_PUSH_TILE_DOWN, handle_tile_action, META_TILE_BOTTOM); + add_builtin_keybinding (display, + "switch-to-tile-left", + common_keybindings, + META_KEY_BINDING_NONE, + META_KEYBINDING_ACTION_SWITCH_TO_TILE_LEFT, + handle_switch_tile_action_left, META_TAB_LIST_NORMAL); + + add_builtin_keybinding (display, + "switch-to-tile-right", + common_keybindings, + META_KEY_BINDING_NONE, + META_KEYBINDING_ACTION_SWITCH_TO_TILE_RIGHT, + handle_switch_tile_action_right, META_TAB_LIST_NORMAL); + + add_builtin_keybinding (display, + "switch-to-tile-up", + common_keybindings, + META_KEY_BINDING_NONE, + META_KEYBINDING_ACTION_SWITCH_TO_TILE_UP, + handle_switch_tile_action_up, META_TAB_LIST_NORMAL); + + add_builtin_keybinding (display, + "switch-to-tile-down", + common_keybindings, + META_KEY_BINDING_NONE, + META_KEYBINDING_ACTION_SWITCH_TO_TILE_DOWN, + handle_switch_tile_action_down, META_TAB_LIST_NORMAL); + add_builtin_keybinding (display, "toggle-above", common_keybindings, diff --git a/src/meta/display.h b/src/meta/display.h index c18d3ec47..9b9f4178a 100644 --- a/src/meta/display.h +++ b/src/meta/display.h @@ -133,6 +133,13 @@ MetaWindow* meta_display_get_tab_next (MetaDisplay *display, MetaWindow *window, gboolean backward); +META_EXPORT +MetaWindow* meta_display_get_tab_in_direction (MetaDisplay *display, + MetaTabList type, + MetaWorkspace *workspace, + MetaWindow *window, + MetaDirection direction); + META_EXPORT MetaWindow* meta_display_get_tab_current (MetaDisplay *display, MetaTabList type, diff --git a/src/meta/prefs.h b/src/meta/prefs.h index ef319b714..d2b1e7fb2 100644 --- a/src/meta/prefs.h +++ b/src/meta/prefs.h @@ -330,6 +330,10 @@ gboolean meta_prefs_get_invert_flip_direction (void); * @META_KEYBINDING_ACTION_PUSH_TILE_RIGHT: FILLME * @META_KEYBINDING_ACTION_PUSH_TILE_UP: FILLME * @META_KEYBINDING_ACTION_PUSH_TILE_DOWN: FILLME + * @META_KEYBINDING_ACTION_SWITCH_TO_TILE_LEFT: FILLME + * @META_KEYBINDING_ACTION_SWITCH_TO_TILE_RIGHT: FILLME + * @META_KEYBINDING_ACTION_SWITCH_TO_TILE_UP: FILLME + * @META_KEYBINDING_ACTION_SWITCH_TO_TILE_DOWN: FILLME * @META_KEYBINDING_ACTION_TOGGLE_ABOVE: FILLME * @META_KEYBINDING_ACTION_MAXIMIZE: FILLME * @META_KEYBINDING_ACTION_UNMAXIMIZE: FILLME @@ -432,6 +436,10 @@ typedef enum _MetaKeyBindingAction META_KEYBINDING_ACTION_PUSH_TILE_RIGHT, META_KEYBINDING_ACTION_PUSH_TILE_UP, META_KEYBINDING_ACTION_PUSH_TILE_DOWN, + META_KEYBINDING_ACTION_SWITCH_TO_TILE_LEFT, + META_KEYBINDING_ACTION_SWITCH_TO_TILE_RIGHT, + META_KEYBINDING_ACTION_SWITCH_TO_TILE_UP, + META_KEYBINDING_ACTION_SWITCH_TO_TILE_DOWN, META_KEYBINDING_ACTION_TOGGLE_ABOVE, META_KEYBINDING_ACTION_MAXIMIZE, META_KEYBINDING_ACTION_UNMAXIMIZE,