diff --git a/include/notcurses.h b/include/notcurses.h index 0fa9ee001..3983ea7c9 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -2138,6 +2138,9 @@ API struct ncselector* ncselector_aligned(struct ncplane* n, int y, ncalign_e al API int ncselector_additem(struct ncselector* n, const struct selector_item* item); 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); + // 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). diff --git a/src/lib/selector.c b/src/lib/selector.c index f5d850c12..ee4b6fc8a 100644 --- a/src/lib/selector.c +++ b/src/lib/selector.c @@ -185,12 +185,19 @@ int ncselector_delitem(ncselector* n, const char* item){ return -1; // wasn't found } +char* ncselector_selected(const ncselector* n){ + if(n->itemcount == 0){ + return NULL; + } + return strdup(n->items[n->selected].option); +} + void ncselector_previtem(ncselector* n, char** newitem){ if(n->selected == 0){ n->selected = n->itemcount; } --n->selected; - if(newitem){ + if(newitem && n->itemcount){ *newitem = strdup(n->items[n->selected].option); } ncselector_draw(n); @@ -201,7 +208,7 @@ void ncselector_nextitem(ncselector* n, char** newitem){ if(n->selected == n->itemcount){ n->selected = 0; } - if(newitem){ + if(newitem && n->itemcount){ *newitem = strdup(n->items[n->selected].option); } ncselector_draw(n); diff --git a/tests/selector.cpp b/tests/selector.cpp index ed3c69055..b06833b1c 100644 --- a/tests/selector.cpp +++ b/tests/selector.cpp @@ -22,6 +22,7 @@ TEST_CASE("SelectorTest") { struct ncselector* ncs = ncselector_create(notcurses_stdplane(nc_), 0, 0, &opts); REQUIRE(nullptr != ncs); CHECK(0 == notcurses_render(nc_)); + CHECK(nullptr == ncselector_selected(ncs)); ncselector_destroy(ncs, nullptr); } @@ -67,6 +68,59 @@ TEST_CASE("SelectorTest") { ncselector_destroy(ncs, nullptr); } + SUBCASE("EmptySelectorMovement") { + struct selector_options opts{}; + 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); + ncselector_nextitem(ncs, &sel); + REQUIRE(nullptr == sel); + CHECK(0 == notcurses_render(nc_)); + ncselector_previtem(ncs, &sel); + REQUIRE(nullptr == sel); + CHECK(0 == notcurses_render(nc_)); + ncselector_destroy(ncs, nullptr); + } + + SUBCASE("SelectorMovement") { + 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.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); + CHECK(0 == strcmp(sel, items[0].option)); + free(sel); + ncselector_nextitem(ncs, &sel); + REQUIRE(nullptr != sel); + CHECK(0 == strcmp(sel, items[1].option)); + free(sel); + ncselector_previtem(ncs, &sel); + REQUIRE(nullptr != sel); + CHECK(0 == strcmp(sel, items[0].option)); + free(sel); + // wrap around from the top to bottom... + ncselector_previtem(ncs, &sel); + REQUIRE(nullptr != sel); + CHECK(0 == strcmp(sel, items[2].option)); + free(sel); + // ...and back to the top + ncselector_nextitem(ncs, &sel); + REQUIRE(nullptr != sel); + CHECK(0 == strcmp(sel, items[0].option)); + free(sel); + ncselector_destroy(ncs, nullptr); + } + CHECK(0 == notcurses_stop(nc_)); CHECK(0 == fclose(outfp_)); }