menu: Dynamically adjust menu width based on widest item

Adds two new theme vars:
- menu.width.min (menu will never be smaller than this)
- menu.width.max (menu will never be wider than this + padding)

A fixed menu width can be achieved by setting
menu.width.min == menu.width.max.
This commit is contained in:
Consolatis 2022-12-05 14:38:16 +01:00 committed by Johan Malm
parent f0666ba5c9
commit d00327bc32
7 changed files with 115 additions and 14 deletions

View file

@ -59,6 +59,14 @@ A theme consists of a themerc file and optionally some xbm icons.
Vertical offset in pixels between submenus and their parents. Positive Vertical offset in pixels between submenus and their parents. Positive
values for downwards and negative for upwards. Default is 0. values for downwards and negative for upwards. Default is 0.
*menu.width.min*
Minimal width for menus. Default is 20.
A fixed width can be achieved by setting .min and .max to the same value.
*menu.width.max*
Maximal width for menus. Default is 200.
A fixed width can be achieved by setting .min and .max to the same value.
*window.active.border.color* *window.active.border.color*
Border color of active window Border color of active window

View file

@ -25,7 +25,7 @@ struct scaled_font_buffer {
/** /**
* Create an auto scaling font buffer, providing a wlr_scene_buffer node for * Create an auto scaling font buffer, providing a wlr_scene_buffer node for
* display. It gets destroyed automatically when the backing scaled_scene_buffer * display. It gets destroyed automatically when the backing scaled_scene_buffer
* is being destoyed which in turn happens automatically when the backing * is being destroyed which in turn happens automatically when the backing
* wlr_scene_buffer (or one of its parents) is being destroyed. * wlr_scene_buffer (or one of its parents) is being destroyed.
* *
* To actually show some text, scaled_font_buffer_update() has to be called. * To actually show some text, scaled_font_buffer_update() has to be called.
@ -48,4 +48,12 @@ struct scaled_font_buffer *scaled_font_buffer_create(struct wlr_scene_tree *pare
void scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, void scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
int max_width, struct font *font, float *color, const char *arrow); int max_width, struct font *font, float *color, const char *arrow);
/**
* Update the max width of an existing auto scaling font buffer
* and force a new render.
*
* No steps are taken to detect if its actually required to render a new buffer.
*/
void scaled_font_buffer_set_max_width(struct scaled_font_buffer *self, int max_width);
#endif /* __LAB_COMMON_SCALED_FONT_BUFFER_H */ #endif /* __LAB_COMMON_SCALED_FONT_BUFFER_H */

View file

@ -33,6 +33,7 @@ struct menuitem {
struct menu *submenu; struct menu *submenu;
bool selectable; bool selectable;
int height; int height;
int native_width;
struct wlr_scene_tree *tree; struct wlr_scene_tree *tree;
struct menu_scene normal; struct menu_scene normal;
struct menu_scene selected; struct menu_scene selected;

View file

@ -53,6 +53,9 @@ struct theme {
float menu_items_active_bg_color[4]; float menu_items_active_bg_color[4];
float menu_items_active_text_color[4]; float menu_items_active_text_color[4];
int menu_min_width;
int menu_max_width;
int menu_separator_line_thickness; int menu_separator_line_thickness;
int menu_separator_padding_width; int menu_separator_padding_width;
int menu_separator_padding_height; int menu_separator_padding_height;

View file

@ -90,3 +90,10 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
/* Invalidate cache and force a new render */ /* Invalidate cache and force a new render */
scaled_scene_buffer_invalidate_cache(self->scaled_buffer); scaled_scene_buffer_invalidate_cache(self->scaled_buffer);
} }
void
scaled_font_buffer_set_max_width(struct scaled_font_buffer *self, int max_width)
{
self->max_width = max_width;
scaled_scene_buffer_invalidate_cache(self->scaled_buffer);
}

View file

@ -17,14 +17,13 @@
#include "common/mem.h" #include "common/mem.h"
#include "common/nodename.h" #include "common/nodename.h"
#include "common/scaled_font_buffer.h" #include "common/scaled_font_buffer.h"
#include "common/scene-helpers.h"
#include "common/string-helpers.h" #include "common/string-helpers.h"
#include "labwc.h" #include "labwc.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "node.h" #include "node.h"
#include "theme.h" #include "theme.h"
#define MENUWIDTH (110)
/* state-machine variables for processing <item></item> */ /* state-machine variables for processing <item></item> */
static bool in_item; static bool in_item;
static struct menuitem *current_item; static struct menuitem *current_item;
@ -52,7 +51,7 @@ menu_create(struct server *server, const char *id, const char *label)
menu->label = xstrdup(label ? label : id); menu->label = xstrdup(label ? label : id);
menu->parent = current_menu; menu->parent = current_menu;
menu->server = server; menu->server = server;
menu->size.width = MENUWIDTH; menu->size.width = server->theme->menu_min_width;
/* menu->size.height will be kept up to date by adding items */ /* menu->size.height will be kept up to date by adding items */
menu->scene_tree = wlr_scene_tree_create(server->menu_tree); menu->scene_tree = wlr_scene_tree_create(server->menu_tree);
wlr_scene_node_set_enabled(&menu->scene_tree->node, false); wlr_scene_node_set_enabled(&menu->scene_tree->node, false);
@ -75,6 +74,59 @@ menu_get_by_id(const char *id)
return NULL; return NULL;
} }
static void
menu_update_width(struct menu *menu)
{
struct menuitem *item;
struct theme *theme = menu->server->theme;
int max_width = theme->menu_min_width;
/* Get widest menu item, clamped by menu_max_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;
}
}
menu->size.width = max_width + 2 * theme->menu_item_padding_x;
/* Update all items for the new size */
wl_list_for_each(item, &menu->menuitems, link) {
wlr_scene_rect_set_size(
lab_wlr_scene_get_rect(item->normal.background),
menu->size.width, item->height);
if (!item->selected.background) {
/* This is a separator. They don't have a selected background. */
wlr_scene_rect_set_size(
lab_wlr_scene_get_rect(item->normal.text),
menu->size.width - 2 * theme->menu_separator_padding_width,
theme->menu_separator_line_thickness);
} else {
/* Usual menu item */
wlr_scene_rect_set_size(
lab_wlr_scene_get_rect(item->selected.background),
menu->size.width, item->height);
if (item->native_width > max_width || item->submenu) {
scaled_font_buffer_set_max_width(item->normal.buffer,
max_width);
scaled_font_buffer_set_max_width(item->selected.buffer,
max_width);
}
}
}
}
static void
post_processing(struct server *server)
{
struct menu *menu;
for (int i = 0; i < nr_menus; ++i) {
menu = menus + i;
menu_update_width(menu);
}
}
static struct menuitem * static struct menuitem *
item_create(struct menu *menu, const char *text, bool show_arrow) item_create(struct menu *menu, const char *text, bool show_arrow)
{ {
@ -84,6 +136,8 @@ item_create(struct menu *menu, const char *text, bool show_arrow)
struct server *server = menu->server; struct server *server = menu->server;
struct theme *theme = server->theme; struct theme *theme = server->theme;
const char *arrow = show_arrow ? "" : NULL;
if (!menu->item_height) { if (!menu->item_height) {
menu->item_height = font_height(&rc.font_menuitem) menu->item_height = font_height(&rc.font_menuitem)
+ 2 * theme->menu_item_padding_y; + 2 * theme->menu_item_padding_y;
@ -91,7 +145,10 @@ item_create(struct menu *menu, const char *text, bool show_arrow)
menuitem->height = menu->item_height; menuitem->height = menu->item_height;
int x, y; int x, y;
int item_max_width = MENUWIDTH - 2 * theme->menu_item_padding_x; menuitem->native_width = font_width(&rc.font_menuitem, text);
if (arrow) {
menuitem->native_width += font_width(&rc.font_menuitem, arrow);
}
/* Menu item root node */ /* Menu item root node */
menuitem->tree = wlr_scene_tree_create(menu->scene_tree); menuitem->tree = wlr_scene_tree_create(menu->scene_tree);
@ -105,11 +162,11 @@ item_create(struct menu *menu, const char *text, bool show_arrow)
/* Item background nodes */ /* Item background nodes */
menuitem->normal.background = &wlr_scene_rect_create( menuitem->normal.background = &wlr_scene_rect_create(
menuitem->normal.tree, menuitem->normal.tree,
MENUWIDTH, menu->item_height, menu->size.width, menu->item_height,
theme->menu_items_bg_color)->node; theme->menu_items_bg_color)->node;
menuitem->selected.background = &wlr_scene_rect_create( menuitem->selected.background = &wlr_scene_rect_create(
menuitem->selected.tree, menuitem->selected.tree,
MENUWIDTH, menu->item_height, menu->size.width, menu->item_height,
theme->menu_items_active_bg_color)->node; theme->menu_items_active_bg_color)->node;
/* Font nodes */ /* Font nodes */
@ -129,10 +186,9 @@ item_create(struct menu *menu, const char *text, bool show_arrow)
menuitem->selected.text = &menuitem->selected.buffer->scene_buffer->node; menuitem->selected.text = &menuitem->selected.buffer->scene_buffer->node;
/* Font buffers */ /* Font buffers */
const char *arrow = show_arrow ? "" : NULL; scaled_font_buffer_update(menuitem->normal.buffer, text, menuitem->native_width,
scaled_font_buffer_update(menuitem->normal.buffer, text, item_max_width,
&rc.font_menuitem, theme->menu_items_text_color, arrow); &rc.font_menuitem, theme->menu_items_text_color, arrow);
scaled_font_buffer_update(menuitem->selected.buffer, text, item_max_width, scaled_font_buffer_update(menuitem->selected.buffer, text, menuitem->native_width,
&rc.font_menuitem, theme->menu_items_active_text_color, arrow); &rc.font_menuitem, theme->menu_items_active_text_color, arrow);
/* Center font nodes */ /* Center font nodes */
@ -173,10 +229,10 @@ separator_create(struct menu *menu, const char *label)
menuitem->normal.tree = wlr_scene_tree_create(menuitem->tree); menuitem->normal.tree = wlr_scene_tree_create(menuitem->tree);
menuitem->normal.background = &wlr_scene_rect_create( menuitem->normal.background = &wlr_scene_rect_create(
menuitem->normal.tree, menuitem->normal.tree,
MENUWIDTH, menuitem->height, menu->size.width, menuitem->height,
theme->menu_items_bg_color)->node; theme->menu_items_bg_color)->node;
int width = MENUWIDTH - 2 * theme->menu_separator_padding_width; int width = menu->size.width - 2 * theme->menu_separator_padding_width;
menuitem->normal.text = &wlr_scene_rect_create( menuitem->normal.text = &wlr_scene_rect_create(
menuitem->normal.tree, menuitem->normal.tree,
width > 0 ? width : 0, width > 0 ? width : 0,
@ -511,7 +567,7 @@ menu_configure(struct menu *menu, int lx, int ly, enum menu_align align)
} }
if (align & LAB_MENU_OPEN_LEFT) { if (align & LAB_MENU_OPEN_LEFT) {
lx -= MENUWIDTH - theme->menu_overlap_x; lx -= menu->size.width - theme->menu_overlap_x;
} }
if (align & LAB_MENU_OPEN_TOP) { if (align & LAB_MENU_OPEN_TOP) {
ly -= menu->size.height; ly -= menu->size.height;
@ -530,7 +586,7 @@ menu_configure(struct menu *menu, int lx, int ly, enum menu_align align)
continue; continue;
} }
if (align & LAB_MENU_OPEN_RIGHT) { if (align & LAB_MENU_OPEN_RIGHT) {
new_lx = lx + MENUWIDTH - theme->menu_overlap_x; new_lx = lx + menu->size.width - theme->menu_overlap_x;
} else { } else {
new_lx = lx; new_lx = lx;
} }
@ -642,6 +698,7 @@ menu_init(struct server *server)
{ {
init_rootmenu(server); init_rootmenu(server);
init_windowmenu(server); init_windowmenu(server);
post_processing(server);
} }
void void

View file

@ -131,6 +131,9 @@ theme_builtin(struct theme *theme)
theme->menu_item_padding_x = 7; theme->menu_item_padding_x = 7;
theme->menu_item_padding_y = 4; theme->menu_item_padding_y = 4;
theme->menu_min_width = 20;
theme->menu_max_width = 200;
theme->menu_separator_line_thickness = 1; theme->menu_separator_line_thickness = 1;
theme->menu_separator_padding_width = 6; theme->menu_separator_padding_width = 6;
theme->menu_separator_padding_height = 3; theme->menu_separator_padding_height = 3;
@ -260,6 +263,13 @@ entry(struct theme *theme, const char *key, const char *value)
theme->window_inactive_button_close_unpressed_image_color); theme->window_inactive_button_close_unpressed_image_color);
} }
if (match(key, "menu.width.min")) {
theme->menu_min_width = atoi(value);
}
if (match(key, "menu.width.max")) {
theme->menu_max_width = atoi(value);
}
if (match(key, "menu.items.bg.color")) { if (match(key, "menu.items.bg.color")) {
parse_hexstr(value, theme->menu_items_bg_color); parse_hexstr(value, theme->menu_items_bg_color);
} }
@ -488,6 +498,13 @@ post_processing(struct theme *theme)
theme->title_height = rc.corner_radius + 1; theme->title_height = rc.corner_radius + 1;
} }
if (theme->menu_max_width < theme->menu_min_width) {
wlr_log(WLR_ERROR,
"Adjusting menu.width.max: .max (%d) lower than .min (%d)",
theme->menu_max_width, theme->menu_min_width);
theme->menu_max_width = theme->menu_min_width;
}
/* Inherit OSD settings if not set */ /* Inherit OSD settings if not set */
if (theme->osd_bg_color[0] == FLT_MIN) { if (theme->osd_bg_color[0] == FLT_MIN) {
memcpy(theme->osd_bg_color, memcpy(theme->osd_bg_color,