Notcurses 3.0.16
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
menu.c File Reference
#include "internal.h"
Include dependency graph for menu.c:

Go to the source code of this file.

Data Structures

struct  ncmenu_int_item
 
struct  ncmenu_int_section
 
struct  ncmenu
 

Macros

#define ALTMOD   "Alt+"
 
#define CTLMOD   "Ctrl+"
 

Typedefs

typedef struct ncmenu_int_item ncmenu_int_item
 
typedef struct ncmenu_int_section ncmenu_int_section
 
typedef struct ncmenu ncmenu
 

Functions

ncmenuncmenu_create (ncplane *n, const ncmenu_options *opts)
 
int ncmenu_unroll (ncmenu *n, int sectionidx)
 
int ncmenu_rollup (ncmenu *n)
 
int ncmenu_nextsection (ncmenu *n)
 
int ncmenu_prevsection (ncmenu *n)
 
int ncmenu_nextitem (ncmenu *n)
 
int ncmenu_previtem (ncmenu *n)
 
const char * ncmenu_selected (const ncmenu *n, ncinput *ni)
 
const char * ncmenu_mouse_selected (const ncmenu *n, const ncinput *click, ncinput *ni)
 
bool ncmenu_offer_input (ncmenu *n, const ncinput *nc)
 
int ncmenu_item_set_status (ncmenu *n, const char *section, const char *item, bool enabled)
 
ncplanencmenu_plane (ncmenu *menu)
 
void ncmenu_destroy (ncmenu *n)
 

Macro Definition Documentation

◆ ALTMOD

#define ALTMOD   "Alt+"

◆ CTLMOD

#define CTLMOD   "Ctrl+"

Typedef Documentation

◆ ncmenu

typedef struct ncmenu ncmenu

◆ ncmenu_int_item

◆ ncmenu_int_section

Function Documentation

◆ ncmenu_create()

ncmenu * ncmenu_create ( ncplane n,
const ncmenu_options opts 
)

Definition at line 377 of file menu.c.

377 {
378 ncmenu_options zeroed = {0};
379 if(!opts){
380 opts = &zeroed;
381 }
382 if(opts->sectioncount <= 0 || !opts->sections){
383 logerror("invalid %d-ary section information", opts->sectioncount);
384 return NULL;
385 }
386 if(opts->flags >= (NCMENU_OPTION_HIDING << 1u)){
387 logwarn("provided unsupported flags %016" PRIx64, opts->flags);
388 }
389 unsigned totalheight = 1;
390 unsigned totalwidth = 2; // start with two-character margin on the left
391 ncmenu* ret = malloc(sizeof(*ret));
392 ret->sectioncount = opts->sectioncount;
393 ret->sections = NULL;
394 unsigned dimy, dimx;
395 ncplane_dim_yx(n, &dimy, &dimx);
396 if(ret){
398 if(dup_menu_sections(ret, opts, &totalwidth, &totalheight) == 0){
399 ret->headerwidth = totalwidth;
400 if(totalwidth < dimx){
401 totalwidth = dimx;
402 }
403 struct ncplane_options nopts = {
404 .y = ret->bottom ? dimy - totalheight : 0,
405 .x = 0,
406 .rows = totalheight,
407 .cols = totalwidth,
408 .userptr = ret,
409 .name = "menu",
410 .resizecb = resize_menu,
411 .flags = NCPLANE_OPTION_FIXED,
412 };
413 ret->ncp = ncplane_create(n, &nopts);
414 if(ret->ncp){
415 if(ncplane_set_widget(ret->ncp, ret, (void(*)(void*))ncmenu_destroy) == 0){
416 ret->unrolledsection = -1;
417 ret->headerchannels = opts->headerchannels;
418 ret->dissectchannels = opts->headerchannels;
419 ncchannels_set_fg_rgb(&ret->dissectchannels, 0xdddddd);
420 ret->sectionchannels = opts->sectionchannels;
422 ncchannels_set_fg_rgb(&ret->disablechannels, 0xdddddd);
424 nccell_set_fg_alpha(&c, NCALPHA_TRANSPARENT);
425 nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT);
427 nccell_release(ret->ncp, &c);
428 if(write_header(ret) == 0){
429 return ret;
430 }
431 }
432 ncplane_destroy(ret->ncp);
433 }
434 free_menu_sections(ret);
435 }
436 free(ret);
437 }
438 logerror("error creating ncmenu");
439 return NULL;
440}
const nccell * c
Definition egcpool.h:207
#define logerror(fmt,...)
Definition logging.h:32
#define logwarn(fmt,...)
Definition logging.h:37
void ncmenu_destroy(ncmenu *n)
Definition menu.c:825
int ncplane_destroy(ncplane *ncp)
Definition notcurses.c:1021
ncplane * ncplane_create(ncplane *n, const ncplane_options *nopts)
Definition notcurses.c:710
int ncplane_set_base_cell(ncplane *ncp, const nccell *c)
Definition notcurses.c:1561
void ncplane_dim_yx(const ncplane *n, unsigned *rows, unsigned *cols)
Definition notcurses.c:304
#define NCMENU_OPTION_HIDING
Definition notcurses.h:4132
const struct ncplane_options * opts
Definition notcurses.h:3487
#define NCALPHA_TRANSPARENT
Definition notcurses.h:106
vopts n
Definition notcurses.h:3506
#define NCMENU_OPTION_BOTTOM
Definition notcurses.h:4131
#define NCCELL_TRIVIAL_INITIALIZER
Definition notcurses.h:737
#define NCPLANE_OPTION_FIXED
Definition notcurses.h:1454
void nccell_release(ncplane *n, nccell *c)
Definition render.c:128
Definition menu.c:25
int unrolledsection
Definition menu.c:29
uint64_t disablechannels
Definition menu.c:34
ncplane * ncp
Definition menu.c:26
uint64_t sectionchannels
Definition menu.c:33
ncmenu_int_section * sections
Definition menu.c:28
int headerwidth
Definition menu.c:30
int sectioncount
Definition menu.c:27
uint64_t headerchannels
Definition menu.c:31
bool bottom
Definition menu.c:35
uint64_t dissectchannels
Definition menu.c:32
return NULL
Definition termdesc.h:229
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_destroy()

