ncmenu: remove dumb restrictions/special casing

pull/344/head
nick black 4 years ago
parent d6ea7f9b6c
commit 74b9690cf3
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -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

@ -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.

@ -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.

@ -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

@ -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

@ -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

@ -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__":

@ -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);
}

@ -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

@ -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;
}

@ -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;

@ -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);

@ -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_));

Loading…
Cancel
Save