ncmenu: step around alignment NULL #332

pull/344/head
nick black 5 years ago committed by Nick Black
parent 3bbb752554
commit 71dbfd74f7

@ -396,7 +396,7 @@ int main(int argc, char** argv){
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGWINCH);
pthread_sigmask(SIG_SETMASK, &sigmask, NULL);
const bool use_menu = false;
const bool use_menu = true;
const char* spec;
bool use_hud, ignore_failures;
notcurses_options nopts;

@ -61,10 +61,17 @@ struct ncmenu* menu_create(struct notcurses* nc){
struct ncmenu_item demo_items[] = {
{ .desc = "Restart", .shortcut = { .id = 'r', .ctrl = true, }, },
};
struct ncmenu_item help_items[] = {
{ .desc = "About", .shortcut = { .id = 'u', .ctrl = true, }, },
};
struct ncmenu_section sections[] = {
{ .name = "notcurses-demo", .items = demo_items,
.itemcount = sizeof(demo_items) / sizeof(*demo_items),
.shortcut = { .id = 'o', .alt = true, }, },
{ .name = NULL, .items = NULL, .itemcount = 0, },
{ .name = "help", .items = help_items,
.itemcount = sizeof(help_items) / sizeof(*help_items),
.shortcut = { .id = 'h', .alt = true, }, },
};
uint64_t headerchannels = 0;
uint64_t sectionchannels = 0;

@ -31,7 +31,7 @@ int intro(struct notcurses* nc){
cell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
cell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER;
cell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
if(ncplane_cursor_move_yx(ncp, 0, 0)){
if(ncplane_cursor_move_yx(ncp, 1, 0)){
return -1;
}
if(cells_rounded_box(ncp, NCSTYLE_BOLD, 0, &ul, &ur, &ll, &lr, &hl, &vl)){
@ -45,7 +45,7 @@ int intro(struct notcurses* nc){
cell_set_bg(&ll, 0x002000);
cell_set_fg(&lr, 0xffffff);
cell_set_bg(&lr, 0x002000);
if(ncplane_box_sized(ncp, &ul, &ur, &ll, &lr, &hl, &vl, rows, cols,
if(ncplane_box_sized(ncp, &ul, &ur, &ll, &lr, &hl, &vl, rows - 1, cols,
NCBOXGRAD_TOP | NCBOXGRAD_BOTTOM |
NCBOXGRAD_RIGHT | NCBOXGRAD_LEFT)){
return -1;

@ -241,13 +241,13 @@ int trans_demo(struct notcurses* nc){
uint64_t channels = 0;
channels_set_fg_rgb(&channels, 0, 128, 128);
channels_set_bg_rgb(&channels, 90, 0, 90);
int y = 0, x = 0;
int y = 1, x = 0;
ncplane_cursor_move_yx(n, y, x);
if(ncplane_rounded_box_sized(n, 0, channels, maxy, maxx, 0)){
if(ncplane_rounded_box_sized(n, 0, channels, maxy - 1, maxx, 0)){
return -1;
}
uint32_t rgb = 0;
for(y = 1 ; y < maxy - 1 ; ++y){
while(++y < maxy - 1){
x = 1;
if(ncplane_cursor_move_yx(n, y, x)){
return -1;

@ -37,7 +37,7 @@ perframecb(struct notcurses* nc, struct ncvisual* ncv __attribute__ ((unused)),
struct ncplane* nstd = notcurses_stdplane(nc);
ncplane_dim_yx(nstd, &dimy, &dimx);
//y = dimy - sizeof(leg) / sizeof(*leg) - 1;
y = 0;
y = 1;
n = ncplane_new(nc, sizeof(leg) / sizeof(*leg), dimx, y, 0, NULL);
if(n == NULL){
return -1;

@ -122,50 +122,72 @@ dup_menu_section(ncmenu_int_section* dst, const struct ncmenu_section* src){
// Duplicates all menu sections in opts, adding their length to '*totalwidth'.
static int
dup_menu_sections(ncmenu* ncm, const ncmenu_options* opts, int* totalwidth, int* totalheight){
if(opts->sectioncount == 0){
return -1;
}
ncm->sections = malloc(sizeof(*ncm->sections) * opts->sectioncount);
if(ncm->sections == NULL){
return -1;
}
bool rightaligned = false; // can only right-align once. twice is error.
int maxheight = 0;
int maxwidth = *totalwidth;
int xoff = 2;
for(int i = 0 ; i < opts->sectioncount ; ++i){
int cols = mbswidth(opts->sections[i].name);
ncm->sections[i].xoff = xoff;
if(cols < 0 || (ncm->sections[i].name = strdup(opts->sections[i].name)) == NULL){
while(i--){
free_menu_section(&ncm->sections[i]);
int i;
for(i = 0 ; i < opts->sectioncount ; ++i){
if(opts->sections[i].name){
int cols = mbswidth(opts->sections[i].name);
ncm->sections[i].xoff = xoff;
if(cols < 0 || (ncm->sections[i].name = strdup(opts->sections[i].name)) == NULL){
goto err;
}
return -1;
}
if(dup_menu_section(&ncm->sections[i], &opts->sections[i])){
free(ncm->sections[i].name);
while(i--){
free_menu_section(&ncm->sections[i]);
if(dup_menu_section(&ncm->sections[i], &opts->sections[i])){
free(ncm->sections[i].name);
goto err;
}
return -1;
}
if(ncm->sections[i].itemcount > maxheight){
maxheight = ncm->sections[i].itemcount;
}
if(*totalwidth + cols + 2 > maxwidth){
maxwidth = *totalwidth + cols + 2;
}
if(*totalwidth + ncm->sections[i].bodycols + 2 > maxwidth){
maxwidth = *totalwidth + ncm->sections[i].bodycols + 2;
}
*totalwidth += cols + 2;
memcpy(&ncm->sections[i].shortcut, &opts->sections[i].shortcut, sizeof(ncm->sections[i].shortcut));
if(mbstr_find_codepoint(ncm->sections[i].name,
ncm->sections[i].shortcut.id,
&ncm->sections[i].shortcut_offset) < 0){
if(ncm->sections[i].itemcount > maxheight){
maxheight = ncm->sections[i].itemcount;
}
if(*totalwidth + cols + 2 > maxwidth){
maxwidth = *totalwidth + cols + 2;
}
if(*totalwidth + ncm->sections[i].bodycols + 2 > maxwidth){
maxwidth = *totalwidth + ncm->sections[i].bodycols + 2;
}
*totalwidth += cols + 2;
memcpy(&ncm->sections[i].shortcut, &opts->sections[i].shortcut, sizeof(ncm->sections[i].shortcut));
if(mbstr_find_codepoint(ncm->sections[i].name,
ncm->sections[i].shortcut.id,
&ncm->sections[i].shortcut_offset) < 0){
ncm->sections[i].shortcut_offset = -1;
}
xoff += cols + 2;
}else{ // divider; remaining sections are right-aligned
if(rightaligned){
goto err;
}
rightaligned = true;
ncm->sections[i].name = NULL;
ncm->sections[i].items = NULL;
ncm->sections[i].itemcount = 0;
ncm->sections[i].xoff = -1;
ncm->sections[i].bodycols = 0;
ncm->sections[i].itemselected = -1;
ncm->sections[i].shortcut_offset = -1;
}
xoff += cols + 2;
}
if(ncm->sectioncount == 1 && rightaligned){
goto err;
}
*totalwidth = maxwidth;
*totalheight += maxheight + 2; // two rows of border
return 0;
err:
while(i--){
free_menu_section(&ncm->sections[i]);
}
return -1;
}
static int
@ -186,27 +208,29 @@ write_header(ncmenu* ncm){ ncm->ncp->channels = ncm->headerchannels;
return -1;
}
for(int i = 0 ; i < ncm->sectioncount ; ++i){
if(ncplane_putstr_yx(ncm->ncp, ypos, xoff, ncm->sections[i].name) < 0){
return -1;
}
if(ncplane_putc(ncm->ncp, &c) < 0){
return -1;
}
if(ncplane_putc(ncm->ncp, &c) < 0){
return -1;
}
if(ncm->sections[i].shortcut_offset >= 0){
cell cl = CELL_TRIVIAL_INITIALIZER;
if(ncplane_at_yx(ncm->ncp, ypos, xoff + ncm->sections[i].shortcut_offset, &cl) < 0){
if(ncm->sections[i].name){
if(ncplane_putstr_yx(ncm->ncp, ypos, xoff, ncm->sections[i].name) < 0){
return -1;
}
if(ncplane_putc(ncm->ncp, &c) < 0){
return -1;
}
cell_styles_on(&cl, NCSTYLE_UNDERLINE|NCSTYLE_BOLD);
if(ncplane_putc_yx(ncm->ncp, ypos, xoff + ncm->sections[i].shortcut_offset, &cl) < 0){
if(ncplane_putc(ncm->ncp, &c) < 0){
return -1;
}
cell_release(ncm->ncp, &cl);
if(ncm->sections[i].shortcut_offset >= 0){
cell cl = CELL_TRIVIAL_INITIALIZER;
if(ncplane_at_yx(ncm->ncp, ypos, xoff + ncm->sections[i].shortcut_offset, &cl) < 0){
return -1;
}
cell_styles_on(&cl, NCSTYLE_UNDERLINE|NCSTYLE_BOLD);
if(ncplane_putc_yx(ncm->ncp, ypos, xoff + ncm->sections[i].shortcut_offset, &cl) < 0){
return -1;
}
cell_release(ncm->ncp, &cl);
}
xoff += mbswidth(ncm->sections[i].name) + 2;
}
xoff += mbswidth(ncm->sections[i].name) + 2;
}
while(xoff < dimx){
if(ncplane_putc_yx(ncm->ncp, ypos, xoff, &c) < 0){
@ -291,6 +315,9 @@ int ncmenu_unroll(ncmenu* n, int sectionidx){
if(ncmenu_rollup(n)){ // roll up any unrolled section
return -1;
}
if(n->sections[sectionidx].name == NULL){
return -1;
}
n->unrolledsection = sectionidx;
int dimy, dimx;
ncplane_dim_yx(n->ncp, &dimy, &dimx);
@ -383,6 +410,11 @@ int ncmenu_nextsection(ncmenu* n){
nextsection = 0;
}
}
if(n->sections[nextsection].name == NULL){
if(++nextsection == n->sectioncount){
nextsection = 0;
}
}
return ncmenu_unroll(n, nextsection);
}
@ -396,6 +428,11 @@ int ncmenu_prevsection(ncmenu* n){
if(--prevsection == -1){
prevsection = n->sectioncount - 1;
}
if(n->sections[prevsection].name == NULL){
if(--prevsection < 0){
prevsection = n->sectioncount - 1;
}
}
return ncmenu_unroll(n, prevsection);
}

@ -54,6 +54,11 @@ run_menu(struct notcurses* nc, struct ncmenu* ncm){
goto err;
}
break;
case 'h': case 'H':
if(ncmenu_unroll(ncm, 3)){
goto err;
}
break;
}
}else if(keypress == 'q'){
ncmenu_destroy(nc, ncm);
@ -92,6 +97,9 @@ int main(void){
{ .desc = NULL, },
{ .desc = "Quit", .shortcut = { .id = 'q', .ctrl = true, }, },
};
struct ncmenu_item help_items[] = {
{ .desc = "About", .shortcut = { .id = 'a', .ctrl = true, }, },
};
struct ncmenu_section sections[] = {
{ .name = "Schwarzgerät", .items = demo_items,
.itemcount = sizeof(demo_items) / sizeof(*demo_items),
@ -99,6 +107,10 @@ int main(void){
{ .name = "File", .items = file_items,
.itemcount = sizeof(file_items) / sizeof(*file_items),
.shortcut = { .id = 'f', .alt = true, }, },
{ .name = NULL, .items = NULL, .itemcount = 0, },
{ .name = "Help", .items = help_items,
.itemcount = sizeof(help_items) / sizeof(*help_items),
.shortcut = { .id = 'h', .alt = true, }, },
};
ncmenu_options mopts;
memset(&mopts, 0, sizeof(mopts));

@ -123,6 +123,60 @@ TEST_CASE("MenuTest") {
CHECK(0 == notcurses_render(nc_));
}
SUBCASE("RightAlignedSection") {
struct ncmenu_item items[] = {
{ .desc = strdup("Yet another crappy item"), .shortcut = {}, },
};
struct ncmenu_section sections[] = {
{ .name = strdup("Left section"), .itemcount = sizeof(items) / sizeof(*items),
.items = items, .shortcut = {}, },
{ .name = nullptr, .itemcount = sizeof(items) / sizeof(*items),
.items = items, .shortcut = {}, },
{ .name = strdup("Right section"), .itemcount = sizeof(items) / sizeof(*items),
.items = items, .shortcut = {}, },
};
struct ncmenu_options opts{};
opts.sections = sections;
opts.sectioncount = sizeof(sections) / sizeof(*sections);
struct ncmenu* ncm = ncmenu_create(nc_, &opts);
REQUIRE(nullptr != ncm);
CHECK(0 == notcurses_render(nc_));
}
// you must have sections, not just an alignment NULL section
SUBCASE("OnlyAlignRejected") {
struct ncmenu_section sections[] = {
{ .name = nullptr, .itemcount = 0, .items = nullptr, .shortcut = {}, },
};
struct ncmenu_options opts{};
opts.sections = sections;
opts.sectioncount = sizeof(sections) / sizeof(*sections);
struct ncmenu* ncm = ncmenu_create(nc_, &opts);
REQUIRE(nullptr == ncm);
}
// you can only shift to right alignment once in a menu
SUBCASE("DoubleAlignRejected") {
struct ncmenu_item items[] = {
{ .desc = strdup("Yet another crappy item"), .shortcut = {}, },
};
struct ncmenu_section sections[] = {
{ .name = strdup("Left section"), .itemcount = sizeof(items) / sizeof(*items),
.items = items, .shortcut = {}, },
{ .name = nullptr, .itemcount = sizeof(items) / sizeof(*items),
.items = items, .shortcut = {}, },
{ .name = nullptr, .itemcount = sizeof(items) / sizeof(*items),
.items = items, .shortcut = {}, },
{ .name = strdup("Right section"), .itemcount = sizeof(items) / sizeof(*items),
.items = items, .shortcut = {}, },
};
struct ncmenu_options opts{};
opts.sections = sections;
opts.sectioncount = sizeof(sections) / sizeof(*sections);
struct ncmenu* ncm = ncmenu_create(nc_, &opts);
REQUIRE(nullptr == ncm);
}
SUBCASE("VeryLongMenu") {
struct ncmenu_item items[] = {
{ .desc = strdup("Generic menu entry"), .shortcut = ncinput(), },

Loading…
Cancel
Save