void ncmenu_destroy ( ncmenu n)

Definition at line 825 of file menu.c.

825 {
826 if(n){
827 free_menu_sections(n);
828 if(ncplane_set_widget(n->ncp, NULL, NULL) == 0){
829 ncplane_destroy(n->ncp);
830 }
831 free(n);
832 }
833}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_item_set_status()

int ncmenu_item_set_status ( ncmenu n,
const char *  section,
const char *  item,
bool  enabled 
)

Definition at line 784 of file menu.c.

785 {
786 for(int si = 0 ; si < n->sectioncount ; ++si){
787 struct ncmenu_int_section* sec = &n->sections[si];
788 if(strcmp(sec->name, section) == 0){
789 for(unsigned ii = 0 ; ii < sec->itemcount ; ++ii){
790 struct ncmenu_int_item* i = &sec->items[ii];
791 if(strcmp(i->desc, item) == 0){
792 const bool changed = (i->disabled != enabled);
793 i->disabled = !enabled;
794 if(changed){
795 if(i->disabled){
796 if(--sec->enabled_item_count == 0){
797 write_header(n);
798 }
799 }else{
800 if(++sec->enabled_item_count == 1){
801 write_header(n);
802 }
803 }
804 if(n->unrolledsection == si){
805 if(sec->enabled_item_count == 0){
807 }else{
808 ncmenu_unroll(n, n->unrolledsection);
809 }
810 }
811 }
812 return 0;
813 }
814 }
815 break;
816 }
817 }
818 return -1;
819}
int ncmenu_rollup(ncmenu *n)
Definition menu.c:554
int ncmenu_unroll(ncmenu *n, int sectionidx)
Definition menu.c:452
char * desc
Definition menu.c:5
bool disabled
Definition menu.c:10
ncmenu_int_item * items
Definition menu.c:16
char * name
Definition menu.c:14
unsigned itemcount
Definition menu.c:15
int enabled_item_count
Definition menu.c:22
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_mouse_selected()

const char * ncmenu_mouse_selected ( const ncmenu n,
const ncinput click,
ncinput ni 
)

Definition at line 673 of file menu.c.

