From afc06a0271974feee1886207cc7c906dc4845d3f Mon Sep 17 00:00:00 2001 From: nick black Date: Tue, 25 Aug 2020 08:26:07 -0400 Subject: [PATCH 1/3] reader PoC: accept -hs for horizontal scrolling #839 --- src/lib/reader.c | 6 +++--- src/poc/reader.cpp | 27 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/lib/reader.c b/src/lib/reader.c index 3c18e3116..1066e0bb7 100644 --- a/src/lib/reader.c +++ b/src/lib/reader.c @@ -89,10 +89,10 @@ int ncreader_move_left(ncreader* n){ if(y == 0){ return -1; // no move possible } - viewx = n->textarea->lenx - 1; // FIXME find end of particular row + viewx = n->ncp->lenx - 1; // FIXME find end of particular row --y; - textx = viewx; - n->xproject = 0; + textx = n->textarea->x - 1; + n->xproject = n->textarea->x - n->ncp->x; }else{ // if we're on the first column of the viewarea, but not the first column // of the textarea, we must be able to scroll to the left. do so. diff --git a/src/poc/reader.cpp b/src/poc/reader.cpp index d9e93dbc7..fafdcb10e 100644 --- a/src/poc/reader.cpp +++ b/src/poc/reader.cpp @@ -7,11 +7,26 @@ using namespace ncpp; -auto main() -> int { +auto usage(const char* argv0, int ret) -> void { + std::cerr << "usage: " << argv0 << " [ -hs ]" << std::endl; + exit(ret); +} + +auto main(int argc, const char** argv) -> int { if(!setlocale(LC_ALL, "")){ std::cout << "Error setting locale\n"; return EXIT_FAILURE; } + bool horscroll = false; + if(argc == 2){ + if(strcmp(argv[1], "-hs") == 0){ + horscroll = true; + }else{ + usage(argv[0], EXIT_FAILURE); + } + }else if(argc > 2){ + usage(argv[0], EXIT_FAILURE); + } notcurses_options nopts{}; nopts.flags = NCOPTION_INHIBIT_SETLOCALE; NotCurses nc(nopts); @@ -22,6 +37,7 @@ auto main() -> int { opts.physrows = dimy / 8; opts.physcols = dimx / 2; opts.egc = "░"; + opts.flags = horscroll ? NCREADER_OPTION_HORSCROLL : 0; // FIXME c++ is crashing //Reader nr(nc, 0, 0, &opts); auto nr = ncreader_create(**n, 2, 2, &opts); @@ -38,8 +54,15 @@ auto main() -> int { break; } int y, x; - ncplane_cursor_yx(ncreader_plane(nr), &y, &x); + struct ncplane* ncp = ncreader_plane(nr); + ncplane_cursor_yx(ncp, &y, &x); nc.cursor_enable(y + 2, x + 2); + int ncpy, ncpx; + ncplane_cursor_yx(ncp, &ncpy, &ncpx); + int tgeomy, tgeomx, vgeomy, vgeomx; + (*n)->get_dim(&tgeomy, &tgeomx); + ncplane_dim_yx(ncp, &vgeomy, &vgeomx); + (*n)->printf(0, 0, "Cursor: %d/%d Viewgeom: %d/%d Textgeom: %d/%d", ncpy, ncpx, vgeomy, vgeomx, tgeomy, tgeomx); nc.render(); } nc.render(); From 70a28feb638ec8c4a9dd41b02fc3a47598014a55 Mon Sep 17 00:00:00 2001 From: nick black Date: Tue, 25 Aug 2020 08:55:30 -0400 Subject: [PATCH 2/3] ncreader: horizontal scrolling mostly works #839 --- NEWS.md | 1 + USAGE.md | 6 ++++++ doc/man/man3/notcurses_plane.3.md | 6 ++++++ include/notcurses/notcurses.h | 4 ++++ python/src/notcurses/build_notcurses.py | 2 ++ src/lib/notcurses.c | 8 ++++++++ src/lib/reader.c | 11 ++++++++--- src/poc/reader.cpp | 9 ++++++--- 8 files changed, 41 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9a2b15519..dd88a5fd6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,6 +17,7 @@ rearrangements of Notcurses. * `int ncreader_move_up(struct ncreader* n);` * `int ncreader_move_down(struct ncreader* n);` * `int ncreader_write_egc(struct ncreader* n, const char* egc);` + * Added `ncplane_above()` and `notcurses_bottom()`. * 1.6.17 (2020-08-22) * `ncdirect_flush()` now takes a `const struct ncdirect*`. diff --git a/USAGE.md b/USAGE.md index ca58278fb..209ab815c 100644 --- a/USAGE.md +++ b/USAGE.md @@ -214,6 +214,9 @@ Utility functions operating on the toplevel `notcurses` object include: // Return the topmost ncplane, of which there is always at least one. struct ncplane* notcurses_top(struct notcurses* n); +// Return the bottommost ncplane, of which there is always at least one. +struct ncplane* notcurses_bottom(struct notcurses* n); + // Return our current idea of the terminal dimensions in rows and cols. static inline void notcurses_term_dim_yx(const struct notcurses* n, int* restrict rows, @@ -826,6 +829,9 @@ int ncplane_move_above(struct ncplane* restrict n, struct ncplane* restrict abov // Return the ncplane below this one, or NULL if this is at the stack's bottom. struct ncplane* ncplane_below(struct ncplane* n); + +// Return the ncplane above this one, or NULL if this is at the stack's top. +struct ncplane* ncplane_above(struct ncplane* n); ``` Each plane holds a user pointer which can be retrieved and set (or ignored). In diff --git a/doc/man/man3/notcurses_plane.3.md b/doc/man/man3/notcurses_plane.3.md index 1eaff6a66..c37148028 100644 --- a/doc/man/man3/notcurses_plane.3.md +++ b/doc/man/man3/notcurses_plane.3.md @@ -10,6 +10,10 @@ notcurses_plane - operations on ncplanes **#include ** +**struct ncplane* notcurses_top(struct notcurses* n);** + +**struct ncplane* notcurses_bottom(struct notcurses* n);** + **struct ncplane* ncplane_new(struct notcurses* nc, int rows, int cols, int yoff, int xoff, void* opaque);** **struct ncplane* ncplane_new_named(struct notcurses* nc, int rows, int cols, int yoff, int xoff, void* opaque, const char* name);** @@ -52,6 +56,8 @@ notcurses_plane - operations on ncplanes **struct ncplane* ncplane_below(struct ncplane* n);** +**struct ncplane* ncplane_above(struct ncplane* n);** + **char* ncplane_at_cursor(struct ncplane* n, uint16_t* stylemask, uint64_t* channels);** **int ncplane_at_cursor_cell(struct ncplane* n, cell* c);** diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index ef86dfd88..2b590a70f 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -858,6 +858,9 @@ API int notcurses_render_to_file(struct notcurses* nc, FILE* fp); // Return the topmost ncplane, of which there is always at least one. API struct ncplane* notcurses_top(struct notcurses* n); +// Return the bottommost ncplane, of which there is always at least one. +API struct ncplane* notcurses_bottom(struct notcurses* n); + // Destroy all ncplanes other than the stdplane. API void notcurses_drop_planes(struct notcurses* nc); @@ -1197,6 +1200,7 @@ API int ncplane_move_below(struct ncplane* RESTRICT n, // Return the plane below this one, or NULL if this is at the bottom. API struct ncplane* ncplane_below(struct ncplane* n); +API struct ncplane* ncplane_above(struct ncplane* n); // Rotate the plane π/2 radians clockwise or counterclockwise. This cannot // be performed on arbitrary planes, because glyphs cannot be arbitrarily diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index 77d62f0f1..5f33e2b13 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -62,6 +62,7 @@ int ncplane_set_base_cell(struct ncplane* ncp, const cell* c); int ncplane_set_base(struct ncplane* ncp, const char* egc, uint32_t styles, uint64_t channels); int ncplane_base(struct ncplane* ncp, cell* c); struct ncplane* notcurses_top(struct notcurses* n); +struct ncplane* notcurses_bottom(struct notcurses* n); void notcurses_drop_planes(struct notcurses* nc); int notcurses_refresh(struct notcurses* n, int* restrict y, int* restrict x); struct ncplane* ncplane_reparent(struct ncplane* n, struct ncplane* newparent); @@ -102,6 +103,7 @@ void ncplane_move_bottom(struct ncplane* n); int ncplane_move_below(struct ncplane* restrict n, struct ncplane* restrict below); int ncplane_move_above(struct ncplane* restrict n, struct ncplane* restrict above); struct ncplane* ncplane_below(struct ncplane* n); +struct ncplane* ncplane_above(struct ncplane* n); char* notcurses_at_yx(struct notcurses* nc, int yoff, int xoff, uint16_t* stylemask, uint64_t* channels); char* ncplane_at_cursor(struct ncplane* n, uint16_t* stylemask, uint64_t* channels); char* ncplane_at_yx(const struct ncplane* n, int y, int x, uint16_t* stylemask, uint64_t* channels); diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 083a360fc..d95784c6c 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1888,10 +1888,18 @@ ncplane* notcurses_top(notcurses* n){ return n->top; } +ncplane* notcurses_bottom(notcurses* n){ + return n->bottom; +} + ncplane* ncplane_below(ncplane* n){ return n->below; } +ncplane* ncplane_above(ncplane* n){ + return n->above; +} + #define SET_BTN_EVENT_MOUSE "1002" #define SET_FOCUS_EVENT_MOUSE "1004" #define SET_SGR_MODE_MOUSE "1006" diff --git a/src/lib/reader.c b/src/lib/reader.c index 1066e0bb7..0c7f92068 100644 --- a/src/lib/reader.c +++ b/src/lib/reader.c @@ -181,10 +181,15 @@ int ncreader_write_egc(ncreader* n, const char* egc){ logerror(n->ncp->nc, "Fed illegal UTF-8 [%s]\n", egc); return -1; } - if(n->textarea->x >= n->textarea->lenx - (cols + 1)){ + if(n->textarea->x >= n->textarea->lenx - cols){ if(n->horscroll){ - // FIXME resize + if(ncplane_resize_simple(n->textarea, n->textarea->leny, n->textarea->lenx + cols)){ + return -1; + } + ++n->xproject; } + }else if(n->ncp->x >= n->ncp->lenx){ + ++n->xproject; } // use ncplane_putegc on both planes because it'll get cursor movement right if(ncplane_putegc(n->textarea, egc, NULL) < 0){ @@ -193,7 +198,6 @@ int ncreader_write_egc(ncreader* n, const char* egc){ if(ncplane_putegc(n->ncp, egc, NULL) < 0){ return -1; } - // FIXME pan right if necessary return 0; } @@ -225,6 +229,7 @@ bool ncreader_offer_input(ncreader* n, const ncinput* ni){ } ncplane_putegc_yx(n->textarea, y, x, "", NULL); ncplane_cursor_move_yx(n->textarea, y, x); + ncplane_cursor_move_yx(n->ncp, n->ncp->y, n->ncp->x - 1); ncreader_redraw(n); return true; } diff --git a/src/poc/reader.cpp b/src/poc/reader.cpp index fafdcb10e..ed9a71f44 100644 --- a/src/poc/reader.cpp +++ b/src/poc/reader.cpp @@ -56,13 +56,16 @@ auto main(int argc, const char** argv) -> int { int y, x; struct ncplane* ncp = ncreader_plane(nr); ncplane_cursor_yx(ncp, &y, &x); - nc.cursor_enable(y + 2, x + 2); + nc.cursor_enable(y + 2, 2 + (x >= ncplane_dim_x(ncp) ? ncplane_dim_x(ncp) - 1 : x)); int ncpy, ncpx; ncplane_cursor_yx(ncp, &ncpy, &ncpx); + struct ncplane* tplane = ncplane_above(ncp); int tgeomy, tgeomx, vgeomy, vgeomx; - (*n)->get_dim(&tgeomy, &tgeomx); + ncplane_dim_yx(tplane, &tgeomy, &tgeomx); ncplane_dim_yx(ncp, &vgeomy, &vgeomx); - (*n)->printf(0, 0, "Cursor: %d/%d Viewgeom: %d/%d Textgeom: %d/%d", ncpy, ncpx, vgeomy, vgeomx, tgeomy, tgeomx); + (*n)->printf(0, 0, "Scroll: %lc Cursor: %d/%d Viewgeom: %d/%d Textgeom: %d/%d", + horscroll ? L'✔' : L'🗴', + ncpy, ncpx, vgeomy, vgeomx, tgeomy, tgeomx); nc.render(); } nc.render(); From 902d8472e8913576b01834c2cfe8f57b0530cb30 Mon Sep 17 00:00:00 2001 From: nick black Date: Tue, 25 Aug 2020 09:09:48 -0400 Subject: [PATCH 3/3] zoo: break up lines --- src/demo/zoo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/demo/zoo.c b/src/demo/zoo.c index 27ccaa9d5..bbb7a57fe 100644 --- a/src/demo/zoo.c +++ b/src/demo/zoo.c @@ -189,7 +189,7 @@ selector_run(struct notcurses* nc, struct ncreader* reader, struct ncselector* s "Notcurses provides several widgets to quickly build vivid TUIs.\n\n" "This NCReader widget facilitates free-form text entry complete with readline-style bindings.\n\n" "NCSelector allows a single option to be selected from a list.\n\n" - "NCFdplane streams a file descriptor, while NCSubproc spawns a subprocess and streams its output. "; + "NCFdplane streams a file descriptor, while NCSubproc spawns a subprocess and streams its output.\n\n"; int ret = 0, dimy, dimx; ncplane_dim_yx(notcurses_stdplane(nc), &dimy, &dimx); const int centery = (dimy - ncplane_dim_y(ncreader_plane(reader))) / 2;