Fix T100772: Joins with Interfering Tiny Areas

Detect unlikely situation of an area (that is smaller than our allowed
minimums) sharing an edge that will be moved during a join.

See D16519 for more details.

Differential Revision: https://developer.blender.org/D16519

Reviewed by Campbell Barton
This commit is contained in:
Harley Acheson 2022-11-16 09:06:03 -08:00
parent 6077fe7fae
commit a81abbbb8f
Notes: blender-bot 2023-08-16 07:26:36 +02:00
Referenced by issue #100772, Incorrect UI state after joining a narrow editor
Referenced by issue #111086, UI Region Doesn't Close Under Specific Conditions
1 changed files with 54 additions and 14 deletions

View File

@ -360,11 +360,59 @@ static void screen_verts_valign(const wmWindow *win,
}
}
/* Test if two adjoining areas can be aligned by having their screen edges adjusted. */
static bool screen_areas_can_align(bScreen *screen, ScrArea *sa1, ScrArea *sa2, eScreenDir dir)
{
if (dir == SCREEN_DIR_NONE) {
return false;
}
int offset1;
int offset2;
area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
const int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX;
if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) {
/* Misalignment is too great. */
return false;
}
/* Areas that are _smaller_ than minimum sizes, sharing an edge to be moved. See T100772. */
if (SCREEN_DIR_IS_VERTICAL(dir)) {
const short xmin = MIN2(sa1->v1->vec.x, sa2->v1->vec.x);
const short xmax = MAX2(sa1->v3->vec.x, sa2->v3->vec.x);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->v3->vec.x - area->v1->vec.x < tolerance &&
(area->v1->vec.x == xmin || area->v3->vec.x == xmax)) {
/* There is a narrow vertical area sharing an edge of the combined bounds. */
return false;
}
}
}
else {
const short ymin = MIN2(sa1->v1->vec.y, sa2->v1->vec.y);
const short ymax = MAX2(sa1->v3->vec.y, sa2->v3->vec.y);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->v3->vec.y - area->v1->vec.y < tolerance &&
(area->v1->vec.y == ymin || area->v3->vec.y == ymax)) {
/* There is a narrow horizontal area sharing an edge of the combined bounds. */
return false;
}
}
}
return true;
}
/* Adjust all screen edges to allow joining two areas. 'dir' value is like area_getorientation().
*/
static void screen_areas_align(
static bool screen_areas_align(
bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const eScreenDir dir)
{
if (!screen_areas_can_align(screen, sa1, sa2, dir)) {
return false;
}
wmWindow *win = CTX_wm_window(C);
if (SCREEN_DIR_IS_HORIZONTAL(dir)) {
@ -393,28 +441,20 @@ static void screen_areas_align(
screen_verts_halign(win, screen, sa2->v1->vec.x, left);
screen_verts_halign(win, screen, sa2->v3->vec.x, right);
}
return true;
}
/* Simple join of two areas without any splitting. Will return false if not possible. */
static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
{
const eScreenDir dir = area_getorientation(sa1, sa2);
if (dir == SCREEN_DIR_NONE) {
/* Ensure that the area edges are exactly aligned. */
if (!screen_areas_align(C, screen, sa1, sa2, dir)) {
return false;
}
int offset1;
int offset2;
area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX;
if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) {
return false;
}
/* Align areas if they are not. */
screen_areas_align(C, screen, sa1, sa2, dir);
if (dir == SCREEN_DIR_W) { /* sa1 to right of sa2 = West. */
sa1->v1 = sa2->v1; /* BL */
sa1->v2 = sa2->v2; /* TL */