674 {
675 if(click->id != NCKEY_BUTTON1){
676 return NULL;
677 }
678 if(click->evtype != NCTYPE_RELEASE){
679 return NULL;
680 }
681 struct ncplane* nc = n->ncp;
682 int y = click->y;
683 int x = click->x;
684 unsigned dimy, dimx;
685 ncplane_dim_yx(nc, &dimy, &dimx);
686 if(!ncplane_translate_abs(nc, &y, &x)){
687 return NULL;
688 }
689 if(n->unrolledsection < 0){
690 return NULL;
691 }
692 const struct ncmenu_int_section* sec = &n->sections[n->unrolledsection];
693 int itemidx = ncsection_click_index(n, sec, dimy, dimx, y, x);
694 if(itemidx < 0){
695 return NULL;
696 }
697 // don't allow a disabled item to be selected
698 if(sec->items[itemidx].disabled){
699 return NULL;
700 }
701 if(ni){
702 memcpy(ni, &sec->items[itemidx].shortcut, sizeof(*ni));
703 }
704 return sec->items[itemidx].desc;
705}
#define NCKEY_BUTTON1
Definition nckeys.h:166
bool ncplane_translate_abs(const ncplane *n, int *restrict y, int *restrict x)
Definition notcurses.c:2595
int y
Definition notcurses.h:1905
@ NCTYPE_RELEASE
Definition notcurses.h:1198
int int x
Definition notcurses.h:1905
ncintype_e evtype
Definition notcurses.h:1218
uint32_t id
Definition notcurses.h:1210
ncinput shortcut
Definition menu.c:6
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_nextitem()

int ncmenu_nextitem ( ncmenu n)

Definition at line 593 of file menu.c.

593 {
594 if(n->unrolledsection == -1){
595 if(ncmenu_unroll(n, 0)){
596 return -1;
597 }
598 }
599 ncmenu_int_section* sec = &n->sections[n->unrolledsection];
600 int origselected = sec->itemselected;
601 if(origselected >= 0){
602 do{
603 if((unsigned)++sec->itemselected == sec->itemcount){
604 sec->itemselected = 0;
605 }
606 if(sec->itemselected == origselected){
607 break;
608 }
609 }while(!sec->items[sec->itemselected].desc || sec->items[sec->itemselected].disabled);
610 }
611 return ncmenu_unroll(n, n->unrolledsection);
612}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_nextsection()

int ncmenu_nextsection ( ncmenu n)

Definition at line 563 of file menu.c.

563 {
564 int nextsection = n->unrolledsection;
565 int origselected = n->unrolledsection;
566 do{
567 if(++nextsection == n->sectioncount){
568 nextsection = 0;
569 }
570 if(nextsection == origselected){
571 break;
572 }
573 }while(n->sections[nextsection].name == NULL ||
574 n->sections[nextsection].enabled_item_count == 0);
575 return ncmenu_unroll(n, nextsection);
576}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_offer_input()

bool ncmenu_offer_input ( ncmenu n,
const ncinput nc 
)

Definition at line 707 of file menu.c.

707 {
708 // we can't actually select menu items in this function, since we need to
709 // invoke an arbitrary function as a result.
710 if(nc->id == NCKEY_BUTTON1 && nc->evtype == NCTYPE_RELEASE){
711 int y = nc->y;
712 int x = nc->x;
713 unsigned dimy, dimx;
714 ncplane_dim_yx(n->ncp, &dimy, &dimx);
715 if(!ncplane_translate_abs(n->ncp, &y, &x)){
716 return false;
717 }
718 if(n->unrolledsection >= 0){
719 struct ncmenu_int_section* sec = &n->sections[n->unrolledsection];
720 int itemidx = ncsection_click_index(n, sec, dimy, dimx, y, x);
721 if(itemidx >= 0){
722 if(!sec->items[itemidx].disabled){
723 sec->itemselected = itemidx;
724 ncmenu_unroll(n, n->unrolledsection);
725 return false;
726 }
727 }
728 }
729 if(y != (n->bottom ? (int)dimy - 1 : 0)){
730 return false;
731 }
732 int i = section_x(n, x);
733 if(i < 0 || i == n->unrolledsection){
735 }else{
736 ncmenu_unroll(n, i);
737 }
738 return true;
739 }else if(nc->evtype == NCTYPE_RELEASE){
740 return false;
741 }
742 for(int si = 0 ; si < n->sectioncount ; ++si){
743 const ncmenu_int_section* sec = &n->sections[si];
744 if(sec->enabled_item_count == 0){
745 continue;
746 }
747 if(!ncinput_equal_p(&sec->shortcut, nc)){
748 continue;
749 }
750 ncmenu_unroll(n, si);
751 return true;
752 }
753 if(n->unrolledsection < 0){ // all following need an unrolled section
754 return false;
755 }
756 if(nc->id == NCKEY_LEFT){
758 return false;
759 }
760 return true;
761 }else if(nc->id == NCKEY_RIGHT){
763 return false;
764 }
765 return true;
766 }else if(nc->id == NCKEY_UP || nc->id == NCKEY_SCROLL_UP){
767 if(ncmenu_previtem(n)){
768 return false;
769 }
770 return true;
771 }else if(nc->id == NCKEY_DOWN || nc->id == NCKEY_SCROLL_DOWN){
772 if(ncmenu_nextitem(n)){
773 return false;
774 }
775 return true;
776 }else if(nc->id == NCKEY_ESC){
778 return true;
779 }
780 return false;
781}
int ncmenu_nextsection(ncmenu *n)
Definition menu.c:563
int ncmenu_prevsection(ncmenu *n)
Definition menu.c:578
int ncmenu_previtem(ncmenu *n)
Definition menu.c:614
int ncmenu_nextitem(ncmenu *n)
Definition menu.c:593
#define NCKEY_SCROLL_UP
Definition nckeys.h:192
#define NCKEY_SCROLL_DOWN
Definition nckeys.h:193
#define NCKEY_UP
Definition nckeys.h:37
#define NCKEY_DOWN
Definition nckeys.h:39
#define NCKEY_RIGHT
Definition nckeys.h:38
#define NCKEY_ESC
Definition nckeys.h:196
#define NCKEY_LEFT
Definition nckeys.h:40
ncinput shortcut
Definition menu.c:17
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_plane()

