mirror of
https://github.com/labwc/labwc.git
synced 2026-02-28 01:40:29 -05:00
src/menu: add support for scalable menu icons
This commit is contained in:
parent
060b59f7ed
commit
1fa4070025
3 changed files with 72 additions and 13 deletions
|
|
@ -15,29 +15,37 @@ The menu file must be entirely enclosed within <openbox_menu> and
|
||||||
</openbox_menu> tags. Inside these tags, menus are specified as follows:
|
</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="">
|
<menu id="">
|
||||||
|
|
||||||
<!-- A menu entry with an action, for example to execute an application -->
|
<!-- A menu entry with an action, for example to execute an application -->
|
||||||
<item label="">
|
<item label="" icon="">
|
||||||
<action></action>
|
<action></action>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<!-- A submenu defined elsewhere -->
|
<!-- A submenu defined elsewhere, uses external label and icon attributes -->
|
||||||
<menu id="" />
|
<menu id="external_menu_settings" />
|
||||||
|
|
||||||
<!-- Horizontal line -->
|
<!-- Horizontal line -->
|
||||||
<separator />
|
<separator />
|
||||||
|
|
||||||
<!-- An inline submenu -->
|
<!-- An inline submenu -->
|
||||||
<menu id="" label="">
|
<menu id="" label="" icon="">
|
||||||
...some content...
|
...some content...
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<separator label=""/>
|
<separator label="" />
|
||||||
|
|
||||||
<!-- Pipemenu -->
|
<!-- Pipemenu -->
|
||||||
<menu id="" label="" execute="COMMAND"/>
|
<menu id="" label="" icon="" execute="COMMAND" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
```
|
```
|
||||||
|
|
@ -56,15 +64,28 @@ The menu file must be entirely enclosed within <openbox_menu> and
|
||||||
view to that workspace when selected.
|
view to that workspace when selected.
|
||||||
|
|
||||||
*menu.id* (when nested under other *<menu>* element)
|
*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*
|
*menu.label*
|
||||||
The title of the menu, shown in its parent. A label must be given when
|
The title of the menu, shown in its parent. A label must be given when
|
||||||
defining a menu.
|
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*
|
*menu.item.label*
|
||||||
The visible name of the menu item.
|
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*
|
*menu.item.action*
|
||||||
See labwc-actions(5). Note: XML CDATA is supported for this node in
|
See labwc-actions(5). Note: XML CDATA is supported for this node in
|
||||||
order to maintain compatibility with obmenu-generator.
|
order to maintain compatibility with obmenu-generator.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ struct menuitem {
|
||||||
char *execute;
|
char *execute;
|
||||||
char *id; /* needed for pipemenus */
|
char *id; /* needed for pipemenus */
|
||||||
char *text;
|
char *text;
|
||||||
|
char *icon_name;
|
||||||
const char *arrow;
|
const char *arrow;
|
||||||
struct menu *parent;
|
struct menu *parent;
|
||||||
struct menu *submenu;
|
struct menu *submenu;
|
||||||
|
|
@ -41,6 +42,7 @@ struct menuitem {
|
||||||
struct menu {
|
struct menu {
|
||||||
char *id;
|
char *id;
|
||||||
char *label;
|
char *label;
|
||||||
|
char *icon_name;
|
||||||
struct menu *parent;
|
struct menu *parent;
|
||||||
struct menu_pipe_context *pipe_ctx;
|
struct menu_pipe_context *pipe_ctx;
|
||||||
|
|
||||||
|
|
@ -57,6 +59,7 @@ struct menu {
|
||||||
struct wlr_scene_tree *scene_tree;
|
struct wlr_scene_tree *scene_tree;
|
||||||
bool is_pipemenu;
|
bool is_pipemenu;
|
||||||
bool align_left;
|
bool align_left;
|
||||||
|
bool has_icons;
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#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/scaled-icon-buffer.h"
|
||||||
#include "common/scaled-rect-buffer.h"
|
#include "common/scaled-rect-buffer.h"
|
||||||
#include "common/scene-helpers.h"
|
#include "common/scene-helpers.h"
|
||||||
#include "common/spawn.h"
|
#include "common/spawn.h"
|
||||||
|
|
@ -34,6 +35,8 @@
|
||||||
#define PIPEMENU_MAX_BUF_SIZE 1048576 /* 1 MiB */
|
#define PIPEMENU_MAX_BUF_SIZE 1048576 /* 1 MiB */
|
||||||
#define PIPEMENU_TIMEOUT_IN_MS 4000 /* 4 seconds */
|
#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> */
|
/* 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;
|
||||||
|
|
@ -163,6 +166,12 @@ item_create_scene_for_state(struct menuitem *item, float *text_color,
|
||||||
/* Tree to hold background and label buffers */
|
/* Tree to hold background and label buffers */
|
||||||
struct wlr_scene_tree *tree = wlr_scene_tree_create(item->tree);
|
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 */
|
/* Create background */
|
||||||
int bg_width = menu->size.width
|
int bg_width = menu->size.width
|
||||||
- 2 * theme->menu_border_width;
|
- 2 * theme->menu_border_width;
|
||||||
|
|
@ -170,7 +179,16 @@ item_create_scene_for_state(struct menuitem *item, float *text_color,
|
||||||
|
|
||||||
int arrow_width = item->arrow ?
|
int arrow_width = item->arrow ?
|
||||||
font_width(&rc.font_menuitem, item->arrow) : 0;
|
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 */
|
/* Create label */
|
||||||
struct scaled_font_buffer *label_buffer = scaled_font_buffer_create(tree);
|
struct scaled_font_buffer *label_buffer = scaled_font_buffer_create(tree);
|
||||||
|
|
@ -178,7 +196,7 @@ item_create_scene_for_state(struct menuitem *item, float *text_color,
|
||||||
scaled_font_buffer_update(label_buffer, item->text, label_max_width,
|
scaled_font_buffer_update(label_buffer, item->text, label_max_width,
|
||||||
&rc.font_menuitem, text_color, bg_color);
|
&rc.font_menuitem, text_color, bg_color);
|
||||||
/* Vertically center and left-align label */
|
/* 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;
|
int y = (theme->menu_item_height - label_buffer->height) / 2;
|
||||||
wlr_scene_node_set_position(&label_buffer->scene_buffer->node, x, y);
|
wlr_scene_node_set_position(&label_buffer->scene_buffer->node, x, y);
|
||||||
|
|
||||||
|
|
@ -363,6 +381,10 @@ menu_update_scene(struct menu *menu)
|
||||||
+ 2 * theme->menu_border_width;
|
+ 2 * theme->menu_border_width;
|
||||||
menu->size.width = MAX(menu->size.width, 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 = MAX(menu->size.width, theme->menu_min_width);
|
||||||
menu->size.width = MIN(menu->size.width, theme->menu_max_width);
|
menu->size.width = MIN(menu->size.width, theme->menu_max_width);
|
||||||
|
|
||||||
|
|
@ -436,10 +458,13 @@ fill_item(char *nodename, char *content)
|
||||||
wlr_log(WLR_ERROR, "expect <item label=\"\"> element first. "
|
wlr_log(WLR_ERROR, "expect <item label=\"\"> element first. "
|
||||||
"nodename: '%s' content: '%s'", nodename, content);
|
"nodename: '%s' content: '%s'", nodename, content);
|
||||||
} else if (!strcmp(nodename, "icon")) {
|
} else if (!strcmp(nodename, "icon")) {
|
||||||
/*
|
#if HAVE_LIBSFDO
|
||||||
* Do nothing as we don't support menu icons - just avoid
|
// TODO: add some rc.menu_icons bool
|
||||||
* logging errors if a menu.xml file contains icon="" entries.
|
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")) {
|
} else if (!strcmp(nodename, "name.action")) {
|
||||||
current_item_action = action_create(content);
|
current_item_action = action_create(content);
|
||||||
if (current_item_action) {
|
if (current_item_action) {
|
||||||
|
|
@ -468,6 +493,7 @@ item_destroy(struct menuitem *item)
|
||||||
free(item->execute);
|
free(item->execute);
|
||||||
free(item->id);
|
free(item->id);
|
||||||
free(item->text);
|
free(item->text);
|
||||||
|
free(item->icon_name);
|
||||||
free(item);
|
free(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -647,6 +673,7 @@ static void
|
||||||
handle_menu_element(xmlNode *n, struct server *server)
|
handle_menu_element(xmlNode *n, struct server *server)
|
||||||
{
|
{
|
||||||
char *label = (char *)xmlGetProp(n, (const xmlChar *)"label");
|
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 *execute = (char *)xmlGetProp(n, (const xmlChar *)"execute");
|
||||||
char *id = (char *)xmlGetProp(n, (const xmlChar *)"id");
|
char *id = (char *)xmlGetProp(n, (const xmlChar *)"id");
|
||||||
|
|
||||||
|
|
@ -666,6 +693,7 @@ handle_menu_element(xmlNode *n, struct server *server)
|
||||||
} else {
|
} else {
|
||||||
current_item = item_create(current_menu, label,
|
current_item = item_create(current_menu, label,
|
||||||
/* arrow */ true);
|
/* arrow */ true);
|
||||||
|
fill_item("icon", icon_name);
|
||||||
current_item_action = NULL;
|
current_item_action = NULL;
|
||||||
current_item->execute = xstrdup(execute);
|
current_item->execute = xstrdup(execute);
|
||||||
current_item->id = xstrdup(id);
|
current_item->id = xstrdup(id);
|
||||||
|
|
@ -695,6 +723,7 @@ handle_menu_element(xmlNode *n, struct server *server)
|
||||||
*/
|
*/
|
||||||
current_item = item_create(current_menu, label, true);
|
current_item = item_create(current_menu, label, true);
|
||||||
if (current_item) {
|
if (current_item) {
|
||||||
|
fill_item("icon", icon_name);
|
||||||
submenu = ¤t_item->submenu;
|
submenu = ¤t_item->submenu;
|
||||||
} else {
|
} else {
|
||||||
submenu = NULL;
|
submenu = NULL;
|
||||||
|
|
@ -702,6 +731,9 @@ handle_menu_element(xmlNode *n, struct server *server)
|
||||||
}
|
}
|
||||||
++menu_level;
|
++menu_level;
|
||||||
current_menu = menu_create(server, id, label);
|
current_menu = menu_create(server, id, label);
|
||||||
|
if (icon_name) {
|
||||||
|
current_menu->icon_name = xstrdup(icon_name);
|
||||||
|
}
|
||||||
if (submenu) {
|
if (submenu) {
|
||||||
*submenu = current_menu;
|
*submenu = current_menu;
|
||||||
}
|
}
|
||||||
|
|
@ -739,6 +771,7 @@ handle_menu_element(xmlNode *n, struct server *server)
|
||||||
if (menu) {
|
if (menu) {
|
||||||
current_item = item_create(current_menu, menu->label, true);
|
current_item = item_create(current_menu, menu->label, true);
|
||||||
if (current_item) {
|
if (current_item) {
|
||||||
|
fill_item("icon", menu->icon_name);
|
||||||
current_item->submenu = menu;
|
current_item->submenu = menu;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -747,6 +780,7 @@ handle_menu_element(xmlNode *n, struct server *server)
|
||||||
}
|
}
|
||||||
error:
|
error:
|
||||||
free(label);
|
free(label);
|
||||||
|
free(icon_name);
|
||||||
free(execute);
|
free(execute);
|
||||||
free(id);
|
free(id);
|
||||||
}
|
}
|
||||||
|
|
@ -1209,6 +1243,7 @@ menu_free(struct menu *menu)
|
||||||
wl_list_remove(&menu->link);
|
wl_list_remove(&menu->link);
|
||||||
zfree(menu->id);
|
zfree(menu->id);
|
||||||
zfree(menu->label);
|
zfree(menu->label);
|
||||||
|
zfree(menu->icon_name);
|
||||||
zfree(menu);
|
zfree(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue