From 74b9690cf39a9253383339498a051f9873932d5a Mon Sep 17 00:00:00 2001 From: nick black Date: Mon, 10 Feb 2020 15:18:28 -0500 Subject: [PATCH] ncmenu: remove dumb restrictions/special casing --- README.md | 23 +++++++--------- doc/man/man3/notcurses_menu.3.md | 20 +++++++------- doc/man/man3/notcurses_ncplane.3.md | 6 ++--- doc/man/man3/notcurses_reel.3.md | 4 --- doc/man/man3/notcurses_stdplane.3.md | 2 -- include/notcurses.h | 9 +++---- python/src/notcurses/build_notcurses.py | 3 --- src/demo/hud.c | 7 +++-- src/lib/internal.h | 17 ------------ src/lib/menu.c | 31 +++++----------------- src/lib/notcurses.c | 8 ------ src/poc/menu.c | 4 +-- tests/menu.cpp | 35 +++++-------------------- 13 files changed, 47 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index 0f3fa6ee3..58cc8e429 100644 --- a/README.md +++ b/README.md @@ -269,7 +269,6 @@ handle on the standard plane can be acquired with two top-level functions: // terminal size) for this terminal. The standard plane always exists, and its // origin is always at the uppermost, leftmost cell of the screen. struct ncplane* notcurses_stdplane(struct notcurses* nc); -const struct ncplane* notcurses_stdplane_const(const struct notcurses* nc); ``` A reference to the standard plane *is* persistent across a screen resize, as are @@ -293,9 +292,8 @@ int notcurses_resize(struct notcurses* n, int* RESTRICT y, int* RESTRICT x); // Return our current idea of the terminal dimensions in rows and cols. static inline void -notcurses_term_dim_yx(const struct notcurses* n, int* RESTRICT rows, - int* RESTRICT cols){ - ncplane_dim_yx(notcurses_stdplane_const(n), rows, cols); +notcurses_term_dim_yx(struct notcurses* n, int* RESTRICT rows, int* RESTRICT cols){ + ncplane_dim_yx(notcurses_stdplane(n), rows, cols); } // Refresh the physical screen to match what was last rendered (i.e., without @@ -776,7 +774,6 @@ int ncplane_at_yx(struct ncplane* n, int y, int x, cell* c); // it with 'opaque'. the others simply return the userptr. void* ncplane_set_userptr(struct ncplane* n, void* opaque); void* ncplane_userptr(struct ncplane* n); -const void* ncplane_userptr_const(const struct ncplane* n); ``` All output is to `ncplane`s. There is no cost in moving the cursor around the @@ -1955,11 +1952,9 @@ struct nctablet* ncreel_prev(struct ncreel* pr); int ncreel_destroy(struct ncreel* pr); void* nctablet_userptr(struct nctablet* t); -const void* nctablet_userptr_const(const struct nctablet* t); // Access the ncplane associated with this tablet, if one exists. struct ncplane* nctablet_ncplane(struct nctablet* t); -const struct ncplane* nctablet_ncplane_const(const struct nctablet* t); ``` #### ncreel examples @@ -2194,11 +2189,10 @@ void ncselector_destroy(struct ncselector* n, char** item); ### Menus -Horizontal menu bars are supported, on the top 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, but any -unrolled section will be visible. Menus may be either visible or invisible by -default; set the `hiding` option to get an invisible menu. In the event of a +Horizontal menu bars are supported, on the top or bottom rows of the screen. If +the menu bar is longer than the screen, it will be only partially visible, but +any unrolled section will be 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 @@ -2235,8 +2229,11 @@ int ncmenu_rollup(struct ncmenu* n); // Return the selected item description, or NULL if no section is unrolled. const char* ncmenu_selected(const struct ncmenu* n); +// Return the ncplane backing this ncmenu. +struct ncplane* ncmenu_plane(struct ncmenu* n); + // Destroy a menu created with ncmenu_create(). -int ncmenu_destroy(struct notcurses* nc, struct ncmenu* n); +int ncmenu_destroy(struct ncmenu* n); ``` ### Channels diff --git a/doc/man/man3/notcurses_menu.3.md b/doc/man/man3/notcurses_menu.3.md index f09435e29..24aea887c 100644 --- a/doc/man/man3/notcurses_menu.3.md +++ b/doc/man/man3/notcurses_menu.3.md @@ -40,19 +40,19 @@ typedef struct ncmenu_options { **const char* ncmenu_selected(const struct ncmenu* n);** -**int ncmenu_destroy(struct notcurses* nc, struct ncmenu* n);** +**struct ncplane* ncmenu_plane(struct ncmenu* n);** -# DESCRIPTION +**int ncmenu_destroy(struct ncmenu* n);** -A notcurses instance supports a single menu bar, on the top or bottom row of -the true screen. It will be kept logically above other ncplanes on the z-axis. -Attempting to create a menu when one already exists is an error. +# DESCRIPTION -A menu is composed of sections, which are in turn composed of items. Either no -sections are visible, and the menu is *rolled up*, or exactly one section is -*unrolled*. **ncmenu_rollup** places an ncmenu in the rolled up state. -**ncmenu_unroll** rolls up any unrolled section, and unrolls the specified one. -**ncmenu_destroy** removes a menu bar, and frees all associated resources. +A notcurses instance supports menu bars on the top or bottom row of the true +screen. A menu is composed of sections, which are in turn composed of items. +Either no sections are visible, and the menu is *rolled up*, or exactly one +section is *unrolled*. **ncmenu_rollup** places an ncmenu in the rolled up +state. **ncmenu_unroll** rolls up any unrolled section, and unrolls the +specified one. **ncmenu_destroy** removes a menu bar, and frees all associated +resources. **ncmenu_selected** return the selected item description, or NULL if no section is unrolled. diff --git a/doc/man/man3/notcurses_ncplane.3.md b/doc/man/man3/notcurses_ncplane.3.md index 03aee5fbd..b9bfe90c4 100644 --- a/doc/man/man3/notcurses_ncplane.3.md +++ b/doc/man/man3/notcurses_ncplane.3.md @@ -48,8 +48,6 @@ notcurses_ncplane - operations on notcurses planes **void* ncplane_userptr(struct ncplane* n);** -**const void* ncplane_userptr_const(const struct ncplane* n);** - **void ncplane_dim_yx(struct ncplane* n, int* restrict rows, int* restrict cols);** **static inline int ncplane_dim_y(struct ncplane* n);** @@ -139,8 +137,8 @@ anywhere. In addition to its framebuffer--a rectilinear matrix of cells **ncplane_new(3)**, **ncplane_aligned(3)**, and **ncplane_dup(3)** all return a new **struct ncplane** on success, or **NULL** on failure. -**ncplane_userptr(3)** and **ncplane_userptr_const(3)** both return the configured user -pointer for the ncplane. They cannot fail. +**ncplane_userptr(3)** returns the configured user pointer for the ncplane, and +cannot fail. **ncplane_below(3)** returns the plane below the specified ncplane. If the provided plane is the bottommost plane, NULL is returned. It cannot fail. diff --git a/doc/man/man3/notcurses_reel.3.md b/doc/man/man3/notcurses_reel.3.md index 8ab0a5e67..dfce6d4dc 100644 --- a/doc/man/man3/notcurses_reel.3.md +++ b/doc/man/man3/notcurses_reel.3.md @@ -87,12 +87,8 @@ typedef struct ncreel_options { **void* nctablet_userptr(struct nctablet* t);** -**const void* nctablet_userptr_const(const struct nctablet* t);** - **struct ncplane* nctablet_ncplane(struct nctablet* t);** -**const struct ncplane* nctablet_ncplane_const(const struct nctablet* t);** - # DESCRIPTION # RETURN VALUES diff --git a/doc/man/man3/notcurses_stdplane.3.md b/doc/man/man3/notcurses_stdplane.3.md index e64a7c821..9a55b1863 100644 --- a/doc/man/man3/notcurses_stdplane.3.md +++ b/doc/man/man3/notcurses_stdplane.3.md @@ -12,8 +12,6 @@ notcurses_stdplane - acquire the standard ncplane **struct ncplane* notcurses_stdplane(struct notcurses* nc);** -**const struct ncplane* notcurses_const_stdplane(const struct notcurses* nc);** - # DESCRIPTION **notcurses_stdplane** returns a handle to the standard ncplane for the context diff --git a/include/notcurses.h b/include/notcurses.h index efd04b517..6547282f2 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -414,7 +414,6 @@ API int notcurses_refresh(struct notcurses* n); // terminal size) for this terminal. The standard plane always exists, and its // origin is always at the uppermost, leftmost cell of the screen. API struct ncplane* notcurses_stdplane(struct notcurses* nc); -API const struct ncplane* notcurses_stdplane_const(const struct notcurses* nc); // Retrieve the contents of the specified cell as last rendered. The EGC is // returned, or NULL on error. This EGC must be free()d by the caller. The cell @@ -620,7 +619,6 @@ API int ncplane_at_yx(struct ncplane* n, int y, int x, cell* c); // it with 'opaque'. the others simply return the userptr. API void* ncplane_set_userptr(struct ncplane* n, void* opaque); API void* ncplane_userptr(struct ncplane* n); -API const void* ncplane_userptr_const(const struct ncplane* n); // Return the column at which 'c' cols ought start in order to be aligned // according to 'align' within ncplane 'n'. Returns INT_MAX on invalid 'align'. @@ -2042,11 +2040,9 @@ API struct nctablet* ncreel_prev(struct ncreel* pr); API int ncreel_destroy(struct ncreel* pr); API void* nctablet_userptr(struct nctablet* t); -API const void* nctablet_userptr_const(const struct nctablet* t); // Access the ncplane associated with this nctablet, if one exists. API struct ncplane* nctablet_ncplane(struct nctablet* t); -API const struct ncplane* nctablet_ncplane_const(const struct nctablet* t); #define PREFIXSTRLEN 7 // Does not include a '\0' (xxx.xxU) #define IPREFIXSTRLEN 8 // Does not include a '\0' (xxxx.xxU) @@ -2274,8 +2270,11 @@ API int ncmenu_previtem(struct ncmenu* n); // Return the selected item description, or NULL if no section is unrolled. API const char* ncmenu_selected(const struct ncmenu* n); +// Return the ncplane backing this ncmenu. +API struct ncplane* ncmenu_plane(struct ncmenu* n); + // Destroy a menu created with ncmenu_create(). -API int ncmenu_destroy(struct notcurses* nc, struct ncmenu* n); +API int ncmenu_destroy(struct ncmenu* n); #undef API diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index f65490d8c..57610c037 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -123,7 +123,6 @@ void* ncplane_set_userptr(struct ncplane* n, void* opaque); void* ncplane_userptr(struct ncplane* n); int ncplane_resize(struct ncplane* n, int keepy, int keepx, int keepleny, int keeplenx, int yoff, int xoff, int ylen, int xlen); -const void* ncplane_userptr_const(const struct ncplane* n); uint64_t ncplane_channels(struct ncplane* n); uint32_t ncplane_attr(struct ncplane* n); unsigned ncplane_bchannel(struct ncplane* nc); @@ -349,9 +348,7 @@ struct nctablet* ncreel_next(struct ncreel* pr); struct nctablet* ncreel_prev(struct ncreel* pr); int ncreel_destroy(struct ncreel* pr); void* nctablet_userptr(struct nctablet* t); -const void* nctablet_userptr_const(const struct nctablet* t); struct ncplane* nctablet_ncplane(struct nctablet* t); -const struct ncplane* nctablet_ncplane_const(const struct nctablet* t); """) if __name__ == "__main__": diff --git a/src/demo/hud.c b/src/demo/hud.c index aa8f02a4c..ef9219a93 100644 --- a/src/demo/hud.c +++ b/src/demo/hud.c @@ -129,8 +129,8 @@ struct ncmenu* menu_create(struct notcurses* nc){ }; uint64_t headerchannels = 0; uint64_t sectionchannels = 0; - channels_set_fg(&headerchannels, 0xaf64af); - channels_set_bg(&headerchannels, 0x103010); + channels_set_fg(&headerchannels, 0xffffff); + channels_set_bg(&headerchannels, 0xaf64af); const ncmenu_options mopts = { .bottom = false, .hiding = false, @@ -311,5 +311,8 @@ int demo_render(struct notcurses* nc){ return -1; } } + if(menu){ + ncplane_move_top(ncmenu_plane(menu)); + } return notcurses_render(nc); } diff --git a/src/lib/internal.h b/src/lib/internal.h index 674d55e7c..2a442a98b 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -272,9 +272,6 @@ typedef struct notcurses { unsigned inputbuf_valid_starts; unsigned inputbuf_write_at; - // we can have one menu bar at a time, on either the top or bottom - ncmenu* menu; - palette256 palette; // 256-indexed palette can be used instead of/with RGB bool palette_damage[NCPALETTESIZE]; struct esctrie* inputescapes; // trie of input escapes -> ncspecial_keys @@ -282,20 +279,6 @@ typedef struct notcurses { void sigwinch_handler(int signo); -// create a new ncplane, and remove it from the z-axis. used for menus. gross. -static inline ncplane* -ncplane_new_uncoupled(notcurses* nc, int rows, int cols, int yoff, int xoff, void* opaque){ - ncplane* n = ncplane_new(nc, rows, cols, yoff, xoff, opaque); - if(n == NULL){ - return n; - } - assert(nc->top == n); - nc->top = n->z; - assert(nc->top != n); - n->z = NULL; - return n; -} - // Search the provided multibyte (UTF8) string 's' for the provided unicode // codepoint 'cp'. If found, return the column offset of the EGC in which the // codepoint appears in 'col', and the byte offset as the return value. If not diff --git a/src/lib/menu.c b/src/lib/menu.c index f3320d4e0..30ffdfce7 100644 --- a/src/lib/menu.c +++ b/src/lib/menu.c @@ -253,20 +253,6 @@ write_header(ncmenu* ncm){ ncm->ncp->channels = ncm->headerchannels; return 0; } -// lock the notcurses object, and try to set up the new menu. return -1 if a -// menu is already associated with this instance. -static int -set_menu(notcurses* nc, ncmenu* ncm){ - int ret = -1; - pthread_mutex_lock(&nc->lock); - if(!nc->menu){ - nc->menu = ncm; - ret = 0; - } - pthread_mutex_unlock(&nc->lock); - return ret; -} - ncmenu* ncmenu_create(notcurses* nc, const ncmenu_options* opts){ if(opts->sectioncount <= 0 || !opts->sections){ return NULL; @@ -286,7 +272,7 @@ ncmenu* ncmenu_create(notcurses* nc, const ncmenu_options* opts){ totalwidth = dimx; } int ypos = opts->bottom ? dimy - totalheight : 0; - ret->ncp = ncplane_new_uncoupled(nc, totalheight, totalwidth, ypos, 0, NULL); + ret->ncp = ncplane_new(nc, totalheight, totalwidth, ypos, 0, NULL); if(ret->ncp){ ret->unrolledsection = -1; ret->headerchannels = opts->headerchannels; @@ -297,9 +283,7 @@ ncmenu* ncmenu_create(notcurses* nc, const ncmenu_options* opts){ ncplane_set_base_cell(ret->ncp, &c); cell_release(ret->ncp, &c); if(write_header(ret) == 0){ - if(set_menu(nc, ret) == 0){ - return ret; - } + return ret; } ncplane_destroy(ret->ncp); } @@ -487,17 +471,16 @@ const char* ncmenu_selected(const ncmenu* n){ return n->sections[n->unrolledsection].items[n->sections[n->unrolledsection].itemselected].desc; } -int ncmenu_destroy(notcurses* nc, ncmenu* n){ +ncplane* ncmenu_plane(ncmenu* menu){ + return menu->ncp; +} + +int ncmenu_destroy(ncmenu* n){ int ret = 0; if(n){ free_menu_sections(n); ncplane_destroy(n->ncp); free(n); - pthread_mutex_lock(&nc->lock); - if(nc->menu == n){ - nc->menu = NULL; - } - pthread_mutex_unlock(&nc->lock); } return ret; } diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index ff4954d93..fbc466098 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -315,10 +315,6 @@ ncplane* notcurses_stdplane(notcurses* nc){ return nc->stdscr; } -const ncplane* notcurses_stdplane_const(const notcurses* nc){ - return nc->stdscr; -} - ncplane* ncplane_new(notcurses* nc, int rows, int cols, int yoff, int xoff, void* opaque){ ncplane* n = ncplane_create(nc, rows, cols, yoff, xoff); if(n == NULL){ @@ -840,7 +836,6 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ free(ret); return NULL; } - ret->menu = NULL; ret->stats.fbbytes = 0; ret->stashstats.fbbytes = 0; reset_stats(&ret->stats); @@ -993,9 +988,6 @@ int notcurses_stop(notcurses* nc){ int ret = 0; if(nc){ ret |= notcurses_stop_minimal(nc); - if(nc->menu){ - ncmenu_destroy(nc, nc->menu); - } while(nc->top){ ncplane* p = nc->top; nc->top = p->z; diff --git a/src/poc/menu.c b/src/poc/menu.c index 8985589ab..f25080f58 100644 --- a/src/poc/menu.c +++ b/src/poc/menu.c @@ -61,7 +61,7 @@ run_menu(struct notcurses* nc, struct ncmenu* ncm){ break; } }else if(keypress == 'q'){ - ncmenu_destroy(nc, ncm); + ncmenu_destroy(ncm); ncplane_destroy(selplane); return 0; } @@ -70,7 +70,7 @@ run_menu(struct notcurses* nc, struct ncmenu* ncm){ ncplane_putstr_aligned(selplane, 1, NCALIGN_CENTER, selitem ? selitem : ""); notcurses_render(nc); } - ncmenu_destroy(nc, ncm); + ncmenu_destroy(ncm); err: ncplane_destroy(selplane); diff --git a/tests/menu.cpp b/tests/menu.cpp index 1ca2201cd..13ff5df57 100644 --- a/tests/menu.cpp +++ b/tests/menu.cpp @@ -26,7 +26,7 @@ TEST_CASE("MenuTest") { struct ncmenu* ncm = ncmenu_create(nc_, &opts); REQUIRE(nullptr == ncm); CHECK(0 == notcurses_render(nc_)); - ncmenu_destroy(nc_, ncm); + ncmenu_destroy(ncm); } SUBCASE("EmptyMenuBottomReject") { @@ -35,7 +35,7 @@ TEST_CASE("MenuTest") { struct ncmenu* ncm = ncmenu_create(nc_, &opts); REQUIRE(nullptr == ncm); CHECK(0 == notcurses_render(nc_)); - ncmenu_destroy(nc_, ncm); + ncmenu_destroy(ncm); } // an empty section ought be rejected @@ -49,7 +49,7 @@ TEST_CASE("MenuTest") { struct ncmenu* ncm = ncmenu_create(nc_, &opts); REQUIRE(nullptr == ncm); CHECK(0 == notcurses_render(nc_)); - ncmenu_destroy(nc_, ncm); + ncmenu_destroy(ncm); } // a section with only separators ought be rejected @@ -66,7 +66,7 @@ TEST_CASE("MenuTest") { struct ncmenu* ncm = ncmenu_create(nc_, &opts); REQUIRE(nullptr == ncm); CHECK(0 == notcurses_render(nc_)); - ncmenu_destroy(nc_, ncm); + ncmenu_destroy(ncm); } SUBCASE("MenuOneSection") { @@ -82,29 +82,7 @@ TEST_CASE("MenuTest") { struct ncmenu* ncm = ncmenu_create(nc_, &opts); REQUIRE(nullptr != ncm); CHECK(0 == notcurses_render(nc_)); - ncmenu_destroy(nc_, ncm); - } - - // only one menu at a time per notcurses object - SUBCASE("RejectDoubleMenu") { - struct ncmenu_item file_items[] = { - { .desc = strdup("I would like a new file"), .shortcut = ncinput(), }, - }; - struct ncmenu_section sections[] = { - { .name = strdup("File"), .itemcount = sizeof(file_items) / sizeof(*file_items), .items = file_items, .shortcut = ncinput(), }, - }; - struct ncmenu_options opts{}; - opts.sections = sections; - opts.sectioncount = sizeof(sections) / sizeof(*sections); - struct ncmenu* ncm = ncmenu_create(nc_, &opts); - REQUIRE(nullptr != ncm); - struct ncmenu* ncmdup = ncmenu_create(nc_, &opts); - REQUIRE(nullptr == ncmdup); - opts.bottom = true; - ncmdup = ncmenu_create(nc_, &opts); - REQUIRE(nullptr == ncmdup); - CHECK(0 == notcurses_render(nc_)); - ncmenu_destroy(nc_, ncm); + ncmenu_destroy(ncm); } // don't call ncmenu_destroy(), invoking destruction in notcurses_stop() @@ -141,6 +119,7 @@ TEST_CASE("MenuTest") { struct ncmenu* ncm = ncmenu_create(nc_, &opts); REQUIRE(nullptr != ncm); CHECK(0 == notcurses_render(nc_)); + ncmenu_destroy(ncm); } // you must have sections, not just an alignment NULL section @@ -194,7 +173,7 @@ TEST_CASE("MenuTest") { struct ncmenu* ncm = ncmenu_create(nc_, &opts); REQUIRE(nullptr != ncm); CHECK(0 == notcurses_render(nc_)); - ncmenu_destroy(nc_, ncm); + ncmenu_destroy(ncm); } CHECK(0 == notcurses_stop(nc_));