diff options
author | Harley Acheson <harley.acheson@gmail.com> | 2021-04-23 05:57:49 +0300 |
---|---|---|
committer | Harley Acheson <harley.acheson@gmail.com> | 2021-04-23 05:57:49 +0300 |
commit | 8b049e4c2a53e51db295ab300bf8377d63086708 (patch) | |
tree | 87d5b5b193455c11381e83bc8aa0e29c99ea4bf2 /source | |
parent | 5a33965627008b30e2be822a7cca80598490ae0a (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')
-rw-r--r-- | source/blender/editors/include/ED_screen.h | 2 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_draw.c | 303 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_edit.c | 190 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_intern.h | 7 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_ops.c | 140 |
5 files changed, 362 insertions, 280 deletions
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); |