From e613b81b829317c66d198d3b52fdbdcd6a06f8bd Mon Sep 17 00:00:00 2001 From: nick black Date: Mon, 4 Oct 2021 05:16:58 -0400 Subject: [PATCH] ncplane_move_family_{below, above}() with unit tests #2232 --- include/notcurses/notcurses.h | 55 +++++++++++++----------- src/lib/notcurses.c | 80 +++++++++++++++++------------------ src/tests/zaxis.cpp | 67 +++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 65 deletions(-) diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index bfcf84a03..f53b54e89 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -1629,6 +1629,22 @@ ncplane_descendant_p(const struct ncplane* n, const struct ncplane* ancestor){ return 1; } +// Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'. +// Returns non-zero if 'n' is already in the desired location. 'n' and +// 'above' must not be the same plane. If 'above' is NULL, 'n' is moved +// to the bottom of its pile. +API int ncplane_move_above(struct ncplane* RESTRICT n, + struct ncplane* RESTRICT above) + __attribute__ ((nonnull (1))); + +// Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'. +// Returns non-zero if 'n' is already in the desired location. 'n' and +// 'below' must not be the same plane. If 'below' is NULL, 'n' is moved to +// the top of its pile. +API int ncplane_move_below(struct ncplane* RESTRICT n, + struct ncplane* RESTRICT below) + __attribute__ ((nonnull (1))); + // Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or // bottom. FIXME these both become static inline wrappers around // ncplane_move_below() and ncplane_move_above() in ABI3. @@ -1638,38 +1654,27 @@ API void ncplane_move_bottom(struct ncplane* n) __attribute__ ((nonnull (1))); // Splice ncplane 'n' and its bound planes out of the z-buffer, and reinsert -// them at the top or bottom. Relative order will be maintained between the +// them above or below 'targ'. Relative order will be maintained between the // reinserted planes. For a plane E bound to C, with z-ordering A B C D E, // moving the C family to the top results in C E A B D, while moving it to // the bottom results in A B D C E. -API void ncplane_move_family_top(struct ncplane* n) - __attribute__ ((nonnull (1))); -API void ncplane_move_family_bottom(struct ncplane* n) +API void ncplane_move_family_above(struct ncplane* n, struct ncplane* targ) __attribute__ ((nonnull (1))); -// Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'. -// Returns non-zero if 'n' is already in the desired location. 'n' and -// 'above' must not be the same plane. If 'above' is NULL, 'n' is moved -// to the bottom of its pile. -API int ncplane_move_above(struct ncplane* RESTRICT n, - struct ncplane* RESTRICT above) - __attribute__ ((nonnull (1, 2))); +API void ncplane_move_family_below(struct ncplane* n, struct ncplane* targ) + __attribute__ ((nonnull (1))); -// Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'. -// Returns non-zero if 'n' is already in the desired location. 'n' and -// 'below' must not be the same plane. If 'below' is NULL, 'n' is moved to -// the top of its pile. -API int ncplane_move_below(struct ncplane* RESTRICT n, - struct ncplane* RESTRICT below) - __attribute__ ((nonnull (1, 2))); +__attribute__ ((nonnull (1))) +static inline void +ncplane_move_family_top(struct ncplane* n){ + ncplane_move_family_below(n, NULL); +} -API void ncplane_move_family_above(struct ncplane* RESTRICT n, - struct ncplane* RESTRICT above) - __attribute__ ((nonnull (1, 2))); - -API void ncplane_move_family_below(struct ncplane* RESTRICT n, - struct ncplane* RESTRICT below) - __attribute__ ((nonnull (1, 2))); +__attribute__ ((nonnull (1))) +static inline void +ncplane_move_family_bottom(struct ncplane* n){ + ncplane_move_family_above(n, NULL); +} // Return the plane below this one, or NULL if this is at the bottom. API struct ncplane* ncplane_below(struct ncplane* n) diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 60b2bde01..3a9659009 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1358,6 +1358,10 @@ int ncplane_move_above(ncplane* restrict n, ncplane* restrict above){ if(n == above){ return -1; } + if(above == NULL){ + ncplane_move_bottom(n); + return 0; + } if(ncplane_pile(n) != ncplane_pile(above)){ // can't move among piles return -1; } @@ -1389,6 +1393,10 @@ int ncplane_move_below(ncplane* restrict n, ncplane* restrict below){ if(n == below){ return -1; } + if(below == NULL){ + ncplane_move_top(n); + return 0; + } if(ncplane_pile(n) != ncplane_pile(below)){ // can't move among piles return -1; } @@ -1444,39 +1452,10 @@ void ncplane_move_bottom(ncplane* n){ } } -void ncplane_move_family_top(ncplane* n){ - ncplane* below = ncplane_below(n); - ncplane_move_top(n); - // traverse the planes below n, until we hit NULL. do the planes below n - // first, so that we know the bottommost element of our new ensplicification. - // at this point, n is the topmost plane, and we're inserting below it. - ncplane* targ = n; - while(below){ - ncplane* tmp = ncplane_below(below); - if(ncplane_descendant_p(below, n)){ - ncplane_move_below(below, targ); - targ = below; - } - below = tmp; - } - // n remains the topmost plane, and we're inserting above it. we have to be - // careful this time not to cross into any we moved below n. - const ncplane* bottommost = targ; - targ = n; +// if above is NULL, we're moving to the bottom +void ncplane_move_family_above(ncplane* restrict n, ncplane* restrict bpoint){ ncplane* above = ncplane_above(n); - while(above && above != bottommost){ - ncplane* tmp = ncplane_above(above); - if(ncplane_descendant_p(above, n)){ - ncplane_move_above(above, targ); - targ = above; - } - above = tmp; - } -} - -void ncplane_move_family_bottom(ncplane* n){ - ncplane* above = ncplane_above(n); - ncplane_move_bottom(n); + ncplane_move_above(n, bpoint); // traverse the planes above n, until we hit NULL. do the planes above n // first, so that we know the topmost element of our new ensplicification. // at this point, n is the bottommost plane, and we're inserting above it. @@ -1504,14 +1483,35 @@ void ncplane_move_family_bottom(ncplane* n){ } } -void ncplane_move_family_above(ncplane* restrict n, ncplane* restrict above){ - ncplane_move_above(n, above); - // FIXME walk above and below, moving descendants -} - -void ncplane_move_family_below(ncplane* restrict n, ncplane* restrict below){ - ncplane_move_below(n, below); - // FIXME walk above and below, moving descendants +// if below is NULL, we're moving to the top +void ncplane_move_family_below(ncplane* restrict n, ncplane* restrict bpoint){ + ncplane* below = ncplane_below(n); + ncplane_move_below(n, bpoint); + // traverse the planes below n, until we hit NULL. do the planes below n + // first, so that we know the bottommost element of our new ensplicification. + // we're inserting below n... + ncplane* targ = n; + while(below){ + ncplane* tmp = ncplane_below(below); + if(ncplane_descendant_p(below, n)){ + ncplane_move_below(below, targ); + targ = below; + } + below = tmp; + } + // n remains the topmost plane, and we're inserting above it. we have to be + // careful this time not to cross into any we moved below n. + const ncplane* bottommost = targ; + targ = n; + ncplane* above = ncplane_above(n); + while(above && above != bottommost){ + ncplane* tmp = ncplane_above(above); + if(ncplane_descendant_p(above, n)){ + ncplane_move_above(above, targ); + targ = above; + } + above = tmp; + } } void ncplane_cursor_yx(const ncplane* n, int* y, int* x){ diff --git a/src/tests/zaxis.cpp b/src/tests/zaxis.cpp index 371512b7c..047446223 100644 --- a/src/tests/zaxis.cpp +++ b/src/tests/zaxis.cpp @@ -168,5 +168,72 @@ TEST_CASE("ZAxis") { CHECK(0 == notcurses_render(nc_)); } + SUBCASE("FamilyTop") { + struct ncplane_options nopts{}; + nopts.rows = nopts.cols = 1; + auto a = ncplane_create(n_, &nopts); + REQUIRE(nullptr != a); + auto b = ncplane_create(n_, &nopts); + REQUIRE(nullptr != b); + auto c = ncplane_create(n_, &nopts); + REQUIRE(nullptr != c); + auto d = ncplane_create(n_, &nopts); + REQUIRE(nullptr != d); + auto e = ncplane_create(c, &nopts); + REQUIRE(nullptr != e); + ncplane_move_below(b, a); + ncplane_move_below(c, b); + ncplane_move_below(d, c); + ncplane_move_below(e, d); + CHECK(ncpile_top(n_) == a); + CHECK(ncplane_below(a) == b); + CHECK(ncplane_below(b) == c); + CHECK(ncplane_below(c) == d); + CHECK(ncplane_below(d) == e); + ncplane_move_family_top(c); + CHECK(ncpile_top(n_) == c); + CHECK(ncplane_below(c) == e); + CHECK(ncplane_below(e) == a); + CHECK(ncplane_below(a) == b); + CHECK(ncplane_below(b) == d); + CHECK(ncpile_bottom(n_) == n_); + } + + SUBCASE("FamilyBottom") { + struct ncplane_options nopts{}; + nopts.rows = nopts.cols = 1; + auto a = ncplane_create(n_, &nopts); + REQUIRE(nullptr != a); + auto b = ncplane_create(n_, &nopts); + REQUIRE(nullptr != b); + auto c = ncplane_create(n_, &nopts); + REQUIRE(nullptr != c); + auto d = ncplane_create(n_, &nopts); + REQUIRE(nullptr != d); + auto e = ncplane_create(c, &nopts); + REQUIRE(nullptr != e); + ncplane_move_below(b, a); + ncplane_move_below(c, b); + ncplane_move_below(d, c); + ncplane_move_below(e, d); + // ABCDEs, E is bound to C + CHECK(ncpile_top(n_) == a); + CHECK(ncplane_below(a) == b); + CHECK(ncplane_below(b) == c); + CHECK(ncplane_below(c) == d); + CHECK(ncplane_below(d) == e); + CHECK(ncplane_below(e) == n_); + CHECK(ncplane_bottom(n_) == n_); + ncplane_move_family_bottom(c); + // ABDsCE, E is bound to C (FIXME have ABDEsC) + CHECK(ncpile_top(n_) == a); + CHECK(ncplane_below(a) == b); + CHECK(ncplane_below(b) == d); + CHECK(ncplane_below(d) == e); + CHECK(ncplane_below(e) == n_); + CHECK(ncplane_below(n_) == c); + CHECK(ncpile_bottom(n_) == c); + } + CHECK(0 == notcurses_stop(nc_)); }