menu: allow for Unicode accelerators

This commit is contained in:
Alex Chernika 2026-04-13 23:06:08 +02:00
parent cea22422d5
commit 1c646af144
No known key found for this signature in database
GPG key ID: 6029FAD8ABFB076A
3 changed files with 32 additions and 34 deletions

View file

@ -23,7 +23,7 @@ struct menuitem {
char *text; char *text;
char *icon_name; char *icon_name;
const char *arrow; const char *arrow;
char accelerator; uint32_t accelerator;
struct menu *parent; struct menu *parent;
struct menu *submenu; struct menu *submenu;
bool selectable; bool selectable;
@ -78,7 +78,7 @@ void menu_item_select_previous(void);
* Return: a boolean value that represents whether the newly selected item * Return: a boolean value that represents whether the newly selected item
* needs to be executed. * needs to be executed.
*/ */
bool menu_item_select_by_accelerator(char accelerator); bool menu_item_select_by_accelerator(uint32_t accelerator);
void menu_submenu_enter(void); void menu_submenu_enter(void);
void menu_submenu_leave(void); void menu_submenu_leave(void);

View file

@ -419,20 +419,6 @@ handle_change_vt_key(struct keyboard *keyboard,
return false; return false;
} }
static char
keysym_to_char(uint32_t keysym)
{
if (keysym >= 0x0020 && keysym <= 0x00FF) {
return (char)keysym;
}
if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) {
return (char)('0' + (keysym - XKB_KEY_KP_0));
}
return '\0';
}
static void static void
handle_menu_keys(struct keysyms *syms) handle_menu_keys(struct keysyms *syms)
{ {
@ -454,15 +440,17 @@ handle_menu_keys(struct keysyms *syms)
break; break;
case XKB_KEY_Return: case XKB_KEY_Return:
case XKB_KEY_KP_Enter: case XKB_KEY_KP_Enter:
menu_call_selected_actions(); if (!menu_call_selected_actions()) {
menu_submenu_enter();
};
break; break;
case XKB_KEY_Escape: case XKB_KEY_Escape:
menu_close_root(); menu_close_root();
cursor_update_focus(); cursor_update_focus();
break; break;
default: { default: {
char accelerator = keysym_to_char(syms->syms[i]); uint32_t accelerator = xkb_keysym_to_utf32(syms->syms[i]);
if (accelerator == '\0') { if (accelerator == 0) {
continue; continue;
} }
if (menu_item_select_by_accelerator(accelerator)) { if (menu_item_select_by_accelerator(accelerator)) {

View file

@ -8,7 +8,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <uchar.h>
#include <unistd.h> #include <unistd.h>
#include <wctype.h>
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xdg_shell.h> #include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
@ -131,7 +133,7 @@ validate(void)
} }
static struct menuitem * static struct menuitem *
item_create(struct menu *menu, const char *text, const char *icon_name, bool show_arrow) item_create(struct menu *menu, char *text, const char *icon_name, bool show_arrow)
{ {
assert(menu); assert(menu);
assert(text); assert(text);
@ -145,26 +147,34 @@ item_create(struct menu *menu, const char *text, const char *icon_name, bool sho
menuitem->arrow = show_arrow ? "" : NULL; menuitem->arrow = show_arrow ? "" : NULL;
const char *it = text; const char *it = text;
/* Skip emojis and whitespace */ uint32_t accelerator = 0;
while (*it != '\0') {
unsigned char c = (unsigned char)*it;
if (isspace(c) || c > 127) {
it++;
} else {
break;
}
}
menuitem->accelerator = tolower(*it);
while (*it != '\0') { while (*it != '\0') {
if (*it == '_') { if (*it == '_') {
menuitem->accelerator = tolower(*(it + 1)); char32_t codepoint = 0;
mbstate_t state = {0};
size_t bytes = mbrtoc32(&codepoint, it + 1, MB_CUR_MAX, &state);
if (bytes > 0 && bytes <= 4) {
accelerator = (uint32_t)towlower((wint_t)codepoint);
}
break; break;
} }
it++; it++;
} }
/* Fallback to the first character of the label */
if (accelerator == 0 && text[0] != '\0') {
char32_t codepoint = 0;
mbstate_t state = {0};
size_t bytes = mbrtoc32(&codepoint, text, MB_CUR_MAX, &state);
if (bytes > 0 && bytes <= 4) {
accelerator = (uint32_t)towlower((wint_t)codepoint);
}
}
menuitem->accelerator = accelerator;
#if HAVE_LIBSFDO #if HAVE_LIBSFDO
if (rc.menu_show_icons && !string_null_or_empty(icon_name)) { if (rc.menu_show_icons && !string_null_or_empty(icon_name)) {
menuitem->icon_name = xstrdup(icon_name); menuitem->icon_name = xstrdup(icon_name);
@ -1483,7 +1493,7 @@ menu_item_select_previous(void)
} }
bool bool
menu_item_select_by_accelerator(char accelerator) menu_item_select_by_accelerator(uint32_t accelerator)
{ {
struct menu *menu = get_selection_leaf(); struct menu *menu = get_selection_leaf();
if (!menu || wl_list_empty(&menu->menuitems)) { if (!menu || wl_list_empty(&menu->menuitems)) {