From 429df42a8fa9788eae6a4473ec66ffe3db4e8981 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 2 Aug 2022 22:00:24 +0100 Subject: [PATCH] menu: render submenu arrows --- include/common/font.h | 4 ++- include/common/scaled_font_buffer.h | 3 +- src/common/font.c | 48 +++++++++++++++++++++++------ src/common/scaled_font_buffer.c | 10 ++++-- src/menu/menu.c | 35 +++++++++++---------- src/ssd/ssd_titlebar.c | 4 +-- 6 files changed, 70 insertions(+), 34 deletions(-) diff --git a/include/common/font.h b/include/common/font.h index e64dbbac..afb78cf9 100644 --- a/include/common/font.h +++ b/include/common/font.h @@ -28,9 +28,11 @@ int font_width(struct font *font, const char *string); * @text: text to be generated as texture * @font: font description * @color: foreground color in rgba format + * @arrow: arrow (utf8) character to show or NULL for none */ void font_buffer_create(struct lab_data_buffer **buffer, int max_width, - const char *text, struct font *font, float *color, double scale); + const char *text, struct font *font, float *color, const char *arrow, + double scale); /** * font_finish - free some font related resources diff --git a/include/common/scaled_font_buffer.h b/include/common/scaled_font_buffer.h index 94debeaa..dbce8fff 100644 --- a/include/common/scaled_font_buffer.h +++ b/include/common/scaled_font_buffer.h @@ -16,6 +16,7 @@ struct scaled_font_buffer { char *text; int max_width; float color[4]; + char *arrow; struct font font; struct scaled_scene_buffer *scaled_buffer; }; @@ -44,6 +45,6 @@ struct scaled_font_buffer *scaled_font_buffer_create(struct wlr_scene_tree *pare * - font and color the same */ void scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, - int max_width, struct font *font, float *color); + int max_width, struct font *font, float *color, const char *arrow); #endif /* __LAB_COMMON_SCALED_FONT_BUFFER_H */ diff --git a/src/common/font.c b/src/common/font.c index 02cfc197..9fd49b50 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include @@ -12,7 +13,10 @@ static PangoRectangle font_extents(struct font *font, const char *string) { - PangoRectangle rect; + PangoRectangle rect = { 0 }; + if (!string) { + return rect; + } cairo_surface_t *surface; cairo_t *c; PangoLayout *layout; @@ -58,20 +62,37 @@ font_width(struct font *font, const char *string) void font_buffer_create(struct lab_data_buffer **buffer, int max_width, - const char *text, struct font *font, float *color, double scale) + const char *text, struct font *font, float *color, const char *arrow, + double scale) { + /* Allow a minimum of one pixel each for text and arrow */ + if (max_width < 2) { + max_width = 2; + } + if (!text || !*text) { return; } - PangoRectangle rect = font_extents(font, text); - if (max_width && rect.width > max_width) { - rect.width = max_width; + PangoRectangle text_extents = font_extents(font, text); + PangoRectangle arrow_extents = font_extents(font, arrow); + + if (arrow) { + if (arrow_extents.width >= max_width - 1) { + /* It would be weird to get here, but just in case */ + arrow_extents.width = max_width - 1; + text_extents.width = 1; + } else { + text_extents.width = max_width - arrow_extents.width; + } + } else if (text_extents.width > max_width) { + text_extents.width = max_width; } - *buffer = buffer_create_cairo(rect.width, rect.height, scale, true); + + *buffer = buffer_create_cairo(text_extents.width + arrow_extents.width, + text_extents.height, scale, true); if (!*buffer) { - wlr_log(WLR_ERROR, "Failed to create font buffer of size %dx%d", - rect.width, rect.height); + wlr_log(WLR_ERROR, "Failed to create font buffer"); return; } @@ -82,7 +103,7 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width, cairo_move_to(cairo, 0, 0); PangoLayout *layout = pango_cairo_create_layout(cairo); - pango_layout_set_width(layout, rect.width * PANGO_SCALE); + pango_layout_set_width(layout, text_extents.width * PANGO_SCALE); pango_layout_set_text(layout, text, -1); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); @@ -92,8 +113,15 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width, pango_layout_set_font_description(layout, desc); pango_font_description_free(desc); pango_cairo_update_layout(cairo, layout); - pango_cairo_show_layout(cairo, layout); + + if (arrow) { + cairo_move_to(cairo, text_extents.width, 0); + pango_layout_set_width(layout, arrow_extents.width * PANGO_SCALE); + pango_layout_set_text(layout, arrow, -1); + pango_cairo_show_layout(cairo, layout); + } + g_object_unref(layout); cairo_surface_flush(surf); diff --git a/src/common/scaled_font_buffer.c b/src/common/scaled_font_buffer.c index 66375a72..ef2548dd 100644 --- a/src/common/scaled_font_buffer.c +++ b/src/common/scaled_font_buffer.c @@ -19,7 +19,7 @@ _create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) /* Buffer gets free'd automatically along the backing wlr_buffer */ font_buffer_create(&buffer, self->max_width, self->text, - &self->font, self->color, scale); + &self->font, self->color, self->arrow, scale); self->width = buffer ? buffer->unscaled_width : 0; self->height = buffer ? buffer->unscaled_height : 0; @@ -36,6 +36,7 @@ _destroy(struct scaled_scene_buffer *scaled_buffer) if (self->font.name) { zfree(self->font.name); } + zfree(self->arrow); zfree(scaled_buffer->data); } @@ -68,8 +69,9 @@ scaled_font_buffer_create(struct wlr_scene_tree *parent) } void -scaled_font_buffer_update(struct scaled_font_buffer *self, - const char *text, int max_width, struct font *font, float *color) +scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text, + int max_width, struct font *font, float *color, + const char *arrow) { assert(self); assert(text); @@ -83,6 +85,7 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, if (self->font.name) { zfree(self->font.name); } + zfree(self->arrow); /* Update internal state */ self->text = strdup(text); @@ -92,6 +95,7 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, } self->font.size = font->size; memcpy(self->color, color, sizeof(self->color)); + self->arrow = arrow ? strdup(arrow) : NULL; /* Invalidate cache and force a new render */ scaled_scene_buffer_invalidate_cache(self->scaled_buffer); diff --git a/src/menu/menu.c b/src/menu/menu.c index fa4d2609..0aeae373 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -78,7 +78,7 @@ menu_get_by_id(const char *id) } static struct menuitem * -item_create(struct menu *menu, const char *text) +item_create(struct menu *menu, const char *text, bool show_arrow) { struct menuitem *menuitem = calloc(1, sizeof(struct menuitem)); if (!menuitem) { @@ -137,10 +137,11 @@ item_create(struct menu *menu, const char *text) 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, - &font, theme->menu_items_text_color); + &font, theme->menu_items_text_color, arrow); scaled_font_buffer_update(menuitem->selected.buffer, text, item_max_width, - &font, theme->menu_items_active_text_color); + &font, theme->menu_items_active_text_color, arrow); /* Center font nodes */ x = MENU_ITEM_PADDING_X; @@ -221,7 +222,7 @@ fill_item(char *nodename, char *content) /* defines the start of a new item */ if (!strcmp(nodename, "label")) { - current_item = item_create(current_menu, content); + current_item = item_create(current_menu, content, false); current_item_action = NULL; } else if (!current_item) { wlr_log(WLR_ERROR, "expect element first. " @@ -349,7 +350,7 @@ handle_menu_element(xmlNode *n, struct server *server) * In a nested (inline) menu definition we need to * create an item pointing to the new submenu */ - current_item = item_create(current_menu, label); + current_item = item_create(current_menu, label, true); if (current_item) { submenu = ¤t_item->submenu; } else { @@ -371,7 +372,7 @@ handle_menu_element(xmlNode *n, struct server *server) */ struct menu *menu = menu_get_by_id(id); if (menu) { - current_item = item_create(current_menu, menu->label); + current_item = item_create(current_menu, menu->label, true); if (current_item) { current_item->submenu = menu; } @@ -592,9 +593,9 @@ menu_init_rootmenu(struct server *server) menu = menu_create(server, "root-menu", ""); } if (wl_list_empty(&menu->menuitems)) { - current_item = item_create(menu, _("Reconfigure")); + current_item = item_create(menu, _("Reconfigure"), false); fill_item("name.action", "Reconfigure"); - current_item = item_create(menu, _("Exit")); + current_item = item_create(menu, _("Exit"), false); fill_item("name.action", "Exit"); } } @@ -610,33 +611,33 @@ menu_init_windowmenu(struct server *server) menu = menu_create(server, "client-menu", ""); } if (wl_list_empty(&menu->menuitems)) { - current_item = item_create(menu, _("Minimize")); + current_item = item_create(menu, _("Minimize"), false); fill_item("name.action", "Iconify"); - current_item = item_create(menu, _("Maximize")); + current_item = item_create(menu, _("Maximize"), false); fill_item("name.action", "ToggleMaximize"); - current_item = item_create(menu, _("Fullscreen")); + current_item = item_create(menu, _("Fullscreen"), false); fill_item("name.action", "ToggleFullscreen"); - current_item = item_create(menu, _("Decorations")); + current_item = item_create(menu, _("Decorations"), false); fill_item("name.action", "ToggleDecorations"); - current_item = item_create(menu, _("AlwaysOnTop")); + current_item = item_create(menu, _("AlwaysOnTop"), false); fill_item("name.action", "ToggleAlwaysOnTop"); /* Workspace sub-menu */ struct menu *workspace_menu = menu_create(server, "workspaces", ""); - current_item = item_create(workspace_menu, _("Move left")); + current_item = item_create(workspace_menu, _("Move left"), false); fill_item("name.action", "SendToDesktop"); fill_item("to.action", "left"); fill_item("name.action", "GoToDesktop"); fill_item("to.action", "left"); - current_item = item_create(workspace_menu, _("Move right")); + current_item = item_create(workspace_menu, _("Move right"), false); fill_item("name.action", "SendToDesktop"); fill_item("to.action", "right"); fill_item("name.action", "GoToDesktop"); fill_item("to.action", "right"); - current_item = item_create(menu, _("Workspace")); + current_item = item_create(menu, _("Workspace"), true); current_item->submenu = workspace_menu; - current_item = item_create(menu, _("Close")); + current_item = item_create(menu, _("Close"), false); fill_item("name.action", "Close"); } diff --git a/src/ssd/ssd_titlebar.c b/src/ssd/ssd_titlebar.c index bac4c7ed..3ae0b78b 100644 --- a/src/ssd/ssd_titlebar.c +++ b/src/ssd/ssd_titlebar.c @@ -254,8 +254,8 @@ ssd_update_title(struct view *view) } if (part->buffer) { - scaled_font_buffer_update(part->buffer, - title, title_bg_width, &font, text_color); + scaled_font_buffer_update(part->buffer, title, + title_bg_width, &font, text_color, NULL); } /* And finally update the cache */ -- 2.52.0