From de595380b6e18730714c2268226468d050725dcf Mon Sep 17 00:00:00 2001 From: nick black Date: Thu, 12 Dec 2019 07:01:05 -0500 Subject: [PATCH] ncplane_putstr_aligned(), use it in intro #102 --- include/notcurses.h | 10 +++++++++ src/demo/demo.c | 19 +++++------------- src/lib/notcurses.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/include/notcurses.h b/include/notcurses.h index 2fda9fa95..fc89ed992 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -433,6 +433,16 @@ ncplane_putwegc_yx(struct ncplane* n, int y, int x, const wchar_t* gclust, // which were written before the error. API int ncplane_putstr(struct ncplane* n, const char* gclustarr); +// Alignment within the ncplane. Left/right-justified, or centered. +typedef enum { + NCALIGN_LEFT, + NCALIGN_CENTER, + NCALIGN_RIGHT, +} ncalign_e; + +API int ncplane_putstr_aligned(struct ncplane* n, int y, const char* s, + ncalign_e atype); + static inline int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclustarr){ if(ncplane_cursor_move_yx(n, y, x)){ diff --git a/src/demo/demo.c b/src/demo/demo.c index 16da2847a..dec6b378b 100644 --- a/src/demo/demo.c +++ b/src/demo/demo.c @@ -100,30 +100,21 @@ intro(struct notcurses* nc){ if(ncplane_set_bg_rgb(ncp, 0, 40, 0)){ return -1; } - if(ncplane_cursor_move_yx(ncp, rows / 2 - 2, (cols - strlen(s1) + 4) / 2)){ - return -1; - } - if(ncplane_putstr(ncp, s1) != (int)strlen(s1)){ - return -1; - } - if(ncplane_cursor_move_yx(ncp, rows / 2, (cols - strlen(str) + 4) / 2)){ + if(ncplane_putstr_aligned(ncp, rows / 2 - 2, s1, NCALIGN_CENTER) != (int)strlen(s1)){ return -1; } ncplane_styles_on(ncp, CELL_STYLE_ITALIC | CELL_STYLE_BOLD); - if(ncplane_putstr(ncp, str) != (int)strlen(str)){ + if(ncplane_putstr_aligned(ncp, rows / 2, str, NCALIGN_CENTER) != (int)strlen(str)){ return -1; } ncplane_styles_off(ncp, CELL_STYLE_ITALIC | CELL_STYLE_BOLD); const wchar_t wstr[] = L"▏▁ ▂ ▃ ▄ ▅ ▆ ▇ █ █ ▇ ▆ ▅ ▄ ▃ ▂ ▁▕"; - char mbstr[128]; - if(wcstombs(mbstr, wstr, sizeof(mbstr)) <= 0){ - return -1; - } + // FIXME need NCALIGN_CENTER if(ncplane_cursor_move_yx(ncp, rows / 2 - 5, (cols - wcslen(wstr) + 4) / 2)){ return -1; } - if(ncplane_putstr(ncp, mbstr) != (int)strlen(mbstr)){ - return -1; + if(ncplane_putwstr(ncp, wstr) != (int)wcslen(wstr)){ + //return -1; } if(notcurses_render(nc)){ return -1; diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index f59c1dd15..96c81a983 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -112,6 +112,55 @@ drop_signals(notcurses* nc){ return 0; } +// make a heap-allocated wchar_t expansion of the multibyte string at s +wchar_t* wchar_from_utf8(const char* s){ + mbstate_t ps; + memset(&ps, 0, sizeof(ps)); + // worst case is a wchar_t for every byte of input (all-ASCII case) + const size_t maxw = strlen(s) + 1; + wchar_t* dst = malloc(sizeof(*dst) * maxw); + size_t wcount = mbsrtowcs(dst, &s, maxw, &ps); + if(wcount == (size_t)-1){ + free(dst); + return NULL; + } + if(s){ + free(dst); + return NULL; + } + return dst; +} + +int ncplane_putstr_aligned(ncplane* n, int y, const char* s, ncalign_e atype){ + wchar_t* w = wchar_from_utf8(s); + if(w == NULL){ + return -1; + } + int width = wcswidth(w, INT_MAX); + free(w); + int cols; + int xpos; + switch(atype){ + case NCALIGN_LEFT: + xpos = 0; + break; + case NCALIGN_CENTER: + ncplane_dim_yx(n, NULL, &cols); + xpos = (cols - width) / 2; + break; + case NCALIGN_RIGHT: + ncplane_dim_yx(n, NULL, &cols); + xpos = cols - width; + break; + default: + return -1; + } + if(ncplane_cursor_move_yx(n, y, xpos)){ + return -1; + } + return ncplane_putstr(n, s); +} + static inline uint64_t timespec_to_ns(const struct timespec* t){ return t->tv_sec * NANOSECS_IN_SEC + t->tv_nsec;