diff --git a/README.md b/README.md
index 902d89c1f..8e7313f62 100644
--- a/README.md
+++ b/README.md
@@ -2184,6 +2184,50 @@ void ncselector_nextitem(struct ncselector* n, char** newitem);
void ncselector_destroy(struct ncselector* n, char** item);
```
+### Menus
+
+Horizontal menu bars are supported, on the top and/or bottom rows of the
+screen (menus are bound to a `notcurses` object, not particular `ncplane`s).
+If the menu bar is longer than the screen, it will be only partially visible.
+Menus may be either visible or invisible by default; set the `hiding` option
+to get an invisible menu. In the event of a screen resize, menus will be
+automatically moved/resized.
+
+```c
+typedef struct menu_options {
+ bool bottom; // on the bottom row, as opposed to top row
+ bool hiding; // hide the menu when not being used
+ struct {
+ char* name; // utf-8 c string
+ struct {
+ char* desc; // utf-8 menu item, NULL for horizontal separator
+ ncinput shortcut; // shortcut, all should be distinct
+ }* items;
+ int itemcount;
+ }* sections; // array of menu sections
+ int headercount; // must be positive
+ uint64_t headerchannels; // styling for header
+ uint64_t sectionchannels; // styling for sections
+} menu_options;
+
+struct ncmenu;
+
+// Create a menu with the specified options. Menus are currently bound to an
+// overall notcurses object (as opposed to a particular plane), and are
+// implemented as ncplanes kept atop other ncplanes.
+struct ncmenu* ncmenu_create(struct notcurses* nc, const menu_options* opts);
+
+// Unroll the specified menu section, making the menu visible if it was
+// invisible, and rolling up any menu section that is already unrolled.
+int ncmenu_unroll(struct ncmenu* n, int sectionidx);
+
+// Roll up any unrolled menu section, and hide the menu if using hiding.
+int ncmenu_rollup(struct ncmenu* n);
+
+// Destroy a menu created with ncmenu_create().
+int ncmenu_destroy(struct ncmenu* n);
+```
+
### Channels
A channel encodes 24 bits of RGB color, using 8 bits for each component. It
diff --git a/doc/man/index.html b/doc/man/index.html
index bfb0f99a0..6228b333c 100644
--- a/doc/man/index.html
+++ b/doc/man/index.html
@@ -29,6 +29,7 @@
notcurses_init—initialization
notcurses_input—collecting input
notcurses_lines—drawing lines and boxes on ncplanes
+ notcurses_menu—menus on the top or bottom rows
notcurses_ncplane—operations on ncplane objects
notcurses_ncvisual—operations on ncvisual objects
notcurses_output—drawing text on ncplanes
diff --git a/doc/man/man3/notcurses_cell.3.md b/doc/man/man3/notcurses_cell.3.md
index 549b4be71..a389c7912 100644
--- a/doc/man/man3/notcurses_cell.3.md
+++ b/doc/man/man3/notcurses_cell.3.md
@@ -11,6 +11,7 @@ notcurses_cell - operations on notcurses cells
**#include **
```c
+// See DESCRIPTION below for information on EGC encoding
typedef struct cell {
uint32_t gcluster;
uint32_t attrword;
diff --git a/doc/man/man3/notcurses_menu.3.md b/doc/man/man3/notcurses_menu.3.md
index 7b4496201..a916dd172 100644
--- a/doc/man/man3/notcurses_menu.3.md
+++ b/doc/man/man3/notcurses_menu.3.md
@@ -10,6 +10,33 @@ notcurses_menu - operations on menus
**#include **
+```
+struct ncmenu;
+
+typedef struct menu_options {
+ bool bottom; // on the bottom row, as opposed to top row
+ bool hiding; // hide the menu when not being used
+ struct {
+ char* name; // utf-8 c string
+ struct {
+ char* desc; // utf-8 menu item, NULL for separator
+ ncinput shortcut; // shortcut, all should be distinct
+ }* items;
+ int itemcount;
+ }* sections; // array of menu sections
+ int headercount; // must be positive
+ uint64_t headerchannels; // styling for header
+ uint64_t sectionchannels; // styling for sections
+} menu_options;
+```
+
+**struct ncmenu* ncmenu_create(struct notcurses* nc, const menu_options* opts);**
+
+**int ncmenu_unroll(struct ncmenu* n, int sectionidx);**
+
+**int ncmenu_rollup(struct ncmenu* n);**
+
+**int ncmenu_destroy(struct ncmenu* n);**
# DESCRIPTION
diff --git a/include/notcurses.h b/include/notcurses.h
index 90410ea24..21c668fd8 100644
--- a/include/notcurses.h
+++ b/include/notcurses.h
@@ -2210,15 +2210,35 @@ API void ncselector_destroy(struct ncselector* n, char** item);
typedef struct menu_options {
bool bottom; // on the bottom row, as opposed to top row
+ bool hiding; // hide the menu when not being used
struct {
char* name; // utf-8 c string
- char** items; // argv-style list of UTF8 c strings
+ struct {
+ char* desc; // utf-8 menu item, NULL for horizontal separator
+ ncinput shortcut; // shortcut, all should be distinct
+ }* items;
+ int itemcount;
}* sections; // array of menu sections
int headercount; // must be positive
uint64_t headerchannels; // styling for header
uint64_t sectionchannels; // styling for sections
} menu_options;
+// Create a menu with the specified options. Menus are currently bound to an
+// overall notcurses object (as opposed to a particular plane), and are
+// implemented as ncplanes kept atop other ncplanes.
+API struct ncmenu* ncmenu_create(struct notcurses* nc, const menu_options* opts);
+
+// Unroll the specified menu section, making the menu visible if it was
+// invisible, and rolling up any menu section that is already unrolled.
+API int ncmenu_unroll(struct ncmenu* n, int sectionidx);
+
+// Roll up any unrolled menu section, and hide the menu if using hiding.
+API int ncmenu_rollup(struct ncmenu* n);
+
+// Destroy a menu created with ncmenu_create().
+API int ncmenu_destroy(struct ncmenu* n);
+
#undef API
#ifdef __cplusplus
diff --git a/src/demo/demo.c b/src/demo/demo.c
index 5e79e8da5..f33f116f6 100644
--- a/src/demo/demo.c
+++ b/src/demo/demo.c
@@ -263,6 +263,18 @@ table_segment(struct ncdirect* nc, const char* str, const char* delim){
return 0;
}
+static int
+table_printf(struct ncdirect* nc, const char* delim, const char* fmt, ...){
+ ncdirect_fg_rgb8(nc, 0xD4, 0xAF, 0x37);
+ va_list va;
+ va_start(va, fmt);
+ vfprintf(stdout, fmt, va);
+ va_end(va);
+ ncdirect_fg_rgb8(nc, 178, 102, 255);
+ fputs(delim, stdout);
+ return 0;
+}
+
static int
summary_table(struct ncdirect* nc, const char* spec){
bool failed = false;
@@ -313,10 +325,13 @@ summary_table(struct ncdirect* nc, const char* spec){
bprefix(totalbytes, 1, totalbuf, 0);
qprefix(totalrenderns, GIG, rtimebuf, 0);
table_segment(nc, "", "══╧═╧════════╪══════╪═════════╪═════════╪═══╪═══════╪═══════╝\n");
- printf(" %*ss│%6lu│%*s│ %*ss│%3ld│%7.1f│\n", PREFIXSTRLEN, timebuf,
- totalframes, BPREFIXSTRLEN, totalbuf, PREFIXSTRLEN, rtimebuf,
- nsdelta ? totalrenderns * 100 / nsdelta : 0,
- nsdelta ? totalframes / ((double)nsdelta / GIG) : 0);
+ table_printf(nc, "│", " %*ss", PREFIXSTRLEN, timebuf);
+ table_printf(nc, "│", "%6lu", totalframes);
+ table_printf(nc, "│", "%*s", BPREFIXSTRLEN, totalbuf);
+ table_printf(nc, "│", " %*ss", PREFIXSTRLEN, rtimebuf);
+ table_printf(nc, "│", "%3ld", nsdelta ? totalrenderns * 100 / nsdelta : 0);
+ table_printf(nc, "│", "%7.1f", nsdelta ? totalframes / ((double)nsdelta / GIG) : 0);
+ printf("\n");
ncdirect_fg_rgb8(nc, 0xff, 0xb0, 0xb0);
fflush(stdout); // in case we print to stderr below, we want color from above
if(failed){
diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c
index c3476c960..a21652cfa 100644
--- a/src/lib/notcurses.c
+++ b/src/lib/notcurses.c
@@ -1013,7 +1013,7 @@ int notcurses_stop(notcurses* nc){
NANOSECS_IN_SEC * (double)nc->stashstats.renders / nc->stashstats.render_ns : 0.0,
nc->stashstats.failed_renders,
nc->stashstats.failed_renders == 1 ? "" : "s");
- fprintf(stderr, "Emits/elides: def %lu/%lu fg %lu/%lu bg %lu/%lu\n",
+ fprintf(stderr, "RGB emits/elides: def %lu/%lu fg %lu/%lu bg %lu/%lu\n",
nc->stashstats.defaultemissions,
nc->stashstats.defaultelisions,
nc->stashstats.fgemissions,
@@ -1027,7 +1027,7 @@ int notcurses_stop(notcurses* nc){
(nc->stashstats.fgelisions * 100.0) / (nc->stashstats.fgemissions + nc->stashstats.fgelisions),
(nc->stashstats.bgemissions + nc->stashstats.bgelisions) == 0 ? 0 :
(nc->stashstats.bgelisions * 100.0) / (nc->stashstats.bgemissions + nc->stashstats.bgelisions));
- fprintf(stderr, "Cells emitted: %ju elided: %ju (%.2f%%)\n",
+ fprintf(stderr, "Cell emits/elides: %ju/%ju (%.2f%%)\n",
nc->stashstats.cellemissions, nc->stashstats.cellelisions,
(nc->stashstats.cellemissions + nc->stashstats.cellelisions) == 0 ? 0 :
(nc->stashstats.cellelisions * 100.0) / (nc->stashstats.cellemissions + nc->stashstats.cellelisions));
diff --git a/src/poc/wcwidth.c b/src/poc/wcwidth.c
index e963166f2..498282965 100644
--- a/src/poc/wcwidth.c
+++ b/src/poc/wcwidth.c
@@ -10,6 +10,9 @@ int main(void){
for(int i = 0 ; i < 128 ; ++i){
wchar_t w = i;
printf("width('%02x'): %d\t", i, wcwidth(w));
+ if(i % 4 == 3){
+ printf("\n");
+ }
}
printf("\n");
return EXIT_SUCCESS;