selector: implement maxdisplay. unit tests!

pull/312/head
nick black 5 years ago
parent ac2b951fa9
commit 3991b3e53a
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -2141,6 +2141,8 @@ API int ncselector_delitem(struct ncselector* n, const char* item);
// Return a copy of the currently-selected option. NULL if there are no items.
API char* ncselector_selected(const struct ncselector* n);
API struct ncplane* ncselector_plane(struct ncselector* n);
// Move up or down in the list. If 'newitem' is not NULL, the newly-selected
// option will be strdup()ed and assigned to '*newitem' (and must be free()d by
// the caller).

@ -127,6 +127,7 @@ typedef struct ncselector {
ncplane* ncp; // backing ncplane
unsigned selected; // index of selection
unsigned startdisp; // index of first option displayed
unsigned maxdisplay; // max number of items to display, 0 -> no limit
size_t longop; // length of longest option
size_t longdesc; // length of longest description
struct selector_item* items; // list of items and descriptions, heap-copied

@ -48,7 +48,11 @@ ncselector_draw(ncselector* n){
ncplane_rounded_box_sized(n->ncp, 0, channels, dimy - yoff, bodywidth, 0);
unsigned printidx = n->startdisp;
int bodyoffset = dimx - bodywidth + 2;
unsigned printed = 0;
for(yoff += 2 ; yoff < dimy - 2 ; ++yoff){
if(n->maxdisplay && printed == n->maxdisplay){
break;
}
if(printidx == n->selected){
ncplane_styles_on(n->ncp, CELL_STYLE_REVERSE);
}
@ -59,6 +63,7 @@ ncselector_draw(ncselector* n){
ncplane_styles_off(n->ncp, CELL_STYLE_REVERSE);
}
++printidx;
++printed;
}
return notcurses_render(n->ncp->nc);
}
@ -79,7 +84,7 @@ ncselector_dim_yx(notcurses* nc, const ncselector* n, int* ncdimy, int* ncdimx){
if(rows > dimy){ // insufficient height to display selector
return -1;
}
rows += n->itemcount - 1; // rows necessary to display all options
rows += (!n->maxdisplay || n->maxdisplay > n->itemcount ? n->itemcount : n->maxdisplay) - 1; // rows necessary to display all options
if(rows > dimy){ // claw excess back
rows = dimy;
}
@ -104,6 +109,7 @@ ncselector* ncselector_create(ncplane* n, int y, int x, const selector_options*
ns->selected = 0;
ns->startdisp = 0;
ns->longop = 0;
ns->maxdisplay = opts->maxdisplay;
ns->longdesc = 0;
if(opts->itemcount){
if(!(ns->items = malloc(sizeof(*ns->items) * opts->itemcount))){
@ -185,6 +191,10 @@ int ncselector_delitem(ncselector* n, const char* item){
return -1; // wasn't found
}
ncplane* ncselector_plane(ncselector* n){
return n->ncp;
}
char* ncselector_selected(const ncselector* n){
if(n->itemcount == 0){
return NULL;

@ -23,6 +23,12 @@ TEST_CASE("SelectorTest") {
REQUIRE(nullptr != ncs);
CHECK(0 == notcurses_render(nc_));
CHECK(nullptr == ncselector_selected(ncs));
struct ncplane* ncsp = ncselector_plane(ncs);
REQUIRE(nullptr != ncsp);
int dimy, dimx;
ncplane_dim_yx(ncsp, &dimy, &dimx);
CHECK(4 == dimy);
CHECK(5 == dimx);
ncselector_destroy(ncs, nullptr);
}
@ -32,6 +38,12 @@ TEST_CASE("SelectorTest") {
struct ncselector* ncs = ncselector_create(notcurses_stdplane(nc_), 0, 0, &opts);
REQUIRE(nullptr != ncs);
CHECK(0 == notcurses_render(nc_));
struct ncplane* ncsp = ncselector_plane(ncs);
REQUIRE(nullptr != ncsp);
int dimy, dimx;
ncplane_dim_yx(ncsp, &dimy, &dimx);
CHECK(6 == dimy);
CHECK(strlen(opts.title) + 4 == dimx);
ncselector_destroy(ncs, nullptr);
}
@ -41,15 +53,27 @@ TEST_CASE("SelectorTest") {
struct ncselector* ncs = ncselector_create(notcurses_stdplane(nc_), 0, 0, &opts);
REQUIRE(nullptr != ncs);
CHECK(0 == notcurses_render(nc_));
struct ncplane* ncsp = ncselector_plane(ncs);
REQUIRE(nullptr != ncsp);
int dimy, dimx;
ncplane_dim_yx(ncsp, &dimy, &dimx);
CHECK(4 == dimy);
CHECK(strlen(opts.secondary) + 2 == dimx);
ncselector_destroy(ncs, nullptr);
}
SUBCASE("FooterSelector") {
struct selector_options opts{};
opts.secondary = strdup("i am a lone footer, little old footer");
opts.footer = strdup("i am a lone footer, little old footer");
struct ncselector* ncs = ncselector_create(notcurses_stdplane(nc_), 0, 0, &opts);
REQUIRE(nullptr != ncs);
CHECK(0 == notcurses_render(nc_));
struct ncplane* ncsp = ncselector_plane(ncs);
REQUIRE(nullptr != ncsp);
int dimy, dimx;
ncplane_dim_yx(ncsp, &dimy, &dimx);
CHECK(4 == dimy);
CHECK(strlen(opts.footer) + 2 == dimx);
ncselector_destroy(ncs, nullptr);
}
@ -65,6 +89,12 @@ TEST_CASE("SelectorTest") {
struct ncselector* ncs = ncselector_create(notcurses_stdplane(nc_), 0, 0, &opts);
REQUIRE(nullptr != ncs);
CHECK(0 == notcurses_render(nc_));
struct ncplane* ncsp = ncselector_plane(ncs);
REQUIRE(nullptr != ncsp);
int dimy, dimx;
ncplane_dim_yx(ncsp, &dimy, &dimx);
CHECK(7 == dimy);
CHECK(15 < dimx);
ncselector_destroy(ncs, nullptr);
}
@ -95,29 +125,79 @@ TEST_CASE("SelectorTest") {
opts.itemcount = sizeof(items) / sizeof(*items);
struct ncselector* ncs = ncselector_create(notcurses_stdplane(nc_), 0, 0, &opts);
REQUIRE(nullptr != ncs);
CHECK(0 == notcurses_render(nc_));
auto sel = ncselector_selected(ncs);
REQUIRE(nullptr != sel);
CHECK(0 == strcmp(sel, items[0].option));
free(sel);
CHECK(0 == notcurses_render(nc_));
ncselector_nextitem(ncs, &sel);
REQUIRE(nullptr != sel);
CHECK(0 == strcmp(sel, items[1].option));
free(sel);
CHECK(0 == notcurses_render(nc_));
ncselector_previtem(ncs, &sel);
REQUIRE(nullptr != sel);
CHECK(0 == strcmp(sel, items[0].option));
free(sel);
CHECK(0 == notcurses_render(nc_));
// wrap around from the top to bottom...
ncselector_previtem(ncs, &sel);
REQUIRE(nullptr != sel);
CHECK(0 == strcmp(sel, items[2].option));
free(sel);
CHECK(0 == notcurses_render(nc_));
// ...and back to the top
ncselector_nextitem(ncs, &sel);
REQUIRE(nullptr != sel);
CHECK(0 == strcmp(sel, items[0].option));
free(sel);
CHECK(0 == notcurses_render(nc_));
ncselector_destroy(ncs, nullptr);
}
// Provide three items, limited to 1 shown at a time
SUBCASE("ScrollingSelectorOne") {
selector_item items[] = {
{ strdup("op1"), strdup("this is option 1"), },
{ strdup("2ndop"), strdup("this is option #2"), },
{ strdup("tres"), strdup("option the third"), },
};
struct selector_options opts{};
opts.maxdisplay = 1;
opts.items = items;
opts.itemcount = sizeof(items) / sizeof(*items);
struct ncselector* ncs = ncselector_create(notcurses_stdplane(nc_), 0, 0, &opts);
REQUIRE(nullptr != ncs);
CHECK(0 == notcurses_render(nc_));
auto sel = ncselector_selected(ncs);
REQUIRE(nullptr != sel);
struct ncplane* ncsp = ncselector_plane(ncs);
REQUIRE(nullptr != ncsp);
int dimy, dimx;
ncplane_dim_yx(ncsp, &dimy, &dimx);
CHECK(5 == dimy);
ncselector_destroy(ncs, nullptr);
}
// Provide three items, limited to 2 shown at a time
SUBCASE("ScrollingSelectorTwo") {
selector_item items[] = {
{ strdup("op1"), strdup("this is option 1"), },
{ strdup("2ndop"), strdup("this is option #2"), },
{ strdup("tres"), strdup("option the third"), },
};
struct selector_options opts{};
opts.maxdisplay = 2;
opts.items = items;
opts.itemcount = sizeof(items) / sizeof(*items);
struct ncselector* ncs = ncselector_create(notcurses_stdplane(nc_), 0, 0, &opts);
REQUIRE(nullptr != ncs);
CHECK(0 == notcurses_render(nc_));
struct ncplane* ncsp = ncselector_plane(ncs);
REQUIRE(nullptr != ncsp);
int dimy, dimx;
ncplane_dim_yx(ncsp, &dimy, &dimx);
CHECK(6 == dimy);
ncselector_destroy(ncs, nullptr);
}

Loading…
Cancel
Save