ncplane * ncmenu_plane ( ncmenu menu)

Definition at line 821 of file menu.c.

821 {
822 return menu->ncp;
823}

◆ ncmenu_previtem()

int ncmenu_previtem ( ncmenu n)

Definition at line 614 of file menu.c.

614 {
615 if(n->unrolledsection == -1){
616 if(ncmenu_unroll(n, 0)){
617 return -1;
618 }
619 }
620 ncmenu_int_section* sec = &n->sections[n->unrolledsection];
621 int origselected = sec->itemselected;
622 if(origselected >= 0){
623 do{
624 if(sec->itemselected-- == 0){
625 sec->itemselected = sec->itemcount - 1;
626 }
627 if(sec->itemselected == origselected){
628 break;
629 }
630 }while(!sec->items[sec->itemselected].desc || sec->items[sec->itemselected].disabled);
631 }
632 return ncmenu_unroll(n, n->unrolledsection);
633}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_prevsection()

int ncmenu_prevsection ( ncmenu n)

Definition at line 578 of file menu.c.

578 {
579 int prevsection = n->unrolledsection;
580 int origselected = n->unrolledsection;
581 do{
582 if(--prevsection < 0){
583 prevsection = n->sectioncount - 1;
584 }
585 if(prevsection == origselected){
586 break;
587 }
588 }while(n->sections[prevsection].name == NULL ||
589 n->sections[prevsection].enabled_item_count == 0);
590 return ncmenu_unroll(n, prevsection);
591}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_rollup()

int ncmenu_rollup ( ncmenu n)

Definition at line 554 of file menu.c.

554 {
555 if(n->unrolledsection < 0){
556 return 0;
557 }
558 n->unrolledsection = -1;
559 ncplane_erase(n->ncp);
560 return write_header(n);
561}
void ncplane_erase(ncplane *n)
Definition notcurses.c:2464
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ncmenu_selected()

const char * ncmenu_selected ( const ncmenu n,
ncinput ni 
)

Definition at line 635 of file menu.c.

635 {
636 if(n->unrolledsection < 0){
637 return NULL;
638 }
639 const struct ncmenu_int_section* sec = &n->sections[n->unrolledsection];
640 const int itemidx = sec->itemselected;
641 if(itemidx < 0){
642 return NULL;
643 }
644 if(ni){
645 memcpy(ni, &sec->items[itemidx].shortcut, sizeof(*ni));
646 }
647 return sec->items[itemidx].desc;
648}
Here is the caller graph for this function:

◆ ncmenu_unroll()

int ncmenu_unroll ( ncmenu n,
int  sectionidx 
)

Definition at line 452 of file menu.c.

