diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index a8d8bba5..ee413354 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -99,6 +99,10 @@ Actions are used in menus and keyboard/mouse bindings. Resize and move active window according to the given region. See labwc-config(5) for further information on how to define regions. +** + Cycle focus to the next window (mostly) in the given region. + See labwc-config(5) for further information on how to define regions. + ** Cycle focus to next window. diff --git a/include/labwc.h b/include/labwc.h index 419a5ee0..9c44d0e7 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -454,6 +454,14 @@ enum lab_cycle_dir { struct view *desktop_cycle_view(struct server *server, struct view *start_view, enum lab_cycle_dir dir); +/** + * desktop_cycle_view_near_region - return view to 'cycle' to within region + * @active_view: reference point for finding next view to cycle to + * Note: If !active_view, the top-most focusable view is returned + */ +struct view *desktop_cycle_view_near_region(struct server *server, + struct view *active_view, struct region *region); + /** * desktop_focus_topmost_view() - focus the topmost view on the current * workspace, skipping views that claim not to want focus (those can diff --git a/src/action.c b/src/action.c index 6f6bee94..2076d28d 100644 --- a/src/action.c +++ b/src/action.c @@ -100,6 +100,7 @@ enum action_type { ACTION_TYPE_SEND_TO_DESKTOP, ACTION_TYPE_GO_TO_DESKTOP, ACTION_TYPE_SNAP_TO_REGION, + ACTION_TYPE_CYCLE_NEAR_REGION, ACTION_TYPE_TOGGLE_KEYBINDS, ACTION_TYPE_FOCUS_OUTPUT, ACTION_TYPE_MOVE_TO_OUTPUT, @@ -158,6 +159,7 @@ const char *action_names[] = { "SendToDesktop", "GoToDesktop", "SnapToRegion", + "CycleNearRegion", "ToggleKeybinds", "FocusOutput", "MoveToOutput", @@ -405,6 +407,12 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char goto cleanup; } break; + case ACTION_TYPE_CYCLE_NEAR_REGION: + if (!strcmp(argument, "region")) { + action_arg_add_str(action, argument, content); + goto cleanup; + } + break; case ACTION_TYPE_FOCUS_OUTPUT: if (!strcmp(argument, "output")) { action_arg_add_str(action, argument, content); @@ -545,6 +553,9 @@ action_is_valid(struct action *action) case ACTION_TYPE_SNAP_TO_REGION: arg_name = "region"; break; + case ACTION_TYPE_CYCLE_NEAR_REGION: + arg_name = "region"; + break; case ACTION_TYPE_FOCUS_OUTPUT: arg_name = "output"; break; @@ -1026,6 +1037,26 @@ actions_run(struct view *activator, struct server *server, wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name); } break; + case ACTION_TYPE_CYCLE_NEAR_REGION: + { + if (!view) { + break; + } + struct output *output = view->output; + if (!output_is_usable(output)) { + break; + } + const char *region_name = action_get_str(action, "region", NULL); + struct region *region = regions_from_name(region_name, output); + if (region) { + struct view *new_view = desktop_cycle_view_near_region( + server, view, region); + if (new_view) { + desktop_focus_view(new_view, /*raise*/ true); + } + } + } + break; case ACTION_TYPE_TOGGLE_KEYBINDS: if (view) { view_toggle_keybinds(view); diff --git a/src/desktop.c b/src/desktop.c index 2c0db207..3f29111b 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "config.h" #include +#include "common/macros.h" #include "common/scene-helpers.h" #include "common/surface-helpers.h" #include "dnd.h" @@ -139,6 +140,32 @@ desktop_cycle_view(struct server *server, struct view *start_view, return iter(&server->views, start_view, criteria); } +struct view * +desktop_cycle_view_near_region(struct server *server, struct view *active_view, + struct region *region) +{ + struct view *(*iter)(struct wl_list *head, struct view *view, + enum lab_view_criteria criteria); + enum lab_view_criteria criteria = rc.window_switcher.criteria | + LAB_VIEW_CRITERIA_CURRENT_WORKSPACE; + + struct view *cur; + struct wlr_box intersect; + + iter = view_prev_no_head_stop; + + cur = iter(&server->views, active_view, criteria); + while (cur && cur != active_view) { + wlr_box_intersection(&intersect, &cur->current, ®ion->geo); + if (!cur->minimized && !wlr_box_empty(&intersect)) { + return cur; + } + cur = iter(&server->views, cur, criteria); + } + /* no matching views found */ + return NULL; +} + struct view * desktop_topmost_focusable_view(struct server *server) {