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
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
-rw-r--r--release/scripts/startup/bl_ui/space_info.py13
-rw-r--r--source/blender/editors/include/ED_screen.h2
-rw-r--r--source/blender/editors/screen/screen_draw.c303
-rw-r--r--source/blender/editors/screen/screen_edit.c190
-rw-r--r--source/blender/editors/screen/screen_intern.h7
-rw-r--r--source/blender/editors/screen/screen_ops.c140
6 files changed, 368 insertions, 287 deletions
diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index cd65980fc0d..3a97b104271 100644
--- a/release/scripts/startup/bl_ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -92,16 +92,15 @@ class INFO_MT_area(Menu):
layout.separator()
- layout.operator("screen.area_dupli", icon='WINDOW')
-
- layout.separator()
-
layout.operator("screen.screen_full_area")
layout.operator(
"screen.screen_full_area",
- text="Toggle Fullscreen Area",
- icon='FULLSCREEN_ENTER',
- ).use_hide_panels = True
+ text="Toggle Fullscreen Area").use_hide_panels = True
+ layout.operator("screen.area_dupli")
+
+ layout.separator()
+
+ layout.operator("screen.area_close")
class INFO_MT_context_menu(Menu):
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index b3205acb8ee..169b25a1358 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -200,7 +200,7 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area);
/* screens */
void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm);
void ED_screen_draw_edges(struct wmWindow *win);
-void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2);
+void ED_screen_draw_join_highlight(struct ScrArea *sa1, struct ScrArea *sa2);
void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac);
void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win);
void ED_screen_ensure_updated(struct wmWindowManager *wm,
diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c
index 2ba7ef8f972..6d1409a9044 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -33,184 +33,11 @@
#include "WM_api.h"
+#include "UI_interface.h"
#include "UI_resources.h"
#include "screen_intern.h"
-/**
- * Draw horizontal shape visualizing future joining
- * (left as well right direction of future joining).
- */
-static void draw_horizontal_join_shape(ScrArea *area, char dir, uint pos)
-{
- const float width = screen_geom_area_width(area) - 1;
- const float height = screen_geom_area_height(area) - 1;
-
- float w, h;
- if (height < width) {
- h = height / 8;
- w = height / 4;
- }
- else {
- h = width / 8;
- w = width / 4;
- }
-
- vec2f points[10];
- points[0].x = area->v1->vec.x;
- points[0].y = area->v1->vec.y + height / 2;
-
- points[1].x = area->v1->vec.x;
- points[1].y = area->v1->vec.y;
-
- points[2].x = area->v4->vec.x - w;
- points[2].y = area->v4->vec.y;
-
- points[3].x = area->v4->vec.x - w;
- points[3].y = area->v4->vec.y + height / 2 - 2 * h;
-
- points[4].x = area->v4->vec.x - 2 * w;
- points[4].y = area->v4->vec.y + height / 2;
-
- points[5].x = area->v4->vec.x - w;
- points[5].y = area->v4->vec.y + height / 2 + 2 * h;
-
- points[6].x = area->v3->vec.x - w;
- points[6].y = area->v3->vec.y;
-
- points[7].x = area->v2->vec.x;
- points[7].y = area->v2->vec.y;
-
- points[8].x = area->v4->vec.x;
- points[8].y = area->v4->vec.y + height / 2 - h;
-
- points[9].x = area->v4->vec.x;
- points[9].y = area->v4->vec.y + height / 2 + h;
-
- if (dir == 'l') {
- /* when direction is left, then we flip direction of arrow */
- float cx = area->v1->vec.x + width;
- for (int i = 0; i < 10; i++) {
- points[i].x -= cx;
- points[i].x = -points[i].x;
- points[i].x += area->v1->vec.x;
- }
- }
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 0; i < 5; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immEnd();
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 4; i < 8; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immVertex2f(pos, points[0].x, points[0].y);
- immEnd();
-
- immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y);
- immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y);
-}
-
-/**
- * Draw vertical shape visualizing future joining (up/down direction).
- */
-static void draw_vertical_join_shape(ScrArea *area, char dir, uint pos)
-{
- const float width = screen_geom_area_width(area) - 1;
- const float height = screen_geom_area_height(area) - 1;
-
- float w, h;
- if (height < width) {
- h = height / 4;
- w = height / 8;
- }
- else {
- h = width / 4;
- w = width / 8;
- }
-
- vec2f points[10];
- points[0].x = area->v1->vec.x + width / 2;
- points[0].y = area->v3->vec.y;
-
- points[1].x = area->v2->vec.x;
- points[1].y = area->v2->vec.y;
-
- points[2].x = area->v1->vec.x;
- points[2].y = area->v1->vec.y + h;
-
- points[3].x = area->v1->vec.x + width / 2 - 2 * w;
- points[3].y = area->v1->vec.y + h;
-
- points[4].x = area->v1->vec.x + width / 2;
- points[4].y = area->v1->vec.y + 2 * h;
-
- points[5].x = area->v1->vec.x + width / 2 + 2 * w;
- points[5].y = area->v1->vec.y + h;
-
- points[6].x = area->v4->vec.x;
- points[6].y = area->v4->vec.y + h;
-
- points[7].x = area->v3->vec.x;
- points[7].y = area->v3->vec.y;
-
- points[8].x = area->v1->vec.x + width / 2 - w;
- points[8].y = area->v1->vec.y;
-
- points[9].x = area->v1->vec.x + width / 2 + w;
- points[9].y = area->v1->vec.y;
-
- if (dir == 'u') {
- /* when direction is up, then we flip direction of arrow */
- float cy = area->v1->vec.y + height;
- for (int i = 0; i < 10; i++) {
- points[i].y -= cy;
- points[i].y = -points[i].y;
- points[i].y += area->v1->vec.y;
- }
- }
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 0; i < 5; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immEnd();
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 4; i < 8; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immVertex2f(pos, points[0].x, points[0].y);
- immEnd();
-
- immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y);
- immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y);
-}
-
-/**
- * Draw join shape due to direction of joining.
- */
-static void draw_join_shape(ScrArea *area, char dir, uint pos)
-{
- if (ELEM(dir, 'u', 'd')) {
- draw_vertical_join_shape(area, dir, pos);
- }
- else {
- draw_horizontal_join_shape(area, dir, pos);
- }
-}
-
#define CORNER_RESOLUTION 3
static void do_vert_pair(GPUVertBuf *vbo, uint pos, uint *vidx, int corner, int i)
@@ -290,28 +117,6 @@ static GPUBatch *batch_screen_edges_get(int *corner_len)
#undef CORNER_RESOLUTION
-/**
- * Draw screen area darker with arrow (visualization of future joining).
- */
-static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos)
-{
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ub(0, 0, 0, 50);
-
- draw_join_shape(area, dir, pos);
-}
-
-/**
- * Draw screen area lighter with arrow shape ("eraser" of previous dark shape).
- */
-static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos)
-{
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ub(255, 255, 255, 25);
-
- immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y);
-}
-
static void drawscredge_area_draw(
int sizex, int sizey, short x1, short y1, short x2, short y2, float edge_thickness)
{
@@ -427,50 +232,92 @@ void ED_screen_draw_edges(wmWindow *win)
}
/**
- * The blended join arrows.
+ * Visual indication of the two areas involved in a proposed join.
*
* \param sa1: Area from which the resultant originates.
* \param sa2: Target area that will be replaced.
*/
-void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2)
+void ED_screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-
- GPU_line_width(1);
-
- /* blended join arrow */
int dir = area_getorientation(sa1, sa2);
- int dira = -1;
- if (dir != -1) {
- switch (dir) {
- case 0: /* W */
- dir = 'r';
- dira = 'l';
- break;
- case 1: /* N */
- dir = 'd';
- dira = 'u';
- break;
- case 2: /* E */
- dir = 'l';
- dira = 'r';
- break;
- case 3: /* S */
- dir = 'u';
- dira = 'd';
- break;
- }
+ if (dir == -1) {
+ return;
+ }
- GPU_blend(GPU_BLEND_ALPHA);
+ /* Rect of the combined areas.*/
+ bool vertical = ELEM(dir, 1, 3);
+ rctf combined = {.xmin = vertical ? MAX2(sa1->totrct.xmin, sa2->totrct.xmin) :
+ MIN2(sa1->totrct.xmin, sa2->totrct.xmin),
+ .xmax = vertical ? MIN2(sa1->totrct.xmax, sa2->totrct.xmax) :
+ MAX2(sa1->totrct.xmax, sa2->totrct.xmax),
+ .ymin = vertical ? MIN2(sa1->totrct.ymin, sa2->totrct.ymin) :
+ MAX2(sa1->totrct.ymin, sa2->totrct.ymin),
+ .ymax = vertical ? MAX2(sa1->totrct.ymax, sa2->totrct.ymax) :
+ MIN2(sa1->totrct.ymax, sa2->totrct.ymax)};
- scrarea_draw_shape_dark(sa2, dir, pos);
- scrarea_draw_shape_light(sa1, dira, pos);
+ uint pos_id = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_blend(GPU_BLEND_ALPHA);
- GPU_blend(GPU_BLEND_NONE);
+ /* Highlight source (sa1) within combined area. */
+ immUniformColor4fv((const float[4]){1.0f, 1.0f, 1.0f, 0.10f});
+ immRectf(pos_id,
+ MAX2(sa1->totrct.xmin, combined.xmin),
+ MAX2(sa1->totrct.ymin, combined.ymin),
+ MIN2(sa1->totrct.xmax, combined.xmax),
+ MIN2(sa1->totrct.ymax, combined.ymax));
+
+ /* Highlight destination (sa2) within combined area. */
+ immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.25f});
+ immRectf(pos_id,
+ MAX2(sa2->totrct.xmin, combined.xmin),
+ MAX2(sa2->totrct.ymin, combined.ymin),
+ MIN2(sa2->totrct.xmax, combined.xmax),
+ MIN2(sa2->totrct.ymax, combined.ymax));
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+ if (offset1 < 0 || offset2 > 0) {
+ /* Show partial areas that will be closed. */
+ immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.8f});
+ if (vertical) {
+ if (sa1->totrct.xmin < combined.xmin) {
+ immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax);
+ }
+ if (sa2->totrct.xmin < combined.xmin) {
+ immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax);
+ }
+ if (sa1->totrct.xmax > combined.xmax) {
+ immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax);
+ }
+ if (sa2->totrct.xmax > combined.xmax) {
+ immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax);
+ }
+ }
+ else {
+ if (sa1->totrct.ymin < combined.ymin) {
+ immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin);
+ }
+ if (sa2->totrct.ymin < combined.ymin) {
+ immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin);
+ }
+ if (sa1->totrct.ymax > combined.ymax) {
+ immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax);
+ }
+ if (sa2->totrct.ymax > combined.ymax) {
+ immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax);
+ }
+ }
}
immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
+
+ /* Outline the combined area. */
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, (float[4]){1.0f, 1.0f, 1.0f, 0.8f});
}
void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
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 *************************** */
diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h
index c51ff559786..cda7ba6a4a9 100644
--- a/source/blender/editors/screen/screen_intern.h
+++ b/source/blender/editors/screen/screen_intern.h
@@ -34,7 +34,9 @@ struct bContextDataResult;
#define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */
#define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */
-#define AREAJOINTOLERANCE (1.0f * U.widget_unit) /* Edges must be close to allow joining. */
+/* Edges must be within these to allow joining. */
+#define AREAJOINTOLERANCEX (AREAMINX * U.dpi_fac)
+#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac)
/* Expanded interaction influence of area borders. */
#define BORDERPADDING (U.dpi_fac + U.pixelsize)
@@ -58,7 +60,8 @@ ScrArea *area_split(
const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge);
int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2);
int area_getorientation(ScrArea *area, ScrArea *sb);
-
+void area_getoffsets(ScrArea *area, ScrArea *sb, const int dir, int *offset1, int *offset2);
+bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area);
struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]);
/* screen_geometry.c */
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 5cd4e8c353b..c19049b49e2 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -1387,6 +1387,58 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Area Close Operator
+ *
+ * Close selected area, replace by expanding a neighbor
+ * \{ */
+
+/* operator callback */
+static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
+{
+ ScrArea *area = CTX_wm_area(C);
+ if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) {
+ WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool area_close_poll(bContext *C)
+{
+ if (!ED_operator_areaactive(C)) {
+ return false;
+ }
+
+ ScrArea *area = CTX_wm_area(C);
+
+ if (ED_area_is_global(area)) {
+ return false;
+ }
+
+ bScreen *screen = CTX_wm_screen(C);
+
+ /* Can this area join with ANY other area? */
+ LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) {
+ if (area_getorientation(ar, area) != -1) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void SCREEN_OT_area_close(wmOperatorType *ot)
+{
+ ot->name = "Close Area";
+ ot->description = "Close selected area";
+ ot->idname = "SCREEN_OT_area_close";
+ ot->invoke = area_close_invoke;
+ ot->poll = area_close_poll;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Move Area Edge Operator
* \{ */
@@ -3212,7 +3264,7 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
typedef struct sAreaJoinData {
ScrArea *sa1; /* first area to be considered */
ScrArea *sa2; /* second area to be considered */
- void *draw_callback; /* call `ED_screen_draw_join_shape` */
+ void *draw_callback; /* call 'ED_screen_draw_join_highlight' */
} sAreaJoinData;
@@ -3222,7 +3274,7 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata
sAreaJoinData *sd = op->customdata;
if (sd->sa1 && sd->sa2) {
- ED_screen_draw_join_shape(sd->sa1, sd->sa2);
+ ED_screen_draw_join_highlight(sd->sa1, sd->sa2);
}
}
@@ -4069,6 +4121,69 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot)
/** \name Region Context Menu Operator (Header/Footer/Navbar)
* \{ */
+static void screen_area_menu_items(ScrArea *area, uiLayout *layout)
+{
+ if (ED_area_is_global(area)) {
+ return;
+ }
+
+ PointerRNA ptr;
+
+ /* Mouse position as if in middle of area. */
+ const int loc[2] = {BLI_rcti_cent_x(&area->totrct), BLI_rcti_cent_y(&area->totrct)};
+
+ /* Vertical Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Vertical Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+
+ RNA_int_set_array(&ptr, "cursor", loc);
+ RNA_enum_set(&ptr, "direction", 'v');
+
+ /* Horizontal Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Horizontal Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+
+ RNA_int_set_array(&ptr, "cursor", &loc[0]);
+ RNA_enum_set(&ptr, "direction", 'h');
+
+ uiItemS(layout);
+
+ if (area->spacetype != SPACE_FILE) {
+ uiItemO(layout,
+ area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"),
+ ICON_NONE,
+ "SCREEN_OT_screen_full_area");
+
+ if (!area->full) {
+ uiItemFullO(layout,
+ "SCREEN_OT_screen_full_area",
+ IFACE_("Full Screen Area"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+ RNA_boolean_set(&ptr, "use_hide_panels", true);
+ }
+ }
+
+ uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_dupli");
+ uiItemS(layout);
+ uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_close");
+}
+
void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
{
ScrArea *area = CTX_wm_area(C);
@@ -4102,17 +4217,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN
if (!ELEM(area->spacetype, SPACE_TOPBAR)) {
uiItemS(layout);
-
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
- }
-
- /* File browser should be fullscreen all the time, top-bar should
- * never be. But other regions can be maximized/restored. */
- if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
uiItemS(layout);
-
- const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area");
- uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area");
+ screen_area_menu_items(area, layout);
}
}
@@ -4134,14 +4241,8 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
- /* File browser should be fullscreen all the time, top-bar should
- * never be. But other regions can be maximized/restored... */
- if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
- uiItemS(layout);
-
- const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area");
- uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area");
- }
+ uiItemS(layout);
+ screen_area_menu_items(area, layout);
}
void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
@@ -5461,6 +5562,7 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(SCREEN_OT_area_move);
WM_operatortype_append(SCREEN_OT_area_split);
WM_operatortype_append(SCREEN_OT_area_join);
+ WM_operatortype_append(SCREEN_OT_area_close);
WM_operatortype_append(SCREEN_OT_area_options);
WM_operatortype_append(SCREEN_OT_area_dupli);
WM_operatortype_append(SCREEN_OT_area_swap);