From 79a92fcfb1a6e067a7ffc55c1ccaa8e051c2c7d9 Mon Sep 17 00:00:00 2001 From: nick black Date: Mon, 3 Feb 2020 22:14:29 -0500 Subject: [PATCH] ncmenu: highlight shortcut keys for menu items --- include/notcurses.h | 1 + src/lib/menu.c | 17 +++++++++++++++++ src/poc/menu.c | 8 ++++---- tests/menu.cpp | 6 +++--- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/include/notcurses.h b/include/notcurses.h index 7721e387c..788a6f86c 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -2213,6 +2213,7 @@ API void ncselector_destroy(struct ncselector* n, char** item); struct ncmenu_item { char* desc; // utf-8 menu item, NULL for horizontal separator ncinput shortcut; // shortcut, all should be distinct + int shortcut_offset; // used only by library }; struct ncmenu_section { diff --git a/src/lib/menu.c b/src/lib/menu.c index 647338659..346267c85 100644 --- a/src/lib/menu.c +++ b/src/lib/menu.c @@ -47,6 +47,12 @@ dup_menu_section(ncmenu_int_section* dst, const struct ncmenu_section* src){ if(cols > dst->bodycols){ dst->bodycols = cols; } + memcpy(&dst->items[i].shortcut, &src->items[i].shortcut, sizeof(dst->items[i].shortcut)); + if(mbstr_find_codepoint(dst->items[i].desc, + dst->items[i].shortcut.id, + &dst->items[i].shortcut_offset) < 0){ + dst->items[i].shortcut_offset = -1; + } }else{ dst->items[i].desc = NULL; } @@ -244,6 +250,17 @@ int ncmenu_unroll(ncmenu* n, int sectionidx){ return -1; } } + if(sec->items[i].shortcut_offset >= 0){ + cell cl = CELL_TRIVIAL_INITIALIZER; + if(ncplane_at_yx(n->ncp, ypos, xpos + 1 + sec->items[i].shortcut_offset, &cl) < 0){ + return -1; + } + cell_styles_on(&cl, NCSTYLE_UNDERLINE|NCSTYLE_BOLD); + if(ncplane_putc_yx(n->ncp, ypos, xpos + 1 + sec->items[i].shortcut_offset, &cl) < 0){ + return -1; + } + cell_release(n->ncp, &cl); + } }else{ n->ncp->channels = n->headerchannels; ncplane_styles_set(n->ncp, 0); diff --git a/src/poc/menu.c b/src/poc/menu.c index 2632bac6d..303b8ac8c 100644 --- a/src/poc/menu.c +++ b/src/poc/menu.c @@ -64,14 +64,14 @@ int main(void){ return EXIT_FAILURE; } struct ncmenu_item demo_items[] = { - { .desc = "Restart", }, + { .desc = "Restart", .shortcut = { .id = 'r', .ctrl = true, }, }, }; struct ncmenu_item file_items[] = { { .desc = "New", .shortcut = { .id = 'n', .ctrl = true, }, }, - { .desc = "Open", }, - { .desc = "Close", }, + { .desc = "Open", .shortcut = { .id = 'o', .ctrl = true, }, }, + { .desc = "Close", .shortcut = { .id = 'c', .ctrl = true, }, }, { .desc = NULL, }, - { .desc = "Quit", }, + { .desc = "Quit", .shortcut = { .id = 'q', .ctrl = true, }, }, }; struct ncmenu_section sections[] = { { .name = "Schwarzgerät", .items = demo_items, .shortcut = { .id = 0x00e4, .alt = true, }, }, diff --git a/tests/menu.cpp b/tests/menu.cpp index 4c6c767ee..954ba8359 100644 --- a/tests/menu.cpp +++ b/tests/menu.cpp @@ -52,7 +52,7 @@ TEST_CASE("MenuTest") { // a section with only separators ought be rejected SUBCASE("SeparatorSectionReject") { struct ncmenu_item empty_items[] = { - { .desc = nullptr, .shortcut = {}, }, + { .desc = nullptr, .shortcut = {}, .shortcut_offset = -1, }, }; struct ncmenu_section sections[] = { { .name = strdup("Empty"), .itemcount = 1, .items = empty_items, .shortcut{}, }, @@ -68,7 +68,7 @@ TEST_CASE("MenuTest") { SUBCASE("MenuOneSection") { struct ncmenu_item file_items[] = { - { .desc = strdup("I would like a new file"), .shortcut = {}, }, + { .desc = strdup("I would like a new file"), .shortcut = {}, .shortcut_offset = -1, }, }; struct ncmenu_section sections[] = { { .name = strdup("File"), .itemcount = sizeof(file_items) / sizeof(*file_items), .items = file_items, .shortcut{}, }, @@ -84,7 +84,7 @@ TEST_CASE("MenuTest") { SUBCASE("VeryLongMenu") { struct ncmenu_item items[] = { - { .desc = strdup("Generic menu entry"), .shortcut = {}, }, + { .desc = strdup("Generic menu entry"), .shortcut = {}, .shortcut_offset = -1, }, }; struct ncmenu_section sections[] = { { .name = strdup("antidisestablishmentarianism"), .itemcount = sizeof(items) / sizeof(*items), .items = items, .shortcut{}, },