add ncdirect box-drawing functions #753

This commit is contained in:
nick black 2020-07-10 18:37:41 -04:00 committed by Nick Black
parent 96c6dc8e16
commit a6b002fa77
9 changed files with 191 additions and 9 deletions

View File

@ -11,7 +11,9 @@ rearrangements of Notcurses.
been purged, as have `min_` and `max_supported_rows` and `_cols`. There
is no longer any need to provide a pipe/eventfd. `ncreel_touch()`,
`ncreel_del_focused()`, and `ncreel_move()` have been removed.
* Added `ncdirect_hline_interp()` and `ncdirect_vline_interp()`.
* Added `ncdirect_hline_interp()`, `ncdirect_vline_interp()`,
`ncdirect_rounded_box()`, `ncdirect_double_box()`, and the ridiculously
flexible `ncdirect_box()`.
* 1.6.0 (2020-07-04)
* Behavior has changed regarding use of the provided `FILE*` (which, when

View File

@ -357,6 +357,24 @@ int ncdirect_hline_interp(struct ncdirect* n, const char* egc, int len,
int ncdirect_vline_interp(struct ncdirect* n, const char* egc, int len,
uint64_t h1, uint64_t h2);
// Draw a box with its upper-left corner at the current cursor position, having
// dimensions |ylen|x|xlen|. See ncplane_box() for more information. The
// minimum box size is 2x2, and it cannot be drawn off-screen. |wchars| is an
// array of 6 wide characters: UL, UR, LL, LR, HL, VL.
int ncdirect_box(struct ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr, const wchar_t* wchars,
int ylen, int xlen, unsigned ctlword);
// ncdirect_box() with the rounded box-drawing characters
int ncdirect_rounded_box(struct ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr,
int ylen, int xlen, unsigned ctlword);
// ncdirect_box() with the double box-drawing characters
int ncdirect_double_box(struct ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr,
int ylen, int xlen, unsigned ctlword);
// Display an image using the specified blitter and scaling. The image may
// be arbitrarily many rows -- the output will scroll -- but will only occupy
// the column of the cursor, and those to the right.

View File

@ -60,6 +60,16 @@ ncdirect_init - minimal notcurses instances for styling text
**bool ncdirect_canutf8(const struct ncdirect* n);**
**int ncdirect_hline_interp(struct ncdirect* n, const char* egc, int len, uint64_t h1, uint64_t h2);**
**int ncdirect_vline_interp(struct ncdirect* n, const char* egc, int len, uint64_t h1, uint64_t h2);**
**int ncdirect_box(struct ncdirect* n, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, const wchar_t* wchars, int ylen, int xlen, unsigned ctlword);**
**int ncdirect_rounded_box(struct ncdirect* n, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen, unsigned ctlword);**
**int ncdirect_double_box(struct ncdirect* n, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen, unsigned ctlword);**
**nc_err_e ncdirect_render_image(struct ncdirect* n, const char* filename, ncblitter_e blitter, ncscale_e scale);**
# DESCRIPTION

View File

@ -112,6 +112,24 @@ API int ncdirect_hline_interp(struct ncdirect* n, const char* egc, int len,
API int ncdirect_vline_interp(struct ncdirect* n, const char* egc, int len,
uint64_t h1, uint64_t h2);
// Draw a box with its upper-left corner at the current cursor position, having
// dimensions |ylen|x|xlen|. See ncplane_box() for more information. The
// minimum box size is 2x2, and it cannot be drawn off-screen. |wchars| is an
// array of 6 wide characters: UL, UR, LL, LR, HL, VL.
API int ncdirect_box(struct ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr, const wchar_t* wchars,
int ylen, int xlen, unsigned ctlword);
// ncdirect_box() with the rounded box-drawing characters
API int ncdirect_rounded_box(struct ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr,
int ylen, int xlen, unsigned ctlword);
// ncdirect_box() with the double box-drawing characters
API int ncdirect_double_box(struct ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr,
int ylen, int xlen, unsigned ctlword);
// Release 'nc' and any associated resources. 0 on success, non-0 on failure.
API int ncdirect_stop(struct ncdirect* nc);

View File

@ -503,6 +503,9 @@ int ncdirect_cursor_right(struct ncdirect* nc, int num);
int ncdirect_cursor_down(struct ncdirect* nc, int num);
int ncdirect_hline_interp(struct ncdirect* n, const char* egc, int len, uint64_t h1, uint64_t h2);
int ncdirect_vline_interp(struct ncdirect* n, const char* egc, int len, uint64_t h1, uint64_t h2);
int ncdirect_box(struct ncdirect* n, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, const wchar_t* wchars, int ylen, int xlen, unsigned ctlword);
int ncdirect_rounded_box(struct ncdirect* n, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen, unsigned ctlword);
int ncdirect_double_box(struct ncdirect* n, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen, unsigned ctlword);
bool ncdirect_canopen_images(const struct ncdirect* n);
bool ncdirect_canutf8(const struct ncdirect* n);
nc_err_e ncdirect_render_image(struct ncdirect* n, const char* filename, ncalign_e align, ncblitter_e blitter, ncscale_e scale);

View File

@ -9,7 +9,7 @@
#include "notcurses/direct.h"
#include "internal.h"
int ncdirect_putstr(ncdirect* nc, uint64_t channels, const char* egc){
int ncdirect_putstr(ncdirect* nc, uint64_t channels, const char* utf8){
if(channels_fg_default_p(channels)){
if(ncdirect_fg_default(nc)){
return -1;
@ -24,7 +24,7 @@ int ncdirect_putstr(ncdirect* nc, uint64_t channels, const char* egc){
}else if(ncdirect_bg(nc, channels_bg(channels))){
return -1;
}
return fprintf(nc->ttyfp, "%s", egc);
return fprintf(nc->ttyfp, "%s", utf8);
}
int ncdirect_cursor_up(ncdirect* nc, int num){
@ -652,6 +652,123 @@ int ncdirect_vline_interp(ncdirect* n, const char* egc, int len,
return ret;
}
// wchars: wchar_t[6] mapping to UL, UR, BL, BR, HL, VL.
// they cannot be complex EGCs, but only a single wchar_t, alas.
int ncdirect_box(ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr, const wchar_t* wchars,
int ylen, int xlen, unsigned ctlword){
if(xlen < 2 || ylen < 2){
return -1;
}
char hl[WCHAR_MAX_UTF8BYTES + 1];
char vl[WCHAR_MAX_UTF8BYTES + 1];
unsigned edges;
edges = !(ctlword & NCBOXMASK_TOP) + !(ctlword & NCBOXMASK_LEFT);
if(edges >= box_corner_needs(ctlword)){
ncdirect_fg(n, channels_fg(ul));
ncdirect_bg(n, channels_bg(ul));
if(fprintf(n->ttyfp, "%lc", wchars[0]) < 0){
return -1;
}
}else{
ncdirect_cursor_right(n, 1);
}
mbstate_t ps = {};
size_t bytes;
if((bytes = wcrtomb(hl, wchars[4], &ps)) == (size_t)-1){
return -1;
}
hl[bytes] = '\0';
memset(&ps, 0, sizeof(ps));
if((bytes = wcrtomb(vl, wchars[5], &ps)) == (size_t)-1){
return -1;
}
vl[bytes] = '\0';
if(!(ctlword & NCBOXMASK_TOP)){ // draw top border, if called for
if(xlen > 2){
if(ncdirect_hline_interp(n, hl, xlen - 2, ul, ur) < 0){
return -1;
}
}
}else{
ncdirect_cursor_right(n, xlen - 2);
}
edges = !(ctlword & NCBOXMASK_TOP) + !(ctlword & NCBOXMASK_RIGHT);
if(edges >= box_corner_needs(ctlword)){
ncdirect_fg(n, channels_fg(ur));
ncdirect_bg(n, channels_bg(ur));
if(fprintf(n->ttyfp, "%lc", wchars[1]) < 0){
return -1;
}
ncdirect_cursor_left(n, xlen);
}else{
ncdirect_cursor_left(n, xlen - 1);
}
ncdirect_cursor_down(n, 1);
// middle rows (vertical lines)
if(ylen > 2){
if(!(ctlword & NCBOXMASK_LEFT)){
if(ncdirect_vline_interp(n, vl, ylen - 2, ul, ll) < 0){
return -1;
}
ncdirect_cursor_right(n, xlen - 2);
ncdirect_cursor_up(n, ylen - 3);
}else{
ncdirect_cursor_right(n, xlen - 1);
}
if(!(ctlword & NCBOXMASK_RIGHT)){
if(ncdirect_vline_interp(n, vl, ylen - 2, ur, lr) < 0){
return -1;
}
ncdirect_cursor_left(n, xlen);
}else{
ncdirect_cursor_left(n, xlen - 1);
}
}
ncdirect_cursor_down(n, 1);
// bottom line
edges = !(ctlword & NCBOXMASK_BOTTOM) + !(ctlword & NCBOXMASK_LEFT);
if(edges >= box_corner_needs(ctlword)){
ncdirect_fg(n, channels_fg(ll));
ncdirect_bg(n, channels_bg(ll));
if(fprintf(n->ttyfp, "%lc", wchars[2]) < 0){
return -1;
}
}else{
ncdirect_cursor_right(n, 1);
}
if(!(ctlword & NCBOXMASK_BOTTOM)){
if(xlen > 2){
if(ncdirect_hline_interp(n, hl, xlen - 2, ll, lr) < 0){
return -1;
}
}
}else{
ncdirect_cursor_right(n, xlen - 2);
}
edges = !(ctlword & NCBOXMASK_BOTTOM) + !(ctlword & NCBOXMASK_RIGHT);
if(edges >= box_corner_needs(ctlword)){
ncdirect_fg(n, channels_fg(lr));
ncdirect_bg(n, channels_bg(lr));
if(fprintf(n->ttyfp, "%lc", wchars[3]) < 0){
return -1;
}
}
return 0;
}
int ncdirect_rounded_box(ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr,
int ylen, int xlen, unsigned ctlword){
return ncdirect_box(n, ul, ur, ll, lr, L"╭╮╰╯─│", ylen, xlen, ctlword);
}
int ncdirect_double_box(ncdirect* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr,
int ylen, int xlen, unsigned ctlword){
return ncdirect_box(n, ul, ur, ll, lr, L"╔╗╚╝═║", ylen, xlen, ctlword);
}
// Can we load images? This requires being built against FFmpeg/OIIO.
bool ncdirect_canopen_images(const ncdirect* n){
(void)n;

View File

@ -826,6 +826,12 @@ int ffmpeg_log_level(ncloglevel_e level);
int term_setstyle(FILE* out, unsigned cur, unsigned targ, unsigned stylebit,
const char* ton, const char* toff);
// how many edges need touch a corner for it to be printed?
static inline unsigned
box_corner_needs(unsigned ctlword){
return (ctlword & NCBOXCORNER_MASK) >> NCBOXCORNER_SHIFT;
}
#ifdef __cplusplus
}
#endif

View File

@ -1668,12 +1668,6 @@ int ncplane_vline_interp(ncplane* n, const cell* c, int len,
return ret;
}
// how many edges need touch a corner for it to be printed?
static inline unsigned
box_corner_needs(unsigned ctlword){
return (ctlword & NCBOXCORNER_MASK) >> NCBOXCORNER_SHIFT;
}
int ncplane_box(ncplane* n, const cell* ul, const cell* ur,
const cell* ll, const cell* lr, const cell* hl,
const cell* vl, int ystop, int xstop,

View File

@ -34,6 +34,20 @@ int main(void){
}
}
}
printf("\n");
uint64_t ul, ur, ll, lr;
ul = ur = ll = lr = 0;
channels_set_fg_rgb(&ul, 0xff, 0x0, 0xff);
channels_set_fg_rgb(&ur, 0x0, 0xff, 0x0);
channels_set_fg_rgb(&ll, 0x0, 0x0, 0xff);
channels_set_fg_rgb(&lr, 0xff, 0x0, 0x0);
if(ncdirect_rounded_box(n, ul, ur, ll, lr, 10, 10, 0) < 0){
return EXIT_FAILURE;
}
ncdirect_cursor_up(n, 9);
if(ncdirect_double_box(n, ul, ur, ll, lr, 10, 20, 0) < 0){
return EXIT_FAILURE;
}
if(ncdirect_stop(n)){
return EXIT_FAILURE;
}