From 306948507f913642d6f631f4dfb120b624872929 Mon Sep 17 00:00:00 2001 From: nick black Date: Sun, 13 Sep 2020 07:51:49 -0400 Subject: [PATCH] ncmultiselect: normalize per new widget API #627 #1006 --- NEWS.md | 13 ++++++ USAGE.md | 44 ++++++++++---------- doc/man/man3/notcurses_multiselector.3.md | 34 +++++++++------ include/ncpp/MultiSelector.hh | 20 ++++----- include/notcurses/notcurses.h | 47 +++++++++++---------- python/src/notcurses/build_notcurses.py | 2 +- src/demo/zoo.c | 6 ++- src/lib/selector.c | 19 ++++----- src/poc/multiselect.c | 50 +++++++++++++++-------- 9 files changed, 139 insertions(+), 96 deletions(-) diff --git a/NEWS.md b/NEWS.md index cc2166993..040a25338 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,19 @@ This document attempts to list user-visible changes and any major internal rearrangements of Notcurses. +* 1.7.3 (not yet released) + * The long-promised/dreaded Great Widget Review, normalizing behavior across + all widgets, has been effected. Sorry, there was no getting around this + one. Pretty much all widgets have slightly changed, because pretty much all + widgets previously behaved slightly differently: + * `ncselector` no longer accepts `y, x` placement parameters. + * `ncselector` now takes ownership of the provided `ncplane`. It is + destroyed by `ncselector_destroy`/`ncselector_create`. + * `ncmultiselector` no longer accepts `y, x` placement parameters. + * `ncmultiselector` now takes ownership of the provided `ncplane`. It is + destroyed by `ncmultiselector_destroy`/`ncmultiselector_create`. + * ... + * 1.7.2 (2020-09-09) * Exported `ncvisual_default_blitter()`, so that the effective value of `NCBLIT_DEFAULT` can be determined. diff --git a/USAGE.md b/USAGE.md index 64c0d6558..79d96a04f 100644 --- a/USAGE.md +++ b/USAGE.md @@ -2221,36 +2221,36 @@ Selectors: ╭──────────────────────────╮ │This is the primary header│ ╭──────────────────────this is the secondary header──────╮ -│ ↑ │ -│ option1 Long text #1 │ -│ option2 Long text #2 │ -│ option3 Long text #3 │ -│ option4 Long text #4 │ -│ option5 Long text #5 │ -│ option6 Long text #6 │ -│ ↓ │ +│ ↑ │ +│ option1 Long text #1 │ +│ option2 Long text #2 │ +│ option3 Long text #3 │ +│ option4 Long text #4 │ +│ option5 Long text #5 │ +│ option6 Long text #6 │ +│ ↓ │ ╰────────────────────────────────────here's the footer───╯ ``` Multiselectors: ``` - ╭────────────────────────────────────────────────────────────────╮ - │ this is truly an awfully long example of a MULTISELECTOR title │ -╭─────┴─────────────────────────────pick one or more options───────────┤ + ╭───────────────────╮ + │ short round title │ +╭now this secondary is also very, very, very outlandishly long, you see┤ │ ↑ │ -│ ☐ 1 Across the Atlantic Ocean, there was a place called North America│ -│ ☐ 2 Discovered by an Italian in the employ of the queen of Spain │ -│ ☒ 3 Colonized extensively by the Spanish and the French │ -│ ☐ 4 Developed into a rich nation by Dutch-supplied African slaves │ -│ ☐ 5 And thus became the largest English-speaking nation on earth │ -│ ☐ 6 Namely, the United States of America │ -│ ☐ 7 The inhabitants of the United States called themselves Yankees │ -│ ☒ 8 For some reason │ -│ ☐ 9 And, eventually noticing the rest of the world was there, │ -│ ☐ 10 Decided to rule it. │ +│ ☐ Pa231 Protactinium-231 (162kg) │ +│ ☐ U233 Uranium-233 (15kg) │ +│ ☐ U235 Uranium-235 (50kg) │ +│ ☐ Np236 Neptunium-236 (7kg) │ +│ ☐ Np237 Neptunium-237 (60kg) │ +│ ☐ Pu238 Plutonium-238 (10kg) │ +│ ☐ Pu239 Plutonium-239 (10kg) │ +│ ☐ Pu240 Plutonium-240 (40kg) │ +│ ☐ Pu241 Plutonium-241 (13kg) │ +│ ☐ Am241 Americium-241 (100kg) │ │ ↓ │ -╰─────────────────────────press q to exit (there is sartrev("no exit")─╯ +╰────────────────────────press q to exit (there is sartrev("no exit"))─╯ ``` Menus: diff --git a/doc/man/man3/notcurses_multiselector.3.md b/doc/man/man3/notcurses_multiselector.3.md index 559a07a79..5ad20c25c 100644 --- a/doc/man/man3/notcurses_multiselector.3.md +++ b/doc/man/man3/notcurses_multiselector.3.md @@ -42,7 +42,7 @@ typedef struct ncmultiselector_options { } ncmultiselector_options; ``` -**struct ncmultiselector* ncmultiselector_create(struct ncplane* n, int y, int x, const ncmultiselector_options* opts);** +**struct ncmultiselector* ncmultiselector_create(struct ncplane* n, const ncmultiselector_options* opts);** **int ncmultiselector_selected(bool* selected, unsigned n);** @@ -56,24 +56,34 @@ typedef struct ncmultiselector_options { # NOTES -Currently, the **ncplane** **n** provided to **ncmultiselector_create** -must not be **NULL**, though the **ncmultiselector** will always get its -own plane, and this plane will not (currently) be bound to **n**. -**ncmultiselector_selected** returns a bitmap corresponding to the -currently-selected options. +The **ncplane** **n** provided to **ncmultiselector_create** must not be +**NULL**. It will be freely resized by the new **ncmultiselector**. + +**ncmultiselector_selected** returns the index of the option currently +highlighted. It stores to the **n**-ary bitmap pointed to by **selected** +based on the currently-selected options. **ncmultiselector_plane** will return the **ncplane** on which the widget is drawn. -While the **ncmultiselector** can be driven entirely by client code, -input can be run through **ncmultiselector_offer_input** to take -advantage of common controls. It will handle the up and down arrows, -along with PageUp and PageDown, and space to select/deselect options. -If the mouse is enabled, the mouse scrollwheel and mouse clicks on the scroll -arrows will be handled. +Input should be run through **ncmultiselector_offer_input** to take advantage +of common controls. It will handle the up and down arrows, along with PageUp +and PageDown. If the mouse is enabled, the mouse scrollwheel and mouse clicks +on the scroll arrows will be handled. + +**ncmultiselector_destroy** destroys the backing **ncplane**, as does +**ncmultiselector_create** in the event of any error. # RETURN VALUES +**ncmultiselector_create** returns **NULL** on an error, in which case the +passed **ncplane** is destroyed. + +**ncmultiselector_selected** returns -1 if there are no items, or if **n** is +not equal to the number of items. It otherwise returns the index of the +currently highlighted option, and writes a bitmap to **selected** based off +the selected items. + # SEE ALSO **notcurses(3)**, diff --git a/include/ncpp/MultiSelector.hh b/include/ncpp/MultiSelector.hh index 764e10233..4d6d9e007 100644 --- a/include/ncpp/MultiSelector.hh +++ b/include/ncpp/MultiSelector.hh @@ -15,24 +15,24 @@ namespace ncpp static ncmultiselector_options default_options; public: - explicit MultiSelector (Plane *plane, int y, int x, const ncmultiselector_options *opts = nullptr) - : MultiSelector (static_cast(plane), y, x, opts) + explicit MultiSelector (Plane *plane, const ncmultiselector_options *opts = nullptr) + : MultiSelector (static_cast(plane), opts) {} - explicit MultiSelector (Plane const* plane, int y, int x, const ncmultiselector_options *opts = nullptr) + explicit MultiSelector (Plane const* plane, const ncmultiselector_options *opts = nullptr) : Root (Utilities::get_notcurses_cpp (plane)) { - common_init (Utilities::to_ncplane (plane), y, x, opts); + common_init (Utilities::to_ncplane (plane), opts); } - explicit MultiSelector (Plane &plane, int y, int x, const ncmultiselector_options *opts = nullptr) - : MultiSelector (static_cast(plane), y, x, opts) + explicit MultiSelector (Plane &plane, const ncmultiselector_options *opts = nullptr) + : MultiSelector (static_cast(plane), opts) {} - explicit MultiSelector (Plane const& plane, int y, int x, const ncmultiselector_options *opts = nullptr) + explicit MultiSelector (Plane const& plane, const ncmultiselector_options *opts = nullptr) : Root (Utilities::get_notcurses_cpp (plane)) { - common_init (Utilities::to_ncplane (plane), y, x, opts); + common_init (Utilities::to_ncplane (plane), opts); } ~MultiSelector () @@ -54,12 +54,12 @@ namespace ncpp Plane* get_plane () const noexcept; private: - void common_init (ncplane *plane, int y, int x, const ncmultiselector_options *opts) + void common_init (ncplane *plane, const ncmultiselector_options *opts) { if (plane == nullptr) throw invalid_argument ("'plane' must be a valid pointer"); - multiselector = ncmultiselector_create (plane, y, x, opts == nullptr ? &default_options : opts); + multiselector = ncmultiselector_create (plane, opts == nullptr ? &default_options : opts); if (multiselector == nullptr) throw init_error ("Notcurses failed to create a new multiselector"); } diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index e889a51bf..c71b3c727 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -2657,14 +2657,14 @@ API void ncplane_greyscale(struct ncplane* n); // ╭──────────────────────────╮ // │This is the primary header│ // ╭──────────────────────this is the secondary header──────╮ -// │ │ -// │ option1 Long text #1 │ -// │ option2 Long text #2 │ -// │ option3 Long text #3 │ -// │ option4 Long text #4 │ -// │ option5 Long text #5 │ -// │ option6 Long text #6 │ -// │ │ +// │ ↑ │ +// │ option1 Long text #1 │ +// │ option2 Long text #2 │ +// │ option3 Long text #3 │ +// │ option4 Long text #4 │ +// │ option5 Long text #5 │ +// │ option6 Long text #6 │ +// │ ↓ │ // ╰────────────────────────────────────here's the footer───╯ // // At all times, exactly one item is selected. @@ -2736,22 +2736,22 @@ struct ncmselector_item { // multiselection widget -- a selector supporting multiple selections. // -// ╭────────────────────────────────────────────────────────────────╮ -// │ this is truly an awfully long example of a MULTISELECTOR title │ -//╭─────┴─────────────────────────────pick one (you will die regardless)─┤ +// ╭───────────────────╮ +// │ short round title │ +//╭now this secondary is also very, very, very outlandishly long, you see┤ //│ ↑ │ -//│ ☐ 1 Across the Atlantic Ocean, there was a place called North America│ -//│ ☐ 2 Discovered by an Italian in the employ of the queen of Spain │ -//│ ☐ 3 Colonized extensively by the Spanish and the French │ -//│ ☐ 4 Developed into a rich nation by Dutch-supplied African slaves │ -//│ ☐ 5 And thus became the largest English-speaking nation on earth │ -//│ ☐ 6 Namely, the United States of America │ -//│ ☐ 7 The inhabitants of the United States called themselves Yankees │ -//│ ☐ 8 For some reason │ -//│ ☐ 9 And, eventually noticing the rest of the world was there, │ -//│ ☐ 10 Decided to rule it. │ +//│ ☐ Pa231 Protactinium-231 (162kg) │ +//│ ☐ U233 Uranium-233 (15kg) │ +//│ ☐ U235 Uranium-235 (50kg) │ +//│ ☐ Np236 Neptunium-236 (7kg) │ +//│ ☐ Np237 Neptunium-237 (60kg) │ +//│ ☐ Pu238 Plutonium-238 (10kg) │ +//│ ☐ Pu239 Plutonium-239 (10kg) │ +//│ ☐ Pu240 Plutonium-240 (40kg) │ +//│ ☐ Pu241 Plutonium-241 (13kg) │ +//│ ☐ Am241 Americium-241 (100kg) │ //│ ↓ │ -//╰─────────────────────────press q to exit (there is sartrev("no exit")─╯ +//╰────────────────────────press q to exit (there is sartrev("no exit"))─╯ // // Unlike the selector widget, zero to all of the items can be selected, but // also the widget does not support adding or removing items at runtime. @@ -2772,8 +2772,7 @@ typedef struct ncmultiselector_options { uint64_t flags; // bitfield of NCMULTISELECTOR_OPTION_* } ncmultiselector_options; -API struct ncmultiselector* ncmultiselector_create(struct ncplane* n, int y, int x, - const ncmultiselector_options* opts) +API struct ncmultiselector* ncmultiselector_create(struct ncplane* n, const ncmultiselector_options* opts) __attribute__ ((nonnull (1))); // Return selected vector. An array of bools must be provided, along with its diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index 443b279bd..133a5c15e 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -277,7 +277,7 @@ typedef struct ncmultiselector_options { uint64_t boxchannels; // border channels uint64_t bgchannels; // background channels, used only in body } ncmultiselector_options; -struct ncmultiselector* ncmultiselector_create(struct ncplane* n, int y, int x, const ncmultiselector_options* opts); +struct ncmultiselector* ncmultiselector_create(struct ncplane* n, const ncmultiselector_options* opts); int ncmultiselector_selected(struct ncmultiselector* n, bool* selected, unsigned count); struct ncplane* ncmultiselector_plane(struct ncmultiselector* n); bool ncmultiselector_offer_input(struct ncmultiselector* n, const struct ncinput* nc); diff --git a/src/demo/zoo.c b/src/demo/zoo.c index a32493856..73bf847fc 100644 --- a/src/demo/zoo.c +++ b/src/demo/zoo.c @@ -77,7 +77,11 @@ multiselector_demo(struct ncplane* n, struct ncplane* under, int y){ }; channels_set_fg_alpha(&mopts.bgchannels, CELL_ALPHA_BLEND); channels_set_bg_alpha(&mopts.bgchannels, CELL_ALPHA_BLEND); - struct ncmultiselector* mselect = ncmultiselector_create(n, y, 0, &mopts); + struct ncplane* mseln = ncplane_new(ncplane_notcurses(n), 1, 1, y, 0, NULL); + if(mseln == NULL){ + return NULL; + } + struct ncmultiselector* mselect = ncmultiselector_create(mseln, &mopts); if(mselect == NULL){ return NULL; } diff --git a/src/lib/selector.c b/src/lib/selector.c index 57acd6f07..38b2553ec 100644 --- a/src/lib/selector.c +++ b/src/lib/selector.c @@ -773,12 +773,12 @@ bool ncmultiselector_offer_input(ncmultiselector* n, const ncinput* nc){ } // calculate the necessary dimensions based off properties of the selector and -// the containing screen FIXME should be based on containing ncplane +// the containing plane static int -ncmultiselector_dim_yx(notcurses* nc, const ncmultiselector* n, int* ncdimy, int* ncdimx){ +ncmultiselector_dim_yx(const ncmultiselector* n, int* ncdimy, int* ncdimx){ int rows = 0, cols = 0; // desired dimensions int dimy, dimx; // dimensions of containing screen - notcurses_term_dim_yx(nc, &dimy, &dimx); + ncplane_dim_yx(ncplane_parent(n->ncp), &dimy, &dimx); if(n->title){ // header adds two rows for riser rows += 2; } @@ -805,8 +805,7 @@ ncmultiselector_dim_yx(notcurses* nc, const ncmultiselector* n, int* ncdimy, int return 0; } -ncmultiselector* ncmultiselector_create(ncplane* n, int y, int x, - const ncmultiselector_options* opts){ +ncmultiselector* ncmultiselector_create(ncplane* n, const ncmultiselector_options* opts){ ncmultiselector_options zeroed = {}; if(!opts){ opts = &zeroed; @@ -840,9 +839,7 @@ ncmultiselector* ncmultiselector_create(ncplane* n, int y, int x, ns->darrowy = ns->uarrowy = ns->arrowx = -1; if(itemcount){ if(!(ns->items = malloc(sizeof(*ns->items) * itemcount))){ - free(ns->title); free(ns->secondary); free(ns->footer); - free(ns); - return NULL; + goto freeitems; } }else{ ns->items = NULL; @@ -867,10 +864,11 @@ ncmultiselector* ncmultiselector_create(ncplane* n, int y, int x, } } int dimy, dimx; - if(ncmultiselector_dim_yx(n->nc, ns, &dimy, &dimx)){ + ns->ncp = n; + if(ncmultiselector_dim_yx(ns, &dimy, &dimx)){ goto freeitems; } - if(!(ns->ncp = ncplane_bound(n, dimy, dimx, y, x, NULL))){ + if(ncplane_resize_simple(ns->ncp, dimy, dimx)){ goto freeitems; } cell_init(&ns->background); @@ -893,6 +891,7 @@ freeitems: free(ns->items); free(ns->title); free(ns->secondary); free(ns->footer); free(ns); + ncplane_destroy(n); return NULL; } diff --git a/src/poc/multiselect.c b/src/poc/multiselect.c index 2294d92cc..4ab715ffd 100644 --- a/src/poc/multiselect.c +++ b/src/poc/multiselect.c @@ -7,17 +7,27 @@ // http://theboomerbible.com/tbb112.html static struct ncmselector_item items[] = { - { "1", "Across the Atlantic Ocean, there was a place called North America", .selected = false, }, - { "2", "Discovered by an Italian in the employ of the queen of Spain", .selected = false, }, - { "3", "Colonized extensively by the Spanish and the French", .selected = false, }, - { "4", "Developed into a rich nation by Dutch-supplied African slaves", .selected = false, }, - { "5", "And thus became the largest English-speaking nation on earth", .selected = false, }, - { "6", "Namely, the United States of America", .selected = false, }, - { "7", "The inhabitants of the United States called themselves Yankees", .selected = false, }, - { "8", "For some reason", .selected = false, }, - { "9", "And, eventually noticing the rest of the world was there,", .selected = false, }, - { "10", "Decided to rule it.", .selected = false, }, - { "11", "This is their story.", .selected = false, }, + { "Pa231", "Protactinium-231 (162kg)", .selected = false, }, + { "U233", "Uranium-233 (15kg)", .selected = false, }, + { "U235", "Uranium-235 (50kg)", .selected = false, }, + { "Np236", "Neptunium-236 (7kg)", .selected = false, }, + { "Np237", "Neptunium-237 (60kg)", .selected = false, }, + { "Pu238", "Plutonium-238 (10kg)", .selected = false, }, + { "Pu239", "Plutonium-239 (10kg)", .selected = false, }, + { "Pu240", "Plutonium-240 (40kg)", .selected = false, }, + { "Pu241", "Plutonium-241 (13kg)", .selected = false, }, + { "Am241", "Americium-241 (100kg)", .selected = false, }, + { "Pu242", "Plutonium-242 (100kg)", .selected = false, }, + { "Am242", "Americium-242 (18kg)", .selected = false, }, + { "Am243", "Americium-243 (155kg)", .selected = false, }, + { "Cm243", "Curium-243 (10kg)", .selected = false, }, + { "Cm244", "Curium-244 (30kg)", .selected = false, }, + { "Cm245", "Curium-245 (13kg)", .selected = false, }, + { "Cm246", "Curium-246 (84kg)", .selected = false, }, + { "Cm247", "Curium-247 (7kg)", .selected = false, }, + { "Bk247", "Berkelium-247 (10kg)", .selected = false, }, + { "Cf249", "Californium-249 (6kg)", .selected = false, }, + { "Cf251", "Californium-251 (9kg)", .selected = false, }, { NULL, NULL, .selected = false, }, }; @@ -96,28 +106,36 @@ int main(void){ ncplane_set_fg(n, 0x40f040); ncplane_putstr_aligned(n, 0, NCALIGN_RIGHT, "multiselect widget demo"); - struct ncmultiselector* ns = ncmultiselector_create(n, 3, 0, &sopts); + struct ncplane* mseln = ncplane_new(nc, 1, 1, 3, 0, NULL); + if(mseln == NULL){ + goto err; + } + struct ncmultiselector* ns = ncmultiselector_create(mseln, &sopts); run_mselect(nc, ns); sopts.title = "short round title"; - ns = ncmultiselector_create(n, 3, 0, &sopts); + mseln = ncplane_new(nc, 1, 1, 3, 0, NULL); + ns = ncmultiselector_create(mseln, &sopts); run_mselect(nc, ns); sopts.title = "short round title"; sopts.secondary = "now this secondary is also very, very, very outlandishly long, you see"; - ns = ncmultiselector_create(n, 3, 0, &sopts); + mseln = ncplane_new(nc, 1, 1, 3, 0, NULL); + ns = ncmultiselector_create(mseln, &sopts); run_mselect(nc, ns); sopts.title = "the whole world is watching"; sopts.secondary = NULL; sopts.footer = "now this FOOTERFOOTER is also very, very, very outlandishly long, you see"; - ns = ncmultiselector_create(n, 3, 0, &sopts); + mseln = ncplane_new(nc, 1, 1, 3, 0, NULL); + ns = ncmultiselector_create(mseln, &sopts); run_mselect(nc, ns); sopts.title = "chomps"; sopts.secondary = NULL; sopts.footer = NULL; - ns = ncmultiselector_create(n, 3, 0, &sopts); + mseln = ncplane_new(nc, 1, 1, 3, 0, NULL); + ns = ncmultiselector_create(mseln, &sopts); run_mselect(nc, ns); if(notcurses_stop(nc)){