452 {
453 if(ncmenu_rollup(n)){ // roll up any unrolled section
454 return -1;
455 }
456 if(sectionidx < 0 || sectionidx >= n->sectioncount){
457 logerror("unrolled invalid sectionidx %d", sectionidx);
458 return -1;
459 }
460 if(n->sections[sectionidx].enabled_item_count <= 0){
461 return 0;
462 }
463 if(n->sections[sectionidx].name == NULL){
464 return -1;
465 }
466 n->unrolledsection = sectionidx;
467 unsigned dimy, dimx;
468 ncplane_dim_yx(n->ncp, &dimy, &dimx);
469 const int height = section_height(n, sectionidx);
470 const int width = section_width(n, sectionidx);
471 int xpos = n->sections[sectionidx].xoff < 0 ?
472 (int)dimx + (n->sections[sectionidx].xoff - 2) : n->sections[sectionidx].xoff;
473 if(xpos + width >= (int)dimx){
474 xpos = dimx - (width + 2);
475 }
476 int ypos = n->bottom ? dimy - height - 1 : 1;
477 if(ncplane_cursor_move_yx(n->ncp, ypos, xpos)){
478 return -1;
479 }
480 if(ncplane_rounded_box_sized(n->ncp, 0, n->headerchannels, height, width, 0)){
481 return -1;
482 }
483 ncmenu_int_section* sec = &n->sections[sectionidx];
484 for(unsigned i = 0 ; i < sec->itemcount ; ++i){
485 ++ypos;
486 if(sec->items[i].desc){
487 // FIXME the user ought be able to configure the disabled channel
488 if(!sec->items[i].disabled){
489 ncplane_set_channels(n->ncp, n->sectionchannels);
490 if(sec->itemselected < 0){
491 sec->itemselected = i;
492 }
493 }else{
494 ncplane_set_channels(n->ncp, n->disablechannels);
495 }
496 if(sec->itemselected >= 0){
497 if(i == (unsigned)sec->itemselected){
498 ncplane_set_channels(n->ncp, ncchannels_reverse(ncplane_channels(n->ncp)));
499 }
500 }
501 ncplane_set_styles(n->ncp, 0);
502 int cols = ncplane_putstr_yx(n->ncp, ypos, xpos + 1, sec->items[i].desc);
503 if(cols < 0){
504 return -1;
505 }
506 // we need pad out the remaining columns of this line with spaces. if
507 // there's a shortcut description, we align it to the right, printing
508 // spaces only through the start of the aligned description.
509 int thiswidth = width;
510 if(sec->items[i].shortdesc){
511 thiswidth -= sec->items[i].shortdesccols;
512 }
513 // print any necessary padding spaces
514 for(int j = cols + 1 ; j < thiswidth - 1 ; ++j){
515 if(ncplane_putchar(n->ncp, ' ') < 0){
516 return -1;
517 }
518 }
519 if(sec->items[i].shortdesc){
520 if(ncplane_putstr(n->ncp, sec->items[i].shortdesc) < 0){
521 return -1;
522 }
523 }
524 if(sec->items[i].shortcut_offset >= 0){
526 if(ncplane_at_yx_cell(n->ncp, ypos, xpos + 1 + sec->items[i].shortcut_offset, &cl) < 0){
527 return -1;
528 }
529 nccell_on_styles(&cl, NCSTYLE_UNDERLINE|NCSTYLE_BOLD);
530 if(ncplane_putc_yx(n->ncp, ypos, xpos + 1 + sec->items[i].shortcut_offset, &cl) < 0){
531 return -1;
532 }
533 nccell_release(n->ncp, &cl);
534 }
535 }else{
536 n->ncp->channels = n->headerchannels;
537 ncplane_set_styles(n->ncp, 0);
538 if(ncplane_putegc_yx(n->ncp, ypos, xpos, "├", NULL) < 0){
539 return -1;
540 }
541 for(int j = 1 ; j < width - 1 ; ++j){
542 if(ncplane_putegc(n->ncp, "─", NULL) < 0){
543 return -1;
544 }
545 }
546 if(ncplane_putegc(n->ncp, "┤", NULL) < 0){
547 return -1;
548 }
549 }
550 }
551 return 0;
552}
int ncplane_cursor_move_yx(ncplane *n, int y, int x)
Definition notcurses.c:723
void ncplane_set_channels(ncplane *n, uint64_t channels)
Definition notcurses.c:1497
void ncplane_set_styles(ncplane *n, unsigned stylebits)
Definition notcurses.c:2077
int ncplane_putegc_yx(ncplane *n, int y, int x, const char *gclust, size_t *sbytes)
Definition notcurses.c:2001
int ncplane_at_yx_cell(ncplane *n, int y, int x, nccell *c)
Definition notcurses.c:273
uint64_t ncplane_channels(const ncplane *n)
Definition notcurses.c:1493
int ncplane_putc_yx(ncplane *n, int y, int x, const nccell *c)
Definition notcurses.c:1986
#define NCSTYLE_UNDERLINE
Definition notcurses.h:771
#define NCSTYLE_BOLD
Definition notcurses.h:773
int shortdesccols
Definition menu.c:9
char * shortdesc
Definition menu.c:8
int shortcut_offset
Definition menu.c:7
Here is the call graph for this function:
Here is the caller graph for this function: