mirror of
https://github.com/labwc/labwc.git
synced 2025-10-29 05:40:24 -04:00
menu: improve algorithm for menu placement with xdg-positioner
This commit delegates the calculation for menu position into wlroots utilities for xdg_positioner. Notable functional changes are: - Slide the menu to fit in the output when it's opened out of the output (e.g. top-left window menu is opened when the window is overflowing to the left), rather than not updating the menu at all. - The horizontal alignment of menus is now determined based on the size of each (sub)menu alone rather than the total width of entire menu tree. This means submenus can now overlap with is parents, but this is no longer a problem since we recently added support for menu borders. - Fixed that pipemenus always follow the alignment of its parent even when it overflows from the output.
This commit is contained in:
parent
08de4f3d6b
commit
40c7350064
2 changed files with 59 additions and 92 deletions
|
|
@ -12,14 +12,6 @@ struct wlr_scene_tree;
|
||||||
struct wlr_scene_node;
|
struct wlr_scene_node;
|
||||||
struct scaled_font_buffer;
|
struct scaled_font_buffer;
|
||||||
|
|
||||||
enum menu_align {
|
|
||||||
LAB_MENU_OPEN_AUTO = 0,
|
|
||||||
LAB_MENU_OPEN_LEFT = 1 << 0,
|
|
||||||
LAB_MENU_OPEN_RIGHT = 1 << 1,
|
|
||||||
LAB_MENU_OPEN_TOP = 1 << 2,
|
|
||||||
LAB_MENU_OPEN_BOTTOM = 1 << 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum menuitem_type {
|
enum menuitem_type {
|
||||||
LAB_MENU_ITEM = 0,
|
LAB_MENU_ITEM = 0,
|
||||||
LAB_MENU_SEPARATOR_LINE,
|
LAB_MENU_SEPARATOR_LINE,
|
||||||
|
|
@ -63,7 +55,7 @@ struct menu {
|
||||||
} selection;
|
} selection;
|
||||||
struct wlr_scene_tree *scene_tree;
|
struct wlr_scene_tree *scene_tree;
|
||||||
bool is_pipemenu;
|
bool is_pipemenu;
|
||||||
enum menu_align align;
|
bool align_left;
|
||||||
|
|
||||||
/* Used to match a window-menu to the view that triggered it. */
|
/* Used to match a window-menu to the view that triggered it. */
|
||||||
struct view *triggered_by_view; /* may be NULL */
|
struct view *triggered_by_view; /* may be NULL */
|
||||||
|
|
|
||||||
141
src/menu/menu.c
141
src/menu/menu.c
|
|
@ -9,6 +9,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "action.h"
|
#include "action.h"
|
||||||
#include "common/buf.h"
|
#include "common/buf.h"
|
||||||
|
|
@ -825,109 +826,83 @@ parse_xml(const char *filename, struct server *server)
|
||||||
paths_destroy(&paths);
|
paths_destroy(&paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
/*
|
||||||
menu_get_full_width(struct menu *menu)
|
* Returns the box of a menuitem next to which its submenu is opened.
|
||||||
{
|
* This box can be shrunk or expanded by menu overlaps and borders.
|
||||||
struct theme *theme = menu->server->theme;
|
|
||||||
int width = menu->size.width - theme->menu_overlap_x
|
|
||||||
- theme->menu_border_width;
|
|
||||||
int child_width;
|
|
||||||
int max_child_width = 0;
|
|
||||||
struct menuitem *item;
|
|
||||||
wl_list_for_each(item, &menu->menuitems, link) {
|
|
||||||
if (!item->submenu) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
child_width = menu_get_full_width(item->submenu);
|
|
||||||
if (child_width > max_child_width) {
|
|
||||||
max_child_width = child_width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return width + max_child_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get_submenu_position() - get output layout coordinates of menu window
|
|
||||||
* @item: the menuitem that triggers the submenu (static or dynamic)
|
|
||||||
*/
|
*/
|
||||||
static struct wlr_box
|
static struct wlr_box
|
||||||
get_submenu_position(struct menuitem *item, enum menu_align align)
|
get_item_anchor_rect(struct theme *theme, struct menuitem *item)
|
||||||
{
|
{
|
||||||
struct wlr_box pos = { 0 };
|
|
||||||
struct menu *menu = item->parent;
|
struct menu *menu = item->parent;
|
||||||
struct theme *theme = menu->server->theme;
|
int menu_x = menu->scene_tree->node.x;
|
||||||
pos.x = menu->scene_tree->node.x;
|
int menu_y = menu->scene_tree->node.y;
|
||||||
pos.y = menu->scene_tree->node.y;
|
int overlap_x = theme->menu_overlap_x + theme->menu_border_width;
|
||||||
|
int overlap_y = theme->menu_overlap_y - theme->menu_border_width;
|
||||||
if (align & LAB_MENU_OPEN_RIGHT) {
|
return (struct wlr_box) {
|
||||||
pos.x += menu->size.width - theme->menu_overlap_x
|
.x = menu_x + overlap_x,
|
||||||
- theme->menu_border_width;
|
.y = menu_y + item->tree->node.y + overlap_y,
|
||||||
}
|
.width = menu->size.width - 2 * overlap_x,
|
||||||
pos.y += item->tree->node.y + theme->menu_overlap_y
|
.height = theme->menu_item_height - 2 * overlap_y,
|
||||||
- theme->menu_border_width;
|
};
|
||||||
return pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
menu_configure(struct menu *menu, int lx, int ly, enum menu_align align)
|
menu_configure(struct menu *menu, struct wlr_box anchor_rect)
|
||||||
{
|
{
|
||||||
struct theme *theme = menu->server->theme;
|
struct theme *theme = menu->server->theme;
|
||||||
|
|
||||||
/* Get output local coordinates + output usable area */
|
/* Get output usable area to place the menu within */
|
||||||
double ox = lx;
|
struct output *output = output_nearest_to(menu->server,
|
||||||
double oy = ly;
|
anchor_rect.x, anchor_rect.y);
|
||||||
struct wlr_output *wlr_output = wlr_output_layout_output_at(
|
|
||||||
menu->server->output_layout, lx, ly);
|
|
||||||
struct output *output = wlr_output ? output_from_wlr_output(
|
|
||||||
menu->server, wlr_output) : NULL;
|
|
||||||
if (!output) {
|
if (!output) {
|
||||||
wlr_log(WLR_ERROR,
|
wlr_log(WLR_ERROR, "no output found around (%d,%d)",
|
||||||
"Failed to position menu %s (%s) and its submenus: "
|
anchor_rect.x, anchor_rect.y);
|
||||||
"Not enough screen space", menu->id, menu->label);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wlr_output_layout_output_coords(menu->server->output_layout,
|
struct wlr_box usable = output_usable_area_in_layout_coords(output);
|
||||||
wlr_output, &ox, &oy);
|
|
||||||
|
|
||||||
if (align == LAB_MENU_OPEN_AUTO) {
|
/* Policy for menu placement */
|
||||||
int full_width = menu_get_full_width(menu);
|
struct wlr_xdg_positioner_rules rules = {0};
|
||||||
if (ox + full_width > output->usable_area.width) {
|
rules.size.width = menu->size.width;
|
||||||
align = LAB_MENU_OPEN_LEFT;
|
rules.size.height = menu->size.height;
|
||||||
} else {
|
/* A rectangle next to which the menu is opened */
|
||||||
align = LAB_MENU_OPEN_RIGHT;
|
rules.anchor_rect = anchor_rect;
|
||||||
}
|
/*
|
||||||
}
|
* Place menu at left or right side of anchor_rect, with their
|
||||||
|
* top edges aligned. The alignment is inherited from parent.
|
||||||
if (oy + menu->size.height > output->usable_area.height) {
|
*/
|
||||||
align &= ~LAB_MENU_OPEN_BOTTOM;
|
if (menu->parent && menu->parent->align_left) {
|
||||||
align |= LAB_MENU_OPEN_TOP;
|
rules.anchor = XDG_POSITIONER_ANCHOR_TOP_LEFT;
|
||||||
|
rules.gravity = XDG_POSITIONER_GRAVITY_BOTTOM_LEFT;
|
||||||
} else {
|
} else {
|
||||||
align &= ~LAB_MENU_OPEN_TOP;
|
rules.anchor = XDG_POSITIONER_ANCHOR_TOP_RIGHT;
|
||||||
align |= LAB_MENU_OPEN_BOTTOM;
|
rules.gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT;
|
||||||
|
}
|
||||||
|
/* Flip or slide the menu when it overflows from the output */
|
||||||
|
rules.constraint_adjustment =
|
||||||
|
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X
|
||||||
|
| XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X
|
||||||
|
| XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
|
||||||
|
if (!menu->parent) {
|
||||||
|
/* Allow vertically flipping the root menu */
|
||||||
|
rules.constraint_adjustment |=
|
||||||
|
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (align & LAB_MENU_OPEN_LEFT) {
|
struct wlr_box box;
|
||||||
lx -= menu->size.width - theme->menu_overlap_x - theme->menu_border_width;
|
wlr_xdg_positioner_rules_get_geometry(&rules, &box);
|
||||||
}
|
wlr_xdg_positioner_rules_unconstrain_box(&rules, &usable, &box);
|
||||||
if (align & LAB_MENU_OPEN_TOP) {
|
wlr_scene_node_set_position(&menu->scene_tree->node, box.x, box.y);
|
||||||
ly -= menu->size.height;
|
|
||||||
if (menu->parent) {
|
|
||||||
/* For submenus adjust y to bottom left corner */
|
|
||||||
ly += theme->menu_item_height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wlr_scene_node_set_position(&menu->scene_tree->node, lx, ly);
|
|
||||||
|
|
||||||
/* Needed for pipemenus to inherit alignment */
|
menu->align_left = (box.x < anchor_rect.x);
|
||||||
menu->align = align;
|
|
||||||
|
|
||||||
struct menuitem *item;
|
struct menuitem *item;
|
||||||
wl_list_for_each(item, &menu->menuitems, link) {
|
wl_list_for_each(item, &menu->menuitems, link) {
|
||||||
if (!item->submenu) {
|
if (!item->submenu) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
struct wlr_box pos = get_submenu_position(item, align);
|
anchor_rect = get_item_anchor_rect(theme, item);
|
||||||
menu_configure(item->submenu, pos.x, pos.y, align);
|
menu_configure(item->submenu, anchor_rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1353,7 +1328,7 @@ menu_open_root(struct menu *menu, int x, int y)
|
||||||
}
|
}
|
||||||
close_all_submenus(menu);
|
close_all_submenus(menu);
|
||||||
menu_set_selection(menu, NULL);
|
menu_set_selection(menu, NULL);
|
||||||
menu_configure(menu, x, y, LAB_MENU_OPEN_AUTO);
|
menu_configure(menu, (struct wlr_box){.x = x, .y = y});
|
||||||
wlr_scene_node_set_enabled(&menu->scene_tree->node, true);
|
wlr_scene_node_set_enabled(&menu->scene_tree->node, true);
|
||||||
menu->server->menu_current = menu;
|
menu->server->menu_current = menu;
|
||||||
menu->server->input_mode = LAB_INPUT_STATE_MENU;
|
menu->server->input_mode = LAB_INPUT_STATE_MENU;
|
||||||
|
|
@ -1403,9 +1378,9 @@ create_pipe_menu(struct menu_pipe_context *ctx)
|
||||||
/* Set menu-widths before configuring */
|
/* Set menu-widths before configuring */
|
||||||
post_processing(ctx->server);
|
post_processing(ctx->server);
|
||||||
|
|
||||||
enum menu_align align = ctx->item->parent->align;
|
struct wlr_box anchor_rect =
|
||||||
struct wlr_box pos = get_submenu_position(ctx->item, align);
|
get_item_anchor_rect(ctx->server->theme, ctx->item);
|
||||||
menu_configure(pipe_menu, pos.x, pos.y, align);
|
menu_configure(pipe_menu, anchor_rect);
|
||||||
|
|
||||||
validate(ctx->server);
|
validate(ctx->server);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue