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
This commit is contained in:
Harley Acheson 2021-04-22 19:57:49 -07:00
parent 5a33965627
commit 8b049e4c2a
6 changed files with 370 additions and 289 deletions

View File

@ -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):

View File

@ -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,

View File

@ -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;
}
/* 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)};
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);
/* 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);
}
}
GPU_blend(GPU_BLEND_ALPHA);
scrarea_draw_shape_dark(sa2, dir, pos);
scrarea_draw_shape_light(sa1, dira, pos);
GPU_blend(GPU_BLEND_NONE);
}
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)

View File

@ -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;
}
/* Align areas if they are not. Do sanity checking before getting here. */
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. */
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 *************************** */

View File

@ -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 */

View File

@ -1386,6 +1386,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);