diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index af431479..0c5db010 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -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 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* Border color of active window diff --git a/include/common/scaled_font_buffer.h b/include/common/scaled_font_buffer.h index fb5e925d..de9a365f 100644 --- a/include/common/scaled_font_buffer.h +++ b/include/common/scaled_font_buffer.h @@ -25,7 +25,7 @@ struct scaled_font_buffer { /** * Create an auto scaling font buffer, providing a wlr_scene_buffer node for * 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. * * 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, 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 */ diff --git a/include/menu/menu.h b/include/menu/menu.h index bbb4905e..5d07fed0 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -33,6 +33,7 @@ struct menuitem { struct menu *submenu; bool selectable; int height; + int native_width; struct wlr_scene_tree *tree; struct menu_scene normal; struct menu_scene selected; diff --git a/include/theme.h b/include/theme.h index 35972481..11386cad 100644 --- a/include/theme.h +++ b/include/theme.h @@ -53,6 +53,9 @@ struct theme { float menu_items_active_bg_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_padding_width; int menu_separator_padding_height; diff --git a/src/common/scaled_font_buffer.c b/src/common/scaled_font_buffer.c index ec4c6161..3daae593 100644 --- a/src/common/scaled_font_buffer.c +++ b/src/common/scaled_font_buffer.c @@ -90,3 +90,10 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, /* Invalidate cache and force a new render */ 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); +} diff --git a/src/menu/menu.c b/src/menu/menu.c index f02939ef..30661910 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -17,14 +17,13 @@ #include "common/mem.h" #include "common/nodename.h" #include "common/scaled_font_buffer.h" +#include "common/scene-helpers.h" #include "common/string-helpers.h" #include "labwc.h" #include "menu/menu.h" #include "node.h" #include "theme.h" -#define MENUWIDTH (110) - /* state-machine variables for processing */ static bool in_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->parent = current_menu; 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->scene_tree = wlr_scene_tree_create(server->menu_tree); wlr_scene_node_set_enabled(&menu->scene_tree->node, false); @@ -75,6 +74,59 @@ menu_get_by_id(const char *id) 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 * 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 theme *theme = server->theme; + const char *arrow = show_arrow ? "›" : NULL; + if (!menu->item_height) { menu->item_height = font_height(&rc.font_menuitem) + 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; 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 */ 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 */ menuitem->normal.background = &wlr_scene_rect_create( menuitem->normal.tree, - MENUWIDTH, menu->item_height, + menu->size.width, menu->item_height, theme->menu_items_bg_color)->node; menuitem->selected.background = &wlr_scene_rect_create( menuitem->selected.tree, - MENUWIDTH, menu->item_height, + menu->size.width, menu->item_height, theme->menu_items_active_bg_color)->node; /* 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; /* Font buffers */ - const char *arrow = show_arrow ? "›" : NULL; - scaled_font_buffer_update(menuitem->normal.buffer, text, item_max_width, + scaled_font_buffer_update(menuitem->normal.buffer, text, menuitem->native_width, &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); /* 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.background = &wlr_scene_rect_create( menuitem->normal.tree, - MENUWIDTH, menuitem->height, + menu->size.width, menuitem->height, 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.tree, 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) { - lx -= MENUWIDTH - theme->menu_overlap_x; + lx -= menu->size.width - theme->menu_overlap_x; } if (align & LAB_MENU_OPEN_TOP) { ly -= menu->size.height; @@ -530,7 +586,7 @@ menu_configure(struct menu *menu, int lx, int ly, enum menu_align align) continue; } 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 { new_lx = lx; } @@ -642,6 +698,7 @@ menu_init(struct server *server) { init_rootmenu(server); init_windowmenu(server); + post_processing(server); } void diff --git a/src/theme.c b/src/theme.c index 5ae471ce..a0518634 100644 --- a/src/theme.c +++ b/src/theme.c @@ -131,6 +131,9 @@ theme_builtin(struct theme *theme) theme->menu_item_padding_x = 7; 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_padding_width = 6; 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); } + 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")) { parse_hexstr(value, theme->menu_items_bg_color); } @@ -488,6 +498,13 @@ post_processing(struct theme *theme) 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 */ if (theme->osd_bg_color[0] == FLT_MIN) { memcpy(theme->osd_bg_color,