From 8850368ab943d49dbd312d34fccd10e336b68ec9 Mon Sep 17 00:00:00 2001 From: Droc Date: Thu, 5 Sep 2024 07:52:23 -0500 Subject: [PATCH] Add position arguments for menus ...utilizing x,y coordinates where values can be a number, a negative number, a percentage or "center". - (0,0) is top left corner - (-0,-0) is bottom right corner - % is percentage of width and/or height - 'center' centers the menu vertically and/or horizontally root-menu 0 0 Note: both x and y values must be supplied for positioning to work. --- docs/labwc-actions.5.scd | 11 +++++++ src/action.c | 69 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 6b9dfe8d..443268e8 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -123,6 +123,17 @@ Actions are used in menus and keyboard/mouse bindings. upper-left corner of the window associated with the action. Default is yes. + *position* Show the menu in the specified position on the monitor + that has cursor focus, see below. + + The position tag has two sub-tags. and specify a position and + take either a pixel value, the string "center" which will center the + menu in that dimension, or a relative value specified as a percentage + A relative value is interpreted in terms of the monitor the menu will + be shown on, and will be relative to the left/top edge of the menu + window and monitor for positive values, and to the right/bottom edge + for negative values. + ** Set decorations of focused window. diff --git a/src/action.c b/src/action.c index 79008566..a157d6e8 100644 --- a/src/action.c +++ b/src/action.c @@ -343,6 +343,14 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char action_arg_add_bool(action, argument, parse_bool(content, true)); goto cleanup; } + if (!strcasecmp(argument, "x.position")) { + action_arg_add_str(action, argument, content); + goto cleanup; + } + if (!strcasecmp(argument, "y.position")) { + action_arg_add_str(action, argument, content); + goto cleanup; + } break; case ACTION_TYPE_TOGGLE_MAXIMIZE: case ACTION_TYPE_MAXIMIZE: @@ -611,7 +619,8 @@ action_list_free(struct wl_list *action_list) static void show_menu(struct server *server, struct view *view, - const char *menu_name, bool at_cursor) + const char *menu_name, bool at_cursor, + const char *pos_x, const char *pos_y) { if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH && server->input_mode != LAB_INPUT_STATE_MENU) { @@ -638,8 +647,62 @@ show_menu(struct server *server, struct view *view, y = view->current.y; } + /* + * determine placement by looking at x and y + * x/y can be number, "center" or a %percent of screen dimensions + */ + if (pos_x && pos_y) { + struct output *output = output_nearest_to(server, + server->seat.cursor->x, server->seat.cursor->y); + struct menuitem *item; + struct theme *theme = server->theme; + int max_width = theme->menu_min_width; + + wl_list_for_each(item, &menu->menuitems, link) { + if (item->native_width > max_width) { + max_width = item->native_width < theme->menu_max_width + ? item->native_width : theme->menu_max_width; + } + } + + if (!strcasecmp(pos_x, "center")) { + x = (output->usable_area.width / 2) - (max_width / 2); + } else if (strchr(pos_x, '%')) { + x = (output->usable_area.width * atoi(pos_x)) / 100; + } else { + if (pos_x[0] == '-') { + int neg_x = strtol(pos_x, NULL, 10); + x = output->usable_area.width + neg_x; + } else { + x = atoi(pos_x); + } + } + + if (!strcasecmp(pos_y, "center")) { + y = (output->usable_area.height / 2) - (menu->size.height / 2); + } else if (strchr(pos_y, '%')) { + y = (output->usable_area.height * atoi(pos_y)) / 100; + } else { + if (pos_y[0] == '-') { + int neg_y = strtol(pos_y, NULL, 10); + y = output->usable_area.height + neg_y; + } else { + y = atoi(pos_y); + } + } + /* keep menu from being off screen */ + x = MAX(x, 0); + x = MIN(x, (output->usable_area.width -1)); + y = MAX(y, 0); + y = MIN(y, (output->usable_area.height -1)); + /* adjust for which monitor to appear on */ + x += output->usable_area.x; + y += output->usable_area.y; + } + /* Replaced by next show_menu() or cleaned on view_destroy() */ menu->triggered_by_view = view; + menu->server->menu_current = menu; menu_open_root(menu, x, y); } @@ -843,7 +906,9 @@ actions_run(struct view *activator, struct server *server, case ACTION_TYPE_SHOW_MENU: show_menu(server, view, action_get_str(action, "menu", NULL), - action_get_bool(action, "atCursor", true)); + action_get_bool(action, "atCursor", true), + action_get_str(action, "x.position", NULL), + action_get_str(action, "y.position", NULL)); break; case ACTION_TYPE_TOGGLE_MAXIMIZE: if (view) {