cycle: add <action name="NextWindow" output="" and identifier="">
Some checks failed
labwc.github.io / notify (push) Has been cancelled

output="all|focused|cursor" filters windows by the output they are on.
identifier="all|current" filters windows by their app-id.
This commit is contained in:
tokyo4j 2025-12-12 04:34:38 +09:00 committed by Hiroaki Yamamoto
parent a5c6ff499c
commit 610d869561
6 changed files with 61 additions and 3 deletions

View file

@ -125,8 +125,8 @@ Actions are used in menus and keyboard/mouse bindings.
Resize and move the active window back to its untiled or unmaximized Resize and move the active window back to its untiled or unmaximized
position if it had been maximized or tiled to a direction or region. position if it had been maximized or tiled to a direction or region.
*<action name="NextWindow" workspace="current" />*++ *<action name="NextWindow" workspace="current" output="all" identifier="all" />*++
*<action name="PreviousWindow" workspace="current" />* *<action name="PreviousWindow" workspace="current" output="all" identifier="all" />*
Cycle focus to next/previous window, respectively. Cycle focus to next/previous window, respectively.
Default keybinds for NextWindow and PreviousWindow are Alt-Tab and Default keybinds for NextWindow and PreviousWindow are Alt-Tab and
@ -137,6 +137,14 @@ Actions are used in menus and keyboard/mouse bindings.
This determines whether to cycle through windows on all workspaces or the This determines whether to cycle through windows on all workspaces or the
current workspace. Default is "current". current workspace. Default is "current".
*output* [all|focused|cursor]
This determines whether to cycle through windows on all outputs, the focused
output, or the output under the cursor. Default is "all".
*identifier* [all|current]
This determines whether to cycle through all windows or only windows of the
same application as the currently focused window. Default is "all".
*<action name="Reconfigure" />* *<action name="Reconfigure" />*
Re-load configuration and theme files. Re-load configuration and theme files.

View file

@ -98,7 +98,7 @@
Some contents are fixed-length and others are variable-length. Some contents are fixed-length and others are variable-length.
See "man 5 labwc-config" for details. See "man 5 labwc-config" for details.
<windowSwitcher preview="no" outlines="no" allWorkspaces="yes"> <windowSwitcher preview="no" outlines="no">
<osd show="yes" /> <osd show="yes" />
<fields> <fields>
<field content="workspace" width="5%" /> <field content="workspace" width="5%" />

View file

@ -128,4 +128,9 @@ enum cycle_output_filter {
CYCLE_OUTPUT_FOCUSED, CYCLE_OUTPUT_FOCUSED,
}; };
enum cycle_app_id_filter {
CYCLE_APP_ID_ALL,
CYCLE_APP_ID_CURRENT,
};
#endif /* LABWC_CONFIG_TYPES_H */ #endif /* LABWC_CONFIG_TYPES_H */

View file

@ -45,6 +45,8 @@ struct cycle_osd_field {
struct cycle_filter { struct cycle_filter {
enum cycle_workspace_filter workspace; enum cycle_workspace_filter workspace;
enum cycle_output_filter output;
enum cycle_app_id_filter app_id;
}; };
struct buf; struct buf;

View file

@ -379,6 +379,30 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
} }
goto cleanup; goto cleanup;
} }
if (!strcasecmp(argument, "output")) {
if (!strcasecmp(content, "all")) {
action_arg_add_int(action, argument, CYCLE_OUTPUT_ALL);
} else if (!strcasecmp(content, "cursor")) {
action_arg_add_int(action, argument, CYCLE_OUTPUT_CURSOR);
} else if (!strcasecmp(content, "focused")) {
action_arg_add_int(action, argument, CYCLE_OUTPUT_FOCUSED);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
}
if (!strcasecmp(argument, "identifier")) {
if (!strcasecmp(content, "all")) {
action_arg_add_int(action, argument, CYCLE_APP_ID_ALL);
} else if (!strcasecmp(content, "current")) {
action_arg_add_int(action, argument, CYCLE_APP_ID_CURRENT);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
}
break; break;
case ACTION_TYPE_SHOW_MENU: case ACTION_TYPE_SHOW_MENU:
if (!strcmp(argument, "menu")) { if (!strcmp(argument, "menu")) {
@ -1146,6 +1170,10 @@ run_action(struct view *view, struct server *server, struct action *action,
struct cycle_filter filter = { struct cycle_filter filter = {
.workspace = action_get_int(action, "workspace", .workspace = action_get_int(action, "workspace",
rc.window_switcher.workspace_filter), rc.window_switcher.workspace_filter),
.output = action_get_int(action, "output",
CYCLE_OUTPUT_ALL),
.app_id = action_get_int(action, "identifier",
CYCLE_APP_ID_ALL),
}; };
if (server->input_mode == LAB_INPUT_STATE_CYCLE) { if (server->input_mode == LAB_INPUT_STATE_CYCLE) {
cycle_step(server, dir); cycle_step(server, dir);

View file

@ -325,8 +325,23 @@ init_cycle(struct server *server, struct cycle_filter filter)
criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE; criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
} }
uint64_t cycle_outputs =
get_outputs_by_filter(server, filter.output);
const char *cycle_app_id = NULL;
if (filter.app_id == CYCLE_APP_ID_CURRENT && server->active_view) {
cycle_app_id = server->active_view->app_id;
}
struct view *view; struct view *view;
for_each_view(view, &server->views, criteria) { for_each_view(view, &server->views, criteria) {
if (!(cycle_outputs & view->output->id_bit)) {
continue;
}
if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) {
continue;
}
if (rc.window_switcher.order == WINDOW_SWITCHER_ORDER_AGE) { if (rc.window_switcher.order == WINDOW_SWITCHER_ORDER_AGE) {
insert_view_ordered_by_age(&server->cycle.views, view); insert_view_ordered_by_age(&server->cycle.views, view);
} else { } else {