</openbox_menu> tags. Inside these tags, menus are specified as follows:
```
+<!-- A toplevel menu -->
+<menu id="external_menu_settings" label="External Settings" icon="preferences-other">
+ <item label="Display setting">
+ <action name="Execute" command="wdisplays" />
+ </item>
+</menu>
+
+<!-- Another toplevel menu -->
<menu id="">
<!-- A menu entry with an action, for example to execute an application -->
- <item label="">
+ <item label="" icon="">
<action></action>
</item>
- <!-- A submenu defined elsewhere -->
- <menu id="" />
+ <!-- A submenu defined elsewhere, uses external label and icon attributes -->
+ <menu id="external_menu_settings" />
<!-- Horizontal line -->
<separator />
<!-- An inline submenu -->
- <menu id="" label="">
+ <menu id="" label="" icon="">
...some content...
</menu>
<!-- Title -->
- <separator label=""/>
+ <separator label="" />
<!-- Pipemenu -->
- <menu id="" label="" execute="COMMAND"/>
+ <menu id="" label="" icon="" execute="COMMAND" />
</menu>
```
view to that workspace when selected.
*menu.id* (when nested under other *<menu>* element)
- Link to a submenu defined elsewhere (by a *<menu id="">* at toplevel)
+ Link to a submenu defined elsewhere (by a *<menu id="">* at toplevel).
*menu.label*
The title of the menu, shown in its parent. A label must be given when
defining a menu.
+*menu.icon*
+ An icon to be rendered, shown in its parent. This argument is optional.
+ See *menu.item.icon* for further details.
+
*menu.item.label*
The visible name of the menu item.
+*menu.item.icon*
+ The name of an icon to be rendered in front of the menu entry. This
+ attribute is optional. The name follows naming conventions of "Icon="
+ entries in .desktop files. If used, it is recommended to use the name of
+ the icon only rather than a full path. This ensures that the icon will
+ be looked up in the scale of the output where the menu is rendered. E.g.
+ use of icon="vlc" is suggested over using
+ icon="/usr/share/icons/hicolor/16x16/apps/vlc.png".
+
*menu.item.action*
See labwc-actions(5). Note: XML CDATA is supported for this node in
order to maintain compatibility with obmenu-generator.
#include "common/mem.h"
#include "common/nodename.h"
#include "common/scaled-font-buffer.h"
+#include "common/scaled-icon-buffer.h"
#include "common/scaled-rect-buffer.h"
#include "common/scene-helpers.h"
#include "common/spawn.h"
#define PIPEMENU_MAX_BUF_SIZE 1048576 /* 1 MiB */
#define PIPEMENU_TIMEOUT_IN_MS 4000 /* 4 seconds */
+#define ICON_SIZE (rc.theme->menu_item_height - 2 * rc.theme->menu_items_padding_y)
+
/* state-machine variables for processing <item></item> */
static bool in_item;
static struct menuitem *current_item;
/* Tree to hold background and label buffers */
struct wlr_scene_tree *tree = wlr_scene_tree_create(item->tree);
+ int icon_width = 0;
+ int icon_size = ICON_SIZE;
+ if (item->parent->has_icons) {
+ icon_width = theme->menu_items_padding_x + icon_size;
+ }
+
/* Create background */
int bg_width = menu->size.width
- 2 * theme->menu_border_width;
int arrow_width = item->arrow ?
font_width(&rc.font_menuitem, item->arrow) : 0;
- int label_max_width = bg_width - 2 * theme->menu_items_padding_x - arrow_width;
+ int label_max_width = bg_width - 2 * theme->menu_items_padding_x - arrow_width - icon_width;
+
+ /* Create icon */
+ if (item->icon_name) {
+ struct scaled_icon_buffer *icon_buffer = scaled_icon_buffer_create(
+ tree, menu->server, icon_size, icon_size);
+ scaled_icon_buffer_set_icon_name(icon_buffer, item->icon_name);
+ wlr_scene_node_set_position(&icon_buffer->scene_buffer->node,
+ theme->menu_items_padding_x, theme->menu_items_padding_y);
+ }
/* Create label */
struct scaled_font_buffer *label_buffer = scaled_font_buffer_create(tree);
scaled_font_buffer_update(label_buffer, item->text, label_max_width,
&rc.font_menuitem, text_color, bg_color);
/* Vertically center and left-align label */
- int x = theme->menu_items_padding_x;
+ int x = theme->menu_items_padding_x + icon_width;
int y = (theme->menu_item_height - label_buffer->height) / 2;
wlr_scene_node_set_position(&label_buffer->scene_buffer->node, x, y);
+ 2 * theme->menu_border_width;
menu->size.width = MAX(menu->size.width, width);
}
+
+ if (menu->has_icons) {
+ menu->size.width += theme->menu_items_padding_x + ICON_SIZE;
+ }
menu->size.width = MAX(menu->size.width, theme->menu_min_width);
menu->size.width = MIN(menu->size.width, theme->menu_max_width);
wlr_log(WLR_ERROR, "expect <item label=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content);
} else if (!strcmp(nodename, "icon")) {
- /*
- * Do nothing as we don't support menu icons - just avoid
- * logging errors if a menu.xml file contains icon="" entries.
- */
+#if HAVE_LIBSFDO
+ // TODO: add some rc.menu_icons bool
+ if (true && !string_null_or_empty(content)) {
+ xstrdup_replace(current_item->icon_name, content);
+ current_menu->has_icons = true;
+ }
+#endif
} else if (!strcmp(nodename, "name.action")) {
current_item_action = action_create(content);
if (current_item_action) {
free(item->execute);
free(item->id);
free(item->text);
+ free(item->icon_name);
free(item);
}
handle_menu_element(xmlNode *n, struct server *server)
{
char *label = (char *)xmlGetProp(n, (const xmlChar *)"label");
+ char *icon_name = (char *)xmlGetProp(n, (const xmlChar *)"icon");
char *execute = (char *)xmlGetProp(n, (const xmlChar *)"execute");
char *id = (char *)xmlGetProp(n, (const xmlChar *)"id");
} else {
current_item = item_create(current_menu, label,
/* arrow */ true);
+ fill_item("icon", icon_name);
current_item_action = NULL;
current_item->execute = xstrdup(execute);
current_item->id = xstrdup(id);
*/
current_item = item_create(current_menu, label, true);
if (current_item) {
+ fill_item("icon", icon_name);
submenu = ¤t_item->submenu;
} else {
submenu = NULL;
}
++menu_level;
current_menu = menu_create(server, id, label);
+ if (icon_name) {
+ current_menu->icon_name = xstrdup(icon_name);
+ }
if (submenu) {
*submenu = current_menu;
}
if (menu) {
current_item = item_create(current_menu, menu->label, true);
if (current_item) {
+ fill_item("icon", menu->icon_name);
current_item->submenu = menu;
}
} else {
}
error:
free(label);
+ free(icon_name);
free(execute);
free(id);
}
wl_list_remove(&menu->link);
zfree(menu->id);
zfree(menu->label);
+ zfree(menu->icon_name);
zfree(menu);
}