Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarley Acheson <harley.acheson@gmail.com>2021-04-23 05:57:49 +0300
committerHarley Acheson <harley.acheson@gmail.com>2021-04-23 05:57:49 +0300
commit8b049e4c2a53e51db295ab300bf8377d63086708 (patch)
tree87d5b5b193455c11381e83bc8aa0e29c99ea4bf2 /source/blender/editors/screen/screen_edit.c
parent5a33965627008b30e2be822a7cca80598490ae0a (diff)
UI: Join or Close Any Screen Area
Corner action zones allow joining any neighbors. New 'Area Close' operator. Improved Header Context Menu. Differential Revision: https://developer.blender.org/D8084 Reviewed by Campbell Barton
Diffstat (limited to 'source/blender/editors/screen/screen_edit.c')
-rw-r--r--source/blender/editors/screen/screen_edit.c190
1 files changed, 160 insertions, 30 deletions
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 7ad8eada3b9..8e51772d801 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -284,42 +284,37 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new)
/* used with join operator */
int area_getorientation(ScrArea *area, ScrArea *sb)
{
- if (area == NULL || sb == NULL) {
+ if (area == NULL || sb == NULL || area == sb) {
return -1;
}
- ScrVert *saBL = area->v1;
- ScrVert *saTL = area->v2;
- ScrVert *saTR = area->v3;
- ScrVert *saBR = area->v4;
+ vec2s saBL = area->v1->vec;
+ vec2s saTL = area->v2->vec;
+ vec2s saTR = area->v3->vec;
+ vec2s saBR = area->v4->vec;
- ScrVert *sbBL = sb->v1;
- ScrVert *sbTL = sb->v2;
- ScrVert *sbTR = sb->v3;
- ScrVert *sbBR = sb->v4;
+ vec2s sbBL = sb->v1->vec;
+ vec2s sbTL = sb->v2->vec;
+ vec2s sbTR = sb->v3->vec;
+ vec2s sbBR = sb->v4->vec;
- if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */
- if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) &&
- (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) {
+ if (saBL.x == sbBR.x && saTL.x == sbTR.x) { /* area to right of sb = W */
+ if ((MIN2(saTL.y, sbTR.y) - MAX2(saBL.y, sbBR.y)) > AREAJOINTOLERANCEY) {
return 0;
}
}
- else if (saTL->vec.y == sbBL->vec.y &&
- saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */
- if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) &&
- (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) {
+ else if (saTL.y == sbBL.y && saTR.y == sbBR.y) { /* area to bottom of sb = N */
+ if ((MIN2(saTR.x, sbBR.x) - MAX2(saTL.x, sbBL.x)) > AREAJOINTOLERANCEX) {
return 1;
}
}
- else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */
- if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) &&
- (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) {
+ else if (saTR.x == sbTL.x && saBR.x == sbBL.x) { /* area to left of sb = E */
+ if ((MIN2(saTR.y, sbTL.y) - MAX2(saBR.y, sbBL.y)) > AREAJOINTOLERANCEY) {
return 2;
}
}
- else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/
- if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) &&
- (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) {
+ else if (saBL.y == sbTL.y && saBR.y == sbTR.y) { /* area on top of sb = S */
+ if ((MIN2(saBR.x, sbTR.x) - MAX2(saBL.x, sbTL.x)) > AREAJOINTOLERANCEX) {
return 3;
}
}
@@ -327,6 +322,35 @@ int area_getorientation(ScrArea *area, ScrArea *sb)
return -1;
}
+/* Get alignment offset of adjacent areas. 'dir' value is like area_getorientation(). */
+void area_getoffsets(ScrArea *area, ScrArea *sb, const int dir, int *offset1, int *offset2)
+{
+ if (area == NULL || sb == NULL) {
+ *offset1 = INT_MAX;
+ *offset2 = INT_MAX;
+ }
+ else if (dir == 0) { /* West: sa on right and sb to the left. */
+ *offset1 = sb->v3->vec.y - area->v2->vec.y;
+ *offset2 = sb->v4->vec.y - area->v1->vec.y;
+ }
+ else if (dir == 1) { /* North: sa below and sb above. */
+ *offset1 = area->v2->vec.x - sb->v1->vec.x;
+ *offset2 = area->v3->vec.x - sb->v4->vec.x;
+ }
+ else if (dir == 2) { /* East: sa on left and sb to the right. */
+ *offset1 = sb->v2->vec.y - area->v3->vec.y;
+ *offset2 = sb->v1->vec.y - area->v4->vec.y;
+ }
+ else if (dir == 3) { /* South: sa above and sb below. */
+ *offset1 = area->v1->vec.x - sb->v2->vec.x;
+ *offset2 = area->v4->vec.x - sb->v3->vec.x;
+ }
+ else {
+ *offset1 = INT_MAX;
+ *offset2 = INT_MAX;
+ }
+}
+
/* Screen verts with horizontal position equal to from_x are moved to to_x. */
static void screen_verts_halign(const wmWindow *win,
const bScreen *screen,
@@ -390,18 +414,24 @@ static void screen_areas_align(
}
}
-/* Helper function to join 2 areas, it has a return value, 0=failed 1=success
- * used by the split, join operators
- */
-int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
+/* 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)
{
int dir = area_getorientation(sa1, sa2);
-
if (dir == -1) {
- return 0;
+ return false;
+ }
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+
+ int tolerance = ELEM(dir, 0, 2) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX;
+ if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) {
+ return false;
}
- /* Align areas if they are not. Do sanity checking before getting here. */
+ /* Align areas if they are not. */
screen_areas_align(C, screen, sa1, sa2, dir);
if (dir == 0) { /* sa1 to right of sa2 = W */
@@ -434,7 +464,107 @@ int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
/* Update preview thumbnail */
BKE_icon_changed(screen->id.icon_id);
- return 1;
+ return true;
+}
+
+/* Slice off and return new area. "Reverse" gives right/bottom, rather than left/top. */
+static ScrArea *screen_area_trim(
+ bContext *C, bScreen *screen, ScrArea **area, int size, int dir, bool reverse)
+{
+ bool vertical = ELEM(dir, 1, 3);
+ if (abs(size) < (vertical ? AREAJOINTOLERANCEX : AREAJOINTOLERANCEY)) {
+ return NULL;
+ }
+
+ /* Measurement with ScrVerts because winx and winy might not be correct at this time. */
+ float fac = abs(size) / (float)(vertical ? ((*area)->v3->vec.x - (*area)->v1->vec.x) :
+ ((*area)->v3->vec.y - (*area)->v1->vec.y));
+ fac = (reverse == vertical) ? 1.0f - fac : fac;
+ ScrArea *newsa = area_split(CTX_wm_window(C), screen, *area, vertical ? 'v' : 'h', fac, 1);
+
+ /* area_split always returns smallest of the two areas, so might have to swap. */
+ if (((fac > 0.5f) == vertical) != reverse) {
+ ScrArea *temp = *area;
+ *area = newsa;
+ newsa = temp;
+ }
+
+ return newsa;
+}
+
+/* Join any two neighboring areas. Might create new areas, kept if over min_remainder. */
+static bool screen_area_join_ex(
+ bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, bool close_all_remainders)
+{
+ int dir = area_getorientation(sa1, sa2);
+ if (dir == -1) {
+ return false;
+ }
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+
+ /* Split Left/Top into new area if overhanging. */
+ ScrArea *side1 = screen_area_trim(C, screen, (offset1 > 0) ? &sa2 : &sa1, offset1, dir, false);
+
+ /* Split Right/Bottom into new area if overhanging. */
+ ScrArea *side2 = screen_area_trim(C, screen, (offset2 > 0) ? &sa1 : &sa2, offset2, dir, true);
+
+ /* The two areas now line up, so join them. */
+ screen_area_join_aligned(C, screen, sa1, sa2);
+
+ if (close_all_remainders || offset1 < 0 || offset2 > 0) {
+ /* Close both if trimiming sa1. */
+ screen_area_close(C, screen, side1);
+ screen_area_close(C, screen, side2);
+ }
+
+ BKE_icon_changed(screen->id.icon_id);
+ return true;
+}
+
+/* Join any two neighboring areas. Might involve complex changes. */
+int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
+{
+ return screen_area_join_ex(C, screen, sa1, sa2, false);
+}
+
+/* Close a screen area, allowing any neighbor to take its place. */
+bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area)
+{
+ if (area == NULL) {
+ return false;
+ }
+
+ ScrArea *sa2 = NULL;
+
+ /* Find the most-aligned joinable area. Larger size breaks ties. */
+ int min_alignment = INT_MAX;
+ int max_size = 0;
+ LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) {
+ int dir = area_getorientation(area, ar);
+ if (dir != -1) {
+ int offset1;
+ int offset2;
+ area_getoffsets(area, ar, dir, &offset1, &offset2);
+ int area_alignment = abs(offset1) + abs(offset2);
+ if (area_alignment < min_alignment) {
+ min_alignment = area_alignment;
+ max_size = ar->winx * ar->winy;
+ sa2 = ar;
+ }
+ else if (area_alignment == min_alignment) {
+ int area_size = ar->winx * ar->winy;
+ if (area_size > max_size) {
+ max_size = area_size;
+ sa2 = ar;
+ }
+ }
+ }
+ }
+
+ return screen_area_join_ex(C, screen, sa2, area, true);
}
/* ****************** EXPORTED API TO OTHER MODULES *************************** */