mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
Fix up some subtle pile issues
ncplane_destroy() needs to call ncplane_reparent_family(), not ncplane_reparent() as it was doing (closes #1291). ->absy and ->absx actually are absolute; return them directly for an O(1) ncplane_abs_yx() (down from O(N), huzzah). Add some unit tests related to destroying and reparenting (#1286). Add ncplane_abs_y() and ncplane_abs_x(), document them, etc.
This commit is contained in:
parent
7932859b50
commit
1108ebb5b6
3
NEWS.md
3
NEWS.md
@ -9,8 +9,7 @@ rearrangements of Notcurses.
|
|||||||
`ncplane_putnstr_yx()` now return the number of columns output, as
|
`ncplane_putnstr_yx()` now return the number of columns output, as
|
||||||
long documented (they were mistakenly returning the number of bytes).
|
long documented (they were mistakenly returning the number of bytes).
|
||||||
* `ncplane_abs_yx()` has been added, returning the absolute coordinates of
|
* `ncplane_abs_yx()` has been added, returning the absolute coordinates of
|
||||||
the plane's origin (i.e. coordinates relative to its pile). This call is
|
the plane's origin (i.e. coordinates relative to its pile).
|
||||||
O(N) on the binding depth of the plane in question.
|
|
||||||
|
|
||||||
* 2.1.4 (2021-01-03):
|
* 2.1.4 (2021-01-03):
|
||||||
* Direct mode now supports `NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS`, and by
|
* Direct mode now supports `NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS`, and by
|
||||||
|
6
USAGE.md
6
USAGE.md
@ -808,9 +808,11 @@ void ncplane_yx(const struct ncplane* n, int* restrict y, int* restrict x);
|
|||||||
int ncplane_y(const struct ncplane* n);
|
int ncplane_y(const struct ncplane* n);
|
||||||
int ncplane_x(const struct ncplane* n);
|
int ncplane_x(const struct ncplane* n);
|
||||||
|
|
||||||
// Get the origin of plane 'n' relative to its pile. This is O(N) on the
|
// Get the origin of plane 'n' relative to its pile. Either or both of 'x' and
|
||||||
// binding depth of 'n'. Either or both of 'x' and y' may be NULL.
|
// 'y' may be NULL.
|
||||||
void ncplane_abs_yx(const struct ncplane* n, int* y, int* x);
|
void ncplane_abs_yx(const struct ncplane* n, int* y, int* x);
|
||||||
|
int ncplane_abs_y(const struct ncplane* n);
|
||||||
|
int ncplane_abs_x(const struct ncplane* n);
|
||||||
|
|
||||||
// Return the dimensions of this ncplane.
|
// Return the dimensions of this ncplane.
|
||||||
void ncplane_dim_yx(struct ncplane* n, int* restrict rows, int* restrict cols);
|
void ncplane_dim_yx(struct ncplane* n, int* restrict rows, int* restrict cols);
|
||||||
|
@ -63,6 +63,10 @@ typedef struct ncplane_options {
|
|||||||
|
|
||||||
**void ncplane_abs_yx(const struct ncplane* ***n***, int* ***y***, int* ***x***);**
|
**void ncplane_abs_yx(const struct ncplane* ***n***, int* ***y***, int* ***x***);**
|
||||||
|
|
||||||
|
**int ncplane_abs_y(const struct ncplane* ***n***);**
|
||||||
|
|
||||||
|
**int ncplane_abs_x(const struct ncplane* ***n***);**
|
||||||
|
|
||||||
**struct ncplane* ncplane_parent(struct ncplane* ***n***);**
|
**struct ncplane* ncplane_parent(struct ncplane* ***n***);**
|
||||||
|
|
||||||
**const struct ncplane* ncplane_parent_const(const struct ncplane* ***n***);**
|
**const struct ncplane* ncplane_parent_const(const struct ncplane* ***n***);**
|
||||||
|
@ -1317,9 +1317,11 @@ API void ncplane_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
|
|||||||
API int ncplane_y(const struct ncplane* n);
|
API int ncplane_y(const struct ncplane* n);
|
||||||
API int ncplane_x(const struct ncplane* n);
|
API int ncplane_x(const struct ncplane* n);
|
||||||
|
|
||||||
// Get the origin of plane 'n' relative to its pile. This is O(N) on the
|
// Get the origin of plane 'n' relative to its pile. Either or both of 'x' and
|
||||||
// binding depth of 'n'. Either or both of 'x' and y' may be NULL.
|
// 'y' may be NULL.
|
||||||
API void ncplane_abs_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
|
API void ncplane_abs_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
|
||||||
|
API int ncplane_abs_y(const struct ncplane* n);
|
||||||
|
API int ncplane_abs_x(const struct ncplane* n);
|
||||||
|
|
||||||
// Get the plane to which the plane 'n' is bound, if any.
|
// Get the plane to which the plane 'n' is bound, if any.
|
||||||
API struct ncplane* ncplane_parent(struct ncplane* n);
|
API struct ncplane* ncplane_parent(struct ncplane* n);
|
||||||
|
@ -67,7 +67,7 @@ typedef struct ncplane {
|
|||||||
// ncplane_yx() etc. use coordinates relative to the plane to which this
|
// ncplane_yx() etc. use coordinates relative to the plane to which this
|
||||||
// plane is bound, but absx/absy are always relative to the terminal origin.
|
// plane is bound, but absx/absy are always relative to the terminal origin.
|
||||||
// they must thus be translated by any function which moves a parent plane.
|
// they must thus be translated by any function which moves a parent plane.
|
||||||
int absx, absy; // origin of the plane relative to the screen
|
int absx, absy; // origin of the plane relative to the pile's origin
|
||||||
int lenx, leny; // size of the plane, [0..len{x,y}) is addressable
|
int lenx, leny; // size of the plane, [0..len{x,y}) is addressable
|
||||||
egcpool pool; // attached storage pool for UTF-8 EGCs
|
egcpool pool; // attached storage pool for UTF-8 EGCs
|
||||||
uint64_t channels; // works the same way as cells
|
uint64_t channels; // works the same way as cells
|
||||||
|
@ -630,6 +630,7 @@ int ncplane_destroy(ncplane* ncp){
|
|||||||
logerror(ncplane_notcurses(ncp), "Won't destroy standard plane\n");
|
logerror(ncplane_notcurses(ncp), "Won't destroy standard plane\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
//notcurses_debug(ncplane_notcurses(ncp), stderr);
|
||||||
if(ncp->above){
|
if(ncp->above){
|
||||||
ncp->above->below = ncp->below;
|
ncp->above->below = ncp->below;
|
||||||
}else{
|
}else{
|
||||||
@ -649,7 +650,7 @@ int ncplane_destroy(ncplane* ncp){
|
|||||||
struct ncplane* bound = ncp->blist;
|
struct ncplane* bound = ncp->blist;
|
||||||
while(bound){
|
while(bound){
|
||||||
struct ncplane* tmp = bound->bnext;
|
struct ncplane* tmp = bound->bnext;
|
||||||
if(ncplane_reparent(bound, ncp->boundto) == NULL){
|
if(ncplane_reparent_family(bound, ncp->boundto) == NULL){
|
||||||
ret = -1;
|
ret = -1;
|
||||||
}
|
}
|
||||||
bound = tmp;
|
bound = tmp;
|
||||||
@ -2147,17 +2148,21 @@ const notcurses* ncplane_notcurses_const(const ncplane* n){
|
|||||||
return ncplane_pile_const(n)->nc;
|
return ncplane_pile_const(n)->nc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
int ncplane_abs_y(const ncplane* n){
|
||||||
ncplane_abs_yx_recurse(const ncplane* n, int* RESTRICT y, int* RESTRICT x){
|
return n->absy; // FIXME adjust for margins?
|
||||||
if(n->boundto != n){
|
}
|
||||||
ncplane_translate(n, n->boundto, y, x);
|
|
||||||
ncplane_abs_yx_recurse(n->boundto, y, x);
|
int ncplane_abs_x(const ncplane* n){
|
||||||
}
|
return n->absx; // FIXME adjust for margins?
|
||||||
}
|
}
|
||||||
|
|
||||||
void ncplane_abs_yx(const ncplane* n, int* RESTRICT y, int* RESTRICT x){
|
void ncplane_abs_yx(const ncplane* n, int* RESTRICT y, int* RESTRICT x){
|
||||||
ncplane_yx(n, y, x);
|
if(y){
|
||||||
ncplane_abs_yx_recurse(n->boundto, y, x);
|
*y = ncplane_abs_y(n);
|
||||||
|
}
|
||||||
|
if(x){
|
||||||
|
*x = ncplane_abs_x(n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ncplane* ncplane_parent(ncplane* n){
|
ncplane* ncplane_parent(ncplane* n){
|
||||||
@ -2218,6 +2223,7 @@ ncplane* ncplane_reparent(ncplane* n, ncplane* newparent){
|
|||||||
if(n->boundto == newparent){
|
if(n->boundto == newparent){
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
//notcurses_debug(ncplane_notcurses(n), stderr);
|
||||||
if(n->blist){
|
if(n->blist){
|
||||||
if(n->boundto == n){ // children become new root planes
|
if(n->boundto == n){ // children become new root planes
|
||||||
ncplane* lastlink;
|
ncplane* lastlink;
|
||||||
@ -2244,7 +2250,7 @@ ncplane* ncplane_reparent(ncplane* n, ncplane* newparent){
|
|||||||
}
|
}
|
||||||
n->blist = NULL;
|
n->blist = NULL;
|
||||||
}
|
}
|
||||||
//notcurses_debug(ncplane_notcurses(n), stderr);
|
// FIXME would be nice to skip ncplane_descendant_p() on this call...:/
|
||||||
return ncplane_reparent_family(n, newparent);
|
return ncplane_reparent_family(n, newparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2292,14 +2298,10 @@ ncplane* ncplane_reparent_family(ncplane* n, ncplane* newparent){
|
|||||||
if(ncplane_descendant_p(newparent, n)){
|
if(ncplane_descendant_p(newparent, n)){
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
//notcurses_debug(ncplane_notcurses(n), stderr);
|
||||||
if(n->boundto == newparent){ // no-op
|
if(n->boundto == newparent){ // no-op
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
// if we are not a root plane, adjust our origin
|
|
||||||
if(n->boundto != n){
|
|
||||||
n->absx -= n->boundto->absx;
|
|
||||||
n->absy -= n->boundto->absy;
|
|
||||||
}
|
|
||||||
if(n->bprev){ // extract from sibling list
|
if(n->bprev){ // extract from sibling list
|
||||||
if( (*n->bprev = n->bnext) ){
|
if( (*n->bprev = n->bnext) ){
|
||||||
n->bnext->bprev = n->bprev;
|
n->bnext->bprev = n->bprev;
|
||||||
@ -2321,8 +2323,6 @@ ncplane* ncplane_reparent_family(ncplane* n, ncplane* newparent){
|
|||||||
pthread_mutex_unlock(&ncplane_notcurses(n)->pilelock);
|
pthread_mutex_unlock(&ncplane_notcurses(n)->pilelock);
|
||||||
splice_zaxis_recursive(n);
|
splice_zaxis_recursive(n);
|
||||||
}else{ // establish ourselves as a sibling of new parent's children
|
}else{ // establish ourselves as a sibling of new parent's children
|
||||||
n->absx += n->boundto->absx;
|
|
||||||
n->absy += n->boundto->absy;
|
|
||||||
if( (n->bnext = newparent->blist) ){
|
if( (n->bnext = newparent->blist) ){
|
||||||
n->bnext->bprev = &n->bnext;
|
n->bnext->bprev = &n->bnext;
|
||||||
}
|
}
|
||||||
|
@ -246,12 +246,14 @@ TEST_CASE("Piles") {
|
|||||||
CHECK(30 == x);
|
CHECK(30 == x);
|
||||||
CHECK(10 == ncplane_y(gen3));
|
CHECK(10 == ncplane_y(gen3));
|
||||||
CHECK(10 == ncplane_x(gen3));
|
CHECK(10 == ncplane_x(gen3));
|
||||||
|
notcurses_debug(nc_, stderr);
|
||||||
CHECK(nullptr != ncplane_reparent(gen1, gen2));
|
CHECK(nullptr != ncplane_reparent(gen1, gen2));
|
||||||
|
notcurses_debug(nc_, stderr);
|
||||||
ncplane_abs_yx(gen2, &y, &x); // gen2 is now the parent
|
ncplane_abs_yx(gen2, &y, &x); // gen2 is now the parent
|
||||||
CHECK(20 == y);
|
CHECK(20 == y);
|
||||||
CHECK(20 == x);
|
CHECK(20 == x);
|
||||||
CHECK(20 == ncplane_y(gen1));
|
CHECK(20 == ncplane_y(gen2));
|
||||||
CHECK(20 == ncplane_x(gen1));
|
CHECK(20 == ncplane_x(gen2));
|
||||||
ncplane_abs_yx(gen1, &y, &x); // gen1 is below and to the left of gen2
|
ncplane_abs_yx(gen1, &y, &x); // gen1 is below and to the left of gen2
|
||||||
CHECK(10 == y);
|
CHECK(10 == y);
|
||||||
CHECK(10 == x);
|
CHECK(10 == x);
|
||||||
@ -260,8 +262,8 @@ TEST_CASE("Piles") {
|
|||||||
ncplane_abs_yx(gen3, &y, &x);
|
ncplane_abs_yx(gen3, &y, &x);
|
||||||
CHECK(30 == y); // should stay the same
|
CHECK(30 == y); // should stay the same
|
||||||
CHECK(30 == x);
|
CHECK(30 == x);
|
||||||
CHECK(20 == ncplane_y(gen3)); // should reflect new parentage
|
CHECK(10 == ncplane_y(gen3)); // remain the same; still a child of gen2
|
||||||
CHECK(20 == ncplane_x(gen3));
|
CHECK(10 == ncplane_x(gen3));
|
||||||
ncplane_destroy(gen1);
|
ncplane_destroy(gen1);
|
||||||
ncplane_destroy(gen2);
|
ncplane_destroy(gen2);
|
||||||
ncplane_destroy(gen3);
|
ncplane_destroy(gen3);
|
||||||
|
@ -913,17 +913,26 @@ TEST_CASE("Plane") {
|
|||||||
int absy, absx;
|
int absy, absx;
|
||||||
CHECK(0 == notcurses_render(nc_));
|
CHECK(0 == notcurses_render(nc_));
|
||||||
ncplane_yx(nsub, &absy, &absx);
|
ncplane_yx(nsub, &absy, &absx);
|
||||||
CHECK(1 == absy); // actually at 2, 2
|
CHECK(1 == absy); // relatively at 1, 1
|
||||||
CHECK(1 == absx);
|
CHECK(1 == absx);
|
||||||
|
ncplane_abs_yx(nsub, &absy, &absx);
|
||||||
|
CHECK(2 == absy); // actually at 2, 2
|
||||||
|
CHECK(2 == absx);
|
||||||
ncplane_reparent(nsub, nsub);
|
ncplane_reparent(nsub, nsub);
|
||||||
CHECK(ncplane_pile(nsub) != ncplane_pile(ndom));
|
CHECK(ncplane_pile(nsub) != ncplane_pile(ndom));
|
||||||
|
ncplane_abs_yx(nsub, &absy, &absx);
|
||||||
|
CHECK(2 == absy); // now relatively at 1, 1
|
||||||
|
CHECK(2 == absx);
|
||||||
ncplane_yx(nsub, &absy, &absx);
|
ncplane_yx(nsub, &absy, &absx);
|
||||||
CHECK(1 == absy); // now truly at 1, 1
|
CHECK(2 == absy);
|
||||||
CHECK(1 == absx);
|
CHECK(2 == absx);
|
||||||
CHECK(0 == ncplane_move_yx(ndom, 0, 0));
|
CHECK(0 == ncplane_move_yx(ndom, 0, 0));
|
||||||
|
ncplane_abs_yx(nsub, &absy, &absx);
|
||||||
|
CHECK(2 == absy); // still at 1, 1
|
||||||
|
CHECK(2 == absx);
|
||||||
ncplane_yx(nsub, &absy, &absx);
|
ncplane_yx(nsub, &absy, &absx);
|
||||||
CHECK(1 == absy); // still at 1, 1
|
CHECK(2 == absy);
|
||||||
CHECK(1 == absx);
|
CHECK(2 == absx);
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("NoReparentStdPlane") {
|
SUBCASE("NoReparentStdPlane") {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user