diff options
Diffstat (limited to 'source/blender/editors/screen')
-rw-r--r-- | source/blender/editors/screen/CMakeLists.txt | 5 | ||||
-rw-r--r-- | source/blender/editors/screen/area.c | 1883 | ||||
-rw-r--r-- | source/blender/editors/screen/glutil.c | 765 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_context.c | 327 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_draw.c | 454 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_edit.c | 1117 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_intern.h | 29 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_ops.c | 796 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_user_menu.c | 164 | ||||
-rw-r--r-- | source/blender/editors/screen/screendump.c | 27 | ||||
-rw-r--r-- | source/blender/editors/screen/workspace_edit.c | 508 | ||||
-rw-r--r-- | source/blender/editors/screen/workspace_layout_edit.c | 199 |
12 files changed, 3751 insertions, 2523 deletions
diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt index 43e044b613a..ee114eba3c5 100644 --- a/source/blender/editors/screen/CMakeLists.txt +++ b/source/blender/editors/screen/CMakeLists.txt @@ -23,8 +23,10 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../blenloader ../../blentranslation ../../bmesh + ../../depsgraph ../../gpu ../../imbuf ../../makesdna @@ -45,7 +47,10 @@ set(SRC screen_draw.c screen_edit.c screen_ops.c + screen_user_menu.c screendump.c + workspace_edit.c + workspace_layout_edit.c screen_intern.h ) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 6e4cbf27e28..d9851487569 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -41,24 +41,28 @@ #include "BLI_utildefines.h" #include "BLI_linklist_stack.h" - #include "BKE_context.h" #include "BKE_global.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "RNA_access.h" #include "RNA_types.h" #include "WM_api.h" #include "WM_types.h" -#include "wm_subwindow.h" +#include "WM_message.h" +#include "WM_toolsystem.h" #include "ED_screen.h" #include "ED_screen_types.h" #include "ED_space_api.h" -#include "BIF_gl.h" -#include "BIF_glutil.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_draw.h" + #include "BLF_api.h" #include "IMB_imbuf.h" @@ -72,11 +76,17 @@ #include "screen_intern.h" -extern void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3); /* xxx temp */ +enum RegionEmbossSide { + REGION_EMBOSS_LEFT = (1 << 0), + REGION_EMBOSS_TOP = (1 << 1), + REGION_EMBOSS_BOTTOM = (1 << 2), + REGION_EMBOSS_RIGHT = (1 << 3), + REGION_EMBOSS_ALL = REGION_EMBOSS_LEFT | REGION_EMBOSS_TOP | REGION_EMBOSS_RIGHT | REGION_EMBOSS_BOTTOM, +}; /* general area and region code */ -static void region_draw_emboss(const ARegion *ar, const rcti *scirct) +static void region_draw_emboss(const ARegion *ar, const rcti *scirct, int sides) { rcti rect; @@ -88,35 +98,57 @@ static void region_draw_emboss(const ARegion *ar, const rcti *scirct) /* set transp line */ glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + float color[4] = {0.0f, 0.0f, 0.0f, 0.25f}; + UI_GetThemeColor3fv(TH_EDITOR_OUTLINE, color); - /* right */ - glColor4ub(0, 0, 0, 30); - sdrawline(rect.xmax, rect.ymin, rect.xmax, rect.ymax); + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4fv(color); + + immBeginAtMost(GWN_PRIM_LINES, 8); + + /* right */ + if (sides & REGION_EMBOSS_RIGHT) { + immVertex2f(pos, rect.xmax, rect.ymax); + immVertex2f(pos, rect.xmax, rect.ymin); + } + + /* bottom */ + if (sides & REGION_EMBOSS_BOTTOM) { + immVertex2f(pos, rect.xmax, rect.ymin); + immVertex2f(pos, rect.xmin, rect.ymin); + } - /* bottom */ - glColor4ub(0, 0, 0, 30); - sdrawline(rect.xmin, rect.ymin, rect.xmax, rect.ymin); + /* left */ + if (sides & REGION_EMBOSS_LEFT) { + immVertex2f(pos, rect.xmin, rect.ymin); + immVertex2f(pos, rect.xmin, rect.ymax); + } - /* top */ - glColor4ub(255, 255, 255, 30); - sdrawline(rect.xmin, rect.ymax, rect.xmax, rect.ymax); + /* top */ + if (sides & REGION_EMBOSS_TOP) { + immVertex2f(pos, rect.xmin, rect.ymax); + immVertex2f(pos, rect.xmax, rect.ymax); + } - /* left */ - glColor4ub(255, 255, 255, 30); - sdrawline(rect.xmin, rect.ymin, rect.xmin, rect.ymax); + immEnd(); + immUnbindProgram(); glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } void ED_region_pixelspace(ARegion *ar) { wmOrtho2_region_pixelspace(ar); - glLoadIdentity(); + gpuLoadIdentity(); } /* only exported for WM */ -void ED_region_do_listen(bScreen *sc, ScrArea *sa, ARegion *ar, wmNotifier *note) +void ED_region_do_listen(bScreen *sc, ScrArea *sa, ARegion *ar, wmNotifier *note, const Scene *scene) { /* generic notes first */ switch (note->category) { @@ -130,15 +162,15 @@ void ED_region_do_listen(bScreen *sc, ScrArea *sa, ARegion *ar, wmNotifier *note } if (ar->type && ar->type->listener) - ar->type->listener(sc, sa, ar, note); + ar->type->listener(sc, sa, ar, note, scene); } /* only exported for WM */ -void ED_area_do_listen(bScreen *sc, ScrArea *sa, wmNotifier *note) +void ED_area_do_listen(bScreen *sc, ScrArea *sa, wmNotifier *note, Scene *scene, WorkSpace *workspace) { /* no generic notes? */ if (sa->type && sa->type->listener) { - sa->type->listener(sc, sa, note); + sa->type->listener(sc, sa, note, scene, workspace); } } @@ -172,11 +204,29 @@ void ED_area_azones_update(ScrArea *sa, const int mouse_xy[2]) break; } } + else if (az->type == AZONE_REGION_SCROLL) { + /* only if mouse is not hovering the azone */ + if (BLI_rcti_isect_pt_v(&az->rect, mouse_xy) == false) { + View2D *v2d = &az->ar->v2d; + + if (az->direction == AZ_SCROLL_VERT) { + az->alpha = v2d->alpha_vert = 0; + changed = true; + } + else if (az->direction == AZ_SCROLL_HOR) { + az->alpha = v2d->alpha_hor = 0; + changed = true; + } + else { + BLI_assert(0); + } + } + } } if (changed) { sa->flag &= ~AREA_FLAG_ACTIONZONES_UPDATE; - ED_area_tag_redraw(sa); + ED_area_tag_redraw_no_rebuild(sa); } } @@ -207,82 +257,35 @@ static void area_draw_azone_fullscreen(short x1, short y1, short x2, short y2, f if (G.debug_value == 1) { rcti click_rect; float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC; - char alpha_debug = 255 * alpha; BLI_rcti_init(&click_rect, x, x + icon_size, y, y + icon_size); - glColor4ub(255, 0, 0, alpha_debug); - fdrawbox(click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax); - glColor4ub(0, 255, 255, alpha_debug); - fdrawline(click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax); - fdrawline(click_rect.xmin, click_rect.ymax, click_rect.xmax, click_rect.ymin); + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4f(1.0f, 0.0f, 0.0f, alpha); + imm_draw_box_wire_2d(pos, click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax); + + immUniformColor4f(0.0f, 1.0f, 1.0f, alpha); + immBegin(GWN_PRIM_LINES, 4); + immVertex2f(pos, click_rect.xmin, click_rect.ymin); + immVertex2f(pos, click_rect.xmax, click_rect.ymax); + immVertex2f(pos, click_rect.xmin, click_rect.ymax); + immVertex2f(pos, click_rect.xmax, click_rect.ymin); + immEnd(); + + immUnbindProgram(); } } /** * \brief Corner widgets use for dragging and splitting the view. */ -static void area_draw_azone(short x1, short y1, short x2, short y2) +static void area_draw_azone(short UNUSED(x1), short UNUSED(y1), short UNUSED(x2), short UNUSED(y2)) { - int dx = x2 - x1; - int dy = y2 - y1; - - dx = copysign(ceilf(0.3f * abs(dx)), dx); - dy = copysign(ceilf(0.3f * abs(dy)), dy); - - glEnable(GL_BLEND); - glEnable(GL_LINE_SMOOTH); - - glColor4ub(255, 255, 255, 180); - fdrawline(x1, y2, x2, y1); - glColor4ub(255, 255, 255, 130); - fdrawline(x1, y2 - dy, x2 - dx, y1); - glColor4ub(255, 255, 255, 80); - fdrawline(x1, y2 - 2 * dy, x2 - 2 * dx, y1); - - glColor4ub(0, 0, 0, 210); - fdrawline(x1, y2 + 1, x2 + 1, y1); - glColor4ub(0, 0, 0, 180); - fdrawline(x1, y2 - dy + 1, x2 - dx + 1, y1); - glColor4ub(0, 0, 0, 150); - fdrawline(x1, y2 - 2 * dy + 1, x2 - 2 * dx + 1, y1); - - glDisable(GL_LINE_SMOOTH); - glDisable(GL_BLEND); -} - -static void region_draw_azone_icon(AZone *az) -{ - GLUquadricObj *qobj = NULL; - short midx = az->x1 + (az->x2 - az->x1) / 2; - short midy = az->y1 + (az->y2 - az->y1) / 2; - - qobj = gluNewQuadric(); - - glPushMatrix(); - glTranslatef(midx, midy, 0.0); - - /* outlined circle */ - glEnable(GL_LINE_SMOOTH); - - glColor4f(1.f, 1.f, 1.f, 0.8f); - - gluQuadricDrawStyle(qobj, GLU_FILL); - gluDisk(qobj, 0.0, 4.25f, 16, 1); - - glColor4f(0.2f, 0.2f, 0.2f, 0.9f); - - gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); - gluDisk(qobj, 0.0, 4.25f, 16, 1); - - glDisable(GL_LINE_SMOOTH); - - glPopMatrix(); - gluDeleteQuadric(qobj); - - /* + */ - sdrawline(midx, midy - 2, midx, midy + 3); - sdrawline(midx - 2, midy, midx + 3, midy); + /* No drawing needed since all corners are action zone, and visually distinguishable. */ } static void draw_azone_plus(float x1, float y1, float x2, float y2) @@ -290,9 +293,19 @@ static void draw_azone_plus(float x1, float y1, float x2, float y2) float width = 0.1f * U.widget_unit; float pad = 0.2f * U.widget_unit; - glRectf((x1 + x2 - width) * 0.5f, y1 + pad, (x1 + x2 + width) * 0.5f, y2 - pad); - glRectf(x1 + pad, (y1 + y2 - width) * 0.5f, (x1 + x2 - width) * 0.5f, (y1 + y2 + width) * 0.5f); - glRectf((x1 + x2 + width) * 0.5f, (y1 + y2 - width) * 0.5f, x2 - pad, (y1 + y2 + width) * 0.5f); + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + glEnable(GL_BLEND); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4f(0.8f, 0.8f, 0.8f, 0.4f); + + immRectf(pos, (x1 + x2 - width) * 0.5f, y1 + pad, (x1 + x2 + width) * 0.5f, y2 - pad); + immRectf(pos, x1 + pad, (y1 + y2 - width) * 0.5f, (x1 + x2 - width) * 0.5f, (y1 + y2 + width) * 0.5f); + immRectf(pos, (x1 + x2 + width) * 0.5f, (y1 + y2 - width) * 0.5f, x2 - pad, (y1 + y2 + width) * 0.5f); + + immUnbindProgram(); + glDisable(GL_BLEND); } static void region_draw_azone_tab_plus(AZone *az) @@ -315,87 +328,10 @@ static void region_draw_azone_tab_plus(AZone *az) break; } - glColor4f(0.05f, 0.05f, 0.05f, 0.4f); - UI_draw_roundbox((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f); - - glEnable(GL_BLEND); + float color[4] = {0.05f, 0.05f, 0.05f, 0.4f}; + UI_draw_roundbox_aa(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, color); - glColor4f(0.8f, 0.8f, 0.8f, 0.4f); draw_azone_plus((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2); - - glDisable(GL_BLEND); -} - -static void region_draw_azone_tab(AZone *az) -{ - float col[3]; - - glEnable(GL_BLEND); - UI_GetThemeColor3fv(TH_HEADER, col); - glColor4f(col[0], col[1], col[2], 0.5f); - - /* add code to draw region hidden as 'too small' */ - switch (az->edge) { - case AE_TOP_TO_BOTTOMRIGHT: - UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_RB_ALPHA); - - UI_draw_roundbox_shade_x(GL_POLYGON, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f); - glColor4ub(0, 0, 0, 255); - UI_draw_roundbox_unfilled((float)az->x1, 0.3f + (float)az->y1, (float)az->x2, 0.3f + (float)az->y2, 4.0f); - break; - case AE_BOTTOM_TO_TOPLEFT: - UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT | UI_RB_ALPHA); - - UI_draw_roundbox_shade_x(GL_POLYGON, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f); - glColor4ub(0, 0, 0, 255); - UI_draw_roundbox_unfilled((float)az->x1, 0.3f + (float)az->y1, (float)az->x2, 0.3f + (float)az->y2, 4.0f); - break; - case AE_LEFT_TO_TOPRIGHT: - UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT | UI_RB_ALPHA); - - UI_draw_roundbox_shade_x(GL_POLYGON, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f); - glColor4ub(0, 0, 0, 255); - UI_draw_roundbox_unfilled((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f); - break; - case AE_RIGHT_TO_TOPLEFT: - UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT | UI_RB_ALPHA); - - UI_draw_roundbox_shade_x(GL_POLYGON, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f); - glColor4ub(0, 0, 0, 255); - UI_draw_roundbox_unfilled((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f); - break; - } - - glDisable(GL_BLEND); -} - -static void region_draw_azone_tria(AZone *az) -{ - glEnable(GL_BLEND); - //UI_GetThemeColor3fv(TH_HEADER, col); - glColor4f(0.0f, 0.0f, 0.0f, 0.35f); - - /* add code to draw region hidden as 'too small' */ - switch (az->edge) { - case AE_TOP_TO_BOTTOMRIGHT: - ui_draw_anti_tria((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y1, (float)(az->x1 + az->x2) / 2, (float)az->y2); - break; - - case AE_BOTTOM_TO_TOPLEFT: - ui_draw_anti_tria((float)az->x1, (float)az->y2, (float)az->x2, (float)az->y2, (float)(az->x1 + az->x2) / 2, (float)az->y1); - break; - - case AE_LEFT_TO_TOPRIGHT: - ui_draw_anti_tria((float)az->x2, (float)az->y1, (float)az->x2, (float)az->y2, (float)az->x1, (float)(az->y1 + az->y2) / 2); - break; - - case AE_RIGHT_TO_TOPLEFT: - ui_draw_anti_tria((float)az->x1, (float)az->y1, (float)az->x1, (float)az->y2, (float)az->x2, (float)(az->y1 + az->y2) / 2); - break; - - } - - glDisable(GL_BLEND); } static void area_azone_tag_update(ScrArea *sa) @@ -412,10 +348,10 @@ static void region_draw_azones(ScrArea *sa, ARegion *ar) glLineWidth(1.0f); glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glPushMatrix(); - glTranslatef(-ar->winrct.xmin, -ar->winrct.ymin, 0.0f); + gpuPushMatrix(); + gpuTranslate2f(-ar->winrct.xmin, -ar->winrct.ymin); for (az = sa->actionzones.first; az; az = az->next) { /* test if action zone is over this region */ @@ -427,18 +363,10 @@ static void region_draw_azones(ScrArea *sa, ARegion *ar) area_draw_azone(az->x1, az->y1, az->x2, az->y2); } else if (az->type == AZONE_REGION) { - if (az->ar) { /* only display tab or icons when the region is hidden */ if (az->ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) { - if (G.debug_value == 3) - region_draw_azone_icon(az); - else if (G.debug_value == 2) - region_draw_azone_tria(az); - else if (G.debug_value == 1) - region_draw_azone_tab(az); - else - region_draw_azone_tab_plus(az); + region_draw_azone_tab_plus(az); } } } @@ -449,59 +377,84 @@ static void region_draw_azones(ScrArea *sa, ARegion *ar) area_azone_tag_update(sa); } } + else if (az->type == AZONE_REGION_SCROLL) { + if (az->alpha != 0.0f) { + area_azone_tag_update(sa); + } + /* Don't draw this azone. */ + } } } - glPopMatrix(); + gpuPopMatrix(); glDisable(GL_BLEND); } +/* Follow wmMsgNotifyFn spec */ +void ED_region_do_msg_notify_tag_redraw( + bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + ARegion *ar = msg_val->owner; + ED_region_tag_redraw(ar); + + /* This avoids _many_ situations where header/properties control display settings. + * the common case is space properties in the header */ + if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_UI)) { + while (ar && ar->prev) { + ar = ar->prev; + } + for (; ar; ar = ar->next) { + if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS)) { + ED_region_tag_redraw(ar); + } + } + } +} +/* Follow wmMsgNotifyFn spec */ +void ED_area_do_msg_notify_tag_refresh( + bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val) +{ + ScrArea *sa = msg_val->user_data; + ED_area_tag_refresh(sa); +} + /* only exported for WM */ -/* makes region ready for drawing, sets pixelspace */ -void ED_region_set(const bContext *C, ARegion *ar) +void ED_region_do_layout(bContext *C, ARegion *ar) { - wmWindow *win = CTX_wm_window(C); + /* This is optional, only needed for dynamically sized regions. */ ScrArea *sa = CTX_wm_area(C); + ARegionType *at = ar->type; - ar->drawrct = ar->winrct; + if (!at->layout) { + return; + } - /* note; this sets state, so we can use wmOrtho and friends */ - wmSubWindowScissorSet(win, ar->swinid, &ar->drawrct, true); + if (at->do_lock) { + return; + } - UI_SetTheme(sa ? sa->spacetype : 0, ar->type ? ar->type->regionid : 0); + ar->do_draw |= RGN_DRAWING; - ED_region_pixelspace(ar); + UI_SetTheme(sa ? sa->spacetype : 0, at->regionid); + at->layout(C, ar); } - /* only exported for WM */ void ED_region_do_draw(bContext *C, ARegion *ar) { wmWindow *win = CTX_wm_window(C); ScrArea *sa = CTX_wm_area(C); ARegionType *at = ar->type; - bool scissor_pad; /* see BKE_spacedata_draw_locks() */ if (at->do_lock) return; - /* if no partial draw rect set, full rect */ - if (ar->drawrct.xmin == ar->drawrct.xmax) { - ar->drawrct = ar->winrct; - scissor_pad = true; - } - else { - /* extra clip for safety */ - BLI_rcti_isect(&ar->winrct, &ar->drawrct, &ar->drawrct); - scissor_pad = false; - } - ar->do_draw |= RGN_DRAWING; - /* note; this sets state, so we can use wmOrtho and friends */ - wmSubWindowScissorSet(win, ar->swinid, &ar->drawrct, scissor_pad); + /* Set viewport, scissor, ortho and ar->drawrct. */ + wmPartialViewport(&ar->drawrct, &ar->winrct, &ar->drawrct); wmOrtho2_region_pixelspace(ar); @@ -512,7 +465,7 @@ void ED_region_do_draw(bContext *C, ARegion *ar) UI_ThemeClearColor(TH_HEADER); glClear(GL_COLOR_BUFFER_BIT); - UI_ThemeColor(TH_TEXT); + UI_FontThemeColor(BLF_default(), TH_TEXT); BLF_draw_default(UI_UNIT_X, 0.4f * UI_UNIT_Y, 0.0f, ar->headerstr, BLF_DRAW_STR_DUMMY_MAX); } else if (at->draw) { @@ -529,9 +482,13 @@ void ED_region_do_draw(bContext *C, ARegion *ar) /* for debugging unneeded area redraws and partial redraw */ #if 0 glEnable(GL_BLEND); - glColor4f(drand48(), drand48(), drand48(), 0.1f); - glRectf(ar->drawrct.xmin - ar->winrct.xmin, ar->drawrct.ymin - ar->winrct.ymin, + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4f(drand48(), drand48(), drand48(), 0.1f); + immRectf(pos, ar->drawrct.xmin - ar->winrct.xmin, ar->drawrct.ymin - ar->winrct.ymin, ar->drawrct.xmax - ar->winrct.xmin, ar->drawrct.ymax - ar->winrct.ymin); + immUnbindProgram(); glDisable(GL_BLEND); #endif @@ -540,11 +497,46 @@ void ED_region_do_draw(bContext *C, ARegion *ar) UI_blocklist_free_inactive(C, &ar->uiblocks); if (sa) { - /* disable emboss when the area is full, - * unless we need to see division between regions (quad-split for eg) */ - if (((win->screen->state == SCREENFULL) && (ar->alignment == RGN_ALIGN_NONE)) == 0) { - region_draw_emboss(ar, &ar->winrct); + const bScreen *screen = WM_window_get_active_screen(win); + + /* Only draw region emboss for top-bar and quad-view. */ + if ((screen->state != SCREENFULL) && ED_area_is_global(sa)) { + region_draw_emboss(ar, &ar->winrct, (REGION_EMBOSS_LEFT | REGION_EMBOSS_RIGHT)); + } + else if ((ar->regiontype == RGN_TYPE_WINDOW) && (ar->alignment == RGN_ALIGN_QSPLIT)) { + region_draw_emboss(ar, &ar->winrct, REGION_EMBOSS_ALL); + } + } + + /* We may want to detach message-subscriptions from drawing. */ + { + WorkSpace *workspace = CTX_wm_workspace(C); + wmWindowManager *wm = CTX_wm_manager(C); + bScreen *screen = WM_window_get_active_screen(win); + Scene *scene = CTX_data_scene(C); + struct wmMsgBus *mbus = wm->message_bus; + WM_msgbus_clear_by_owner(mbus, ar); + + /* Cheat, always subscribe to this space type properties. + * + * This covers most cases and avoids copy-paste similar code for each space type. + */ + if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS, RGN_TYPE_UI, RGN_TYPE_TOOLS)) { + SpaceLink *sl = sa->spacedata.first; + + PointerRNA ptr; + RNA_pointer_create(&screen->id, &RNA_Space, sl, &ptr); + + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }; + /* All properties for this space type. */ + WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_region_tag_redraw, __func__); } + + ED_region_message_subscribe(C, workspace, scene, screen, sa, ar, mbus); } } @@ -559,7 +551,7 @@ void ED_region_tag_redraw(ARegion *ar) * but python scripts can cause this to happen indirectly */ if (ar && !(ar->do_draw & RGN_DRAWING)) { /* zero region means full region redraw */ - ar->do_draw &= ~RGN_DRAW_PARTIAL; + ar->do_draw &= ~(RGN_DRAW_PARTIAL | RGN_DRAW_NO_REBUILD); ar->do_draw |= RGN_DRAW; memset(&ar->drawrct, 0, sizeof(ar->drawrct)); } @@ -571,6 +563,15 @@ void ED_region_tag_redraw_overlay(ARegion *ar) ar->do_draw_overlay = RGN_DRAW; } +void ED_region_tag_redraw_no_rebuild(ARegion *ar) +{ + if (ar && !(ar->do_draw & (RGN_DRAWING | RGN_DRAW))) { + ar->do_draw &= ~RGN_DRAW_PARTIAL; + ar->do_draw |= RGN_DRAW_NO_REBUILD; + memset(&ar->drawrct, 0, sizeof(ar->drawrct)); + } +} + void ED_region_tag_refresh_ui(ARegion *ar) { if (ar) { @@ -581,7 +582,7 @@ void ED_region_tag_refresh_ui(ARegion *ar) void ED_region_tag_redraw_partial(ARegion *ar, const rcti *rct) { if (ar && !(ar->do_draw & RGN_DRAWING)) { - if (!(ar->do_draw & (RGN_DRAW | RGN_DRAW_PARTIAL))) { + if (!(ar->do_draw & (RGN_DRAW | RGN_DRAW_NO_REBUILD | RGN_DRAW_PARTIAL))) { /* no redraw set yet, set partial region */ ar->do_draw |= RGN_DRAW_PARTIAL; ar->drawrct = *rct; @@ -592,7 +593,7 @@ void ED_region_tag_redraw_partial(ARegion *ar, const rcti *rct) BLI_rcti_union(&ar->drawrct, rct); } else { - BLI_assert((ar->do_draw & RGN_DRAW) != 0); + BLI_assert((ar->do_draw & (RGN_DRAW | RGN_DRAW_NO_REBUILD)) != 0); /* Else, full redraw is already requested, nothing to do here. */ } } @@ -607,6 +608,15 @@ void ED_area_tag_redraw(ScrArea *sa) ED_region_tag_redraw(ar); } +void ED_area_tag_redraw_no_rebuild(ScrArea *sa) +{ + ARegion *ar; + + if (sa) + for (ar = sa->regionbase.first; ar; ar = ar->next) + ED_region_tag_redraw_no_rebuild(ar); +} + void ED_area_tag_redraw_regiontype(ScrArea *sa, int regiontype) { ARegion *ar; @@ -656,7 +666,7 @@ void ED_area_headerprint(ScrArea *sa, const char *str) /* ************************************************************ */ -static void area_azone_initialize(wmWindow *win, bScreen *screen, ScrArea *sa) +static void area_azone_initialize(wmWindow *win, const bScreen *screen, ScrArea *sa) { AZone *az; @@ -671,39 +681,62 @@ static void area_azone_initialize(wmWindow *win, bScreen *screen, ScrArea *sa) return; } - /* can't click on bottom corners on OS X, already used for resizing */ + if (ED_area_is_global(sa)) { + return; + } + + float coords[4][4] = { + /* Bottom-left. */ + {sa->totrct.xmin, + sa->totrct.ymin, + sa->totrct.xmin + (AZONESPOT - 1), + sa->totrct.ymin + (AZONESPOT - 1)}, + /* Bottom-right. */ + {sa->totrct.xmax, + sa->totrct.ymin, + sa->totrct.xmax - (AZONESPOT - 1), + sa->totrct.ymin + (AZONESPOT - 1)}, + /* Top-left. */ + {sa->totrct.xmin, + sa->totrct.ymax, + sa->totrct.xmin + (AZONESPOT - 1), + sa->totrct.ymax - (AZONESPOT - 1)}, + /* Top-right. */ + {sa->totrct.xmax, + sa->totrct.ymax, + sa->totrct.xmax - (AZONESPOT - 1), + sa->totrct.ymax - (AZONESPOT - 1)}}; + + for (int i = 0; i < 4; i++) { + /* can't click on bottom corners on OS X, already used for resizing */ #ifdef __APPLE__ - if (!(sa->totrct.xmin == 0 && sa->totrct.ymin == 0) || WM_window_is_fullscreen(win)) + if (!WM_window_is_fullscreen(win) && + ((coords[i][0] == 0 && coords[i][1] == 0) || + (coords[i][0] == WM_window_pixels_x(win) && coords[i][1] == 0))) + { + continue; + } #else - (void)win; + (void)win; #endif - { + /* set area action zones */ az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone"); BLI_addtail(&(sa->actionzones), az); az->type = AZONE_AREA; - az->x1 = sa->totrct.xmin; - az->y1 = sa->totrct.ymin; - az->x2 = sa->totrct.xmin + (AZONESPOT - 1); - az->y2 = sa->totrct.ymin + (AZONESPOT - 1); + az->x1 = coords[i][0]; + az->y1 = coords[i][1]; + az->x2 = coords[i][2]; + az->y2 = coords[i][3]; BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } - - az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone"); - BLI_addtail(&(sa->actionzones), az); - az->type = AZONE_AREA; - az->x1 = sa->totrct.xmax; - az->y1 = sa->totrct.ymax; - az->x2 = sa->totrct.xmax - (AZONESPOT - 1); - az->y2 = sa->totrct.ymax - (AZONESPOT - 1); - BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } static void fullscreen_azone_initialize(ScrArea *sa, ARegion *ar) { AZone *az; - if (ar->regiontype != RGN_TYPE_WINDOW) + if (ED_area_is_global(sa) || (ar->regiontype != RGN_TYPE_WINDOW)) return; az = (AZone *)MEM_callocN(sizeof(AZone), "fullscreen action zone"); @@ -753,65 +786,6 @@ static void region_azone_edge(AZone *az, ARegion *ar) BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } -static void region_azone_icon(ScrArea *sa, AZone *az, ARegion *ar) -{ - AZone *azt; - int tot = 0; - - /* count how many actionzones with along same edge are available. - * This allows for adding more action zones in the future without - * having to worry about correct offset */ - for (azt = sa->actionzones.first; azt; azt = azt->next) { - if (azt->edge == az->edge) tot++; - } - - switch (az->edge) { - case AE_TOP_TO_BOTTOMRIGHT: - az->x1 = ar->winrct.xmax - tot * 2 * AZONEPAD_ICON; - az->y1 = ar->winrct.ymax + AZONEPAD_ICON; - az->x2 = ar->winrct.xmax - tot * AZONEPAD_ICON; - az->y2 = ar->winrct.ymax + 2 * AZONEPAD_ICON; - break; - case AE_BOTTOM_TO_TOPLEFT: - az->x1 = ar->winrct.xmin + AZONEPAD_ICON; - az->y1 = ar->winrct.ymin - 2 * AZONEPAD_ICON; - az->x2 = ar->winrct.xmin + 2 * AZONEPAD_ICON; - az->y2 = ar->winrct.ymin - AZONEPAD_ICON; - break; - case AE_LEFT_TO_TOPRIGHT: - az->x1 = ar->winrct.xmin - 2 * AZONEPAD_ICON; - az->y1 = ar->winrct.ymax - tot * 2 * AZONEPAD_ICON; - az->x2 = ar->winrct.xmin - AZONEPAD_ICON; - az->y2 = ar->winrct.ymax - tot * AZONEPAD_ICON; - break; - case AE_RIGHT_TO_TOPLEFT: - az->x1 = ar->winrct.xmax + AZONEPAD_ICON; - az->y1 = ar->winrct.ymax - tot * 2 * AZONEPAD_ICON; - az->x2 = ar->winrct.xmax + 2 * AZONEPAD_ICON; - az->y2 = ar->winrct.ymax - tot * AZONEPAD_ICON; - break; - } - - BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); - - /* if more azones on 1 spot, set offset */ - for (azt = sa->actionzones.first; azt; azt = azt->next) { - if (az != azt) { - if (ABS(az->x1 - azt->x1) < 2 && ABS(az->y1 - azt->y1) < 2) { - if (az->edge == AE_TOP_TO_BOTTOMRIGHT || az->edge == AE_BOTTOM_TO_TOPLEFT) { - az->x1 += AZONESPOT; - az->x2 += AZONESPOT; - } - else { - az->y1 -= AZONESPOT; - az->y2 -= AZONESPOT; - } - BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); - } - } - } -} - #define AZONEPAD_TAB_PLUSW (0.7f * U.widget_unit) #define AZONEPAD_TAB_PLUSH (0.7f * U.widget_unit) @@ -856,143 +830,99 @@ static void region_azone_tab_plus(ScrArea *sa, AZone *az, ARegion *ar) BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } - -#define AZONEPAD_TABW (0.9f * U.widget_unit) -#define AZONEPAD_TABH (0.35f * U.widget_unit) - -/* region already made zero sized, in shape of edge */ -static void region_azone_tab(ScrArea *sa, AZone *az, ARegion *ar) +static void region_azone_edge_initialize(ScrArea *sa, ARegion *ar, AZEdge edge, const bool is_fullscreen) { - AZone *azt; - int tot = 0, add; + AZone *az = NULL; + const bool is_hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)); - for (azt = sa->actionzones.first; azt; azt = azt->next) { - if (azt->edge == az->edge) tot++; + if (is_hidden && is_fullscreen) { + return; } - switch (az->edge) { - case AE_TOP_TO_BOTTOMRIGHT: - add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0; - az->x1 = ar->winrct.xmax - 2 * AZONEPAD_TABW; - az->y1 = ar->winrct.ymax - add; - az->x2 = ar->winrct.xmax - AZONEPAD_TABW; - az->y2 = ar->winrct.ymax - add + AZONEPAD_TABH; - break; - case AE_BOTTOM_TO_TOPLEFT: - az->x1 = ar->winrct.xmin + AZONEPAD_TABW; - az->y1 = ar->winrct.ymin - AZONEPAD_TABH; - az->x2 = ar->winrct.xmin + 2 * AZONEPAD_TABW; - az->y2 = ar->winrct.ymin; - break; - case AE_LEFT_TO_TOPRIGHT: - az->x1 = ar->winrct.xmin + 1 - AZONEPAD_TABH; - az->y1 = ar->winrct.ymax - 2 * AZONEPAD_TABW; - az->x2 = ar->winrct.xmin + 1; - az->y2 = ar->winrct.ymax - AZONEPAD_TABW; - break; - case AE_RIGHT_TO_TOPLEFT: - az->x1 = ar->winrct.xmax - 1; - az->y1 = ar->winrct.ymax - 2 * AZONEPAD_TABW; - az->x2 = ar->winrct.xmax - 1 + AZONEPAD_TABH; - az->y2 = ar->winrct.ymax - AZONEPAD_TABW; - break; + az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone"); + BLI_addtail(&(sa->actionzones), az); + az->type = AZONE_REGION; + az->ar = ar; + az->edge = edge; + + if (is_hidden) { + region_azone_tab_plus(sa, az, ar); + } + else if (!is_hidden && (ar->regiontype != RGN_TYPE_HEADER)) { + region_azone_edge(az, ar); } - /* rect needed for mouse pointer test */ - BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } -#define AZONEPAD_TRIAW (0.8f * U.widget_unit) -#define AZONEPAD_TRIAH (0.45f * U.widget_unit) - - -/* region already made zero sized, in shape of edge */ -static void region_azone_tria(ScrArea *sa, AZone *az, ARegion *ar) +static void region_azone_scrollbar_initialize(ScrArea *sa, ARegion *ar, AZScrollDirection direction) { - AZone *azt; - int tot = 0, add; + rcti scroller_vert = (direction == AZ_SCROLL_VERT) ? ar->v2d.vert : ar->v2d.hor; + AZone *az = MEM_callocN(sizeof(*az), __func__); - for (azt = sa->actionzones.first; azt; azt = azt->next) { - if (azt->edge == az->edge) tot++; - } + BLI_addtail(&sa->actionzones, az); + az->type = AZONE_REGION_SCROLL; + az->ar = ar; + az->direction = direction; - switch (az->edge) { - case AE_TOP_TO_BOTTOMRIGHT: - add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0; - az->x1 = ar->winrct.xmax - 2 * AZONEPAD_TRIAW; - az->y1 = ar->winrct.ymax - add; - az->x2 = ar->winrct.xmax - AZONEPAD_TRIAW; - az->y2 = ar->winrct.ymax - add + AZONEPAD_TRIAH; - break; - case AE_BOTTOM_TO_TOPLEFT: - az->x1 = ar->winrct.xmin + AZONEPAD_TRIAW; - az->y1 = ar->winrct.ymin - AZONEPAD_TRIAH; - az->x2 = ar->winrct.xmin + 2 * AZONEPAD_TRIAW; - az->y2 = ar->winrct.ymin; - break; - case AE_LEFT_TO_TOPRIGHT: - az->x1 = ar->winrct.xmin + 1 - AZONEPAD_TRIAH; - az->y1 = ar->winrct.ymax - 2 * AZONEPAD_TRIAW; - az->x2 = ar->winrct.xmin + 1; - az->y2 = ar->winrct.ymax - AZONEPAD_TRIAW; - break; - case AE_RIGHT_TO_TOPLEFT: - az->x1 = ar->winrct.xmax - 1; - az->y1 = ar->winrct.ymax - 2 * AZONEPAD_TRIAW; - az->x2 = ar->winrct.xmax - 1 + AZONEPAD_TRIAH; - az->y2 = ar->winrct.ymax - AZONEPAD_TRIAW; - break; + if (direction == AZ_SCROLL_VERT) { + az->ar->v2d.alpha_vert = 0; } - /* rect needed for mouse pointer test */ + else if (direction == AZ_SCROLL_HOR) { + az->ar->v2d.alpha_hor = 0; + } + + BLI_rcti_translate(&scroller_vert, ar->winrct.xmin, ar->winrct.ymin); + az->x1 = scroller_vert.xmin - AZONEFADEIN; + az->y1 = scroller_vert.ymin - AZONEFADEIN; + az->x2 = scroller_vert.xmax + AZONEFADEIN; + az->y2 = scroller_vert.ymax + AZONEFADEIN; + BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } - -static void region_azone_initialize(ScrArea *sa, ARegion *ar, AZEdge edge, const bool is_fullscreen) +static void region_azones_scrollbars_initialize(ScrArea *sa, ARegion *ar) { - AZone *az = NULL; - const bool is_hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) == 0; + const View2D *v2d = &ar->v2d; - if (is_hidden || !is_fullscreen) { - az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone"); - BLI_addtail(&(sa->actionzones), az); - az->type = AZONE_REGION; - az->ar = ar; - az->edge = edge; + if ((v2d->scroll & V2D_SCROLL_VERTICAL) && ((v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) == 0)) { + region_azone_scrollbar_initialize(sa, ar, AZ_SCROLL_VERT); } - - if (!is_hidden) { - if (!is_fullscreen) { - if (G.debug_value == 3) - region_azone_icon(sa, az, ar); - else if (G.debug_value == 2) - region_azone_tria(sa, az, ar); - else if (G.debug_value == 1) - region_azone_tab(sa, az, ar); - else - region_azone_tab_plus(sa, az, ar); - } + if ((v2d->scroll & V2D_SCROLL_HORIZONTAL) && ((v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) == 0)) { + region_azone_scrollbar_initialize(sa, ar, AZ_SCROLL_HOR); } - else { - region_azone_edge(az, ar); - } - } /* *************************************************************** */ -static void region_azone_add(ScrArea *sa, ARegion *ar, const int alignment, const bool is_fullscreen) +static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar, const int alignment) { + const bool is_fullscreen = screen->state == SCREENFULL; + /* edge code (t b l r) is along which area edge azone will be drawn */ - if (alignment == RGN_ALIGN_TOP) - region_azone_initialize(sa, ar, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); + if (ar->regiontype == RGN_TYPE_HEADER && ar->winy + 6 > sa->winy) { + /* The logic for this is: when the header takes up the full area, + * disallow hiding it to view the main window. + * + * Without this, you can drag down the file selectors header and hide it + * by accident very easily (highly annoying!), the value 6 is arbitrary + * but accounts for small common rounding problems when scaling the UI, + * must be minimum '4' */ + } + else if (alignment == RGN_ALIGN_TOP) + region_azone_edge_initialize(sa, ar, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); else if (alignment == RGN_ALIGN_BOTTOM) - region_azone_initialize(sa, ar, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); + region_azone_edge_initialize(sa, ar, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); else if (alignment == RGN_ALIGN_RIGHT) - region_azone_initialize(sa, ar, AE_LEFT_TO_TOPRIGHT, is_fullscreen); + region_azone_edge_initialize(sa, ar, AE_LEFT_TO_TOPRIGHT, is_fullscreen); else if (alignment == RGN_ALIGN_LEFT) - region_azone_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen); + region_azone_edge_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen); + + if (is_fullscreen) { + fullscreen_azone_initialize(sa, ar); + } + + region_azones_scrollbars_initialize(sa, ar); } /* dir is direction to check, not the splitting edge direction! */ @@ -1018,7 +948,14 @@ static void region_overlap_fix(ScrArea *sa, ARegion *ar) /* find overlapping previous region on same place */ for (ar1 = ar->prev; ar1; ar1 = ar1->prev) { + if (ar1->flag & (RGN_FLAG_HIDDEN)) { + continue; + } + if (ar1->overlap && ((ar1->alignment & RGN_SPLIT_PREV) == 0)) { + if (ELEM(ar1->alignment, RGN_ALIGN_FLOAT)) { + continue; + } align1 = ar1->alignment; if (BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) { if (align1 != align) { @@ -1057,6 +994,13 @@ static void region_overlap_fix(ScrArea *sa, ARegion *ar) /* At this point, 'ar' is in its final position and still open. * Make a final check it does not overlap any previous 'other side' region. */ for (ar1 = ar->prev; ar1; ar1 = ar1->prev) { + if (ar1->flag & (RGN_FLAG_HIDDEN)) { + continue; + } + if (ELEM(ar1->alignment, RGN_ALIGN_FLOAT)) { + continue; + } + if (ar1->overlap && (ar1->alignment & RGN_SPLIT_PREV) == 0) { if ((ar1->alignment != align) && BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) { /* Left overlapping right or vice-versa, forbid this! */ @@ -1068,16 +1012,22 @@ static void region_overlap_fix(ScrArea *sa, ARegion *ar) } /* overlapping regions only in the following restricted cases */ -static bool region_is_overlap(wmWindow *win, ScrArea *sa, ARegion *ar) +bool ED_region_is_overlap(int spacetype, int regiontype) { + if (regiontype == RGN_TYPE_HUD) { + return 1; + } if (U.uiflag2 & USER_REGION_OVERLAP) { - if (WM_is_draw_triple(win)) { - if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_SEQ)) { - if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS)) + if (ELEM(spacetype, SPACE_VIEW3D, SPACE_SEQ, SPACE_IMAGE)) { + if (ELEM(regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS)) + return 1; + + if (ELEM(spacetype, SPACE_VIEW3D, SPACE_IMAGE)) { + if (regiontype == RGN_TYPE_HEADER) return 1; } - else if (sa->spacetype == SPACE_IMAGE) { - if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, RGN_TYPE_PREVIEW)) + else if (spacetype == SPACE_SEQ) { + if (regiontype == RGN_TYPE_PREVIEW) return 1; } } @@ -1086,7 +1036,7 @@ static bool region_is_overlap(wmWindow *win, ScrArea *sa, ARegion *ar) return 0; } -static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti *remainder, int quad) +static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti *remainder, rcti *overlap_remainder, int quad) { rcti *remainder_prev = remainder; int prefsizex, prefsizey; @@ -1106,20 +1056,24 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti alignment = ar->alignment & ~RGN_SPLIT_PREV; /* set here, assuming userpref switching forces to call this again */ - ar->overlap = region_is_overlap(win, sa, ar); + ar->overlap = ED_region_is_overlap(sa->spacetype, ar->regiontype); /* clear state flags first */ ar->flag &= ~RGN_FLAG_TOO_SMALL; /* user errors */ - if (ar->next == NULL && alignment != RGN_ALIGN_QSPLIT) + if ((ar->next == NULL) && !ELEM(alignment, RGN_ALIGN_QSPLIT, RGN_ALIGN_FLOAT)) { alignment = RGN_ALIGN_NONE; + } - /* prefsize, for header we stick to exception (prevent dpi rounding error) */ - prefsizex = UI_DPI_FAC * (ar->sizex > 1 ? ar->sizex + 0.5f : ar->type->prefsizex); + /* prefsize, taking into account DPI */ + prefsizex = UI_DPI_FAC * ((ar->sizex > 1) ? ar->sizex + 0.5f : ar->type->prefsizex); if (ar->regiontype == RGN_TYPE_HEADER) { prefsizey = ED_area_headersize(); } + else if (ED_area_is_global(sa)) { + prefsizey = ED_region_global_size_y(); + } else if (ar->regiontype == RGN_TYPE_UI && sa->spacetype == SPACE_FILE) { prefsizey = UI_UNIT_Y * 2 + (UI_UNIT_Y / 2); } @@ -1132,7 +1086,28 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti /* hidden is user flag */ } else if (alignment == RGN_ALIGN_FLOAT) { - /* XXX floating area region, not handled yet here */ + /** + * \note Currently this window type is only used for #RGN_TYPE_HUD, + * We expect the panel to resize it's self to be larger. + * + * This aligns to the lower left of the area. + */ + rcti overlap_remainder_margin = *overlap_remainder; + BLI_rcti_resize( + &overlap_remainder_margin, + max_ii(0, BLI_rcti_size_x(overlap_remainder) - UI_UNIT_X / 2), + max_ii(0, BLI_rcti_size_y(overlap_remainder) - UI_UNIT_Y / 2)); + ar->winrct.xmin = overlap_remainder_margin.xmin; + ar->winrct.ymin = overlap_remainder_margin.ymin; + ar->winrct.xmax = ar->winrct.xmin + ar->sizex - 1; + ar->winrct.ymax = ar->winrct.ymin + ar->sizey - 1; + + BLI_rcti_isect(&ar->winrct, &overlap_remainder_margin, &ar->winrct); + if (BLI_rcti_size_x(&ar->winrct) < UI_UNIT_X || + BLI_rcti_size_y(&ar->winrct) < UI_UNIT_Y) + { + ar->flag |= RGN_FLAG_TOO_SMALL; + } } else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) { /* remainder is too small for any usage */ @@ -1144,50 +1119,50 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti BLI_rcti_init(remainder, 0, 0, 0, 0); } else if (alignment == RGN_ALIGN_TOP || alignment == RGN_ALIGN_BOTTOM) { + rcti *winrct = (ar->overlap) ? overlap_remainder : remainder; - if (rct_fits(remainder, 'v', prefsizey) < 0) { + if (rct_fits(winrct, 'v', prefsizey) < 0) { ar->flag |= RGN_FLAG_TOO_SMALL; } else { - int fac = rct_fits(remainder, 'v', prefsizey); + int fac = rct_fits(winrct, 'v', prefsizey); if (fac < 0) prefsizey += fac; - ar->winrct = *remainder; + ar->winrct = *winrct; if (alignment == RGN_ALIGN_TOP) { ar->winrct.ymin = ar->winrct.ymax - prefsizey + 1; - remainder->ymax = ar->winrct.ymin - 1; + winrct->ymax = ar->winrct.ymin - 1; } else { ar->winrct.ymax = ar->winrct.ymin + prefsizey - 1; - remainder->ymin = ar->winrct.ymax + 1; + winrct->ymin = ar->winrct.ymax + 1; } } } else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { + rcti *winrct = (ar->overlap) ? overlap_remainder : remainder; - if (rct_fits(remainder, 'h', prefsizex) < 0) { + if (rct_fits(winrct, 'h', prefsizex) < 0) { ar->flag |= RGN_FLAG_TOO_SMALL; } else { - int fac = rct_fits(remainder, 'h', prefsizex); + int fac = rct_fits(winrct, 'h', prefsizex); if (fac < 0) prefsizex += fac; - ar->winrct = *remainder; + ar->winrct = *winrct; if (alignment == RGN_ALIGN_RIGHT) { ar->winrct.xmin = ar->winrct.xmax - prefsizex + 1; - if (ar->overlap == 0) - remainder->xmax = ar->winrct.xmin - 1; + winrct->xmax = ar->winrct.xmin - 1; } else { ar->winrct.xmax = ar->winrct.xmin + prefsizex - 1; - if (ar->overlap == 0) - remainder->xmin = ar->winrct.xmax + 1; + winrct->xmin = ar->winrct.xmax + 1; } } } @@ -1271,12 +1246,13 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti if (ar->winy > 1) ar->sizey = (ar->winy + 0.5f) / UI_DPI_FAC; /* exception for multiple overlapping regions on same spot */ - if (ar->overlap) + if (ar->overlap & (alignment != RGN_ALIGN_FLOAT)) { region_overlap_fix(sa, ar); + } /* set winrect for azones */ if (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) { - ar->winrct = *remainder; + ar->winrct = (ar->overlap) ? *overlap_remainder : *remainder; switch (alignment) { case RGN_ALIGN_TOP: @@ -1305,42 +1281,46 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti } } - /* in end, add azones, where appropriate */ - if (ar->regiontype == RGN_TYPE_HEADER && ar->winy + 6 > sa->winy) { - /* The logic for this is: when the header takes up the full area, - * disallow hiding it to view the main window. - * - * Without this, you can drag down the file selectors header and hide it - * by accident very easily (highly annoying!), the value 6 is arbitrary - * but accounts for small common rounding problems when scaling the UI, - * must be minimum '4' */ - } - else { - if (ELEM(win->screen->state, SCREENNORMAL, SCREENMAXIMIZED)) { - region_azone_add(sa, ar, alignment, false); - } - else { - region_azone_add(sa, ar, alignment, true); - fullscreen_azone_initialize(sa, ar); - } + /* After non-overlapping region, all following overlapping regions + * fit within the remaining space again. */ + if (!ar->overlap) { + *overlap_remainder = *remainder; } - region_rect_recursive(win, sa, ar->next, remainder, quad); + region_rect_recursive(win, sa, ar->next, remainder, overlap_remainder, quad); } -static void area_calc_totrct(ScrArea *sa, int sizex, int sizey) +static void area_calc_totrct(ScrArea *sa, const rcti *window_rect) { - short rt = (short) U.pixelsize; + short px = (short)U.pixelsize; - if (sa->v1->vec.x > 0) sa->totrct.xmin = sa->v1->vec.x + rt; - else sa->totrct.xmin = sa->v1->vec.x; - if (sa->v4->vec.x < sizex - 1) sa->totrct.xmax = sa->v4->vec.x - rt; - else sa->totrct.xmax = sa->v4->vec.x; + sa->totrct.xmin = sa->v1->vec.x; + sa->totrct.xmax = sa->v4->vec.x; + sa->totrct.ymin = sa->v1->vec.y; + sa->totrct.ymax = sa->v2->vec.y; - if (sa->v1->vec.y > 0) sa->totrct.ymin = sa->v1->vec.y + rt; - else sa->totrct.ymin = sa->v1->vec.y; - if (sa->v2->vec.y < sizey - 1) sa->totrct.ymax = sa->v2->vec.y - rt; - else sa->totrct.ymax = sa->v2->vec.y; + /* scale down totrct by 1 pixel on all sides not matching window borders */ + if (sa->totrct.xmin > window_rect->xmin) { + sa->totrct.xmin += px; + } + if (sa->totrct.xmax < (window_rect->xmax - 1)) { + sa->totrct.xmax -= px; + } + if (sa->totrct.ymin > window_rect->ymin) { + sa->totrct.ymin += px; + } + if (sa->totrct.ymax < (window_rect->ymax - 1)) { + sa->totrct.ymax -= px; + } + /* Although the following asserts are correct they lead to a very unstable Blender. + * And the asserts would fail even in 2.7x (they were added in 2.8x as part of the top-bar commit). + * For more details see T54864. */ +#if 0 + BLI_assert(sa->totrct.xmin >= 0); + BLI_assert(sa->totrct.xmax >= 0); + BLI_assert(sa->totrct.ymin >= 0); + BLI_assert(sa->totrct.ymax >= 0); +#endif /* for speedup */ sa->winx = BLI_rcti_size_x(&sa->totrct) + 1; @@ -1349,24 +1329,14 @@ static void area_calc_totrct(ScrArea *sa, int sizex, int sizey) /* used for area initialize below */ -static void region_subwindow(wmWindow *win, ARegion *ar, bool activate) +static void region_subwindow(ARegion *ar) { bool hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) != 0; if ((ar->alignment & RGN_SPLIT_PREV) && ar->prev) hidden = hidden || (ar->prev->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)); - if (hidden) { - if (ar->swinid) - wm_subwindow_close(win, ar->swinid); - ar->swinid = 0; - } - else if (ar->swinid == 0) { - ar->swinid = wm_subwindow_open(win, &ar->winrct, activate); - } - else { - wm_subwindow_position(win, ar->swinid, &ar->winrct, activate); - } + ar->visible = !hidden; } static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *handlers, int flag) @@ -1390,19 +1360,15 @@ static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *hand /* time-markers */ wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Markers", 0, 0); - /* time space only has this keymap, the others get a boundbox restricted map */ - if (sa->spacetype != SPACE_TIME) { - ARegion *ar; - /* same local check for all areas */ - static rcti rect = {0, 10000, 0, -1}; - rect.ymax = UI_MARKER_MARGIN_Y; - ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar) { - WM_event_add_keymap_handler_bb(handlers, keymap, &rect, &ar->winrct); - } + /* use a boundbox restricted map */ + ARegion *ar; + /* same local check for all areas */ + static rcti rect = {0, 10000, 0, -1}; + rect.ymax = UI_MARKER_MARGIN_Y; + ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + if (ar) { + WM_event_add_keymap_handler_bb(handlers, keymap, &rect, &ar->winrct); } - else - WM_event_add_keymap_handler(handlers, keymap); } if (flag & ED_KEYMAP_ANIMATION) { /* frame changing and timeline operators (for time spaces) */ @@ -1436,18 +1402,55 @@ static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *hand } } +void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *area) +{ + rcti rect, overlap_rect; + rcti window_rect; + + if (!(area->flag & AREA_FLAG_REGION_SIZE_UPDATE)) { + return; + } + + WM_window_rect_calc(win, &window_rect); + area_calc_totrct(area, &window_rect); + + /* region rect sizes */ + rect = area->totrct; + overlap_rect = rect; + region_rect_recursive(win, area, area->regionbase.first, &rect, &overlap_rect, 0); + + for (ARegion *ar = area->regionbase.first; ar; ar = ar->next) { + region_subwindow(ar); + + /* region size may have changed, init does necessary adjustments */ + if (ar->type->init) { + ar->type->init(wm, ar); + } + } + + area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE; +} /* called in screen_refresh, or screens_init, also area size changes */ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); + Scene *scene = WM_window_get_active_scene(win); ARegion *ar; - rcti rect; + rcti rect, overlap_rect; + rcti window_rect; + + if (ED_area_is_global(sa) && (sa->global->flag & GLOBAL_AREA_IS_HIDDEN)) { + return; + } + WM_window_rect_calc(win, &window_rect); /* set typedefinitions */ sa->type = BKE_spacetype_from_id(sa->spacetype); if (sa->type == NULL) { - sa->butspacetype = sa->spacetype = SPACE_VIEW3D; + sa->spacetype = SPACE_VIEW3D; sa->type = BKE_spacetype_from_id(sa->spacetype); } @@ -1455,14 +1458,13 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) ar->type = BKE_regiontype_from_id_or_first(sa->type, ar->regiontype); /* area sizes */ - area_calc_totrct(sa, WM_window_pixels_x(win), WM_window_pixels_y(win)); - - /* clear all azones, add the area triange widgets */ - area_azone_initialize(win, win->screen, sa); + area_calc_totrct(sa, &window_rect); /* region rect sizes */ rect = sa->totrct; - region_rect_recursive(win, sa, sa->regionbase.first, &rect, 0); + overlap_rect = rect; + region_rect_recursive(win, sa, sa->regionbase.first, &rect, &overlap_rect, 0); + sa->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE; /* default area handlers */ ed_default_handlers(wm, sa, &sa->handlers, sa->type->keymapflag); @@ -1470,22 +1472,31 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) if (sa->type->init) sa->type->init(wm, sa); + /* clear all azones, add the area triange widgets */ + area_azone_initialize(win, screen, sa); + /* region windows, default and own handlers */ for (ar = sa->regionbase.first; ar; ar = ar->next) { - region_subwindow(win, ar, false); + region_subwindow(ar); - if (ar->swinid) { + if (ar->visible) { /* default region handlers */ ed_default_handlers(wm, sa, &ar->handlers, ar->type->keymapflag); /* own handlers */ - if (ar->type->init) + if (ar->type->init) { ar->type->init(wm, ar); + } } else { /* prevent uiblocks to run */ UI_blocklist_free(NULL, &ar->uiblocks); } + + /* Some AZones use View2D data which is only updated in region init, so call that first! */ + region_azones_add(screen, sa, ar, ar->alignment & ~RGN_SPLIT_PREV); } + + WM_toolsystem_refresh_screen_area(workspace, scene, sa); } static void region_update_rect(ARegion *ar) @@ -1500,22 +1511,16 @@ static void region_update_rect(ARegion *ar) /** * Call to move a popup window (keep OpenGL context free!) */ -void ED_region_update_rect(bContext *C, ARegion *ar) +void ED_region_update_rect(ARegion *ar) { - wmWindow *win = CTX_wm_window(C); - - wm_subwindow_rect_set(win, ar->swinid, &ar->winrct); - region_update_rect(ar); } /* externally called for floating regions like menus */ -void ED_region_init(bContext *C, ARegion *ar) +void ED_region_init(ARegion *ar) { -// ARegionType *at = ar->type; - /* refresh can be called before window opened */ - region_subwindow(CTX_wm_window(C), ar, false); + region_subwindow(ar); region_update_rect(ar); } @@ -1526,10 +1531,25 @@ void ED_region_cursor_set(wmWindow *win, ScrArea *sa, ARegion *ar) ar->type->cursor(win, sa, ar); } else { + if (WM_cursor_set_from_tool(win, sa, ar)) { + return; + } WM_cursor_set(win, CURSOR_STD); } } +/* for use after changing visiblity of regions */ +void ED_region_visibility_change_update(bContext *C, ARegion *ar) +{ + ScrArea *sa = CTX_wm_area(C); + + if (ar->flag & RGN_FLAG_HIDDEN) + WM_event_remove_handlers(C, &ar->handlers); + + ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa); + ED_area_tag_redraw(sa); +} + /* for quick toggle, can skip fades */ void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade) { @@ -1542,11 +1562,7 @@ void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade) region_blend_start(C, sa, ar); } else { - if (ar->flag & RGN_FLAG_HIDDEN) - WM_event_remove_handlers(C, &ar->handlers); - - ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa); - ED_area_tag_redraw(sa); + ED_region_visibility_change_update(C, ar); } } @@ -1566,10 +1582,8 @@ void ED_area_data_copy(ScrArea *sa_dst, ScrArea *sa_src, const bool do_free) const char spacetype = sa_dst->spacetype; const short flag_copy = HEADER_NO_PULLDOWN; - sa_dst->headertype = sa_src->headertype; sa_dst->spacetype = sa_src->spacetype; sa_dst->type = sa_src->type; - sa_dst->butspacetype = sa_src->butspacetype; sa_dst->flag = (sa_dst->flag & ~flag_copy) | (sa_src->flag & flag_copy); @@ -1597,10 +1611,8 @@ void ED_area_data_copy(ScrArea *sa_dst, ScrArea *sa_src, const bool do_free) void ED_area_data_swap(ScrArea *sa_dst, ScrArea *sa_src) { - SWAP(short, sa_dst->headertype, sa_src->headertype); SWAP(char, sa_dst->spacetype, sa_src->spacetype); SWAP(SpaceType *, sa_dst->type, sa_src->type); - SWAP(char, sa_dst->butspacetype, sa_src->butspacetype); SWAP(ListBase, sa_dst->spacedata, sa_src->spacedata); @@ -1639,12 +1651,15 @@ void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2) */ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exit) { + wmWindow *win = CTX_wm_window(C); + if (sa->spacetype != type) { SpaceType *st; SpaceLink *slold; SpaceLink *sl; /* store sa->type->exit callback */ void *sa_exit = sa->type ? sa->type->exit : NULL; + int header_alignment = ED_area_header_alignment(sa); /* in some cases (opening temp space) we don't want to * call area exit callback, so we temporarily unset it */ @@ -1663,9 +1678,12 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi slold = sa->spacedata.first; sa->spacetype = type; - sa->butspacetype = type; sa->type = st; + /* If st->new may be called, don't use context until then. The + * sa->type->context() callback has changed but data may be invalid + * (e.g. with properties editor) until space-data is properly created */ + /* check previously stored space */ for (sl = sa->spacedata.first; sl; sl = sl->next) if (sl->spacetype == type) @@ -1690,11 +1708,22 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi /* put in front of list */ BLI_remlink(&sa->spacedata, sl); BLI_addhead(&sa->spacedata, sl); + + + /* Sync header alignment. */ + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_HEADER) { + ar->alignment = header_alignment; + break; + } + } } else { /* new space */ if (st) { - sl = st->new(C); + /* Don't get scene from context here which may depend on space-data. */ + Scene *scene = WM_window_get_active_scene(win); + sl = st->new(sa, scene); BLI_addhead(&sa->spacedata, sl); /* swap regions */ @@ -1705,7 +1734,7 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi } } - ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa); + ED_area_initialize(CTX_wm_manager(C), win, sa); /* tell WM to refresh, cursor types etc */ WM_event_add_mousemove(C); @@ -1755,32 +1784,184 @@ int ED_area_header_switchbutton(const bContext *C, uiBlock *block, int yco) RNA_pointer_create(&(scr->id), &RNA_Area, sa, &areaptr); uiDefButR(block, UI_BTYPE_MENU, 0, "", xco, yco, 1.6 * U.widget_unit, U.widget_unit, - &areaptr, "type", 0, 0.0f, 0.0f, 0.0f, 0.0f, ""); + &areaptr, "ui_type", 0, 0.0f, 0.0f, 0.0f, 0.0f, ""); return xco + 1.7 * U.widget_unit; } /************************ standard UI regions ************************/ -void ED_region_panels(const bContext *C, ARegion *ar, const char *context, int contextnr, const bool vertical) +static ThemeColorID region_background_color_id(const bContext *C, const ARegion *region) +{ + ScrArea *area = CTX_wm_area(C); + + switch (region->regiontype) { + case RGN_TYPE_HEADER: + if (ED_screen_area_active(C) || ED_area_is_global(area)) { + return TH_HEADER; + } + else { + return TH_HEADERDESEL; + } + case RGN_TYPE_PREVIEW: + return TH_PREVIEW_BACK; + default: + return TH_BACK; + } +} + +static void region_clear_color(const bContext *C, const ARegion *ar, ThemeColorID colorid) +{ + if (ar->alignment == RGN_ALIGN_FLOAT) { + /* handle our own drawing. */ + } + else if (ar->overlap) { + /* view should be in pixelspace */ + UI_view2d_view_restore(C); + + float back[4]; + UI_GetThemeColor4fv(colorid, back); + glClearColor(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]); + glClear(GL_COLOR_BUFFER_BIT); + } + else { + UI_ThemeClearColor(colorid); + glClear(GL_COLOR_BUFFER_BIT); + } +} + +BLI_INLINE bool streq_array_any(const char *s, const char *arr[]) +{ + for (uint i = 0; arr[i]; i++) { + if (STREQ(arr[i], s)) { + return true; + } + } + return false; +} + +static void ed_panel_draw( + const bContext *C, + ScrArea *sa, + ARegion *ar, + ListBase *lb, + PanelType *pt, + Panel *panel, + int w, + int em, + bool vertical) { - ScrArea *sa = CTX_wm_area(C); uiStyle *style = UI_style_get_dpi(); - uiBlock *block; + + /* draw panel */ + uiBlock *block = UI_block_begin(C, ar, pt->idname, UI_EMBOSS); + + bool open; + panel = UI_panel_begin(sa, ar, lb, block, pt, panel, &open); + + /* bad fixed values */ + int xco, yco, h = 0; + + if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { + /* for preset menu */ + panel->layout = UI_block_layout( + block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, + 0, (UI_UNIT_Y * 1.1f) + style->panelspace, UI_UNIT_Y, 1, 0, style); + + pt->draw_header_preset(C, panel); + + int headerend = w - UI_UNIT_X; + + UI_block_layout_resolve(block, &xco, &yco); + UI_block_translate(block, headerend - xco, 0); + panel->layout = NULL; + } + + if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { + int labelx, labely; + UI_panel_label_offset(block, &labelx, &labely); + + /* for enabled buttons */ + panel->layout = UI_block_layout( + block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, + labelx, labely, UI_UNIT_Y, 1, 0, style); + + pt->draw_header(C, panel); + + UI_block_layout_resolve(block, &xco, &yco); + panel->labelofs = xco - labelx; + panel->layout = NULL; + } + else { + panel->labelofs = 0; + } + + if (open) { + short panelContext; + + /* panel context can either be toolbar region or normal panels region */ + if (ar->regiontype == RGN_TYPE_TOOLS) + panelContext = UI_LAYOUT_TOOLBAR; + else + panelContext = UI_LAYOUT_PANEL; + + panel->layout = UI_block_layout( + block, UI_LAYOUT_VERTICAL, panelContext, + style->panelspace, 0, w - 2 * style->panelspace, em, 0, style); + + pt->draw(C, panel); + + UI_block_layout_resolve(block, &xco, &yco); + panel->layout = NULL; + + if (yco != 0) { + h = -yco + 2 * style->panelspace; + } + } + + UI_block_end(C, block); + + /* Draw child panels. */ + if (open) { + for (LinkData *link = pt->children.first; link; link = link->next) { + PanelType *child_pt = link->data; + Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt); + + if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) { + ed_panel_draw(C, sa, ar, &panel->children, child_pt, child_panel, w, em, vertical); + } + } + } + + UI_panel_end(block, w, h); +} + +/** + * \param contexts: A NULL terminated array of context strings to match against. + * Matching against any of these strings will draw the panel. + * Can be NULL to skip context checks. + */ +void ED_region_panels_layout_ex( + const bContext *C, ARegion *ar, + const char *contexts[], int contextnr, const bool vertical) +{ + ar->runtime.category = NULL; + + const WorkSpace *workspace = CTX_wm_workspace(C); + ScrArea *sa = CTX_wm_area(C); PanelType *pt; - Panel *panel; View2D *v2d = &ar->v2d; - View2DScrollers *scrollers; - int x, y, xco, yco, w, em, triangle; + int x, y, w, em; bool is_context_new = 0; - int redo; int scroll; - bool use_category_tabs = (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI)); /* XXX, should use some better check? */ + /* XXX, should use some better check? */ + bool use_category_tabs = (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI)); /* offset panels for small vertical tab area */ const char *category = NULL; const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH; int margin_x = 0; + const bool region_layout_based = ar->flag & RGN_FLAG_DYNAMIC_SIZE; BLI_SMALLSTACK_DECLARE(pt_stack, PanelType *); @@ -1809,8 +1990,18 @@ void ED_region_panels(const bContext *C, ARegion *ar, const char *context, int c /* collect panels to draw */ for (pt = ar->type->paneltypes.last; pt; pt = pt->prev) { + /* Only draw top level panels. */ + if (pt->parent) { + continue; + } + /* verify context */ - if (context && pt->context[0] && !STREQ(context, pt->context)) { + if (contexts && pt->context[0] && !streq_array_any(pt->context, contexts)) { + continue; + } + + /* If we're tagged, only use compatible. */ + if (pt->owner_id[0] && BKE_workspace_owner_id_check(workspace, pt->owner_id) == false) { continue; } @@ -1846,154 +2037,118 @@ void ED_region_panels(const bContext *C, ARegion *ar, const char *context, int c } - /* sortof hack - but we cannot predict the height of panels, until it's being generated */ - /* the layout engine works with fixed width (from v2d->cur), which is being set at end of the loop */ - /* in case scroller settings (hide flags) differ from previous, the whole loop gets done again */ - for (redo = 2; redo > 0; redo--) { - - if (vertical) { - w = BLI_rctf_size_x(&v2d->cur); - em = (ar->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */ - } - else { - w = UI_PANEL_WIDTH; - em = (ar->type->prefsizex) ? 10 : 20; - } - - w -= margin_x; + if (vertical) { + w = BLI_rctf_size_x(&v2d->cur); + em = (ar->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */ + } + else { + w = UI_PANEL_WIDTH; + em = (ar->type->prefsizex) ? 10 : 20; + } - /* create panels */ - UI_panels_begin(C, ar); + w -= margin_x; - /* set view2d view matrix - UI_block_begin() stores it */ - UI_view2d_view_ortho(v2d); + /* create panels */ + UI_panels_begin(C, ar); - BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt) - { - bool open; + /* set view2d view matrix - UI_block_begin() stores it */ + UI_view2d_view_ortho(v2d); - panel = UI_panel_find_by_type(ar, pt); + BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt) + { + Panel *panel = UI_panel_find_by_type(&ar->panels, pt); - if (use_category_tabs && pt->category[0] && !STREQ(category, pt->category)) { - if ((panel == NULL) || ((panel->flag & PNL_PIN) == 0)) { - continue; - } + if (use_category_tabs && pt->category[0] && !STREQ(category, pt->category)) { + if ((panel == NULL) || ((panel->flag & PNL_PIN) == 0)) { + continue; } + } - /* draw panel */ - block = UI_block_begin(C, ar, pt->idname, UI_EMBOSS); - panel = UI_panel_begin(sa, ar, block, pt, panel, &open); - - /* bad fixed values */ - triangle = (int)(UI_UNIT_Y * 1.1f); - - if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { - /* for enabled buttons */ - panel->layout = UI_block_layout( - block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, - triangle, (UI_UNIT_Y * 1.1f) + style->panelspace, UI_UNIT_Y, 1, 0, style); + ed_panel_draw(C, sa, ar, &ar->panels, pt, panel, w, em, vertical); + } + BLI_SMALLSTACK_ITER_END; - pt->draw_header(C, panel); + /* align panels and return size */ + UI_panels_end(C, ar, &x, &y); - UI_block_layout_resolve(block, &xco, &yco); - panel->labelofs = xco - triangle; - panel->layout = NULL; - } - else { - panel->labelofs = 0; + /* before setting the view */ + if (region_layout_based) { + /* XXX, only single panel support atm. + * Can't use x/y values calculated above because they're not using the real height of panels, + * instead they calculate offsets for the next panel to start drawing. */ + Panel *panel = ar->panels.last; + if (panel != NULL) { + int size_dyn[2] = { + UI_UNIT_X * ((panel->flag & PNL_CLOSED) ? 8 : 14), + UI_panel_size_y(panel), + }; + /* region size is layout based and needs to be updated */ + if ((ar->sizex != size_dyn[0]) || + (ar->sizey != size_dyn[1])) + { + ar->sizex = size_dyn[0]; + ar->sizey = size_dyn[1]; + sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE; } - - if (open) { - short panelContext; - - /* panel context can either be toolbar region or normal panels region */ - if (ar->regiontype == RGN_TYPE_TOOLS) - panelContext = UI_LAYOUT_TOOLBAR; - else - panelContext = UI_LAYOUT_PANEL; - - panel->layout = UI_block_layout( - block, UI_LAYOUT_VERTICAL, panelContext, - style->panelspace, 0, w - 2 * style->panelspace, em, 0, style); - - pt->draw(C, panel); - - UI_block_layout_resolve(block, &xco, &yco); - panel->layout = NULL; - - yco -= 2 * style->panelspace; - UI_panel_end(block, w, -yco); + y = ABS(ar->sizey - 1); + } + } + else if (vertical) { + /* we always keep the scroll offset - so the total view gets increased with the scrolled away part */ + if (v2d->cur.ymax < -FLT_EPSILON) { + /* Clamp to lower view boundary */ + if (v2d->tot.ymin < -v2d->winy) { + y = min_ii(y, 0); } else { - yco = 0; - UI_panel_end(block, w, 0); + y = min_ii(y, v2d->cur.ymin); } - - UI_block_end(C, block); } - BLI_SMALLSTACK_ITER_END; - - /* align panels and return size */ - UI_panels_end(C, ar, &x, &y); - /* before setting the view */ - if (vertical) { - /* we always keep the scroll offset - so the total view gets increased with the scrolled away part */ - if (v2d->cur.ymax < -FLT_EPSILON) { - /* Clamp to lower view boundary */ - if (v2d->tot.ymin < -v2d->winy) { - y = min_ii(y, 0); - } - else { - y = min_ii(y, v2d->cur.ymin); - } + y = -y; + } + else { + /* don't jump back when panels close or hide */ + if (!is_context_new) { + if (v2d->tot.xmax > v2d->winx) { + x = max_ii(x, 0); } - - y = -y; - } - else { - /* don't jump back when panels close or hide */ - if (!is_context_new) { - if (v2d->tot.xmax > v2d->winx) { - x = max_ii(x, 0); - } - else { - x = max_ii(x, v2d->cur.xmax); - } + else { + x = max_ii(x, v2d->cur.xmax); } - - y = -y; } - /* this also changes the 'cur' */ - UI_view2d_totRect_set(v2d, x, y); + y = -y; + } + + /* this also changes the 'cur' */ + UI_view2d_totRect_set(v2d, x, y); - if (scroll != v2d->scroll) { - /* Note: this code scales fine, but because of rounding differences, positions of elements - * flip +1 or -1 pixel compared to redoing the entire layout again. - * Leaving in commented code for future tests */ + if (scroll != v2d->scroll) { + /* Note: this code scales fine, but because of rounding differences, positions of elements + * flip +1 or -1 pixel compared to redoing the entire layout again. + * Leaving in commented code for future tests */ #if 0 - UI_panels_scale(ar, BLI_rctf_size_x(&v2d->cur)); - break; + UI_panels_scale(ar, BLI_rctf_size_x(&v2d->cur)); + break; #endif - } - else { - break; - } } - /* clear */ - if (ar->overlap) { - /* view should be in pixelspace */ - UI_view2d_view_restore(C); - glEnable(GL_BLEND); - UI_ThemeColor4((ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK); - glRecti(0, 0, BLI_rcti_size_x(&ar->winrct), BLI_rcti_size_y(&ar->winrct) + 1); - glDisable(GL_BLEND); + if (use_category_tabs) { + ar->runtime.category = category; } - else { - UI_ThemeClearColor((ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK); - glClear(GL_COLOR_BUFFER_BIT); +} +void ED_region_panels_layout(const bContext *C, ARegion *ar) +{ + ED_region_panels_layout_ex(C, ar, NULL, -1, true); +} + +void ED_region_panels_draw(const bContext *C, ARegion *ar) +{ + View2D *v2d = &ar->v2d; + + if (ar->alignment != RGN_ALIGN_FLOAT) { + region_clear_color(C, ar, (ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK); } /* reset line width for drawing tabs */ @@ -2008,16 +2163,34 @@ void ED_region_panels(const bContext *C, ARegion *ar, const char *context, int c /* restore view matrix */ UI_view2d_view_restore(C); - if (use_category_tabs) { - UI_panel_category_draw_all(ar, category); + /* Set in layout. */ + if (ar->runtime.category) { + UI_panel_category_draw_all(ar, ar->runtime.category); } /* scrollers */ - scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY); + View2DScrollers *scrollers = UI_view2d_scrollers_calc( + C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY); UI_view2d_scrollers_draw(C, v2d, scrollers); UI_view2d_scrollers_free(scrollers); } +void ED_region_panels_ex( + const bContext *C, ARegion *ar, + const char *contexts[], int contextnr, const bool vertical) +{ + /* TODO: remove? */ + ED_region_panels_layout_ex(C, ar, contexts, contextnr, vertical); + ED_region_panels_draw(C, ar); +} + +void ED_region_panels(const bContext *C, ARegion *ar) +{ + /* TODO: remove? */ + ED_region_panels_layout(C, ar); + ED_region_panels_draw(C, ar); +} + void ED_region_panels_init(wmWindowManager *wm, ARegion *ar) { wmKeyMap *keymap; @@ -2028,7 +2201,7 @@ void ED_region_panels_init(wmWindowManager *wm, ARegion *ar) WM_event_add_keymap_handler(&ar->handlers, keymap); } -void ED_region_header(const bContext *C, ARegion *ar) +void ED_region_header_layout(const bContext *C, ARegion *ar) { uiStyle *style = UI_style_get_dpi(); uiBlock *block; @@ -2037,16 +2210,19 @@ void ED_region_header(const bContext *C, ARegion *ar) Header header = {NULL}; int maxco, xco, yco; int headery = ED_area_headersize(); - - /* clear */ - UI_ThemeClearColor((ED_screen_area_active(C)) ? TH_HEADER : TH_HEADERDESEL); - glClear(GL_COLOR_BUFFER_BIT); + bool region_layout_based = ar->flag & RGN_FLAG_DYNAMIC_SIZE; /* set view2d view matrix for scrolling (without scrollers) */ UI_view2d_view_ortho(&ar->v2d); - xco = maxco = 0.4f * UI_UNIT_X; - yco = headery - floor(0.2f * UI_UNIT_Y); + xco = maxco = UI_HEADER_OFFSET; + yco = headery + (ar->winy - headery) / 2 - floor(0.2f * UI_UNIT_Y); + + /* XXX workaround for 1 px alignment issue. Not sure what causes it... Would prefer a proper fix - Julian */ + if (CTX_wm_area(C)->spacetype == SPACE_TOPBAR) { + xco += 1; + yco += 1; + } /* draw all headers types */ for (ht = ar->type->headertypes.first; ht; ht = ht->next) { @@ -2070,17 +2246,54 @@ void ED_region_header(const bContext *C, ARegion *ar) if (xco > maxco) maxco = xco; + int new_sizex = (maxco + UI_HEADER_OFFSET) / UI_DPI_FAC; + + if (region_layout_based && (ar->sizex != new_sizex)) { + /* region size is layout based and needs to be updated */ + ScrArea *sa = CTX_wm_area(C); + + ar->sizex = new_sizex; + sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE; + } + UI_block_end(C, block); - UI_block_draw(C, block); + } + + if (!region_layout_based) { + maxco += UI_HEADER_OFFSET; } /* always as last */ - UI_view2d_totRect_set(&ar->v2d, maxco + UI_UNIT_X + 80, headery); + UI_view2d_totRect_set(&ar->v2d, maxco, headery); + + /* restore view matrix */ + UI_view2d_view_restore(C); +} + +void ED_region_header_draw(const bContext *C, ARegion *ar) +{ + /* clear */ + region_clear_color(C, ar, region_background_color_id(C, ar)); - /* restore view matrix? */ + UI_view2d_view_ortho(&ar->v2d); + + /* View2D matrix might have changed due to dynamic sized regions. */ + UI_blocklist_update_window_matrix(C, &ar->uiblocks); + + /* draw blocks */ + UI_blocklist_draw(C, &ar->uiblocks); + + /* restore view matrix */ UI_view2d_view_restore(C); } +void ED_region_header(const bContext *C, ARegion *ar) +{ + /* TODO: remove? */ + ED_region_header_layout(C, ar); + ED_region_header_draw(C, ar); +} + void ED_region_header_init(ARegion *ar) { UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy); @@ -2092,42 +2305,132 @@ int ED_area_headersize(void) return (int)(HEADERY * UI_DPI_FAC); } -void ED_region_info_draw(ARegion *ar, const char *text, float fill_color[4], const bool full_redraw) + +int ED_area_header_alignment(const ScrArea *area) +{ + for (ARegion *ar = area->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_HEADER) { + return ar->alignment; + } + } + + return RGN_ALIGN_TOP; +} + +/** + * \return the final height of a global \a area, accounting for DPI. + */ +int ED_area_global_size_y(const ScrArea *area) +{ + BLI_assert(ED_area_is_global(area)); + return round_fl_to_int(area->global->cur_fixed_height * UI_DPI_FAC); +} + +bool ED_area_is_global(const ScrArea *area) +{ + return area->global != NULL; +} + +ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen) +{ + ScrArea *global_area = win->global_areas.areabase.first; + + if (!global_area) { + return screen->areabase.first; + } + else if ((global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) { + return global_area; + } + /* Find next visible area. */ + return ED_screen_areas_iter_next(screen, global_area); +} +ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area) +{ + if (area->global) { + for (ScrArea *area_iter = area->next; area_iter; area_iter = area_iter->next) { + if ((area_iter->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) { + return area_iter; + } + } + /* No visible next global area found, start iterating over layout areas. */ + return screen->areabase.first; + } + + return area->next; +} + +/** + * For now we just assume all global areas are made up out of horizontal bars + * with the same size. A fixed size could be stored in ARegion instead if needed. + * + * \return the DPI aware height of a single bar/region in global areas. + */ +int ED_region_global_size_y(void) +{ + return ED_area_headersize(); /* same size as header */ +} + +void ED_region_info_draw_multiline(ARegion *ar, const char *text_array[], float fill_color[4], const bool full_redraw) { const int header_height = UI_UNIT_Y; uiStyle *style = UI_style_get_dpi(); int fontid = style->widget.uifont_id; GLint scissor[4]; rcti rect; + int num_lines = 0; /* background box */ ED_region_visible_rect(ar, &rect); - rect.ymin = BLI_rcti_size_y(&ar->winrct) - header_height; - /* box fill entire width or just around text */ - if (!full_redraw) - rect.xmax = min_ii(rect.xmax, rect.xmin + BLF_width(fontid, text, BLF_DRAW_STR_DUMMY_MAX) + 1.2f * U.widget_unit); + /* Box fill entire width or just around text. */ + if (!full_redraw) { + const char **text = &text_array[0]; + while (*text) { + rect.xmax = min_ii(rect.xmax, rect.xmin + BLF_width(fontid, *text, BLF_DRAW_STR_DUMMY_MAX) + 1.2f * U.widget_unit); + text++; + num_lines++; + } + } + /* Just count the line number. */ + else { + const char **text = &text_array[0]; + while (*text) { + text++; + num_lines++; + } + } - rect.ymax = BLI_rcti_size_y(&ar->winrct); + rect.ymin = rect.ymax - header_height * num_lines; /* setup scissor */ glGetIntegerv(GL_SCISSOR_BOX, scissor); - glScissor(ar->winrct.xmin + rect.xmin, ar->winrct.ymin + rect.ymin, + glScissor(rect.xmin, rect.ymin, BLI_rcti_size_x(&rect) + 1, BLI_rcti_size_y(&rect) + 1); glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4fv(fill_color); - glRecti(rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4fv(fill_color); + immRecti(pos, rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1); + immUnbindProgram(); glDisable(GL_BLEND); /* text */ - UI_ThemeColor(TH_TEXT_HI); + UI_FontThemeColor(fontid, TH_TEXT_HI); BLF_clipping(fontid, rect.xmin, rect.ymin, rect.xmax, rect.ymax); BLF_enable(fontid, BLF_CLIPPING); - BLF_position(fontid, rect.xmin + 0.6f * U.widget_unit, rect.ymin + 0.3f * U.widget_unit, 0.0f); - - BLF_draw(fontid, text, BLF_DRAW_STR_DUMMY_MAX); + int offset = num_lines - 1; + { + const char **text = &text_array[0]; + while (*text) { + BLF_position(fontid, rect.xmin + 0.6f * U.widget_unit, rect.ymin + 0.3f * U.widget_unit + offset * header_height, 0.0f); + BLF_draw(fontid, *text, BLF_DRAW_STR_DUMMY_MAX); + text++; + offset--; + } + } BLF_disable(fontid, BLF_CLIPPING); @@ -2135,6 +2438,11 @@ void ED_region_info_draw(ARegion *ar, const char *text, float fill_color[4], con glScissor(scissor[0], scissor[1], scissor[2], scissor[3]); } +void ED_region_info_draw(ARegion *ar, const char *text, float fill_color[4], const bool full_redraw) +{ + ED_region_info_draw_multiline(ar, (const char *[2]){text, NULL}, fill_color, full_redraw); +} + #define MAX_METADATA_STR 1024 static const char *meta_data_list[] = @@ -2303,11 +2611,11 @@ void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, const rctf *frame, return; /* find window pixel coordinates of origin */ - glPushMatrix(); + gpuPushMatrix(); /* offset and zoom using ogl */ - glTranslatef(x, y, 0.0f); - glScalef(zoomx, zoomy, 1.0f); + gpuTranslate2f(x, y); + gpuScale2f(zoomx, zoomy); BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f * U.pixelsize, U.dpi); @@ -2317,17 +2625,20 @@ void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, const rctf *frame, box_y = metadata_box_height_get(ibuf, blf_mono_font, true); if (box_y) { - UI_ThemeColor(TH_METADATA_BG); - /* set up rect */ BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymax, frame->ymax + box_y); /* draw top box */ - glRectf(rect.xmin, rect.ymin, rect.xmax, rect.ymax); + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor(TH_METADATA_BG); + immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + immUnbindProgram(); BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax); BLF_enable(blf_mono_font, BLF_CLIPPING); - UI_ThemeColor(TH_METADATA_TEXT); + UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT); metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true); BLF_disable(blf_mono_font, BLF_CLIPPING); @@ -2339,23 +2650,26 @@ void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, const rctf *frame, box_y = metadata_box_height_get(ibuf, blf_mono_font, false); if (box_y) { - UI_ThemeColor(TH_METADATA_BG); - /* set up box rect */ BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymin - box_y, frame->ymin); /* draw top box */ - glRectf(rect.xmin, rect.ymin, rect.xmax, rect.ymax); + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor(TH_METADATA_BG); + immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + immUnbindProgram(); BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax); BLF_enable(blf_mono_font, BLF_CLIPPING); - UI_ThemeColor(TH_METADATA_TEXT); + UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT); metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false); BLF_disable(blf_mono_font, BLF_CLIPPING); } - glPopMatrix(); + gpuPopMatrix(); } void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy) @@ -2365,11 +2679,16 @@ void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy) int x1, y1, x2, y2; /* the image is located inside (0, 0), (1, 1) as set by view2d */ - UI_ThemeColorShade(TH_BACK, 20); - UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &x1, &y1); UI_view2d_view_to_region(&ar->v2d, 1.0f, 1.0f, &x2, &y2); - glRectf(x1, y1, x2, y2); + + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, 20); + immRectf(pos, x1, y1, x2, y2); + immUnbindProgram(); /* gridsize adapted to zoom level */ gridsize = 0.5f * (zoomx + zoomy); @@ -2389,33 +2708,58 @@ void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy) } } - /* the fine resolution level */ blendfac = 0.25f * gridsize - floorf(0.25f * gridsize); CLAMP(blendfac, 0.0f, 1.0f); - UI_ThemeColorShade(TH_BACK, (int)(20.0f * (1.0f - blendfac))); - fac = 0.0f; - glBegin(GL_LINES); - while (fac < 1.0f) { - glVertex2f(x1, y1 * (1.0f - fac) + y2 * fac); - glVertex2f(x2, y1 * (1.0f - fac) + y2 * fac); - glVertex2f(x1 * (1.0f - fac) + x2 * fac, y1); - glVertex2f(x1 * (1.0f - fac) + x2 * fac, y2); - fac += gridstep; - } + int count_fine = 1.0f / gridstep; + int count_large = 1.0f / (4.0f * gridstep); + + if (count_fine > 0) { + GWN_vertformat_clear(format); + pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + unsigned color = GWN_vertformat_attr_add(format, "color", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(GWN_PRIM_LINES, 4 * count_fine + 4 * count_large); + + float theme_color[3]; + UI_GetThemeColorShade3fv(TH_BACK, (int)(20.0f * (1.0f - blendfac)), theme_color); + fac = 0.0f; + + /* the fine resolution level */ + for (int i = 0; i < count_fine; i++) { + immAttrib3fv(color, theme_color); + immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac); + immAttrib3fv(color, theme_color); + immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac); + immAttrib3fv(color, theme_color); + immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1); + immAttrib3fv(color, theme_color); + immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2); + fac += gridstep; + } - /* the large resolution level */ - UI_ThemeColor(TH_BACK); + if (count_large > 0) { + UI_GetThemeColor3fv(TH_BACK, theme_color); + fac = 0.0f; + + /* the large resolution level */ + for (int i = 0; i < count_large; i++) { + immAttrib3fv(color, theme_color); + immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac); + immAttrib3fv(color, theme_color); + immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac); + immAttrib3fv(color, theme_color); + immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1); + immAttrib3fv(color, theme_color); + immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2); + fac += 4.0f * gridstep; + } + } - fac = 0.0f; - while (fac < 1.0f) { - glVertex2f(x1, y1 * (1.0f - fac) + y2 * fac); - glVertex2f(x2, y1 * (1.0f - fac) + y2 * fac); - glVertex2f(x1 * (1.0f - fac) + x2 * fac, y1); - glVertex2f(x1 * (1.0f - fac) + x2 * fac, y2); - fac += 4.0f * gridstep; + immEnd(); + immUnbindProgram(); } - glEnd(); } /* If the area has overlapping regions, it returns visible rect for Region *ar */ @@ -2434,14 +2778,32 @@ void ED_region_visible_rect(ARegion *ar, rcti *rect) for (; arn; arn = arn->next) { if (ar != arn && arn->overlap) { if (BLI_rcti_isect(rect, &arn->winrct, NULL)) { + if (ELEM(arn->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { + /* Overlap left, also check 1 pixel offset (2 regions on one side). */ + if (ABS(rect->xmin - arn->winrct.xmin) < 2) { + rect->xmin = arn->winrct.xmax; + } - /* overlap left, also check 1 pixel offset (2 regions on one side) */ - if (ABS(rect->xmin - arn->winrct.xmin) < 2) - rect->xmin = arn->winrct.xmax; - - /* overlap right */ - if (ABS(rect->xmax - arn->winrct.xmax) < 2) - rect->xmax = arn->winrct.xmin; + /* Overlap right. */ + if (ABS(rect->xmax - arn->winrct.xmax) < 2) { + rect->xmax = arn->winrct.xmin; + } + } + else if (ELEM(arn->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { + /* Same logic as above for vertical regions. */ + if (ABS(rect->ymin - arn->winrct.ymin) < 2) { + rect->ymin = arn->winrct.ymax; + } + if (ABS(rect->ymax - arn->winrct.ymax) < 2) { + rect->ymax = arn->winrct.ymin; + } + } + else if (arn->alignment == RGN_ALIGN_FLOAT) { + /* Skip floating. */ + } + else { + BLI_assert(!"Region overlap with unknown alignment"); + } } } } @@ -2452,8 +2814,11 @@ void ED_region_visible_rect(ARegion *ar, rcti *rect) void ED_region_cache_draw_background(const ARegion *ar) { - glColor4ub(128, 128, 255, 64); - glRecti(0, 0, ar->winx, 8 * UI_DPI_FAC); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ub(128, 128, 255, 64); + immRecti(pos, 0, 0, ar->winx, 8 * UI_DPI_FAC); + immUnbindProgram(); } void ED_region_cache_draw_curfra_label(const int framenr, const float x, const float y) @@ -2469,9 +2834,13 @@ void ED_region_cache_draw_curfra_label(const int framenr, const float x, const f BLF_width_and_height(fontid, numstr, sizeof(numstr), &font_dims[0], &font_dims[1]); - glRecti(x, y, x + font_dims[0] + 6.0f, y + font_dims[1] + 4.0f); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor(TH_CFRAME); + immRecti(pos, x, y, x + font_dims[0] + 6.0f, y + font_dims[1] + 4.0f); + immUnbindProgram(); - UI_ThemeColor(TH_TEXT); + UI_FontThemeColor(fontid, TH_TEXT); BLF_position(fontid, x + 2.0f, y + 2.0f, 0.0f); BLF_draw(fontid, numstr, sizeof(numstr)); } @@ -2479,17 +2848,73 @@ void ED_region_cache_draw_curfra_label(const int framenr, const float x, const f void ED_region_cache_draw_cached_segments(const ARegion *ar, const int num_segments, const int *points, const int sfra, const int efra) { if (num_segments) { - int a; + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ub(128, 128, 255, 128); - glColor4ub(128, 128, 255, 128); + for (int a = 0; a < num_segments; a++) { + float x1 = (float)(points[a * 2] - sfra) / (efra - sfra + 1) * ar->winx; + float x2 = (float)(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * ar->winx; - for (a = 0; a < num_segments; a++) { - float x1, x2; + immRecti(pos, x1, 0, x2, 8 * UI_DPI_FAC); + /* TODO(merwin): use primitive restart to draw multiple rects more efficiently */ + } - x1 = (float)(points[a * 2] - sfra) / (efra - sfra + 1) * ar->winx; - x2 = (float)(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * ar->winx; + immUnbindProgram(); + } +} - glRecti(x1, 0, x2, 8 * UI_DPI_FAC); +/** + * Generate subscriptions for this region. + */ +void ED_region_message_subscribe( + bContext *C, + struct WorkSpace *workspace, struct Scene *scene, + struct bScreen *screen, struct ScrArea *sa, struct ARegion *ar, + struct wmMsgBus *mbus) +{ + if (ar->manipulator_map != NULL) { + WM_manipulatormap_message_subscribe(C, ar->manipulator_map, ar, mbus); + } + + if (BLI_listbase_is_empty(&ar->uiblocks)) { + UI_region_message_subscribe(ar, mbus); + } + + if (ar->type->message_subscribe != NULL) { + ar->type->message_subscribe(C, workspace, scene, screen, sa, ar, mbus); + } +} + +int ED_region_snap_size_test(const ARegion *ar) +{ + /* Use a larger value because toggling scrollbars can jump in size. */ + const int snap_match_threshold = 16; + if (ar->type->snap_size != NULL) { + return ((((ar->sizex - ar->type->snap_size(ar, ar->sizex, 0)) <= snap_match_threshold) << 0) | + (((ar->sizey - ar->type->snap_size(ar, ar->sizey, 1)) <= snap_match_threshold) << 1)); + } + return 0; +} + +bool ED_region_snap_size_apply(ARegion *ar, int snap_flag) +{ + bool changed = false; + if (ar->type->snap_size != NULL) { + if (snap_flag & (1 << 0)) { + short snap_size = ar->type->snap_size(ar, ar->sizex, 0); + if (snap_size != ar->sizex) { + ar->sizex = snap_size; + changed = true; + } + } + if (snap_flag & (1 << 1)) { + short snap_size = ar->type->snap_size(ar, ar->sizey, 1); + if (snap_size != ar->sizey) { + ar->sizey = snap_size; + changed = true; + } } } + return changed; } diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 6e9a580e403..1d73566e5a8 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -49,133 +49,11 @@ #include "IMB_imbuf_types.h" #include "GPU_basic_shader.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" #include "UI_interface.h" -#ifndef GL_CLAMP_TO_EDGE -#define GL_CLAMP_TO_EDGE 0x812F -#endif - -/* UNUSED */ -#if 0 -void fdrawbezier(float vec[4][3]) -{ - float dist; - float curve_res = 24, spline_step = 0.0f; - - dist = 0.5f * fabsf(vec[0][0] - vec[3][0]); - - /* check direction later, for top sockets */ - vec[1][0] = vec[0][0] + dist; - vec[1][1] = vec[0][1]; - - vec[2][0] = vec[3][0] - dist; - vec[2][1] = vec[3][1]; - /* we can reuse the dist variable here to increment the GL curve eval amount */ - dist = 1.0f / curve_res; - - cpack(0x0); - glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, vec[0]); - glBegin(GL_LINE_STRIP); - while (spline_step < 1.000001f) { -#if 0 - if (do_shaded) - UI_ThemeColorBlend(th_col1, th_col2, spline_step); -#endif - glEvalCoord1f(spline_step); - spline_step += dist; - } - glEnd(); -} -#endif - -void fdrawline(float x1, float y1, float x2, float y2) -{ - glBegin(GL_LINES); - glVertex2f(x1, y1); - glVertex2f(x2, y2); - glEnd(); -} - -void fdrawbox(float x1, float y1, float x2, float y2) -{ - glBegin(GL_LINE_LOOP); - - glVertex2f(x1, y1); - glVertex2f(x1, y2); - glVertex2f(x2, y2); - glVertex2f(x2, y1); - - glEnd(); -} - -void fdrawcheckerboard(float x1, float y1, float x2, float y2) -{ - unsigned char col1[4] = {40, 40, 40}, col2[4] = {50, 50, 50}; - - glColor3ubv(col1); - glRectf(x1, y1, x2, y2); - glColor3ubv(col2); - - GPU_basic_shader_bind(GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_CHECKER_8PX); - glRectf(x1, y1, x2, y2); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); -} - -void sdrawline(int x1, int y1, int x2, int y2) -{ - glBegin(GL_LINES); - glVertex2i(x1, y1); - glVertex2i(x2, y2); - glEnd(); -} - -/* UNUSED */ -#if 0 -/* - * x1,y2 - * | \ - * | \ - * | \ - * x1,y1-- x2,y1 - */ - -static void sdrawtripoints(int x1, int y1, int x2, int y2) -{ - glVertex2i(x1, y1); - glVertex2i(x1, y2); - glVertex2i(x2, y1); -} - -void sdrawtri(int x1, int y1, int x2, int y2) -{ - glBegin(GL_LINE_STRIP); - sdrawtripoints(x1, y1, x2, y2); - glEnd(); -} - -void sdrawtrifill(int x1, int y1, int x2, int y2) -{ - glBegin(GL_TRIANGLES); - sdrawtripoints(x1, y1, x2, y2); - glEnd(); -} -#endif - -void sdrawbox(int x1, int y1, int x2, int y2) -{ - glBegin(GL_LINE_LOOP); - - glVertex2i(x1, y1); - glVertex2i(x1, y2); - glVertex2i(x2, y2); - glVertex2i(x2, y1); - - glEnd(); -} - - /* ******************************************** */ void setlinestyle(int nr) @@ -204,122 +82,6 @@ void set_inverted_drawing(int enable) GL_TOGGLE(GL_DITHER, !enable); } -/* UNUSED */ -#if 0 -void sdrawXORline(int x0, int y0, int x1, int y1) -{ - if (x0 == x1 && y0 == y1) return; - - set_inverted_drawing(1); - - glBegin(GL_LINES); - glVertex2i(x0, y0); - glVertex2i(x1, y1); - glEnd(); - - set_inverted_drawing(0); -} - -void sdrawXORline4(int nr, int x0, int y0, int x1, int y1) -{ - static int old[4][2][2]; - static char flags[4] = {0, 0, 0, 0}; - - /* with builtin memory, max 4 lines */ - - set_inverted_drawing(1); - - glBegin(GL_LINES); - if (nr == -1) { /* flush */ - for (nr = 0; nr < 4; nr++) { - if (flags[nr]) { - glVertex2iv(old[nr][0]); - glVertex2iv(old[nr][1]); - flags[nr] = 0; - } - } - } - else { - if (nr >= 0 && nr < 4) { - if (flags[nr]) { - glVertex2iv(old[nr][0]); - glVertex2iv(old[nr][1]); - } - - old[nr][0][0] = x0; - old[nr][0][1] = y0; - old[nr][1][0] = x1; - old[nr][1][1] = y1; - - flags[nr] = 1; - } - - glVertex2i(x0, y0); - glVertex2i(x1, y1); - } - glEnd(); - - set_inverted_drawing(0); -} - -void fdrawXORellipse(float xofs, float yofs, float hw, float hh) -{ - if (hw == 0) return; - - set_inverted_drawing(1); - - glPushMatrix(); - glTranslatef(xofs, yofs, 0.0f); - glScalef(1.0f, hh / hw, 1.0f); - glutil_draw_lined_arc(0.0, M_PI * 2.0, hw, 20); - glPopMatrix(); - - set_inverted_drawing(0); -} - -#endif - -void fdrawXORcirc(float xofs, float yofs, float rad) -{ - set_inverted_drawing(1); - - glPushMatrix(); - glTranslatef(xofs, yofs, 0.0); - glutil_draw_lined_arc(0.0, M_PI * 2.0, rad, 20); - glPopMatrix(); - - set_inverted_drawing(0); -} - -void glutil_draw_filled_arc(float start, float angle, float radius, int nsegments) -{ - int i; - - glBegin(GL_TRIANGLE_FAN); - glVertex2f(0.0, 0.0); - for (i = 0; i < nsegments; i++) { - float t = (float) i / (nsegments - 1); - float cur = start + t * angle; - - glVertex2f(cosf(cur) * radius, sinf(cur) * radius); - } - glEnd(); -} - -void glutil_draw_lined_arc(float start, float angle, float radius, int nsegments) -{ - int i; - - glBegin(GL_LINE_STRIP); - for (i = 0; i < nsegments; i++) { - float t = (float) i / (nsegments - 1); - float cur = start + t * angle; - - glVertex2f(cosf(cur) * radius, sinf(cur) * radius); - } - glEnd(); -} - float glaGetOneFloat(int param) { GLfloat v; @@ -375,25 +137,68 @@ static int get_cached_work_texture(int *r_w, int *r_h) return texid; } -void glaDrawPixelsTexScaled_clipping(float x, float y, int img_w, int img_h, +static void immDrawPixelsTexSetupAttributes(IMMDrawPixelsTexState *state) +{ + Gwn_VertFormat *vert_format = immVertexFormat(); + state->pos = GWN_vertformat_attr_add(vert_format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + state->texco = GWN_vertformat_attr_add(vert_format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); +} + +/* To be used before calling immDrawPixelsTex + * Default shader is GPU_SHADER_2D_IMAGE_COLOR + * You can still set uniforms with : + * GPU_shader_uniform_int(shader, GPU_shader_get_uniform(shader, "name"), 0); + * */ +IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin) +{ + IMMDrawPixelsTexState state; + immDrawPixelsTexSetupAttributes(&state); + + state.shader = GPU_shader_get_builtin_shader(builtin); + + /* Shader will be unbind by immUnbindProgram in immDrawPixelsTexScaled_clipping */ + immBindBuiltinProgram(builtin); + immUniform1i("image", 0); + state.do_shader_unbind = true; + + return state; +} + +/* Use the currently bound shader. + * + * Use immDrawPixelsTexSetup to bind the shader you + * want before calling immDrawPixelsTex. + * + * If using a special shader double check it uses the same + * attributes "pos" "texCoord" and uniform "image". + * + * If color is NULL then use white by default + * + * Be also aware that this function unbinds the shader when + * it's finished. + * */ +void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, + float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, float scaleX, float scaleY, float clip_min_x, float clip_min_y, - float clip_max_x, float clip_max_y) + float clip_max_x, float clip_max_y, + float xzoom, float yzoom, float color[4]) { unsigned char *uc_rect = (unsigned char *) rect; const float *f_rect = (float *)rect; - float xzoom = glaGetOneFloat(GL_ZOOM_X), yzoom = glaGetOneFloat(GL_ZOOM_Y); int subpart_x, subpart_y, tex_w, tex_h; int seamless, offset_x, offset_y, nsubparts_x, nsubparts_y; int texid = get_cached_work_texture(&tex_w, &tex_h); int components; const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y)); + float white[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + GLint unpack_row_length; + glGetIntegerv(GL_UNPACK_ROW_LENGTH, &unpack_row_length); - /* Specify the color outside this function, and tex will modulate it. - * This is useful for changing alpha without using glPixelTransferf() - */ glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texid); /* don't want nasty border artifacts */ @@ -401,12 +206,6 @@ void glaDrawPixelsTexScaled_clipping(float x, float y, int img_w, int img_h, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, zoomfilter); -#if defined(__APPLE__) && 0 - /* [merwin] disable this workaround and see if anyone is affected. If not scrap it! Also at end of this function */ - /* workaround for os x 10.5/10.6 driver bug: http://lists.apple.com/archives/Mac-opengl/2008/Jul/msg00117.html */ - glPixelZoom(1.0f, 1.0f); -#endif - /* setup seamless 2=on, 0=off */ seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0; @@ -420,7 +219,7 @@ void glaDrawPixelsTexScaled_clipping(float x, float y, int img_w, int img_h, components = 4; else if (format == GL_RGB) components = 3; - else if (ELEM(format, GL_LUMINANCE, GL_ALPHA)) + else if (format == GL_RED) components = 1; else { BLI_assert(!"Incompatible format passed to glaDrawPixelsTexScaled"); @@ -429,20 +228,23 @@ void glaDrawPixelsTexScaled_clipping(float x, float y, int img_w, int img_h, if (type == GL_FLOAT) { /* need to set internal format to higher range float */ - - /* NOTE: this could fail on some drivers, like mesa, - * but currently this code is only used by color - * management stuff which already checks on whether - * it's possible to use GL_RGBA16F_ARB - */ - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, tex_w, tex_h, 0, format, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, tex_w, tex_h, 0, format, GL_FLOAT, NULL); } else { /* switch to 8bit RGBA for byte buffer */ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, format, GL_UNSIGNED_BYTE, NULL); } + unsigned int pos = state->pos, texco = state->texco; + + /* optional */ + /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since + * it does not need color. + */ + if (state->shader != NULL && GPU_shader_get_uniform(state->shader, "color") != -1) { + immUniformColor4fv((color) ? color : white); + } + for (subpart_y = 0; subpart_y < nsubparts_y; subpart_y++) { for (subpart_x = 0; subpart_x < nsubparts_x; subpart_x++) { int remainder_x = img_w - subpart_x * offset_x; @@ -494,325 +296,55 @@ void glaDrawPixelsTexScaled_clipping(float x, float y, int img_w, int img_h, glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); } - GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR); - glBegin(GL_QUADS); - glTexCoord2f((float)(0 + offset_left) / tex_w, (float)(0 + offset_bot) / tex_h); - glVertex2f(rast_x + (float)offset_left * xzoom, rast_y + (float)offset_bot * yzoom); + immBegin(GWN_PRIM_TRI_FAN, 4); + immAttrib2f(texco, (float)(0 + offset_left) / tex_w, (float)(0 + offset_bot) / tex_h); + immVertex2f(pos, rast_x + (float)offset_left * xzoom, rast_y + (float)offset_bot * yzoom); - glTexCoord2f((float)(subpart_w - offset_right) / tex_w, (float)(0 + offset_bot) / tex_h); - glVertex2f(rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX, rast_y + (float)offset_bot * yzoom); + immAttrib2f(texco, (float)(subpart_w - offset_right) / tex_w, (float)(0 + offset_bot) / tex_h); + immVertex2f(pos, rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX, rast_y + (float)offset_bot * yzoom); - glTexCoord2f((float)(subpart_w - offset_right) / tex_w, (float)(subpart_h - offset_top) / tex_h); - glVertex2f(rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY); + immAttrib2f(texco, (float)(subpart_w - offset_right) / tex_w, (float)(subpart_h - offset_top) / tex_h); + immVertex2f(pos, rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY); - glTexCoord2f((float)(0 + offset_left) / tex_w, (float)(subpart_h - offset_top) / tex_h); - glVertex2f(rast_x + (float)offset_left * xzoom, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY); - glEnd(); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immAttrib2f(texco, (float)(0 + offset_left) / tex_w, (float)(subpart_h - offset_top) / tex_h); + immVertex2f(pos, rast_x + (float)offset_left * xzoom, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY); + immEnd(); } } - glBindTexture(GL_TEXTURE_2D, 0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + if (state->do_shader_unbind) { + immUnbindProgram(); + } -#if defined(__APPLE__) && 0 - /* workaround for os x 10.5/10.6 driver bug (above) */ - glPixelZoom(xzoom, yzoom); -#endif + glBindTexture(GL_TEXTURE_2D, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, unpack_row_length); } -void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, +void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, + float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, - float scaleX, float scaleY) + float scaleX, float scaleY, float xzoom, float yzoom, float color[4]) { - glaDrawPixelsTexScaled_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, - scaleX, scaleY, 0.0f, 0.0f, 0.0f, 0.0f); + immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect, + scaleX, scaleY, 0.0f, 0.0f, 0.0f, 0.0f, xzoom, yzoom, color); } -void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect) +void immDrawPixelsTex(IMMDrawPixelsTexState *state, + float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, + float xzoom, float yzoom, float color[4]) { - glaDrawPixelsTexScaled_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f, - 0.0f, 0.0f, 0.0f, 0.0f); + immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, xzoom, yzoom, color); } -void glaDrawPixelsTex_clipping(float x, float y, int img_w, int img_h, +void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, + float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, - float clip_min_x, float clip_min_y, float clip_max_x, float clip_max_y) -{ - glaDrawPixelsTexScaled_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f, - clip_min_x, clip_min_y, clip_max_x, clip_max_y); -} - -void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int format, int type, void *rect) -{ - float xzoom = glaGetOneFloat(GL_ZOOM_X); - float yzoom = glaGetOneFloat(GL_ZOOM_Y); - - /* The pixel space coordinate of the intersection of - * the [zoomed] image with the origin. - */ - float ix = -x / xzoom; - float iy = -y / yzoom; - - /* The maximum pixel amounts the image can be cropped - * at the lower left without exceeding the origin. - */ - int off_x = floor(max_ff(ix, 0.0f)); - int off_y = floor(max_ff(iy, 0.0f)); - - /* The zoomed space coordinate of the raster position - * (starting at the lower left most unclipped pixel). - */ - float rast_x = x + off_x * xzoom; - float rast_y = y + off_y * yzoom; - - GLfloat viewport[4]; - int draw_w, draw_h; - - /* Determine the smallest number of pixels we need to draw - * before the image would go off the upper right corner. - * - * It may seem this is just an optimization but some graphics - * cards (ATI) freak out if there is a large zoom factor and - * a large number of pixels off the screen (probably at some - * level the number of image pixels to draw is getting multiplied - * by the zoom and then clamped). Making sure we draw the - * fewest pixels possible keeps everyone mostly happy (still - * fails if we zoom in on one really huge pixel so that it - * covers the entire screen). - */ - glGetFloatv(GL_VIEWPORT, viewport); - draw_w = min_ii(img_w - off_x, ceil((viewport[2] - rast_x) / xzoom)); - draw_h = min_ii(img_h - off_y, ceil((viewport[3] - rast_y) / yzoom)); - - if (draw_w > 0 && draw_h > 0) { - - int bound_options; - GPU_BASIC_SHADER_DISABLE_AND_STORE(bound_options); - - /* Don't use safe RasterPos (slower) if we can avoid it. */ - if (rast_x >= 0 && rast_y >= 0) { - glRasterPos2f(rast_x, rast_y); - } - else { - glaRasterPosSafe2f(rast_x, rast_y, 0, 0); - } - - glPixelStorei(GL_UNPACK_ROW_LENGTH, row_w); - if (format == GL_LUMINANCE || format == GL_RED) { - if (type == GL_FLOAT) { - const float *f_rect = (float *)rect; - glDrawPixels(draw_w, draw_h, format, type, f_rect + (off_y * row_w + off_x)); - } - else if (type == GL_INT || type == GL_UNSIGNED_INT) { - const int *i_rect = (int *)rect; - glDrawPixels(draw_w, draw_h, format, type, i_rect + (off_y * row_w + off_x)); - } - } - else { /* RGBA */ - if (type == GL_FLOAT) { - const float *f_rect = (float *)rect; - glDrawPixels(draw_w, draw_h, format, type, f_rect + (off_y * row_w + off_x) * 4); - } - else if (type == GL_UNSIGNED_BYTE) { - unsigned char *uc_rect = (unsigned char *) rect; - glDrawPixels(draw_w, draw_h, format, type, uc_rect + (off_y * row_w + off_x) * 4); - } - } - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - GPU_BASIC_SHADER_ENABLE_AND_RESTORE(bound_options); - } -} - -/* uses either DrawPixelsSafe or DrawPixelsTex, based on user defined maximum */ -void glaDrawPixelsAuto_clipping(float x, float y, int img_w, int img_h, - int format, int type, int zoomfilter, void *rect, - float clip_min_x, float clip_min_y, - float clip_max_x, float clip_max_y) -{ - if (U.image_draw_method != IMAGE_DRAW_METHOD_DRAWPIXELS) { - glColor4f(1.0, 1.0, 1.0, 1.0); - glaDrawPixelsTex_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, - clip_min_x, clip_min_y, clip_max_x, clip_max_y); - } - else { - glaDrawPixelsSafe(x, y, img_w, img_h, img_w, format, type, rect); - } -} - -void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect) -{ - glaDrawPixelsAuto_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, - 0.0f, 0.0f, 0.0f, 0.0f); -} - -/* 2D Drawing Assistance */ - -void glaDefine2DArea(rcti *screen_rect) -{ - const int sc_w = BLI_rcti_size_x(screen_rect) + 1; - const int sc_h = BLI_rcti_size_y(screen_rect) + 1; - - glViewport(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h); - glScissor(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h); - - /* The GLA_PIXEL_OFS magic number is to shift the matrix so that - * both raster and vertex integer coordinates fall at pixel - * centers properly. For a longer discussion see the OpenGL - * Programming Guide, Appendix H, Correctness Tips. - */ - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, sc_w, 0.0, sc_h, -1, 1); - glTranslatef(GLA_PIXEL_OFS, GLA_PIXEL_OFS, 0.0); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} - -#if 0 /* UNUSED */ - -struct gla2DDrawInfo { - int orig_vp[4], orig_sc[4]; - float orig_projmat[16], orig_viewmat[16]; - - rcti screen_rect; - rctf world_rect; - - float wo_to_sc[2]; -}; - -void gla2DGetMap(gla2DDrawInfo *di, rctf *rect) -{ - *rect = di->world_rect; -} - -void gla2DSetMap(gla2DDrawInfo *di, rctf *rect) -{ - int sc_w, sc_h; - float wo_w, wo_h; - - di->world_rect = *rect; - - sc_w = BLI_rcti_size_x(&di->screen_rect); - sc_h = BLI_rcti_size_y(&di->screen_rect); - wo_w = BLI_rcti_size_x(&di->world_rect); - wo_h = BLI_rcti_size_y(&di->world_rect); - - di->wo_to_sc[0] = sc_w / wo_w; - di->wo_to_sc[1] = sc_h / wo_h; -} - -/** Save the current OpenGL state and initialize OpenGL for 2D - * rendering. glaEnd2DDraw should be called on the returned structure - * to free it and to return OpenGL to its previous state. The - * scissor rectangle is set to match the viewport. - * - * See glaDefine2DArea for an explanation of why this function uses integers. - * - * \param screen_rect The screen rectangle to be used for 2D drawing. - * \param world_rect The world rectangle that the 2D area represented - * by \a screen_rect is supposed to represent. If NULL it is assumed the - * world has a 1 to 1 mapping to the screen. - */ -gla2DDrawInfo *glaBegin2DDraw(rcti *screen_rect, rctf *world_rect) + float clip_min_x, float clip_min_y, float clip_max_x, float clip_max_y, + float xzoom, float yzoom, float color[4]) { - gla2DDrawInfo *di = MEM_mallocN(sizeof(*di), "gla2DDrawInfo"); - int sc_w, sc_h; - float wo_w, wo_h; - - glGetIntegerv(GL_VIEWPORT, (GLint *)di->orig_vp); - glGetIntegerv(GL_SCISSOR_BOX, (GLint *)di->orig_sc); - glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat *)di->orig_projmat); - glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)di->orig_viewmat); - - di->screen_rect = *screen_rect; - if (world_rect) { - di->world_rect = *world_rect; - } - else { - di->world_rect.xmin = di->screen_rect.xmin; - di->world_rect.ymin = di->screen_rect.ymin; - di->world_rect.xmax = di->screen_rect.xmax; - di->world_rect.ymax = di->screen_rect.ymax; - } - - sc_w = BLI_rcti_size_x(&di->screen_rect); - sc_h = BLI_rcti_size_y(&di->screen_rect); - wo_w = BLI_rcti_size_x(&di->world_rect); - wo_h = BLI_rcti_size_y(&di->world_rect); - - di->wo_to_sc[0] = sc_w / wo_w; - di->wo_to_sc[1] = sc_h / wo_h; - - glaDefine2DArea(&di->screen_rect); - - return di; -} - -/** - * Translate the (\a wo_x, \a wo_y) point from world coordinates into screen space. - */ -void gla2DDrawTranslatePt(gla2DDrawInfo *di, float wo_x, float wo_y, int *r_sc_x, int *r_sc_y) -{ - *r_sc_x = (wo_x - di->world_rect.xmin) * di->wo_to_sc[0]; - *r_sc_y = (wo_y - di->world_rect.ymin) * di->wo_to_sc[1]; -} - -/** - * Translate the \a world point from world coordinates into screen space. - */ -void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int r_screen[2]) -{ - screen_r[0] = (world[0] - di->world_rect.xmin) * di->wo_to_sc[0]; - screen_r[1] = (world[1] - di->world_rect.ymin) * di->wo_to_sc[1]; -} - -/** - * Restores the previous OpenGL state and frees the auxiliary gla data. - */ -void glaEnd2DDraw(gla2DDrawInfo *di) -{ - glViewport(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]); - glScissor(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(di->orig_projmat); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(di->orig_viewmat); - - MEM_freeN(di); -} -#endif - - -/* Uses current OpenGL state to get view matrices for gluProject/gluUnProject */ -void bgl_get_mats(bglMats *mats) -{ - const double badvalue = 1.0e-6; - - glGetDoublev(GL_MODELVIEW_MATRIX, mats->modelview); - glGetDoublev(GL_PROJECTION_MATRIX, mats->projection); - glGetIntegerv(GL_VIEWPORT, (GLint *)mats->viewport); - - /* Very strange code here - it seems that certain bad values in the - * modelview matrix can cause gluUnProject to give bad results. */ - if (mats->modelview[0] < badvalue && - mats->modelview[0] > -badvalue) - { - mats->modelview[0] = 0; - } - if (mats->modelview[5] < badvalue && - mats->modelview[5] > -badvalue) - { - mats->modelview[5] = 0; - } - - /* Set up viewport so that gluUnProject will give correct values */ - mats->viewport[0] = 0; - mats->viewport[1] = 0; + immDrawPixelsTexScaled_clipping(state, x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f, + clip_min_x, clip_min_y, clip_max_x, clip_max_y, xzoom, yzoom, color); } /* *************** glPolygonOffset hack ************* */ @@ -831,8 +363,7 @@ void bglPolygonOffset(float viewdist, float dist) // glPolygonOffset(-1.0, -1.0); /* hack below is to mimic polygon offset */ - glMatrixMode(GL_PROJECTION); - glGetFloatv(GL_PROJECTION_MATRIX, (float *)winmat); + gpuGetProjectionMatrix(winmat); /* dist is from camera to center point */ @@ -863,17 +394,13 @@ void bglPolygonOffset(float viewdist, float dist) winmat[14] -= offs; offset += offs; - - glLoadMatrixf(winmat); - glMatrixMode(GL_MODELVIEW); } else { - glMatrixMode(GL_PROJECTION); winmat[14] += offset; offset = 0.0; - glLoadMatrixf(winmat); - glMatrixMode(GL_MODELVIEW); } + + gpuLoadProjectionMatrix(winmat); } /* **** Color management helper functions for GLSL display/transform ***** */ @@ -883,7 +410,8 @@ void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter, ColorManagedViewSettings *view_settings, ColorManagedDisplaySettings *display_settings, float clip_min_x, float clip_min_y, - float clip_max_x, float clip_max_y) + float clip_max_x, float clip_max_y, + float zoom_x, float zoom_y) { bool force_fallback = false; bool need_fallback = true; @@ -902,6 +430,11 @@ void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter, if (force_fallback == false) { int ok; + IMMDrawPixelsTexState state = {0}; + /* We want GLSL state to be fully handled by OCIO. */ + state.do_shader_unbind = false; + immDrawPixelsTexSetupAttributes(&state); + if (ibuf->rect_float) { if (ibuf->float_colorspace) { ok = IMB_colormanagement_setup_glsl_draw_from_space(view_settings, display_settings, @@ -920,8 +453,6 @@ void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter, } if (ok) { - glColor4f(1.0, 1.0, 1.0, 1.0); - if (ibuf->rect_float) { int format = 0; @@ -933,16 +464,20 @@ void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter, BLI_assert(!"Incompatible number of channels for GLSL display"); if (format != 0) { - glaDrawPixelsTex_clipping(x, y, ibuf->x, ibuf->y, format, GL_FLOAT, + immDrawPixelsTex_clipping(&state, + x, y, ibuf->x, ibuf->y, format, GL_FLOAT, zoomfilter, ibuf->rect_float, - clip_min_x, clip_min_y, clip_max_x, clip_max_y); + clip_min_x, clip_min_y, clip_max_x, clip_max_y, + zoom_x, zoom_y, NULL); } } else if (ibuf->rect) { /* ibuf->rect is always RGBA */ - glaDrawPixelsTex_clipping(x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, + immDrawPixelsTex_clipping(&state, + x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, zoomfilter, ibuf->rect, - clip_min_x, clip_min_y, clip_max_x, clip_max_y); + clip_min_x, clip_min_y, clip_max_x, clip_max_y, + zoom_x, zoom_y, NULL); } IMB_colormanagement_finish_glsl_draw(); @@ -959,9 +494,12 @@ void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter, display_buffer = IMB_display_buffer_acquire(ibuf, view_settings, display_settings, &cache_handle); if (display_buffer) { - glaDrawPixelsAuto_clipping(x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, - zoomfilter, display_buffer, - clip_min_x, clip_min_y, clip_max_x, clip_max_y); + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTex_clipping(&state, + x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, + zoomfilter, display_buffer, + clip_min_x, clip_min_y, clip_max_x, clip_max_y, + zoom_x, zoom_y, NULL); } IMB_display_buffer_release(cache_handle); @@ -970,10 +508,11 @@ void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter, void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, ColorManagedViewSettings *view_settings, - ColorManagedDisplaySettings *display_settings) + ColorManagedDisplaySettings *display_settings, + float zoom_x, float zoom_y) { glaDrawImBuf_glsl_clipping(ibuf, x, y, zoomfilter, view_settings, display_settings, - 0.0f, 0.0f, 0.0f, 0.0f); + 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y); } void glaDrawImBuf_glsl_ctx_clipping(const bContext *C, @@ -981,7 +520,8 @@ void glaDrawImBuf_glsl_ctx_clipping(const bContext *C, float x, float y, int zoomfilter, float clip_min_x, float clip_min_y, - float clip_max_x, float clip_max_y) + float clip_max_x, float clip_max_y, + float zoom_x, float zoom_y) { ColorManagedViewSettings *view_settings; ColorManagedDisplaySettings *display_settings; @@ -989,22 +529,19 @@ void glaDrawImBuf_glsl_ctx_clipping(const bContext *C, IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings); glaDrawImBuf_glsl_clipping(ibuf, x, y, zoomfilter, view_settings, display_settings, - clip_min_x, clip_min_y, clip_max_x, clip_max_y); -} - -void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter) -{ - glaDrawImBuf_glsl_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f); + clip_min_x, clip_min_y, clip_max_x, clip_max_y, + zoom_x, zoom_y); } -void cpack(unsigned int x) +void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter, + float zoom_x, float zoom_y) { - glColor3ub(( (x) & 0xFF), - (((x) >> 8) & 0xFF), - (((x) >> 16) & 0xFF)); + glaDrawImBuf_glsl_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y); } -void glaDrawBorderCorners(const rcti *border, float zoomx, float zoomy) +/* don't move to GPU_immediate_util.h because this uses user-prefs + * and isn't very low level */ +void immDrawBorderCorners(unsigned int pos, const rcti *border, float zoomx, float zoomy) { float delta_x = 4.0f * UI_DPI_FAC / zoomx; float delta_y = 4.0f * UI_DPI_FAC / zoomy; @@ -1013,30 +550,30 @@ void glaDrawBorderCorners(const rcti *border, float zoomx, float zoomy) delta_y = min_ff(delta_y, border->ymax - border->ymin); /* left bottom corner */ - glBegin(GL_LINE_STRIP); - glVertex2f(border->xmin, border->ymin + delta_y); - glVertex2f(border->xmin, border->ymin); - glVertex2f(border->xmin + delta_x, border->ymin); - glEnd(); + immBegin(GWN_PRIM_LINE_STRIP, 3); + immVertex2f(pos, border->xmin, border->ymin + delta_y); + immVertex2f(pos, border->xmin, border->ymin); + immVertex2f(pos, border->xmin + delta_x, border->ymin); + immEnd(); /* left top corner */ - glBegin(GL_LINE_STRIP); - glVertex2f(border->xmin, border->ymax - delta_y); - glVertex2f(border->xmin, border->ymax); - glVertex2f(border->xmin + delta_x, border->ymax); - glEnd(); + immBegin(GWN_PRIM_LINE_STRIP, 3); + immVertex2f(pos, border->xmin, border->ymax - delta_y); + immVertex2f(pos, border->xmin, border->ymax); + immVertex2f(pos, border->xmin + delta_x, border->ymax); + immEnd(); /* right bottom corner */ - glBegin(GL_LINE_STRIP); - glVertex2f(border->xmax - delta_x, border->ymin); - glVertex2f(border->xmax, border->ymin); - glVertex2f(border->xmax, border->ymin + delta_y); - glEnd(); + immBegin(GWN_PRIM_LINE_STRIP, 3); + immVertex2f(pos, border->xmax - delta_x, border->ymin); + immVertex2f(pos, border->xmax, border->ymin); + immVertex2f(pos, border->xmax, border->ymin + delta_y); + immEnd(); /* right top corner */ - glBegin(GL_LINE_STRIP); - glVertex2f(border->xmax - delta_x, border->ymax); - glVertex2f(border->xmax, border->ymax); - glVertex2f(border->xmax, border->ymax - delta_y); - glEnd(); + immBegin(GWN_PRIM_LINE_STRIP, 3); + immVertex2f(pos, border->xmax - delta_x, border->ymax); + immVertex2f(pos, border->xmax, border->ymax); + immVertex2f(pos, border->xmax, border->ymax - delta_y); + immEnd(); } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index b90430f27e2..864150be9da 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -30,6 +30,8 @@ #include <stdlib.h> #include <string.h> +#include "MEM_guardedalloc.h" + #include "DNA_object_types.h" #include "DNA_armature_types.h" #include "DNA_gpencil_types.h" @@ -38,17 +40,21 @@ #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "BLI_utildefines.h" - #include "BKE_context.h" #include "BKE_object.h" #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_gpencil.h" +#include "BKE_layer.h" #include "BKE_screen.h" #include "BKE_sequencer.h" +#include "BKE_workspace.h" + +#include "DEG_depsgraph.h" #include "RNA_access.h" @@ -61,20 +67,8 @@ #include "screen_intern.h" -static unsigned int context_layers(bScreen *sc, Scene *scene, ScrArea *sa_ctx) -{ - /* needed for 'USE_ALLSELECT' define, otherwise we end up editing off-screen layers. */ - if (sc && sa_ctx && (sa_ctx->spacetype == SPACE_BUTS)) { - const unsigned int lay = BKE_screen_view3d_layer_all(sc); - if (lay) { - return lay; - } - } - return scene->lay; -} - const char *screen_context_dir[] = { - "scene", "visible_objects", "visible_bases", "selectable_objects", "selectable_bases", + "scene", "view_layer", "visible_objects", "visible_bases", "selectable_objects", "selectable_bases", "selected_objects", "selected_bases", "editable_objects", "editable_bases", "selected_editable_objects", "selected_editable_bases", @@ -93,20 +87,14 @@ const char *screen_context_dir[] = { int ed_screen_context(const bContext *C, const char *member, bContextDataResult *result) { + wmWindow *win = CTX_wm_window(C); bScreen *sc = CTX_wm_screen(C); ScrArea *sa = CTX_wm_area(C); - Scene *scene = sc->scene; - Base *base; - -#if 0 /* Using the context breaks adding objects in the UI. Need to find out why - campbell */ - Object *obact = CTX_data_active_object(C); - Object *obedit = CTX_data_edit_object(C); - base = CTX_data_active_base(C); -#else - Object *obedit = scene->obedit; - Object *obact = OBACT; - base = BASACT; -#endif + Scene *scene = WM_window_get_active_scene(win); + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + ViewLayer *view_layer = BKE_view_layer_from_workspace_get(scene, workspace); + Object *obact = (view_layer && view_layer->basact) ? view_layer->basact->object : NULL; + Object *obedit = view_layer ? OBEDIT_FROM_VIEW_LAYER(view_layer) : NULL; if (CTX_data_dir(member)) { CTX_data_dir_set(result, screen_context_dir); @@ -116,84 +104,100 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult CTX_data_id_pointer_set(result, &scene->id); return 1; } - else if (CTX_data_equals(member, "visible_objects") || CTX_data_equals(member, "visible_bases")) { - const unsigned int lay = context_layers(sc, scene, sa); - const bool visible_objects = CTX_data_equals(member, "visible_objects"); - - for (base = scene->base.first; base; base = base->next) { - if (((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) && (base->lay & lay)) { - if (visible_objects) - CTX_data_id_list_add(result, &base->object->id); - else - CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); + else if (CTX_data_equals(member, "visible_objects")) { + FOREACH_VISIBLE_OBJECT_BEGIN(view_layer, ob) + { + CTX_data_id_list_add(result, &ob->id); + } + FOREACH_VISIBLE_BASE_END; + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return 1; + } + else if (CTX_data_equals(member, "selectable_objects")) { + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (((base->flag & BASE_VISIBLED) != 0) && ((base->flag & BASE_SELECTABLED) != 0)) { + CTX_data_id_list_add(result, &base->object->id); } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } - else if (CTX_data_equals(member, "selectable_objects") || CTX_data_equals(member, "selectable_bases")) { - const unsigned int lay = context_layers(sc, scene, sa); - const bool selectable_objects = CTX_data_equals(member, "selectable_objects"); - - for (base = scene->base.first; base; base = base->next) { - if (base->lay & lay) { - if ((base->object->restrictflag & OB_RESTRICT_VIEW) == 0 && (base->object->restrictflag & OB_RESTRICT_SELECT) == 0) { - if (selectable_objects) - CTX_data_id_list_add(result, &base->object->id); - else - CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); - } + else if (CTX_data_equals(member, "selected_objects")) { + FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob) + { + CTX_data_id_list_add(result, &ob->id); + } + FOREACH_SELECTED_OBJECT_END; + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return 1; + } + else if (CTX_data_equals(member, "selected_editable_objects")) { + FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob) + { + if (0 == BKE_object_is_libdata(ob)) { + CTX_data_id_list_add(result, &ob->id); } } + FOREACH_SELECTED_OBJECT_END; CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } - else if (CTX_data_equals(member, "selected_objects") || CTX_data_equals(member, "selected_bases")) { - const unsigned int lay = context_layers(sc, scene, sa); - const bool selected_objects = CTX_data_equals(member, "selected_objects"); - - for (base = scene->base.first; base; base = base->next) { - if ((base->flag & SELECT) && (base->lay & lay)) { - if (selected_objects) - CTX_data_id_list_add(result, &base->object->id); - else - CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); + else if (CTX_data_equals(member, "editable_objects")) { + /* Visible + Editable, but not necessarily selected */ + FOREACH_VISIBLE_OBJECT_BEGIN(view_layer, ob) + { + if (0 == BKE_object_is_libdata(ob)) { + CTX_data_id_list_add(result, &ob->id); } } + FOREACH_VISIBLE_OBJECT_END; CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } - else if (CTX_data_equals(member, "selected_editable_objects") || CTX_data_equals(member, "selected_editable_bases")) { - const unsigned int lay = context_layers(sc, scene, sa); - const bool selected_editable_objects = CTX_data_equals(member, "selected_editable_objects"); - - for (base = scene->base.first; base; base = base->next) { - if ((base->flag & SELECT) && (base->lay & lay)) { - if ((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) { - if (0 == BKE_object_is_libdata(base->object)) { - if (selected_editable_objects) - CTX_data_id_list_add(result, &base->object->id); - else - CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); - } + else if ( CTX_data_equals(member, "visible_bases")) { + FOREACH_VISIBLE_BASE_BEGIN(view_layer, base) + { + CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); + } + FOREACH_VISIBLE_BASE_END; + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return 1; + } + else if (CTX_data_equals(member, "selectable_bases")) { + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if ((base->flag & BASE_SELECTABLED) != 0) { + CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); + } + } + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return 1; + } + else if (CTX_data_equals(member, "selected_bases")) { + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if ((base->flag & BASE_SELECTED) != 0) { + CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); + } + } + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return 1; + } + else if (CTX_data_equals(member, "selected_editable_bases")) { + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if ((base->flag & BASE_SELECTED) != 0) { + if (0 == BKE_object_is_libdata(base->object)) { + CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); } } } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } - else if (CTX_data_equals(member, "editable_objects") || CTX_data_equals(member, "editable_bases")) { - const unsigned int lay = context_layers(sc, scene, sa); - const bool editable_objects = CTX_data_equals(member, "editable_objects"); - + else if (CTX_data_equals(member, "editable_bases")) { /* Visible + Editable, but not necessarily selected */ - for (base = scene->base.first; base; base = base->next) { - if (((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) && (base->lay & lay)) { + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if ((base->flag & BASE_VISIBLED) != 0) { if (0 == BKE_object_is_libdata(base->object)) { - if (editable_objects) - CTX_data_id_list_add(result, &base->object->id); - else - CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); + CTX_data_list_add(result, &scene->id, &RNA_ObjectBase, base); } } } @@ -206,38 +210,47 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult const bool editable_bones = CTX_data_equals(member, "editable_bones"); if (arm && arm->edbo) { - /* Attention: X-Axis Mirroring is also handled here... */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - /* first and foremost, bone must be visible and selected */ - if (EBONE_VISIBLE(arm, ebone)) { - /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled - * so that most users of this data don't need to explicitly check for it themselves. - * - * We need to make sure that these mirrored copies are not selected, otherwise some - * bones will be operated on twice. - */ - if (arm->flag & ARM_MIRROR_EDIT) - flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); - - /* if we're filtering for editable too, use the check for that instead, as it has selection check too */ - if (editable_bones) { - /* only selected + editable */ - if (EBONE_EDITABLE(ebone)) { + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + arm = ob->data; + + /* Attention: X-Axis Mirroring is also handled here... */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + /* first and foremost, bone must be visible and selected */ + if (EBONE_VISIBLE(arm, ebone)) { + /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled + * so that most users of this data don't need to explicitly check for it themselves. + * + * We need to make sure that these mirrored copies are not selected, otherwise some + * bones will be operated on twice. + */ + if (arm->flag & ARM_MIRROR_EDIT) + flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); + + /* if we're filtering for editable too, use the check for that instead, as it has selection check too */ + if (editable_bones) { + /* only selected + editable */ + if (EBONE_EDITABLE(ebone)) { + CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); + + if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) + CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); + } + } + else { + /* only include bones if visible */ CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); - if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) + if ((flipbone) && EBONE_VISIBLE(arm, flipbone) == 0) CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); } } - else { - /* only include bones if visible */ - CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); - - if ((flipbone) && EBONE_VISIBLE(arm, flipbone) == 0) - CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); - } } } + MEM_freeN(objects); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } @@ -248,53 +261,65 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones"); if (arm && arm->edbo) { - /* Attention: X-Axis Mirroring is also handled here... */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - /* first and foremost, bone must be visible and selected */ - if (EBONE_VISIBLE(arm, ebone) && (ebone->flag & BONE_SELECTED)) { - /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled - * so that most users of this data don't need to explicitly check for it themselves. - * - * We need to make sure that these mirrored copies are not selected, otherwise some - * bones will be operated on twice. - */ - if (arm->flag & ARM_MIRROR_EDIT) - flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); - - /* if we're filtering for editable too, use the check for that instead, as it has selection check too */ - if (selected_editable_bones) { - /* only selected + editable */ - if (EBONE_EDITABLE(ebone)) { + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + arm = ob->data; + + /* Attention: X-Axis Mirroring is also handled here... */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + /* first and foremost, bone must be visible and selected */ + if (EBONE_VISIBLE(arm, ebone) && (ebone->flag & BONE_SELECTED)) { + /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled + * so that most users of this data don't need to explicitly check for it themselves. + * + * We need to make sure that these mirrored copies are not selected, otherwise some + * bones will be operated on twice. + */ + if (arm->flag & ARM_MIRROR_EDIT) + flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone); + + /* if we're filtering for editable too, use the check for that instead, as it has selection check too */ + if (selected_editable_bones) { + /* only selected + editable */ + if (EBONE_EDITABLE(ebone)) { + CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); + + if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) + CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); + } + } + else { + /* only include bones if selected */ CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); } } - else { - /* only include bones if selected */ - CTX_data_list_add(result, &arm->id, &RNA_EditBone, ebone); - - if ((flipbone) && !(flipbone->flag & BONE_SELECTED)) - CTX_data_list_add(result, &arm->id, &RNA_EditBone, flipbone); - } } } + MEM_freeN(objects); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } } else if (CTX_data_equals(member, "visible_pose_bones")) { Object *obpose = BKE_object_pose_armature_get(obact); - bArmature *arm = (obpose) ? obpose->data : NULL; - bPoseChannel *pchan; - - if (obpose && obpose->pose && arm) { - for (pchan = obpose->pose->chanbase.first; pchan; pchan = pchan->next) { - /* ensure that PoseChannel is on visible layer and is not hidden in PoseMode */ - if (PBONE_VISIBLE(arm, pchan->bone)) { + if (obpose && obpose->pose && obpose->data) { + if (obpose != obact) { + FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN (obpose, pchan) { CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); - } + } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + } + else if (obact->mode & OB_MODE_POSE) { + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, OB_MODE_POSE, ob_iter) { + FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN (ob_iter, pchan) { + CTX_data_list_add(result, &ob_iter->id, &RNA_PoseBone, pchan); + } FOREACH_PCHAN_VISIBLE_IN_OBJECT_END; + } FOREACH_OBJECT_IN_MODE_END; } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; @@ -302,16 +327,18 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "selected_pose_bones")) { Object *obpose = BKE_object_pose_armature_get(obact); - bArmature *arm = (obpose) ? obpose->data : NULL; - bPoseChannel *pchan; - - if (obpose && obpose->pose && arm) { - for (pchan = obpose->pose->chanbase.first; pchan; pchan = pchan->next) { - /* ensure that PoseChannel is on visible layer and is not hidden in PoseMode */ - if (PBONE_VISIBLE(arm, pchan->bone)) { - if (pchan->bone->flag & BONE_SELECTED) - CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); - } + if (obpose && obpose->pose && obpose->data) { + if (obpose != obact) { + FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (obpose, pchan) { + CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); + } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + } + else if (obact->mode & OB_MODE_POSE) { + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, OB_MODE_POSE, ob_iter) { + FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) { + CTX_data_list_add(result, &ob_iter->id, &RNA_PoseBone, pchan); + } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; + } FOREACH_OBJECT_IN_MODE_END; } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; @@ -345,8 +372,8 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } else if (CTX_data_equals(member, "active_base")) { - if (base) - CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, base); + if (view_layer->basact) + CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, view_layer->basact); return 1; } diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 12f45faee94..fec39ade110 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -22,18 +22,26 @@ * \ingroup edscr */ -#include "BIF_gl.h" +#include "ED_screen.h" + +#include "GPU_framebuffer.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" + +#include "BLI_math.h" #include "WM_api.h" +#include "WM_types.h" -#include "ED_screen.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 *sa, char dir) +static void draw_horizontal_join_shape(ScrArea *sa, char dir, unsigned int pos) { vec2f points[10]; short i; @@ -90,24 +98,31 @@ static void draw_horizontal_join_shape(ScrArea *sa, char dir) } } - glBegin(GL_POLYGON); - for (i = 0; i < 5; i++) - glVertex2f(points[i].x, points[i].y); - glEnd(); - glBegin(GL_POLYGON); - for (i = 4; i < 8; i++) - glVertex2f(points[i].x, points[i].y); - glVertex2f(points[0].x, points[0].y); - glEnd(); - - glRectf(points[2].x, points[2].y, points[8].x, points[8].y); - glRectf(points[6].x, points[6].y, points[9].x, points[9].y); + immBegin(GWN_PRIM_TRI_FAN, 5); + + for (i = 0; i < 5; i++) { + immVertex2f(pos, points[i].x, points[i].y); + } + + immEnd(); + + immBegin(GWN_PRIM_TRI_FAN, 5); + + for (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 *sa, char dir) +static void draw_vertical_join_shape(ScrArea *sa, char dir, unsigned int pos) { vec2f points[10]; short i; @@ -164,93 +179,252 @@ static void draw_vertical_join_shape(ScrArea *sa, char dir) } } - glBegin(GL_POLYGON); - for (i = 0; i < 5; i++) - glVertex2f(points[i].x, points[i].y); - glEnd(); - glBegin(GL_POLYGON); - for (i = 4; i < 8; i++) - glVertex2f(points[i].x, points[i].y); - glVertex2f(points[0].x, points[0].y); - glEnd(); - - glRectf(points[2].x, points[2].y, points[8].x, points[8].y); - glRectf(points[6].x, points[6].y, points[9].x, points[9].y); + immBegin(GWN_PRIM_TRI_FAN, 5); + + for (i = 0; i < 5; i++) { + immVertex2f(pos, points[i].x, points[i].y); + } + + immEnd(); + + immBegin(GWN_PRIM_TRI_FAN, 5); + + for (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 *sa, char dir) +static void draw_join_shape(ScrArea *sa, char dir, unsigned int pos) { if (dir == 'u' || dir == 'd') { - draw_vertical_join_shape(sa, dir); + draw_vertical_join_shape(sa, dir, pos); + } + else { + draw_horizontal_join_shape(sa, dir, pos); + } +} + +#define CORNER_RESOLUTION 10 +static void drawscredge_corner_geometry( + int sizex, int sizey, + int corner_x, int corner_y, + int center_x, int center_y, + double angle_offset, + const float *color) +{ + const int radius = ABS(corner_x - center_x); + const int line_thickness = U.pixelsize; + + if (corner_x < center_x) { + if (corner_x > 0.0f) { + /* Left (internal) edge. */ + corner_x += line_thickness; + center_x += line_thickness; + } } else { - draw_horizontal_join_shape(sa, dir); + /* Right (internal) edge. */ + if (corner_x < sizex - 1) { + corner_x += 1 - line_thickness; + center_x += 1 - line_thickness; + } + else { + /* Corner case, extreme right edge. */ + corner_x += 1; + center_x += 1; + } + } + + if (corner_y < center_y) { + if (corner_y > 0.0f) { + /* Bottom (internal) edge. */ + corner_y += line_thickness; + center_y += line_thickness; + } + } + else { + /* Top (internal) edge. */ + if (corner_y < sizey) { + corner_y += 1 - line_thickness; + center_y += 1 - line_thickness; + } } + + float tri_array[CORNER_RESOLUTION + 1][2]; + + tri_array[0][0] = corner_x; + tri_array[0][1] = corner_y; + + for (int i = 0; i < CORNER_RESOLUTION; i++) { + double angle = angle_offset + (M_PI_2 * ((float)i / (CORNER_RESOLUTION - 1))); + float x = center_x + (radius * cos(angle)); + float y = center_y + (radius * sin(angle)); + tri_array[i + 1][0] = x; + tri_array[i + 1][1] = y; + } + + UI_draw_anti_fan(tri_array, CORNER_RESOLUTION + 1, color); +} + +#undef CORNER_RESOLUTION + +static void drawscredge_corner(ScrArea *sa, int sizex, int sizey) +{ + int size = 10 * U.pixelsize; + float color[4] = {0}; + UI_GetThemeColor4fv(TH_EDITOR_OUTLINE, color); + + /* Bottom-Left. */ + drawscredge_corner_geometry(sizex, sizey, + sa->v1->vec.x, + sa->v1->vec.y, + sa->v1->vec.x + size, + sa->v1->vec.y + size, + M_PI_2 * 2.0f, + color); + + /* Top-Left. */ + drawscredge_corner_geometry(sizex, sizey, + sa->v2->vec.x, + sa->v2->vec.y, + sa->v2->vec.x + size, + sa->v2->vec.y - size, + M_PI_2, + color); + + /* Top-Right. */ + drawscredge_corner_geometry(sizex, sizey, + sa->v3->vec.x, + sa->v3->vec.y, + sa->v3->vec.x - size, + sa->v3->vec.y - size, + 0.0f, + color); + + /* Bottom-Right. */ + drawscredge_corner_geometry(sizex, sizey, + sa->v4->vec.x, + sa->v4->vec.y, + sa->v4->vec.x - size, + sa->v4->vec.y + size, + M_PI_2 * 3.0f, + color); + + /* Wrap up the corners with a nice embossing. */ + rcti rect = sa->totrct; + + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4fv(color); + immBeginAtMost(GWN_PRIM_LINES, 8); + + /* Right. */ + immVertex2f(pos, rect.xmax, rect.ymax); + immVertex2f(pos, rect.xmax, rect.ymin); + + /* Bottom. */ + immVertex2f(pos, rect.xmax, rect.ymin); + immVertex2f(pos, rect.xmin, rect.ymin); + + /* Left. */ + immVertex2f(pos, rect.xmin, rect.ymin); + immVertex2f(pos, rect.xmin, rect.ymax); + + /* Top. */ + immVertex2f(pos, rect.xmin, rect.ymax); + immVertex2f(pos, rect.xmax, rect.ymax); + + immEnd(); + immUnbindProgram(); } /** * Draw screen area darker with arrow (visualization of future joining). */ -static void scrarea_draw_shape_dark(ScrArea *sa, char dir) +static void scrarea_draw_shape_dark(ScrArea *sa, char dir, unsigned int pos) { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4ub(0, 0, 0, 50); - draw_join_shape(sa, dir); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + immUniformColor4ub(0, 0, 0, 50); + + draw_join_shape(sa, dir, pos); } /** * Draw screen area ligher with arrow shape ("eraser" of previous dark shape). */ -static void scrarea_draw_shape_light(ScrArea *sa, char UNUSED(dir)) +static void scrarea_draw_shape_light(ScrArea *sa, char UNUSED(dir), unsigned int pos) { glBlendFunc(GL_DST_COLOR, GL_SRC_ALPHA); /* value 181 was hardly computed: 181~105 */ - glColor4ub(255, 255, 255, 50); + immUniformColor4ub(255, 255, 255, 50); /* draw_join_shape(sa, dir); */ - glRecti(sa->v1->vec.x, sa->v1->vec.y, sa->v3->vec.x, sa->v3->vec.y); + + immRectf(pos, sa->v1->vec.x, sa->v1->vec.y, sa->v3->vec.x, sa->v3->vec.y); } -static void drawscredge_area_draw(int sizex, int sizey, short x1, short y1, short x2, short y2) +static void drawscredge_area_draw(int sizex, int sizey, short x1, short y1, short x2, short y2, unsigned int pos) { + int count = 0; + + if (x2 < sizex - 1) count += 2; + if (x1 > 0) count += 2; + if (y2 < sizey - 1) count += 2; + if (y1 > 0) count += 2; + + if (count == 0) { + return; + } + + immBegin(GWN_PRIM_LINES, count); + /* right border area */ if (x2 < sizex - 1) { - glVertex2s(x2, y1); - glVertex2s(x2, y2); + immVertex2f(pos, x2, y1); + immVertex2f(pos, x2, y2); } /* left border area */ if (x1 > 0) { /* otherwise it draws the emboss of window over */ - glVertex2s(x1, y1); - glVertex2s(x1, y2); + immVertex2f(pos, x1, y1); + immVertex2f(pos, x1, y2); } /* top border area */ if (y2 < sizey - 1) { - glVertex2s(x1, y2); - glVertex2s(x2, y2); + immVertex2f(pos, x1, y2); + immVertex2f(pos, x2, y2); } /* bottom border area */ if (y1 > 0) { - glVertex2s(x1, y1); - glVertex2s(x2, y1); + immVertex2f(pos, x1, y1); + immVertex2f(pos, x2, y1); } + + immEnd(); } /** * \brief Screen edges drawing. */ -static void drawscredge_area(ScrArea *sa, int sizex, int sizey) +static void drawscredge_area(ScrArea *sa, int sizex, int sizey, unsigned int pos) { short x1 = sa->v1->vec.x; short y1 = sa->v1->vec.y; short x2 = sa->v3->vec.x; short y2 = sa->v3->vec.y; - drawscredge_area_draw(sizex, sizey, x1, y1, x2, y2); + drawscredge_area_draw(sizex, sizey, x1, y1, x2, y2, pos); } /** @@ -258,33 +432,40 @@ static void drawscredge_area(ScrArea *sa, int sizex, int sizey) */ void ED_screen_draw_edges(wmWindow *win) { + bScreen *screen = WM_window_get_active_screen(win); const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); ScrArea *sa; - wmSubWindowSet(win, win->screen->mainwin); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* Note: first loop only draws if U.pixelsize > 1, skip otherwise */ if (U.pixelsize > 1.0f) { /* FIXME: doesn't our glLineWidth already scale by U.pixelsize? */ glLineWidth((2.0f * U.pixelsize) - 1); - glColor3ub(0x50, 0x50, 0x50); - glBegin(GL_LINES); - for (sa = win->screen->areabase.first; sa; sa = sa->next) - drawscredge_area(sa, winsize_x, winsize_y); - glEnd(); + immUniformThemeColor(TH_EDITOR_OUTLINE); + + for (sa = screen->areabase.first; sa; sa = sa->next) { + drawscredge_area(sa, winsize_x, winsize_y, pos); + } } glLineWidth(1); - glColor3ub(0, 0, 0); - glBegin(GL_LINES); - for (sa = win->screen->areabase.first; sa; sa = sa->next) { - drawscredge_area(sa, winsize_x, winsize_y); + immUniformThemeColor(TH_EDITOR_OUTLINE); + + for (sa = screen->areabase.first; sa; sa = sa->next) { + drawscredge_area(sa, winsize_x, winsize_y, pos); + } + + immUnbindProgram(); + + for (sa = screen->areabase.first; sa; sa = sa->next) { + drawscredge_corner(sa, winsize_x, winsize_y); } - glEnd(); - win->screen->do_draw = false; + screen->do_draw = false; } /** @@ -295,6 +476,9 @@ void ED_screen_draw_edges(wmWindow *win) */ void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2) { + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + glLineWidth(1); /* blended join arrow */ @@ -319,37 +503,155 @@ void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2) dira = 'd'; break; } + glEnable(GL_BLEND); - scrarea_draw_shape_dark(sa2, dir); - scrarea_draw_shape_light(sa1, dira); + + scrarea_draw_shape_dark(sa2, dir, pos); + scrarea_draw_shape_light(sa1, dira, pos); + glDisable(GL_BLEND); } + + immUnbindProgram(); } void ED_screen_draw_split_preview(ScrArea *sa, const int dir, const float fac) { + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + /* splitpoint */ glEnable(GL_BLEND); - glBegin(GL_LINES); - glColor4ub(255, 255, 255, 100); + immUniformColor4ub(255, 255, 255, 100); + + immBegin(GWN_PRIM_LINES, 2); if (dir == 'h') { const float y = (1 - fac) * sa->totrct.ymin + fac * sa->totrct.ymax; - glVertex2s(sa->totrct.xmin, y); - glVertex2s(sa->totrct.xmax, y); - glColor4ub(0, 0, 0, 100); - glVertex2s(sa->totrct.xmin, y + 1); - glVertex2s(sa->totrct.xmax, y + 1); + + immVertex2f(pos, sa->totrct.xmin, y); + immVertex2f(pos, sa->totrct.xmax, y); + + immEnd(); + + immUniformColor4ub(0, 0, 0, 100); + + immBegin(GWN_PRIM_LINES, 2); + + immVertex2f(pos, sa->totrct.xmin, y + 1); + immVertex2f(pos, sa->totrct.xmax, y + 1); + + immEnd(); } else { + BLI_assert(dir == 'v'); const float x = (1 - fac) * sa->totrct.xmin + fac * sa->totrct.xmax; - glVertex2s(x, sa->totrct.ymin); - glVertex2s(x, sa->totrct.ymax); - glColor4ub(0, 0, 0, 100); - glVertex2s(x + 1, sa->totrct.ymin); - glVertex2s(x + 1, sa->totrct.ymax); + + immVertex2f(pos, x, sa->totrct.ymin); + immVertex2f(pos, x, sa->totrct.ymax); + + immEnd(); + + immUniformColor4ub(0, 0, 0, 100); + + immBegin(GWN_PRIM_LINES, 2); + + immVertex2f(pos, x + 1, sa->totrct.ymin); + immVertex2f(pos, x + 1, sa->totrct.ymax); + + immEnd(); } - glEnd(); + glDisable(GL_BLEND); + + immUnbindProgram(); } + +/* -------------------------------------------------------------------- */ +/* Screen Thumbnail Preview */ + +/** + * Calculates a scale factor to squash the preview for \a screen into a rectangle of given size and aspect. + */ +static void screen_preview_scale_get( + const bScreen *screen, float size_x, float size_y, + const float asp[2], + float r_scale[2]) +{ + float max_x = 0, max_y = 0; + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + max_x = MAX2(max_x, sa->totrct.xmax); + max_y = MAX2(max_y, sa->totrct.ymax); + } + r_scale[0] = (size_x * asp[0]) / max_x; + r_scale[1] = (size_y * asp[1]) / max_y; +} + +static void screen_preview_draw_areas(const bScreen *screen, const float scale[2], const float col[4], + const float ofs_between_areas) +{ + const float ofs_h = ofs_between_areas * 0.5f; + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4fv(col); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + rctf rect = { + .xmin = sa->totrct.xmin * scale[0] + ofs_h, + .xmax = sa->totrct.xmax * scale[0] - ofs_h, + .ymin = sa->totrct.ymin * scale[1] + ofs_h, + .ymax = sa->totrct.ymax * scale[1] - ofs_h + }; + + immBegin(GWN_PRIM_TRI_FAN, 4); + immVertex2f(pos, rect.xmin, rect.ymin); + immVertex2f(pos, rect.xmax, rect.ymin); + immVertex2f(pos, rect.xmax, rect.ymax); + immVertex2f(pos, rect.xmin, rect.ymax); + immEnd(); + } + + immUnbindProgram(); +} + +static void screen_preview_draw(const bScreen *screen, int size_x, int size_y) +{ + const float asp[2] = {1.0f, 0.8f}; /* square previews look a bit ugly */ + /* could use theme color (tui.wcol_menu_item.text), but then we'd need to regenerate all previews when changing */ + const float col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + float scale[2]; + + wmOrtho2(0.0f, size_x, 0.0f, size_y); + /* center */ + gpuPushMatrix(); + gpuLoadIdentity(); + gpuTranslate2f(size_x * (1.0f - asp[0]) * 0.5f, size_y * (1.0f - asp[1]) * 0.5f); + + screen_preview_scale_get(screen, size_x, size_y, asp, scale); + screen_preview_draw_areas(screen, scale, col, 1.5f); + + gpuPopMatrix(); +} + +/** + * Render the preview for a screen layout in \a screen. + */ +void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, unsigned int *r_rect) +{ + char err_out[256] = "unknown"; + GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, 0, true, false, err_out); + + GPU_offscreen_bind(offscreen, true); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + screen_preview_draw(screen, size_x, size_y); + + GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, r_rect); + GPU_offscreen_unbind(offscreen, true); + + GPU_offscreen_free(offscreen); +} diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 82552c35786..53abe3ed4ea 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -33,7 +33,9 @@ #include "MEM_guardedalloc.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_workspace_types.h" #include "DNA_userdef_types.h" #include "BLI_math.h" @@ -41,18 +43,17 @@ #include "BLI_utildefines.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" +#include "BKE_icons.h" #include "BKE_image.h" #include "BKE_global.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_screen.h" #include "BKE_scene.h" - -#include "BIF_gl.h" -#include "BIF_glutil.h" +#include "BKE_workspace.h" #include "WM_api.h" #include "WM_types.h" @@ -66,198 +67,43 @@ #include "UI_interface.h" -/* XXX actually should be not here... solve later */ -#include "wm_subwindow.h" +#include "WM_message.h" + +#include "DEG_depsgraph_query.h" #include "screen_intern.h" /* own module include */ /* ******************* screen vert, edge, area managing *********************** */ -static ScrVert *screen_addvert(bScreen *sc, short x, short y) +static ScrVert *screen_addvert_ex(ScrAreaMap *area_map, short x, short y) { ScrVert *sv = MEM_callocN(sizeof(ScrVert), "addscrvert"); sv->vec.x = x; sv->vec.y = y; - BLI_addtail(&sc->vertbase, sv); + BLI_addtail(&area_map->vertbase, sv); return sv; } - -static void sortscrvert(ScrVert **v1, ScrVert **v2) +static ScrVert *screen_addvert(bScreen *sc, short x, short y) { - ScrVert *tmp; - - if (*v1 > *v2) { - tmp = *v1; - *v1 = *v2; - *v2 = tmp; - } + return screen_addvert_ex(AREAMAP_FROM_SCREEN(sc), x, y); } -static ScrEdge *screen_addedge(bScreen *sc, ScrVert *v1, ScrVert *v2) +static ScrEdge *screen_addedge_ex(ScrAreaMap *area_map, ScrVert *v1, ScrVert *v2) { ScrEdge *se = MEM_callocN(sizeof(ScrEdge), "addscredge"); - sortscrvert(&v1, &v2); + BKE_screen_sort_scrvert(&v1, &v2); se->v1 = v1; se->v2 = v2; - BLI_addtail(&sc->edgebase, se); + BLI_addtail(&area_map->edgebase, se); return se; } - - -ScrEdge *screen_findedge(bScreen *sc, ScrVert *v1, ScrVert *v2) -{ - ScrEdge *se; - - sortscrvert(&v1, &v2); - for (se = sc->edgebase.first; se; se = se->next) - if (se->v1 == v1 && se->v2 == v2) - return se; - - return NULL; -} - -void removedouble_scrverts(bScreen *sc) -{ - ScrVert *v1, *verg; - ScrEdge *se; - ScrArea *sa; - - verg = sc->vertbase.first; - while (verg) { - if (verg->newv == NULL) { /* !!! */ - v1 = verg->next; - while (v1) { - if (v1->newv == NULL) { /* !?! */ - if (v1->vec.x == verg->vec.x && v1->vec.y == verg->vec.y) { - /* printf("doublevert\n"); */ - v1->newv = verg; - } - } - v1 = v1->next; - } - } - verg = verg->next; - } - - /* replace pointers in edges and faces */ - se = sc->edgebase.first; - while (se) { - if (se->v1->newv) se->v1 = se->v1->newv; - if (se->v2->newv) se->v2 = se->v2->newv; - /* edges changed: so.... */ - sortscrvert(&(se->v1), &(se->v2)); - se = se->next; - } - sa = sc->areabase.first; - while (sa) { - if (sa->v1->newv) sa->v1 = sa->v1->newv; - if (sa->v2->newv) sa->v2 = sa->v2->newv; - if (sa->v3->newv) sa->v3 = sa->v3->newv; - if (sa->v4->newv) sa->v4 = sa->v4->newv; - sa = sa->next; - } - - /* remove */ - verg = sc->vertbase.first; - while (verg) { - v1 = verg->next; - if (verg->newv) { - BLI_remlink(&sc->vertbase, verg); - MEM_freeN(verg); - } - verg = v1; - } - -} - -void removenotused_scrverts(bScreen *sc) -{ - ScrVert *sv, *svn; - ScrEdge *se; - - /* we assume edges are ok */ - - se = sc->edgebase.first; - while (se) { - se->v1->flag = 1; - se->v2->flag = 1; - se = se->next; - } - - sv = sc->vertbase.first; - while (sv) { - svn = sv->next; - if (sv->flag == 0) { - BLI_remlink(&sc->vertbase, sv); - MEM_freeN(sv); - } - else { - sv->flag = 0; - } - sv = svn; - } -} - -void removedouble_scredges(bScreen *sc) -{ - ScrEdge *verg, *se, *sn; - - /* compare */ - verg = sc->edgebase.first; - while (verg) { - se = verg->next; - while (se) { - sn = se->next; - if (verg->v1 == se->v1 && verg->v2 == se->v2) { - BLI_remlink(&sc->edgebase, se); - MEM_freeN(se); - } - se = sn; - } - verg = verg->next; - } -} - -void removenotused_scredges(bScreen *sc) +static ScrEdge *screen_addedge(bScreen *sc, ScrVert *v1, ScrVert *v2) { - ScrEdge *se, *sen; - ScrArea *sa; - int a = 0; - - /* sets flags when edge is used in area */ - sa = sc->areabase.first; - while (sa) { - se = screen_findedge(sc, sa->v1, sa->v2); - if (se == NULL) printf("error: area %d edge 1 doesn't exist\n", a); - else se->flag = 1; - se = screen_findedge(sc, sa->v2, sa->v3); - if (se == NULL) printf("error: area %d edge 2 doesn't exist\n", a); - else se->flag = 1; - se = screen_findedge(sc, sa->v3, sa->v4); - if (se == NULL) printf("error: area %d edge 3 doesn't exist\n", a); - else se->flag = 1; - se = screen_findedge(sc, sa->v4, sa->v1); - if (se == NULL) printf("error: area %d edge 4 doesn't exist\n", a); - else se->flag = 1; - sa = sa->next; - a++; - } - se = sc->edgebase.first; - while (se) { - sen = se->next; - if (se->flag == 0) { - BLI_remlink(&sc->edgebase, se); - MEM_freeN(se); - } - else { - se->flag = 0; - } - se = sen; - } + return screen_addedge_ex(AREAMAP_FROM_SCREEN(sc), v1, v2); } bool scredge_is_horizontal(ScrEdge *se) @@ -265,19 +111,21 @@ bool scredge_is_horizontal(ScrEdge *se) return (se->v1->vec.y == se->v2->vec.y); } -/* need win size to make sure not to include edges along screen edge */ -ScrEdge *screen_find_active_scredge(bScreen *sc, - const int winsize_x, const int winsize_y, - const int mx, const int my) +/** + * \param bounds_rect: Either window or screen bounds. Used to exclude edges along window/screen edges. + */ +ScrEdge *screen_area_map_find_active_scredge( + const ScrAreaMap *area_map, + const rcti *bounds_rect, + const int mx, const int my) { - ScrEdge *se; int safety = U.widget_unit / 10; - if (safety < 2) safety = 2; + CLAMP_MIN(safety, 2); - for (se = sc->edgebase.first; se; se = se->next) { + for (ScrEdge *se = area_map->edgebase.first; se; se = se->next) { if (scredge_is_horizontal(se)) { - if (se->v1->vec.y > 0 && se->v1->vec.y < winsize_y - 1) { + if ((se->v1->vec.y > bounds_rect->ymin) && (se->v1->vec.y < (bounds_rect->ymax - 1))) { short min, max; min = MIN2(se->v1->vec.x, se->v2->vec.x); max = MAX2(se->v1->vec.x, se->v2->vec.x); @@ -287,7 +135,7 @@ ScrEdge *screen_find_active_scredge(bScreen *sc, } } else { - if (se->v1->vec.x > 0 && se->v1->vec.x < winsize_x - 1) { + if ((se->v1->vec.x > bounds_rect->xmin) && (se->v1->vec.x < (bounds_rect->xmax - 1))) { short min, max; min = MIN2(se->v1->vec.y, se->v2->vec.y); max = MAX2(se->v1->vec.y, se->v2->vec.y); @@ -301,23 +149,55 @@ ScrEdge *screen_find_active_scredge(bScreen *sc, return NULL; } +/* need win size to make sure not to include edges along screen edge */ +ScrEdge *screen_find_active_scredge( + const wmWindow *win, const bScreen *screen, + const int mx, const int my) +{ + /* Use layout size (screen excluding global areas) for screen-layout area edges */ + rcti screen_rect; + ScrEdge *se; + + WM_window_screen_rect_calc(win, &screen_rect); + se = screen_area_map_find_active_scredge(AREAMAP_FROM_SCREEN(screen), &screen_rect, mx, my); + + if (!se) { + /* Use entire window size (screen including global areas) for global area edges */ + rcti win_rect; + WM_window_rect_calc(win, &win_rect); + se = screen_area_map_find_active_scredge(&win->global_areas, &win_rect, mx, my); + } + return se; +} + /* adds no space data */ -static ScrArea *screen_addarea(bScreen *sc, ScrVert *v1, ScrVert *v2, ScrVert *v3, ScrVert *v4, short headertype, short spacetype) +static ScrArea *screen_addarea_ex( + ScrAreaMap *area_map, + ScrVert *bottom_left, ScrVert *top_left, ScrVert *top_right, ScrVert *bottom_right, + short spacetype) { ScrArea *sa = MEM_callocN(sizeof(ScrArea), "addscrarea"); - sa->v1 = v1; - sa->v2 = v2; - sa->v3 = v3; - sa->v4 = v4; - sa->headertype = headertype; - sa->spacetype = sa->butspacetype = spacetype; - BLI_addtail(&sc->areabase, sa); + sa->v1 = bottom_left; + sa->v2 = top_left; + sa->v3 = top_right; + sa->v4 = bottom_right; + sa->spacetype = spacetype; + + BLI_addtail(&area_map->areabase, sa); return sa; } +static ScrArea *screen_addarea( + bScreen *sc, + ScrVert *left_bottom, ScrVert *left_top, ScrVert *right_top, ScrVert *right_bottom, + short spacetype) +{ + return screen_addarea_ex(AREAMAP_FROM_SCREEN(sc), left_bottom, left_top, right_top, right_bottom, + spacetype); +} static void screen_delarea(bContext *C, bScreen *sc, ScrArea *sa) { @@ -346,24 +226,24 @@ static short testsplitpoint(ScrArea *sa, char dir, float fac) CLAMP(fac, 0.0f, 1.0f); if (dir == 'h') { - y = sa->v1->vec.y + fac * (sa->v2->vec.y - sa->v1->vec.y); + y = sa->v1->vec.y + + round_fl_to_short(fac * (float)(sa->v2->vec.y - sa->v1->vec.y)); if (y - sa->v1->vec.y < area_min_y) y = sa->v1->vec.y + area_min_y; else if (sa->v2->vec.y - y < area_min_y) y = sa->v2->vec.y - area_min_y; - else y -= (y % AREAGRID); return y; } else { - x = sa->v1->vec.x + fac * (sa->v4->vec.x - sa->v1->vec.x); + x = sa->v1->vec.x + + round_fl_to_short(fac * (float)(sa->v4->vec.x - sa->v1->vec.x)); if (x - sa->v1->vec.x < area_min_x) x = sa->v1->vec.x + area_min_x; else if (sa->v4->vec.x - x < area_min_x) x = sa->v4->vec.x - area_min_x; - else x -= (x % AREAGRID); return x; } @@ -398,7 +278,7 @@ ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge) if (fac > 0.5f) { /* new areas: top */ - newa = screen_addarea(sc, sv1, sa->v2, sa->v3, sv2, sa->headertype, sa->spacetype); + newa = screen_addarea(sc, sv1, sa->v2, sa->v3, sv2, sa->spacetype); /* area below */ sa->v2 = sv1; @@ -406,7 +286,7 @@ ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge) } else { /* new areas: bottom */ - newa = screen_addarea(sc, sa->v1, sv1, sv2, sa->v4, sa->headertype, sa->spacetype); + newa = screen_addarea(sc, sa->v1, sv1, sv2, sa->v4, sa->spacetype); /* area above */ sa->v1 = sv1; @@ -430,7 +310,7 @@ ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge) if (fac > 0.5f) { /* new areas: right */ - newa = screen_addarea(sc, sv1, sv2, sa->v3, sa->v4, sa->headertype, sa->spacetype); + newa = screen_addarea(sc, sv1, sv2, sa->v3, sa->v4, sa->spacetype); /* area left */ sa->v3 = sv2; @@ -438,7 +318,7 @@ ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge) } else { /* new areas: left */ - newa = screen_addarea(sc, sa->v1, sa->v2, sv2, sv1, sa->headertype, sa->spacetype); + newa = screen_addarea(sc, sa->v1, sa->v2, sv2, sv1, sa->spacetype); /* area right */ sa->v1 = sv1; @@ -450,33 +330,29 @@ ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge) /* remove double vertices en edges */ if (merge) - removedouble_scrverts(sc); - removedouble_scredges(sc); - removenotused_scredges(sc); + BKE_screen_remove_double_scrverts(sc); + BKE_screen_remove_double_scredges(sc); + BKE_screen_remove_unused_scredges(sc); return newa; } -/* empty screen, with 1 dummy area without spacedata */ -/* uses window size */ -bScreen *ED_screen_add(Main *bmain, wmWindow *win, Scene *scene, const char *name) +/** + * Empty screen, with 1 dummy area without spacedata. Uses window size. + */ +bScreen *screen_add(Main *bmain, const char *name, const rcti *rect) { - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); - bScreen *sc; ScrVert *sv1, *sv2, *sv3, *sv4; sc = BKE_libblock_alloc(bmain, ID_SCR, name, 0); - sc->scene = scene; sc->do_refresh = true; sc->redraws_flag = TIME_ALL_3D_WIN | TIME_ALL_ANIM_WIN; - sc->winid = win->winid; - sv1 = screen_addvert(sc, 0, 0); - sv2 = screen_addvert(sc, 0, winsize_y - 1); - sv3 = screen_addvert(sc, winsize_x - 1, winsize_y - 1); - sv4 = screen_addvert(sc, winsize_x - 1, 0); + sv1 = screen_addvert(sc, rect->xmin, rect->ymin); + sv2 = screen_addvert(sc, rect->xmin, rect->ymax - 1); + sv3 = screen_addvert(sc, rect->xmax - 1, rect->ymax - 1); + sv4 = screen_addvert(sc, rect->xmax - 1, rect->ymin); screen_addedge(sc, sv1, sv2); screen_addedge(sc, sv2, sv3); @@ -484,12 +360,12 @@ bScreen *ED_screen_add(Main *bmain, wmWindow *win, Scene *scene, const char *nam screen_addedge(sc, sv4, sv1); /* dummy type, no spacedata */ - screen_addarea(sc, sv1, sv2, sv3, sv4, HEADERDOWN, SPACE_EMPTY); + screen_addarea(sc, sv1, sv2, sv3, sv4, SPACE_EMPTY); return sc; } -static void screen_copy(bScreen *to, bScreen *from) +void screen_data_copy(bScreen *to, bScreen *from) { ScrVert *s1, *s2; ScrEdge *se; @@ -511,7 +387,7 @@ static void screen_copy(bScreen *to, bScreen *from) for (se = to->edgebase.first; se; se = se->next) { se->v1 = se->v1->newv; se->v2 = se->v2->newv; - sortscrvert(&(se->v1), &(se->v2)); + BKE_screen_sort_scrvert(&(se->v1), &(se->v2)); } saf = from->areabase.first; @@ -532,7 +408,16 @@ static void screen_copy(bScreen *to, bScreen *from) /* put at zero (needed?) */ for (s1 = from->vertbase.first; s1; s1 = s1->next) s1->newv = NULL; +} +/** + * Prepare a newly created screen for initializing it as active screen. + */ +void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new) +{ + screen_new->winid = win->winid; + screen_new->do_refresh = true; + screen_new->do_draw = true; } @@ -611,15 +496,17 @@ int screen_area_join(bContext *C, bScreen *scr, ScrArea *sa1, ScrArea *sa2) } screen_delarea(C, scr, sa2); - removedouble_scrverts(scr); + BKE_screen_remove_double_scrverts(scr); + /* Update preview thumbnail */ + BKE_icon_changed(scr->id.icon_id); return 1; } -void select_connected_scredge(bScreen *sc, ScrEdge *edge) +void select_connected_scredge(const wmWindow *win, ScrEdge *edge) { + bScreen *sc = WM_window_get_active_screen(win); ScrEdge *se; - ScrVert *sv; int oneselected; char dir; @@ -629,10 +516,8 @@ void select_connected_scredge(bScreen *sc, ScrEdge *edge) if (edge->v1->vec.x == edge->v2->vec.x) dir = 'v'; else dir = 'h'; - sv = sc->vertbase.first; - while (sv) { + ED_screen_verts_iter(win, sc, sv) { sv->flag = 0; - sv = sv->next; } edge->v1->flag = 1; @@ -662,18 +547,24 @@ void select_connected_scredge(bScreen *sc, ScrEdge *edge) } } -/* test if screen vertices should be scaled */ -static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) +/** + * Test if screen vertices should be scaled and do if needed. + */ +static void screen_vertices_scale( + const wmWindow *win, bScreen *sc, + const rcti *window_rect, const rcti *screen_rect) { /* clamp Y size of header sized areas when expanding windows * avoids annoying empty space around file menu */ #define USE_HEADER_SIZE_CLAMP const int headery_init = ED_area_headersize(); + const int screen_size_x = BLI_rcti_size_x(screen_rect); + const int screen_size_y = BLI_rcti_size_y(screen_rect); ScrVert *sv = NULL; ScrArea *sa; - int winsize_x_prev, winsize_y_prev; - float facx, facy, tempf, min[2], max[2]; + int screen_size_x_prev, screen_size_y_prev; + float min[2], max[2]; /* calculate size */ min[0] = min[1] = 20000.0f; @@ -684,14 +575,8 @@ static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) minmax_v2v2_v2(min, max, fv); } - /* always make 0.0 left under */ - for (sv = sc->vertbase.first; sv; sv = sv->next) { - sv->vec.x -= min[0]; - sv->vec.y -= min[1]; - } - - winsize_x_prev = (max[0] - min[0]) + 1; - winsize_y_prev = (max[1] - min[1]) + 1; + screen_size_x_prev = (max[0] - min[0]) + 1; + screen_size_y_prev = (max[1] - min[1]) + 1; #ifdef USE_HEADER_SIZE_CLAMP @@ -699,19 +584,19 @@ static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) #define TEMP_TOP 2 /* if the window's Y axis grows, clamp header sized areas */ - if (winsize_y_prev < winsize_y) { /* growing? */ + if (screen_size_y_prev < screen_size_y) { /* growing? */ const int headery_margin_max = headery_init + 4; for (sa = sc->areabase.first; sa; sa = sa->next) { ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_HEADER); sa->temp = 0; if (ar && !(ar->flag & RGN_FLAG_HIDDEN)) { - if (sa->v2->vec.y == winsize_y_prev - 1) { + if (sa->v2->vec.y == max[1]) { if ((sa->v2->vec.y - sa->v1->vec.y) < headery_margin_max) { sa->temp = TEMP_TOP; } } - else if (sa->v1->vec.y == 0) { + else if (sa->v1->vec.y == min[1]) { if ((sa->v2->vec.y - sa->v1->vec.y) < headery_margin_max) { sa->temp = TEMP_BOTTOM; } @@ -722,33 +607,23 @@ static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) #endif - if (winsize_x_prev != winsize_x || winsize_y_prev != winsize_y) { - facx = ((float)winsize_x - 1) / ((float)winsize_x_prev - 1); - facy = ((float)winsize_y - 1) / ((float)winsize_y_prev - 1); + if (screen_size_x_prev != screen_size_x || screen_size_y_prev != screen_size_y) { + const float facx = ((float)screen_size_x - 1) / ((float)screen_size_x_prev - 1); + const float facy = ((float)screen_size_y - 1) / ((float)screen_size_y_prev - 1); /* make sure it fits! */ for (sv = sc->vertbase.first; sv; sv = sv->next) { - /* FIXME, this re-sizing logic is no good when re-sizing the window + redrawing [#24428] - * need some way to store these as floats internally and re-apply from there. */ - tempf = ((float)sv->vec.x) * facx; - sv->vec.x = (short)(tempf + 0.5f); - //sv->vec.x += AREAGRID - 1; - //sv->vec.x -= (sv->vec.x % AREAGRID); + sv->vec.x = screen_rect->xmin + round_fl_to_short((sv->vec.x - min[0]) * facx); + CLAMP(sv->vec.x, screen_rect->xmin, screen_rect->xmax - 1); - CLAMP(sv->vec.x, 0, winsize_x - 1); - - tempf = ((float)sv->vec.y) * facy; - sv->vec.y = (short)(tempf + 0.5f); - //sv->vec.y += AREAGRID - 1; - //sv->vec.y -= (sv->vec.y % AREAGRID); - - CLAMP(sv->vec.y, 0, winsize_y - 1); + sv->vec.y = screen_rect->ymin + round_fl_to_short((sv->vec.y - min[1]) * facy); + CLAMP(sv->vec.y, screen_rect->ymin, screen_rect->ymax - 1); } } #ifdef USE_HEADER_SIZE_CLAMP - if (winsize_y_prev < winsize_y) { /* growing? */ + if (screen_size_y_prev < screen_size_y) { /* growing? */ for (sa = sc->areabase.first; sa; sa = sa->next) { ScrEdge *se = NULL; @@ -762,9 +637,9 @@ static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) if (sa->temp == TEMP_TOP) { /* lower edge */ const int yval = sa->v2->vec.y - headery_init; - se = screen_findedge(sc, sa->v4, sa->v1); + se = BKE_screen_find_edge(sc, sa->v4, sa->v1); if (se != NULL) { - select_connected_scredge(sc, se); + select_connected_scredge(win, se); } for (sv = sc->vertbase.first; sv; sv = sv->next) { if (sv != sa->v2 && sv != sa->v3) { @@ -777,9 +652,9 @@ static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) else { /* upper edge */ const int yval = sa->v1->vec.y + headery_init; - se = screen_findedge(sc, sa->v2, sa->v3); + se = BKE_screen_find_edge(sc, sa->v2, sa->v3); if (se != NULL) { - select_connected_scredge(sc, se); + select_connected_scredge(win, se); } for (sv = sc->vertbase.first; sv; sv = sv->next) { if (sv != sa->v1 && sv != sa->v4) { @@ -806,21 +681,20 @@ static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) int headery = headery_init; /* adjust headery if verts are along the edge of window */ - if (sa->v1->vec.y > 0) + if (sa->v1->vec.y > window_rect->ymin) headery += U.pixelsize; - if (sa->v2->vec.y < winsize_y - 1) + if (sa->v2->vec.y < window_rect->ymax) headery += U.pixelsize; if (sa->v2->vec.y - sa->v1->vec.y + 1 < headery) { /* lower edge */ - ScrEdge *se = screen_findedge(sc, sa->v4, sa->v1); + ScrEdge *se = BKE_screen_find_edge(sc, sa->v4, sa->v1); if (se && sa->v1 != sa->v2) { - int yval; + const int yval = sa->v2->vec.y - headery + 1; - select_connected_scredge(sc, se); + select_connected_scredge(win, se); /* all selected vertices get the right offset */ - yval = sa->v2->vec.y - headery + 1; for (sv = sc->vertbase.first; sv; sv = sv->next) { /* if is a collapsed area */ if (sv != sa->v2 && sv != sa->v3) { @@ -833,31 +707,45 @@ static void screen_test_scale(bScreen *sc, int winsize_x, int winsize_y) } } + /* Global areas have a fixed size that only changes with the DPI. Here we ensure that exactly this size is set. */ + for (ScrArea *area = win->global_areas.areabase.first; area; area = area->next) { + if (area->global->flag & GLOBAL_AREA_IS_HIDDEN) { + continue; + } + /* width */ + area->v1->vec.x = area->v2->vec.x = window_rect->xmin; + area->v3->vec.x = area->v4->vec.x = window_rect->xmax - 1; + /* height */ + area->v1->vec.y = area->v4->vec.y = window_rect->ymin; + area->v2->vec.y = area->v3->vec.y = window_rect->ymax - 1; + switch (area->global->align) { + case GLOBAL_AREA_ALIGN_TOP: + area->v1->vec.y = area->v4->vec.y = area->v2->vec.y - ED_area_global_size_y(area); + break; + case GLOBAL_AREA_ALIGN_BOTTOM: + area->v2->vec.y = area->v3->vec.y = area->v1->vec.y + ED_area_global_size_y(area); + break; + } + } } + /* ****************** EXPORTED API TO OTHER MODULES *************************** */ -bScreen *ED_screen_duplicate(Main *bmain, wmWindow *win, bScreen *sc) +/* screen sets cursor based on active region */ +static void region_cursor_set(wmWindow *win, bool swin_changed) { - bScreen *newsc; - - if (sc->state != SCREENNORMAL) return NULL; /* XXX handle this case! */ + bScreen *screen = WM_window_get_active_screen(win); - /* make new empty screen: */ - newsc = ED_screen_add(bmain, win, sc->scene, sc->id.name + 2); - /* copy all data */ - screen_copy(newsc, sc); - - return newsc; -} - -/* screen sets cursor based on swinid */ -static void region_cursor_set(wmWindow *win, int swinid, int swin_changed) -{ - for (ScrArea *sa = win->screen->areabase.first; sa; sa = sa->next) { + ED_screen_areas_iter(win, screen, sa) { for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->swinid == swinid) { + if (ar == screen->active_region) { if (swin_changed || (ar->type && ar->type->event_cursor)) { + if (ar->manipulator_map != NULL) { + if (WM_manipulatormap_cursor_set(ar->manipulator_map, win)) { + return; + } + } ED_region_cursor_set(win, sa, ar); } return; @@ -869,23 +757,24 @@ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed) void ED_screen_do_listen(bContext *C, wmNotifier *note) { wmWindow *win = CTX_wm_window(C); + bScreen *screen = CTX_wm_screen(C); /* generic notes */ switch (note->category) { case NC_WM: if (note->data == ND_FILEREAD) - win->screen->do_draw = true; + screen->do_draw = true; break; case NC_WINDOW: - win->screen->do_draw = true; + screen->do_draw = true; break; case NC_SCREEN: if (note->action == NA_EDITED) - win->screen->do_draw = win->screen->do_refresh = true; + screen->do_draw = screen->do_refresh = true; break; case NC_SCENE: if (note->data == ND_MODE) - region_cursor_set(win, note->swinid, true); + region_cursor_set(win, true); break; } } @@ -906,48 +795,40 @@ static void screen_refresh_headersizes(void) /* for file read and first use, for scaling window, area moves */ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) { + bScreen *screen = WM_window_get_active_screen(win); + /* exception for bg mode, we only need the screen context */ if (!G.background) { - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); - ScrArea *sa; - rcti winrct; - - winrct.xmin = 0; - winrct.xmax = winsize_x - 1; - winrct.ymin = 0; - winrct.ymax = winsize_y - 1; + rcti window_rect, screen_rect; /* header size depends on DPI, let's verify */ WM_window_set_dpi(win); screen_refresh_headersizes(); - screen_test_scale(win->screen, winsize_x, winsize_y); + WM_window_rect_calc(win, &window_rect); + WM_window_screen_rect_calc(win, &screen_rect); /* Get screen bounds __after__ updating window DPI! */ - if (win->screen->mainwin == 0) { - win->screen->mainwin = wm_subwindow_open(win, &winrct, false); - } - else { - wm_subwindow_position(win, win->screen->mainwin, &winrct, false); - } + screen_vertices_scale(win, screen, &window_rect, &screen_rect); - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + ED_screen_areas_iter(win, screen, area) { /* set spacetype and region callbacks, calls init() */ /* sets subwindows for regions, adds handlers */ - ED_area_initialize(wm, win, sa); + ED_area_initialize(wm, win, area); } /* wake up animtimer */ - if (win->screen->animtimer) - WM_event_timer_sleep(wm, win, win->screen->animtimer, false); + if (screen->animtimer) + WM_event_timer_sleep(wm, win, screen->animtimer, false); } if (G.debug & G_DEBUG_EVENTS) { printf("%s: set screen\n", __func__); } - win->screen->do_refresh = false; + screen->do_refresh = false; + /* prevent multiwin errors */ + screen->winid = win->winid; - win->screen->context = ed_screen_context; + screen->context = ed_screen_context; } /* file read, set all screens, ... */ @@ -956,10 +837,23 @@ void ED_screens_initialize(Main *bmain, wmWindowManager *wm) wmWindow *win; for (win = wm->windows.first; win; win = win->next) { + if (WM_window_get_active_workspace(win) == NULL) { + WM_window_set_active_workspace(win, bmain->workspaces.first); + } - if (win->screen == NULL) - win->screen = bmain->screen.first; + if (BLI_listbase_is_empty(&win->global_areas.areabase)) { + ED_screen_global_areas_create(win); + } + ED_screen_refresh(wm, win); + if (win->eventstate) { + ED_screen_set_active_region(NULL, win, &win->eventstate->x); + } + } +} +void ED_screen_ensure_updated(wmWindowManager *wm, wmWindow *win, bScreen *screen) +{ + if (screen->do_refresh) { ED_screen_refresh(wm, win); } } @@ -970,17 +864,17 @@ void ED_screens_initialize(Main *bmain, wmWindowManager *wm) void ED_region_exit(bContext *C, ARegion *ar) { wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); ARegion *prevar = CTX_wm_region(C); if (ar->type && ar->type->exit) ar->type->exit(wm, ar); CTX_wm_region_set(C, ar); + WM_event_remove_handlers(C, &ar->handlers); - if (ar->swinid) { - wm_subwindow_close(CTX_wm_window(C), ar->swinid); - ar->swinid = 0; - } + WM_event_modal_handler_region_replace(win, ar, NULL); + WM_draw_region_free(ar); if (ar->headerstr) { MEM_freeN(ar->headerstr); @@ -988,16 +882,19 @@ void ED_region_exit(bContext *C, ARegion *ar) } if (ar->regiontimer) { - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), ar->regiontimer); + WM_event_remove_timer(wm, win, ar->regiontimer); ar->regiontimer = NULL; } + WM_msgbus_clear_by_owner(wm->message_bus, ar); + CTX_wm_region_set(C, prevar); } void ED_area_exit(bContext *C, ScrArea *sa) { wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); ScrArea *prevsa = CTX_wm_area(C); ARegion *ar; @@ -1005,10 +902,13 @@ void ED_area_exit(bContext *C, ScrArea *sa) sa->type->exit(wm, sa); CTX_wm_area_set(C, sa); + for (ar = sa->regionbase.first; ar; ar = ar->next) ED_region_exit(C, ar); WM_event_remove_handlers(C, &sa->handlers); + WM_event_modal_handler_area_replace(win, sa, NULL); + CTX_wm_area_set(C, prevsa); } @@ -1016,8 +916,6 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen) { wmWindowManager *wm = CTX_wm_manager(C); wmWindow *prevwin = CTX_wm_window(C); - ScrArea *sa; - ARegion *ar; CTX_wm_window_set(C, window); @@ -1026,21 +924,23 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen) screen->animtimer = NULL; screen->scrubbing = false; - if (screen->mainwin) - wm_subwindow_close(window, screen->mainwin); - screen->mainwin = 0; - screen->subwinactive = 0; + screen->active_region = NULL; - for (ar = screen->regionbase.first; ar; ar = ar->next) + for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) { ED_region_exit(C, ar); - - for (sa = screen->areabase.first; sa; sa = sa->next) + } + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { ED_area_exit(C, sa); + } + /* Don't use ED_screen_areas_iter here, it skips hidden areas. */ + for (ScrArea *sa = window->global_areas.areabase.first; sa; sa = sa->next) { + ED_area_exit(C, sa); + } /* mark it available for use for other windows */ screen->winid = 0; - if (prevwin->screen->temp == 0) { + if (!WM_window_is_temp_screen(prevwin)) { /* use previous window if possible */ CTX_wm_window_set(C, prevwin); } @@ -1054,16 +954,14 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen) /* *********************************** */ /* case when on area-edge or in azones, or outside window */ -static void screen_cursor_set(wmWindow *win, const wmEvent *event) +static void screen_cursor_set(wmWindow *win, const int xy[2]) { - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); - + const bScreen *screen = WM_window_get_active_screen(win); AZone *az = NULL; ScrArea *sa; - for (sa = win->screen->areabase.first; sa; sa = sa->next) - if ((az = ED_area_actionzone_find_xy(sa, &event->x))) + for (sa = screen->areabase.first; sa; sa = sa->next) + if ((az = ED_area_actionzone_find_xy(sa, xy))) break; if (sa) { @@ -1077,7 +975,7 @@ static void screen_cursor_set(wmWindow *win, const wmEvent *event) } } else { - ScrEdge *actedge = screen_find_active_scredge(win->screen, winsize_x, winsize_y, event->x, event->y); + ScrEdge *actedge = screen_find_active_scredge(win, screen, xy[0], xy[1]); if (actedge) { if (scredge_is_horizontal(actedge)) @@ -1093,20 +991,20 @@ static void screen_cursor_set(wmWindow *win, const wmEvent *event) /* called in wm_event_system.c. sets state vars in screen, cursors */ /* event type is mouse move */ -void ED_screen_set_subwinactive(bContext *C, const wmEvent *event) +void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) { - wmWindow *win = CTX_wm_window(C); + bScreen *scr = WM_window_get_active_screen(win); - if (win->screen) { - bScreen *scr = win->screen; - ScrArea *sa; + if (scr) { + ScrArea *sa = NULL; ARegion *ar; - int oldswin = scr->subwinactive; + ARegion *old_ar = scr->active_region; - for (sa = scr->areabase.first; sa; sa = sa->next) { - if (event->x > sa->totrct.xmin && event->x < sa->totrct.xmax) { - if (event->y > sa->totrct.ymin && event->y < sa->totrct.ymax) { - if (NULL == ED_area_actionzone_refresh_xy(sa, &event->x)) { + ED_screen_areas_iter(win, scr, area_iter) { + if (xy[0] > area_iter->totrct.xmin && xy[0] < area_iter->totrct.xmax) { + if (xy[1] > area_iter->totrct.ymin && xy[1] < area_iter->totrct.ymax) { + if (ED_area_actionzone_refresh_xy(area_iter, xy) == NULL) { + sa = area_iter; break; } } @@ -1115,49 +1013,55 @@ void ED_screen_set_subwinactive(bContext *C, const wmEvent *event) if (sa) { /* make overlap active when mouse over */ for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) { - scr->subwinactive = ar->swinid; + if (BLI_rcti_isect_pt_v(&ar->winrct, xy)) { + scr->active_region = ar; break; } } } else - scr->subwinactive = scr->mainwin; + scr->active_region = NULL; /* check for redraw headers */ - if (oldswin != scr->subwinactive) { + if (old_ar != scr->active_region) { - for (sa = scr->areabase.first; sa; sa = sa->next) { + ED_screen_areas_iter(win, scr, area_iter) { bool do_draw = false; - for (ar = sa->regionbase.first; ar; ar = ar->next) - if (ar->swinid == oldswin || ar->swinid == scr->subwinactive) + for (ar = area_iter->regionbase.first; ar; ar = ar->next) { + if (ar == old_ar || ar == scr->active_region) { do_draw = true; + } + } if (do_draw) { - for (ar = sa->regionbase.first; ar; ar = ar->next) - if (ar->regiontype == RGN_TYPE_HEADER) - ED_region_tag_redraw(ar); + for (ar = area_iter->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_HEADER) { + ED_region_tag_redraw_no_rebuild(ar); + } + } } } } /* cursors, for time being set always on edges, otherwise aregion doesnt switch */ - if (scr->subwinactive == scr->mainwin) { - screen_cursor_set(win, event); + if (scr->active_region == NULL) { + screen_cursor_set(win, xy); } else { /* notifier invokes freeing the buttons... causing a bit too much redraws */ - if (oldswin != scr->subwinactive) { - region_cursor_set(win, scr->subwinactive, true); + if (old_ar != scr->active_region) { + region_cursor_set(win, true); /* this used to be a notifier, but needs to be done immediate * because it can undo setting the right button as active due * to delayed notifier handling */ - UI_screen_free_active_but(C, win->screen); + if (C) { + UI_screen_free_active_but(C, scr); + } } else - region_cursor_set(win, scr->subwinactive, false); + region_cursor_set(win, false); } } } @@ -1176,183 +1080,199 @@ int ED_screen_area_active(const bContext *C) return 1; for (ar = sa->regionbase.first; ar; ar = ar->next) - if (ar->swinid == sc->subwinactive) + if (ar == sc->active_region) return 1; } return 0; } /** - * operator call, WM + Window + screen already existed before - * - * \warning Do NOT call in area/region queues! - * \returns success. + * Add an area and geometry (screen-edges and -vertices) for it to \a area_map, + * with coordinates/dimensions matching \a rect. */ -bool ED_screen_set(bContext *C, bScreen *sc) +static ScrArea *screen_area_create_with_geometry( + ScrAreaMap *area_map, const rcti *rect, + short spacetype) { - Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - bScreen *oldscreen = CTX_wm_screen(C); + ScrVert *bottom_left = screen_addvert_ex(area_map, rect->xmin, rect->ymin); + ScrVert *top_left = screen_addvert_ex(area_map, rect->xmin, rect->ymax); + ScrVert *top_right = screen_addvert_ex(area_map, rect->xmax, rect->ymax); + ScrVert *bottom_right = screen_addvert_ex(area_map, rect->xmax, rect->ymin); - /* validate screen, it's called with notifier reference */ - if (BLI_findindex(&bmain->screen, sc) == -1) { - return true; + screen_addedge_ex(area_map, bottom_left, top_left); + screen_addedge_ex(area_map, top_left, top_right); + screen_addedge_ex(area_map, top_right, bottom_right); + screen_addedge_ex(area_map, bottom_right, bottom_left); + + return screen_addarea_ex(area_map, bottom_left, top_left, top_right, bottom_right, spacetype); +} + +static void screen_global_area_create( + wmWindow *win, eSpace_Type space_type, GlobalAreaAlign align, const rcti *rect, + const short height_cur, const short height_min, const short height_max) +{ + ScrArea *area = screen_area_create_with_geometry(&win->global_areas, rect, space_type); + SpaceType *stype = BKE_spacetype_from_id(space_type); + SpaceLink *slink = stype->new(area, WM_window_get_active_scene(win)); + + area->regionbase = slink->regionbase; + + /* Data specific to global areas. */ + area->global = MEM_callocN(sizeof(*area->global), __func__); + area->global->cur_fixed_height = height_cur; + area->global->size_max = height_max; + area->global->size_min = height_min; + area->global->align = align; + + BLI_addhead(&area->spacedata, slink); + BLI_listbase_clear(&slink->regionbase); +} + +static void screen_global_topbar_area_create(wmWindow *win) +{ + const short size_y = 2.25 * HEADERY; + rcti rect; + + BLI_rcti_init(&rect, 0, WM_window_pixels_x(win) - 1, 0, WM_window_pixels_y(win) - 1); + rect.ymin = rect.ymax - size_y; + + screen_global_area_create(win, SPACE_TOPBAR, GLOBAL_AREA_ALIGN_TOP, &rect, size_y, HEADERY, size_y); +} + +static void screen_global_statusbar_area_create(wmWindow *win) +{ + const short size_y = HEADERY; + rcti rect; + + BLI_rcti_init(&rect, 0, WM_window_pixels_x(win) - 1, 0, WM_window_pixels_y(win) - 1); + rect.ymax = rect.ymin + size_y; + + screen_global_area_create(win, SPACE_STATUSBAR, GLOBAL_AREA_ALIGN_BOTTOM, &rect, size_y, size_y, size_y); +} + +void ED_screen_global_areas_create(wmWindow *win) +{ + bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); + if (screen->temp == 0) { + screen_global_topbar_area_create(win); + screen_global_statusbar_area_create(win); } +} - if (ELEM(sc->state, SCREENMAXIMIZED, SCREENFULL)) { - /* find associated full */ - bScreen *sc1; - for (sc1 = bmain->screen.first; sc1; sc1 = sc1->id.next) { - ScrArea *sa = sc1->areabase.first; - if (sa->full == sc) { - sc = sc1; - break; - } + +/* -------------------------------------------------------------------- */ +/* Screen changing */ + +static bScreen *screen_fullscreen_find_associated_normal_screen(const Main *bmain, bScreen *screen) +{ + for (bScreen *screen_iter = bmain->screen.first; screen_iter; screen_iter = screen_iter->id.next) { + ScrArea *sa = screen_iter->areabase.first; + if (sa && sa->full == screen) { + return screen_iter; } } + return screen; +} + +/** + * \return the screen to activate. + * \warning The returned screen may not always equal \a screen_new! + */ +bScreen *screen_change_prepare(bScreen *screen_old, bScreen *screen_new, Main *bmain, bContext *C, wmWindow *win) +{ + /* validate screen, it's called with notifier reference */ + if (BLI_findindex(&bmain->screen, screen_new) == -1) { + return NULL; + } + + if (ELEM(screen_new->state, SCREENMAXIMIZED, SCREENFULL)) { + screen_new = screen_fullscreen_find_associated_normal_screen(bmain, screen_new); + } + /* check for valid winid */ - if (sc->winid != 0 && sc->winid != win->winid) { - return false; + if (!(screen_new->winid == 0 || screen_new->winid == win->winid)) { + return NULL; } - if (oldscreen != sc) { - wmTimer *wt = oldscreen->animtimer; - ScrArea *sa; - Scene *oldscene = oldscreen->scene; + if (screen_old != screen_new) { + wmTimer *wt = screen_old->animtimer; /* remove handlers referencing areas in old screen */ - for (sa = oldscreen->areabase.first; sa; sa = sa->next) { + for (ScrArea *sa = screen_old->areabase.first; sa; sa = sa->next) { WM_event_remove_area_handler(&win->modalhandlers, sa); } /* we put timer to sleep, so screen_exit has to think there's no timer */ - oldscreen->animtimer = NULL; + screen_old->animtimer = NULL; if (wt) { - WM_event_timer_sleep(wm, win, wt, true); + WM_event_timer_sleep(CTX_wm_manager(C), win, wt, true); } - - ED_screen_exit(C, win, oldscreen); + ED_screen_exit(C, win, screen_old); /* Same scene, "transfer" playback to new screen. */ if (wt) { - if (oldscene == sc->scene) { - sc->animtimer = wt; - } - /* Else, stop playback. */ - else { - oldscreen->animtimer = wt; - ED_screen_animation_play(C, 0, 0); - } + screen_new->animtimer = wt; } - win->screen = sc; - CTX_wm_window_set(C, win); // stores C->wm.screen... hrmf - - /* prevent multiwin errors */ - sc->winid = win->winid; - - ED_screen_refresh(CTX_wm_manager(C), CTX_wm_window(C)); - WM_event_add_notifier(C, NC_WINDOW, NULL); - WM_event_add_notifier(C, NC_SCREEN | ND_SCREENSET, sc); - - /* makes button hilites work */ - WM_event_add_mousemove(C); - - /* Needed to make sure all the derivedMeshes are - * up-to-date before viewport starts acquiring this. - * - * This is needed in cases when, for example, boolean - * modifier uses operant from invisible layer. - * Without this trick boolean wouldn't apply correct. - * - * Quite the same happens when setting screen's scene, - * so perhaps this is in fact correct thing to do. - */ - if (oldscene != sc->scene) { - BKE_scene_set_background(bmain, sc->scene); - } - - /* Always do visible update since it's possible new screen will - * have different layers visible in 3D view-ports. - * This is possible because of view3d.lock_camera_and_layers option. - */ - DAG_on_visible_update(bmain, false); + return screen_new; } - return true; + return NULL; } -static bool ed_screen_used(wmWindowManager *wm, bScreen *sc) +void screen_change_update(bContext *C, wmWindow *win, bScreen *sc) { - wmWindow *win; + Scene *scene = WM_window_get_active_scene(win); + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, sc); - for (win = wm->windows.first; win; win = win->next) { - if (win->screen == sc) { - return true; - } + CTX_wm_window_set(C, win); /* stores C->wm.screen... hrmf */ - if (ELEM(win->screen->state, SCREENMAXIMIZED, SCREENFULL)) { - ScrArea *sa = win->screen->areabase.first; - if (sa->full == sc) { - return true; - } - } - } + ED_screen_refresh(CTX_wm_manager(C), win); - return false; + BKE_screen_view3d_scene_sync(sc, scene); /* sync new screen with scene data */ + WM_event_add_notifier(C, NC_WINDOW, NULL); + WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTSET, layout); + + /* makes button hilites work */ + WM_event_add_mousemove(C); } -/* only call outside of area/region loops */ -bool ED_screen_delete(bContext *C, bScreen *sc) + +/** + * \brief Change the active screen. + * + * Operator call, WM + Window + screen already existed before + * + * \warning Do NOT call in area/region queues! + * \returns if screen changing was successful. + */ +bool ED_screen_change(bContext *C, bScreen *sc) { Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); - bScreen *newsc; + bScreen *screen_old = CTX_wm_screen(C); + bScreen *screen_new = screen_change_prepare(screen_old, sc, bmain, C, win); - /* don't allow deleting temp fullscreens for now */ - if (ELEM(sc->state, SCREENMAXIMIZED, SCREENFULL)) { - return false; - } - - /* screen can only be in use by one window at a time, so as - * long as we are able to find a screen that is unused, we - * can safely assume ours is not in use anywhere an delete it */ - - for (newsc = sc->id.prev; newsc; newsc = newsc->id.prev) - if (!ed_screen_used(wm, newsc) && !newsc->temp) - break; - - if (!newsc) { - for (newsc = sc->id.next; newsc; newsc = newsc->id.next) - if (!ed_screen_used(wm, newsc) && !newsc->temp) - break; - } - - if (!newsc) { - return false; - } + if (screen_new) { + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WM_window_set_active_screen(win, workspace, sc); + screen_change_update(C, win, screen_new); - ED_screen_set(C, newsc); - - if (win->screen != sc) { - BKE_libblock_free(bmain, sc); return true; } - else { - return false; - } + + return false; } -static void ed_screen_set_3dview_camera(Scene *scene, bScreen *sc, ScrArea *sa, View3D *v3d) +static void screen_set_3dview_camera(Scene *scene, ViewLayer *view_layer, ScrArea *sa, View3D *v3d) { /* fix any cameras that are used in the 3d view but not in the scene */ BKE_screen_view3d_sync(v3d, scene); - if (!v3d->camera || !BKE_scene_base_find(scene, v3d->camera)) { - v3d->camera = BKE_scene_camera_find(sc->scene); + if (!v3d->camera || !BKE_view_layer_base_find(view_layer, v3d->camera)) { + v3d->camera = BKE_view_layer_camera_find(view_layer); // XXX if (sc == curscreen) handle_view3d_lock(); if (!v3d->camera) { ARegion *ar; @@ -1376,100 +1296,21 @@ static void ed_screen_set_3dview_camera(Scene *scene, bScreen *sc, ScrArea *sa, } } -/* only call outside of area/region loops */ -void ED_screen_set_scene(bContext *C, bScreen *screen, Scene *scene) +void ED_screen_update_after_scene_change(const bScreen *screen, Scene *scene_new, ViewLayer *view_layer) { - Main *bmain = CTX_data_main(C); - bScreen *sc; - - if (screen == NULL) - return; - - if (ed_screen_used(CTX_wm_manager(C), screen)) { - ED_object_editmode_exit(C, EM_FREEDATA); - } - - for (sc = bmain->screen.first; sc; sc = sc->id.next) { - if ((U.flag & USER_SCENEGLOBAL) || sc == screen) { - - if (scene != sc->scene) { - /* all areas endlocalview */ - // XXX ScrArea *sa = sc->areabase.first; - // while (sa) { - // endlocalview(sa); - // sa = sa->next; - // } - sc->scene = scene; + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + screen_set_3dview_camera(scene_new, view_layer, sa, v3d); } - } } - - // copy_view3d_lock(0); /* space.c */ - - /* are there cameras in the views that are not in the scene? */ - for (sc = bmain->screen.first; sc; sc = sc->id.next) { - if ((U.flag & USER_SCENEGLOBAL) || sc == screen) { - ScrArea *sa = sc->areabase.first; - while (sa) { - SpaceLink *sl = sa->spacedata.first; - while (sl) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *) sl; - ed_screen_set_3dview_camera(scene, sc, sa, v3d); - - } - sl = sl->next; - } - sa = sa->next; - } - } - } - - CTX_data_scene_set(C, scene); - BKE_scene_set_background(bmain, scene); - DAG_on_visible_update(bmain, false); - - ED_render_engine_changed(bmain); - ED_update_for_newframe(bmain, scene, 1); - - /* complete redraw */ - WM_event_add_notifier(C, NC_WINDOW, NULL); - -} - -/** - * \note Only call outside of area/region loops - * \return true if successful - */ -bool ED_screen_delete_scene(bContext *C, Scene *scene) -{ - Main *bmain = CTX_data_main(C); - Scene *newscene; - - if (scene->id.prev) - newscene = scene->id.prev; - else if (scene->id.next) - newscene = scene->id.next; - else - return false; - - ED_screen_set_scene(C, CTX_wm_screen(C), newscene); - - BKE_libblock_remap(bmain, scene, newscene, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); - - id_us_clear_real(&scene->id); - if (scene->id.us == 0) { - BKE_libblock_free(bmain, scene); - } - - return true; } ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) { wmWindow *win = CTX_wm_window(C); - bScreen *screen = CTX_wm_screen(C); ScrArea *newsa = NULL; if (!sa || sa->full == NULL) { @@ -1477,18 +1318,7 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) } if (!newsa) { - if (sa->full && (screen->state == SCREENMAXIMIZED)) { - /* if this has been called from the temporary info header generated in - * temp fullscreen layouts, find the correct fullscreen area to change - * to create a new space inside */ - for (newsa = screen->areabase.first; newsa; newsa = newsa->next) { - if (!(sa->flag & AREA_TEMP_INFO)) - break; - } - } - else { - newsa = sa; - } + newsa = sa; } BLI_assert(newsa); @@ -1571,6 +1401,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s { Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); + WorkSpace *workspace = WM_window_get_active_workspace(win); bScreen *sc, *oldscreen; ARegion *ar; @@ -1592,9 +1423,10 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s } if (sa && sa->full) { + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); /* restoring back to SCREENNORMAL */ sc = sa->full; /* the old screen to restore */ - oldscreen = win->screen; /* the one disappearing */ + oldscreen = WM_window_get_active_screen(win); /* the one disappearing */ sc->state = SCREENNORMAL; @@ -1608,10 +1440,8 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s /* clear full screen state */ old->full = NULL; - old->flag &= ~AREA_TEMP_INFO; } - sa->flag &= ~AREA_TEMP_INFO; sa->full = NULL; if (fullsa == NULL) { @@ -1621,6 +1451,10 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s } if (state == SCREENFULL) { + /* unhide global areas */ + for (ScrArea *glob_area = win->global_areas.areabase.first; glob_area; glob_area = glob_area->next) { + glob_area->global->flag &= ~GLOBAL_AREA_IS_HIDDEN; + } /* restore the old side panels/header visibility */ for (ar = sa->regionbase.first; ar; ar = ar->next) { ar->flag = ar->flagfullscreen; @@ -1633,9 +1467,9 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s sc->animtimer = oldscreen->animtimer; oldscreen->animtimer = NULL; - ED_screen_set(C, sc); + ED_screen_change(C, sc); - BKE_libblock_free(CTX_data_main(C), oldscreen); + BKE_workspace_layout_remove(CTX_data_main(C), workspace, layout_old); /* After we've restored back to SCREENNORMAL, we have to wait with * screen handling as it uses the area coords which aren't updated yet. @@ -1645,14 +1479,20 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s } else { /* change from SCREENNORMAL to new state */ + WorkSpaceLayout *layout_new; ScrArea *newa; char newname[MAX_ID_NAME - 2]; - oldscreen = win->screen; + BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL)); + + oldscreen = WM_window_get_active_screen(win); oldscreen->state = state; BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal"); - sc = ED_screen_add(bmain, win, oldscreen->scene, newname); + + layout_new = ED_workspace_layout_add(bmain, workspace, win, newname); + + sc = BKE_workspace_layout_screen_get(layout_new); sc->state = state; sc->redraws_flag = oldscreen->redraws_flag; sc->temp = oldscreen->temp; @@ -1663,51 +1503,35 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s /* use random area when we have no active one, e.g. when the * mouse is outside of the window and we open a file browser */ - if (!sa) + if (!sa || sa->global) { sa = oldscreen->areabase.first; - - if (state == SCREENMAXIMIZED) { - /* returns the top small area */ - newa = area_split(sc, (ScrArea *)sc->areabase.first, 'h', 0.99f, 1); - ED_area_newspace(C, newa, SPACE_INFO, false); - - /* copy area */ - newa = newa->prev; - ED_area_data_swap(newa, sa); - sa->flag |= AREA_TEMP_INFO; - - sa->full = oldscreen; - newa->full = oldscreen; - newa->next->full = oldscreen; // XXX } - else if (state == SCREENFULL) { - newa = (ScrArea *)sc->areabase.first; - /* copy area */ - ED_area_data_swap(newa, sa); - newa->flag = sa->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */ + newa = (ScrArea *)sc->areabase.first; + /* copy area */ + ED_area_data_swap(newa, sa); + newa->flag = sa->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */ + + if (state == SCREENFULL) { + /* temporarily hide global areas */ + for (ScrArea *glob_area = win->global_areas.areabase.first; glob_area; glob_area = glob_area->next) { + glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN; + } /* temporarily hide the side panels/header */ for (ar = newa->regionbase.first; ar; ar = ar->next) { ar->flagfullscreen = ar->flag; - if (ELEM(ar->regiontype, - RGN_TYPE_UI, - RGN_TYPE_HEADER, - RGN_TYPE_TOOLS)) - { + if (ELEM(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_HEADER, RGN_TYPE_TOOLS)) { ar->flag |= RGN_FLAG_HIDDEN; } } - - sa->full = oldscreen; - newa->full = oldscreen; - } - else { - BLI_assert(false); } - ED_screen_set(C, sc); + sa->full = oldscreen; + newa->full = oldscreen; + + ED_screen_change(C, sc); } /* XXX bad code: setscreen() ends with first area active. fullscreen render assumes this too */ @@ -1794,7 +1618,7 @@ void ED_screen_animation_timer(bContext *C, int redraws, int refresh, int sync, if (sa) spacetype = sa->spacetype; - sad->from_anim_edit = (ELEM(spacetype, SPACE_IPO, SPACE_ACTION, SPACE_NLA, SPACE_TIME)); + sad->from_anim_edit = (ELEM(spacetype, SPACE_IPO, SPACE_ACTION, SPACE_NLA)); screen->animtimer->customdata = sad; @@ -1842,13 +1666,10 @@ void ED_screen_animation_timer_update(bScreen *screen, int redraws, int refresh) } } -/* results in fully updated anim system - * screen can be NULL */ -void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute)) +/* results in fully updated anim system */ +void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph) { - wmWindowManager *wm = bmain->wm.first; - wmWindow *window; - int layers = 0; + Scene *scene = DEG_get_input_scene(depsgraph); #ifdef DURIAN_CAMERA_SWITCH void *camera = BKE_scene_camera_switch_find(scene); @@ -1857,19 +1678,15 @@ void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute)) scene->camera = camera; /* are there cameras in the views that are not in the scene? */ for (sc = bmain->screen.first; sc; sc = sc->id.next) { - BKE_screen_view3d_scene_sync(sc); + BKE_screen_view3d_scene_sync(sc, scene); } } #endif ED_clip_update_frame(bmain, scene->r.cfra); - /* get layers from all windows */ - for (window = wm->windows.first; window; window = window->next) - layers |= BKE_screen_visible_layers(window->screen, scene); - /* this function applies the changes too */ - BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, layers); + BKE_scene_graph_update_for_newframe(depsgraph, bmain); /* composite */ if (scene->use_nodes && scene->nodetree) @@ -1890,11 +1707,10 @@ void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute)) /* * return true if any active area requires to see in 3D */ -bool ED_screen_stereo3d_required(bScreen *screen) +bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene) { ScrArea *sa; - Scene *sce = screen->scene; - const bool is_multiview = (sce->r.scemode & R_MULTIVIEW) != 0; + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; for (sa = screen->areabase.first; sa; sa = sa->next) { switch (sa->spacetype) { @@ -1969,3 +1785,40 @@ bool ED_screen_stereo3d_required(bScreen *screen) return false; } + +/** + * Find the scene displayed in \a screen. + * \note Assumes \a screen to be visible/active! + */ + +Scene *ED_screen_scene_find_with_window(const bScreen *screen, const wmWindowManager *wm, struct wmWindow **r_window) +{ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (WM_window_get_active_screen(win) == screen) { + if (r_window) { + *r_window = win; + } + return WM_window_get_active_scene(win); + } + } + + BLI_assert(0); + return NULL; +} + + +Scene *ED_screen_scene_find(const bScreen *screen, const wmWindowManager *wm) +{ + return ED_screen_scene_find_with_window(screen, wm, NULL); +} + + +wmWindow *ED_screen_window_find(const bScreen *screen, const wmWindowManager *wm) +{ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (WM_window_get_active_screen(win) == screen) { + return win; + } + } + return NULL; +} diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 63d616e986e..2c343fb9d70 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -33,6 +33,7 @@ struct bContext; struct bContextDataResult; +struct Main; /* internal exports only */ @@ -43,23 +44,28 @@ struct bContextDataResult; /* area.c */ void ED_area_data_copy(ScrArea *sa_dst, ScrArea *sa_src, const bool do_free); void ED_area_data_swap(ScrArea *sa1, ScrArea *sa2); -void region_toggle_hidden(struct bContext *C, ARegion *ar, const bool do_fade); +void screen_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *area); +void region_toggle_hidden(struct bContext *C, ARegion *ar, const bool do_fade); /* screen_edit.c */ -ScrEdge *screen_findedge(bScreen *sc, ScrVert *v1, ScrVert *v2); +bScreen *screen_add(struct Main *bmain, const char *name, const rcti *rect); +void screen_data_copy(bScreen *to, bScreen *from); +void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new); +void screen_change_update(struct bContext *C, wmWindow *win, bScreen *sc); +bScreen *screen_change_prepare(bScreen *screen_old, bScreen *screen_new, struct Main *bmain, struct bContext *C, wmWindow *win); ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge); int screen_area_join(struct bContext *C, bScreen *scr, ScrArea *sa1, ScrArea *sa2); int area_getorientation(ScrArea *sa, ScrArea *sb); -void select_connected_scredge(bScreen *sc, ScrEdge *edge); +void select_connected_scredge(const wmWindow *win, ScrEdge *edge); -void removenotused_scrverts(bScreen *sc); -void removedouble_scrverts(bScreen *sc); -void removedouble_scredges(bScreen *sc); -void removenotused_scredges(bScreen *sc); bool scredge_is_horizontal(ScrEdge *se); -ScrEdge *screen_find_active_scredge(bScreen *sc, - const int winsize_x, const int winsize_y, - const int mx, const int my); +ScrEdge *screen_area_map_find_active_scredge( + const struct ScrAreaMap *area_map, + const rcti *bounds_rect, + const int mx, const int my); +ScrEdge *screen_find_active_scredge( + const wmWindow *win, const bScreen *screen, + const int mx, const int my); struct AZone *ED_area_actionzone_find_xy(ScrArea *sa, const int xy[2]); struct AZone *ED_area_actionzone_refresh_xy(ScrArea *sa, const int xy[2]); @@ -77,5 +83,8 @@ void SCREEN_OT_screencast(struct wmOperatorType *ot); /* screen_ops.c */ void region_blend_start(struct bContext *C, struct ScrArea *sa, struct ARegion *ar); +/* workspace_layout_edit.c */ +bool workspace_layout_set_poll(const struct WorkSpaceLayout *layout); + #endif /* __SCREEN_INTERN_H__ */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 1f5b51d211c..d74a9ed288c 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -39,6 +39,7 @@ #include "BLT_translation.h" +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_lattice_types.h" #include "DNA_object_types.h" @@ -48,11 +49,14 @@ #include "DNA_meta_types.h" #include "DNA_mask_types.h" #include "DNA_node_types.h" +#include "DNA_workspace_types.h" #include "DNA_userdef_types.h" #include "BKE_context.h" #include "BKE_customdata.h" +#include "BKE_fcurve.h" #include "BKE_global.h" +#include "BKE_icons.h" #include "BKE_main.h" #include "BKE_object.h" #include "BKE_report.h" @@ -61,10 +65,14 @@ #include "BKE_editmesh.h" #include "BKE_sound.h" #include "BKE_mask.h" +#include "BKE_workspace.h" #include "WM_api.h" #include "WM_types.h" +#include "DEG_depsgraph.h" + +#include "ED_anim_api.h" #include "ED_armature.h" #include "ED_clip.h" #include "ED_image.h" @@ -134,7 +142,7 @@ int ED_operator_screen_mainwinactive(bContext *C) if (CTX_wm_window(C) == NULL) return 0; screen = CTX_wm_screen(C); if (screen == NULL) return 0; - if (screen->subwinactive != screen->mainwin) return 0; + if (screen->active_region != NULL) return 0; return 1; } @@ -200,7 +208,7 @@ int ED_operator_animview_active(bContext *C) { if (ED_operator_areaactive(C)) { SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C); - if (sl && (ELEM(sl->spacetype, SPACE_SEQ, SPACE_ACTION, SPACE_NLA, SPACE_IPO, SPACE_TIME))) + if (sl && (ELEM(sl->spacetype, SPACE_SEQ, SPACE_ACTION, SPACE_NLA, SPACE_IPO))) return true; } @@ -208,11 +216,6 @@ int ED_operator_animview_active(bContext *C) return 0; } -int ED_operator_timeline_active(bContext *C) -{ - return ed_spacetype_test(C, SPACE_TIME); -} - int ED_operator_outliner_active(bContext *C) { return ed_spacetype_test(C, SPACE_OUTLINER); @@ -291,11 +294,6 @@ int ED_operator_nla_active(bContext *C) return ed_spacetype_test(C, SPACE_NLA); } -int ED_operator_logic_active(bContext *C) -{ - return ed_spacetype_test(C, SPACE_LOGIC); -} - int ED_operator_info_active(bContext *C) { return ed_spacetype_test(C, SPACE_INFO); @@ -552,8 +550,8 @@ int ED_operator_mask(bContext *C) case SPACE_IMAGE: { SpaceImage *sima = sa->spacedata.first; - Scene *scene = CTX_data_scene(C); - return ED_space_image_check_show_maskedit(scene, sima); + ViewLayer *view_layer = CTX_data_view_layer(C); + return ED_space_image_check_show_maskedit(sima, view_layer); } } } @@ -561,6 +559,12 @@ int ED_operator_mask(bContext *C) return false; } +int ED_operator_camera(bContext *C) +{ + struct Camera *cam = CTX_data_pointer_get_type(C, "camera", &RNA_Camera).data; + return (cam != NULL); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -723,6 +727,68 @@ static AZone *area_actionzone_refresh_xy(ScrArea *sa, const int xy[2], const boo break; } } + else if (az->type == AZONE_REGION_SCROLL) { + ARegion *ar = az->ar; + View2D *v2d = &ar->v2d; + const short isect_value = UI_view2d_mouse_in_scrollers(ar, v2d, xy[0], xy[1]); + if (test_only) { + if (isect_value != 0) { + break; + } + } + else { + bool redraw = false; + + if (isect_value == 'h') { + if (az->direction == AZ_SCROLL_HOR) { + az->alpha = 1.0f; + v2d->alpha_hor = 255; + v2d->size_hor = V2D_SCROLL_HEIGHT; + redraw = true; + } + } + else if (isect_value == 'v') { + if (az->direction == AZ_SCROLL_VERT) { + az->alpha = 1.0f; + v2d->alpha_vert = 255; + v2d->size_vert = V2D_SCROLL_WIDTH; + redraw = true; + } + } + else { + const int local_xy[2] = {xy[0] - ar->winrct.xmin, xy[1] - ar->winrct.ymin}; + float dist_fac = 0.0f, alpha = 0.0f; + + if (az->direction == AZ_SCROLL_HOR) { + dist_fac = BLI_rcti_length_y(&v2d->hor, local_xy[1]) / AZONEFADEIN; + CLAMP(dist_fac, 0.0f, 1.0f); + alpha = 1.0f - dist_fac; + + v2d->alpha_hor = alpha * 255; + v2d->size_hor = round_fl_to_int( + V2D_SCROLL_HEIGHT - + ((V2D_SCROLL_HEIGHT - V2D_SCROLL_HEIGHT_MIN) * dist_fac)); + } + else if (az->direction == AZ_SCROLL_VERT) { + dist_fac = BLI_rcti_length_x(&v2d->vert, local_xy[0]) / AZONEFADEIN; + CLAMP(dist_fac, 0.0f, 1.0f); + alpha = 1.0f - dist_fac; + + v2d->alpha_vert = alpha * 255; + v2d->size_vert = round_fl_to_int( + V2D_SCROLL_WIDTH - + ((V2D_SCROLL_WIDTH - V2D_SCROLL_WIDTH_MIN) * dist_fac)); + } + az->alpha = alpha; + redraw = true; + } + + if (redraw) { + ED_area_tag_redraw_no_rebuild(sa); + } + /* Don't return! */ + } + } } } @@ -794,6 +860,9 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) actionzone_exit(op); return OPERATOR_FINISHED; } + else if (ELEM(sad->az->type, AZONE_REGION_SCROLL)) { + return OPERATOR_PASS_THROUGH; + } else { /* add modal handler */ WM_event_add_modal_handler(C, op); @@ -805,11 +874,8 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) { - wmWindow *win = CTX_wm_window(C); bScreen *sc = CTX_wm_screen(C); sActionzoneData *sad = op->customdata; - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); switch (event->type) { case MOUSEMOVE: @@ -830,10 +896,15 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) sad->gesture_dir = 'w'; if (sad->az->type == AZONE_AREA) { + const wmWindow *win = CTX_wm_window(C); + rcti screen_rect; + + WM_window_screen_rect_calc(win, &screen_rect); /* once we drag outside the actionzone, register a gesture * check we're not on an edge so join finds the other area */ is_gesture = ((ED_area_actionzone_find_xy(sad->sa1, &event->x) != sad->az) && - (screen_find_active_scredge(sc, winsize_x, winsize_y, event->x, event->y) == NULL)); + (screen_area_map_find_active_scredge( + AREAMAP_FROM_SCREEN(sc), &screen_rect, event->x, event->y) == NULL)); } else { const int delta_min = 1; @@ -1023,13 +1094,17 @@ static void SCREEN_OT_area_swap(wmOperatorType *ot) static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Main *bmain = CTX_data_main(C); - wmWindow *newwin, *win; - bScreen *newsc, *sc; + wmWindow *newwin, *win = CTX_wm_window(C); + Scene *scene; + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); + WorkSpaceLayout *layout_new; + bScreen *newsc; ScrArea *sa; rcti rect; win = CTX_wm_window(C); - sc = CTX_wm_screen(C); + scene = CTX_data_scene(C); sa = CTX_wm_area(C); /* XXX hrmf! */ @@ -1056,9 +1131,13 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) *newwin->stereo3d_format = *win->stereo3d_format; + newwin->scene = scene; + + WM_window_set_active_workspace(newwin, workspace); /* allocs new screen and adds to newly created window, using window size */ - newsc = ED_screen_add(bmain, newwin, CTX_data_scene(C), sc->id.name + 2); - newwin->screen = newsc; + layout_new = ED_workspace_layout_add(bmain, workspace, newwin, BKE_workspace_layout_name_get(layout_old)); + newsc = BKE_workspace_layout_screen_get(layout_new); + WM_window_set_active_layout(newwin, workspace, layout_new); /* copy area to new screen */ ED_area_data_copy((ScrArea *)newsc->areabase.first, sa, true); @@ -1126,32 +1205,82 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) */ typedef struct sAreaMoveData { - int bigger, smaller, origval; + int bigger, smaller, origval, step; char dir; - bool do_snap; + enum AreaMoveSnapType { + /* Snapping disabled */ + SNAP_NONE = 0, + /* Snap to an invisible grid with a unit defined in AREAGRID */ + SNAP_AREAGRID, + /* Snap to mid-point and adjacent edges. */ + SNAP_MIDPOINT_AND_ADJACENT, + /* Snap to either bigger or smaller, nothing in-between (used for + * global areas). This has priority over other snap types, if it is + * used, toggling SNAP_MIDPOINT_AND_ADJACENT doesn't work. */ + SNAP_BIGGER_SMALLER_ONLY, + } snap_type; } sAreaMoveData; /* helper call to move area-edge, sets limits - * need window size in order to get correct limits */ -static void area_move_set_limits(bScreen *sc, int dir, - const int winsize_x, const int winsize_y, - int *bigger, int *smaller) + * need window bounds in order to get correct limits */ +static void area_move_set_limits( + wmWindow *win, bScreen *sc, int dir, + const rcti *screen_rect, + int *bigger, int *smaller, + bool *use_bigger_smaller_snap) { - ScrArea *sa; int areaminy = ED_area_headersize(); int areamin; /* we check all areas and test for free space with MINSIZE */ *bigger = *smaller = 100000; - for (sa = sc->areabase.first; sa; sa = sa->next) { + if (use_bigger_smaller_snap != NULL) { + *use_bigger_smaller_snap = false; + for (ScrArea *area = win->global_areas.areabase.first; area; area = area->next) { + const int size_min = round_fl_to_int(area->global->size_min * UI_DPI_FAC); + const int size_max = round_fl_to_int(area->global->size_max * UI_DPI_FAC); + + /* logic here is only tested for lower edge :) */ + /* left edge */ + if ((area->v1->editflag && area->v2->editflag)) { + *smaller = area->v4->vec.x - size_max; + *bigger = area->v4->vec.x - size_min; + *use_bigger_smaller_snap = true; + return; + } + /* top edge */ + else if ((area->v2->editflag && area->v3->editflag)) { + *smaller = area->v1->vec.y + size_min; + *bigger = area->v1->vec.y + size_max; + *use_bigger_smaller_snap = true; + return; + } + /* right edge */ + else if ((area->v3->editflag && area->v4->editflag)) { + *smaller = area->v1->vec.x + size_min; + *bigger = area->v1->vec.x + size_max; + *use_bigger_smaller_snap = true; + return; + } + /* lower edge */ + else if ((area->v4->editflag && area->v1->editflag)) { + *smaller = area->v2->vec.y - size_max; + *bigger = area->v2->vec.y - size_min; + *use_bigger_smaller_snap = true; + return; + } + } + } + + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { if (dir == 'h') { int y1; areamin = areaminy; - if (sa->v1->vec.y > 0) + if (sa->v1->vec.y > screen_rect->ymin) areamin += U.pixelsize; - if (sa->v2->vec.y < winsize_y - 1) + if (sa->v2->vec.y < (screen_rect->ymax - 1)) areamin += U.pixelsize; y1 = sa->v2->vec.y - sa->v1->vec.y + 1 - areamin; @@ -1166,9 +1295,9 @@ static void area_move_set_limits(bScreen *sc, int dir, int x1; areamin = AREAMINX; - if (sa->v1->vec.x > 0) + if (sa->v1->vec.x > screen_rect->xmin) areamin += U.pixelsize; - if (sa->v4->vec.x < winsize_x - 1) + if (sa->v4->vec.x < (screen_rect->xmax - 1)) areamin += U.pixelsize; x1 = sa->v4->vec.x - sa->v1->vec.x + 1 - areamin; @@ -1190,9 +1319,7 @@ static int area_move_init(bContext *C, wmOperator *op) wmWindow *win = CTX_wm_window(C); ScrEdge *actedge; sAreaMoveData *md; - ScrVert *v1; - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); + rcti screen_rect; int x, y; /* required properties */ @@ -1200,7 +1327,7 @@ static int area_move_init(bContext *C, wmOperator *op) y = RNA_int_get(op->ptr, "y"); /* setup */ - actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, x, y); + actedge = screen_find_active_scredge(win, sc, x, y); if (actedge == NULL) return 0; md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData"); @@ -1210,42 +1337,67 @@ static int area_move_init(bContext *C, wmOperator *op) if (md->dir == 'h') md->origval = actedge->v1->vec.y; else md->origval = actedge->v1->vec.x; - select_connected_scredge(sc, actedge); - /* now all vertices with 'flag==1' are the ones that can be moved. Move this to editflag */ - for (v1 = sc->vertbase.first; v1; v1 = v1->next) + select_connected_scredge(win, actedge); + /* now all vertices with 'flag == 1' are the ones that can be moved. Move this to editflag */ + ED_screen_verts_iter(win, sc, v1) { v1->editflag = v1->flag; + } + + WM_window_screen_rect_calc(win, &screen_rect); - area_move_set_limits(sc, md->dir, winsize_x, winsize_y, &md->bigger, &md->smaller); + bool use_bigger_smaller_snap = false; + area_move_set_limits(win, sc, md->dir, &screen_rect, + &md->bigger, &md->smaller, + &use_bigger_smaller_snap); + + md->snap_type = use_bigger_smaller_snap ? SNAP_BIGGER_SMALLER_ONLY : SNAP_AREAGRID; return 1; } static int area_snap_calc_location( - const bScreen *sc, const int delta, - const int origval, const int dir, + const bScreen *sc, const enum AreaMoveSnapType snap_type, + const int delta, const int origval, const int dir, const int bigger, const int smaller) { + BLI_assert(snap_type != SNAP_NONE); int final_loc = -1; - const int m_loc = origval + delta; - const int axis = (dir == 'v') ? 0 : 1; - int snap_dist; - int dist; - { - /* Test the snap to middle. */ - int middle = origval + (bigger - smaller) / 2; - middle -= (middle % AREAGRID); - snap_dist = abs(m_loc - middle); - final_loc = middle; - } + switch (snap_type) { + case SNAP_AREAGRID: + final_loc = m_loc; + if (delta != bigger && delta != -smaller) { + final_loc -= (m_loc % AREAGRID); + } + break; - for (const ScrVert *v1 = sc->vertbase.first; v1; v1 = v1->next) { - if (v1->editflag) { - const int v_loc = (&v1->vec.x)[!axis]; + case SNAP_BIGGER_SMALLER_ONLY: + final_loc = (m_loc >= bigger) ? bigger : smaller; + break; + + case SNAP_MIDPOINT_AND_ADJACENT: + { + const int axis = (dir == 'v') ? 0 : 1; + int snap_dist; + int dist; + { + /* Test the snap to middle. */ + int middle = origval + (bigger - smaller) / 2; + snap_dist = abs(m_loc - middle); + final_loc = middle; + } - for (const ScrVert *v2 = sc->vertbase.first; v2; v2 = v2->next) { - if (!v2->editflag) { + for (const ScrVert *v1 = sc->vertbase.first; v1; v1 = v1->next) { + if (!v1->editflag) { + continue; + } + const int v_loc = (&v1->vec.x)[!axis]; + + for (const ScrVert *v2 = sc->vertbase.first; v2; v2 = v2->next) { + if (v2->editflag) { + continue; + } if (v_loc == (&v2->vec.x)[!axis]) { const int v_loc2 = (&v2->vec.x)[axis]; /* Do not snap to the vertices at the ends. */ @@ -1259,7 +1411,10 @@ static int area_snap_calc_location( } } } + break; } + case SNAP_NONE: + break; } return final_loc; @@ -1270,29 +1425,26 @@ static void area_move_apply_do( const bContext *C, int delta, const int origval, const int dir, const int bigger, const int smaller, - const bool do_snap) + const enum AreaMoveSnapType snap_type) { + wmWindow *win = CTX_wm_window(C); bScreen *sc = CTX_wm_screen(C); - ScrVert *v1; bool doredraw = false; CLAMP(delta, -smaller, bigger); short final_loc = -1; - if (do_snap) { - final_loc = area_snap_calc_location(sc, delta, origval, dir, bigger, smaller); + if (snap_type == SNAP_NONE) { + final_loc = origval + delta; } else { - final_loc = origval + delta; - if (delta != bigger && delta != -smaller) { - final_loc -= (final_loc % AREAGRID); - } + final_loc = area_snap_calc_location(sc, snap_type, delta, origval, dir, bigger, smaller); } BLI_assert(final_loc != -1); short axis = (dir == 'v') ? 0 : 1; - for (v1 = sc->vertbase.first; v1; v1 = v1->next) { + ED_screen_verts_iter(win, sc, v1) { if (v1->editflag) { short oldval = (&v1->vec.x)[axis]; (&v1->vec.x)[axis] = final_loc; @@ -1307,12 +1459,26 @@ static void area_move_apply_do( /* only redraw if we actually moved a screen vert, for AREAGRID */ if (doredraw) { - for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + bool redraw_all = false; + ED_screen_areas_iter(win, sc, sa) { if (sa->v1->editflag || sa->v2->editflag || sa->v3->editflag || sa->v4->editflag) { + if (ED_area_is_global(sa)) { + sa->global->cur_fixed_height = round_fl_to_int((sa->v2->vec.y - sa->v1->vec.y) / UI_DPI_FAC); + sc->do_refresh = true; + redraw_all = true; + } + ED_area_tag_redraw(sa); + } + } + if (redraw_all) { + ED_screen_areas_iter(win, sc, sa) { ED_area_tag_redraw(sa); } } + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* redraw everything */ + /* Update preview thumbnail */ + BKE_icon_changed(sc->id.icon_id); } } @@ -1321,7 +1487,7 @@ static void area_move_apply(bContext *C, wmOperator *op) sAreaMoveData *md = op->customdata; int delta = RNA_int_get(op->ptr, "delta"); - area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->do_snap); + area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->snap_type); } static void area_move_exit(bContext *C, wmOperator *op) @@ -1331,8 +1497,8 @@ static void area_move_exit(bContext *C, wmOperator *op) op->customdata = NULL; /* this makes sure aligned edges will result in aligned grabbing */ - removedouble_scrverts(CTX_wm_screen(C)); - removedouble_scredges(CTX_wm_screen(C)); + BKE_screen_remove_double_scrverts(CTX_wm_screen(C)); + BKE_screen_remove_double_scredges(CTX_wm_screen(C)); } static int area_move_exec(bContext *C, wmOperator *op) @@ -1400,10 +1566,15 @@ static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; case KM_MODAL_SNAP_ON: - md->do_snap = true; + if (md->snap_type != SNAP_BIGGER_SMALLER_ONLY) { + md->snap_type = SNAP_MIDPOINT_AND_ADJACENT; + } break; + case KM_MODAL_SNAP_OFF: - md->do_snap = false; + if (md->snap_type != SNAP_BIGGER_SMALLER_ONLY) { + md->snap_type = SNAP_AREAGRID; + } break; } break; @@ -1522,7 +1693,7 @@ static int area_split_init(bContext *C, wmOperator *op) { ScrArea *sa = CTX_wm_area(C); sAreaSplitData *sd; - int areaminy = ED_area_headersize() + 1; + int areaminy = ED_area_headersize(); int dir; /* required context */ @@ -1540,8 +1711,14 @@ static int area_split_init(bContext *C, wmOperator *op) op->customdata = sd; sd->sarea = sa; - sd->origsize = dir == 'v' ? sa->winx : sa->winy; - sd->origmin = dir == 'v' ? sa->totrct.xmin : sa->totrct.ymin; + if (dir == 'v') { + sd->origmin = sa->v1->vec.x; + sd->origsize = sa->v4->vec.x - sd->origmin; + } + else { + sd->origmin = sa->v1->vec.y; + sd->origsize = sa->v2->vec.y - sd->origmin; + } return 1; } @@ -1560,16 +1737,16 @@ static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb) ScrVert *sbv4 = sb->v4; if (sav1 == sbv4 && sav2 == sbv3) { /* sa to right of sb = W */ - return screen_findedge(screen, sav1, sav2); + return BKE_screen_find_edge(screen, sav1, sav2); } else if (sav2 == sbv1 && sav3 == sbv4) { /* sa to bottom of sb = N */ - return screen_findedge(screen, sav2, sav3); + return BKE_screen_find_edge(screen, sav2, sav3); } else if (sav3 == sbv2 && sav4 == sbv1) { /* sa to left of sb = E */ - return screen_findedge(screen, sav3, sav4); + return BKE_screen_find_edge(screen, sav3, sav4); } else if (sav1 == sbv2 && sav4 == sbv3) { /* sa on top of sb = S*/ - return screen_findedge(screen, sav1, sav4); + return BKE_screen_find_edge(screen, sav1, sav4); } return NULL; @@ -1608,6 +1785,8 @@ static int area_split_apply(bContext *C, wmOperator *op) ED_area_tag_redraw(sd->narea); WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + /* Update preview thumbnail */ + BKE_icon_changed(sc->id.icon_id); return 1; } @@ -1633,8 +1812,8 @@ static void area_split_exit(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* this makes sure aligned edges will result in aligned grabbing */ - removedouble_scrverts(CTX_wm_screen(C)); - removedouble_scredges(CTX_wm_screen(C)); + BKE_screen_remove_double_scrverts(CTX_wm_screen(C)); + BKE_screen_remove_double_scredges(CTX_wm_screen(C)); } static void area_split_preview_update_cursor(bContext *C, wmOperator *op) @@ -1650,14 +1829,15 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) wmWindow *win = CTX_wm_window(C); bScreen *sc = CTX_wm_screen(C); sAreaSplitData *sd; - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); + rcti screen_rect; int dir; /* no full window splitting allowed */ if (sc->state != SCREENNORMAL) return OPERATOR_CANCELLED; + WM_window_screen_rect_calc(win, &screen_rect); + if (event->type == EVT_ACTIONZONE_AREA) { sActionzoneData *sad = event->customdata; @@ -1704,7 +1884,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) else y = event->x; - actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, x, y); + actedge = screen_area_map_find_active_scredge(AREAMAP_FROM_SCREEN(sc), &screen_rect, x, y); if (actedge == NULL) return OPERATOR_CANCELLED; @@ -1724,7 +1904,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* do the split */ if (area_split_apply(C, op)) { - area_move_set_limits(sc, dir, winsize_x, winsize_y, &sd->bigger, &sd->smaller); + area_move_set_limits(win, sc, dir, &screen_rect, &sd->bigger, &sd->smaller, NULL); /* add temp handler for edge move or cancel */ WM_event_add_modal_handler(C, op); @@ -1842,10 +2022,11 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) if (sd->previewmode == 0) { if (sd->do_snap) { const int snap_loc = area_snap_calc_location( - CTX_wm_screen(C), sd->delta, sd->origval, dir, sd->bigger, sd->smaller); + CTX_wm_screen(C), SNAP_MIDPOINT_AND_ADJACENT, sd->delta, sd->origval, dir, + sd->bigger, sd->smaller); sd->delta = snap_loc - sd->origval; } - area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, false); + area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, SNAP_NONE); } else { if (sd->sarea) { @@ -1857,19 +2038,20 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) if (sd->sarea) { ScrArea *sa = sd->sarea; if (dir == 'v') { - sd->origsize = sa->winx; - sd->origmin = sa->totrct.xmin; + sd->origmin = sa->v1->vec.x; + sd->origsize = sa->v4->vec.x - sd->origmin; } else { - sd->origsize = sa->winy; - sd->origmin = sa->totrct.ymin; + sd->origmin = sa->v1->vec.y; + sd->origsize = sa->v2->vec.y - sd->origmin; } if (sd->do_snap) { sa->v1->editflag = sa->v2->editflag = sa->v3->editflag = sa->v4->editflag = 1; const int snap_loc = area_snap_calc_location( - CTX_wm_screen(C), sd->delta, sd->origval, dir, sd->origmin + sd->origsize, -sd->origmin); + CTX_wm_screen(C), SNAP_MIDPOINT_AND_ADJACENT, sd->delta, sd->origval, dir, + sd->origmin + sd->origsize, -sd->origmin); sa->v1->editflag = sa->v2->editflag = sa->v3->editflag = sa->v4->editflag = 0; sd->delta = snap_loc - sd->origval; @@ -1878,7 +2060,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_area_tag_redraw(sd->sarea); } - CTX_wm_window(C)->screen->do_draw = true; + CTX_wm_screen(C)->do_draw = true; } float fac = (float)(sd->delta + sd->origval - sd->origmin) / sd->origsize; @@ -2047,7 +2229,8 @@ static int region_scale_get_maxsize(RegionMoveData *rmd) if (rmd->ar->regiontype == RGN_TYPE_TOOL_PROPS) { /* this calculation seems overly verbose * can someone explain why this method is necessary? - campbell */ - maxsize = rmd->maxsize - ((rmd->sa->headertype == HEADERTOP) ? UI_UNIT_Y * 2 : UI_UNIT_Y) - (UI_UNIT_Y / 4); + const bool top_header = ED_area_header_alignment(rmd->sa) == RGN_ALIGN_TOP; + maxsize = rmd->maxsize - ((top_header) ? UI_UNIT_Y * 2 : UI_UNIT_Y) - (UI_UNIT_Y / 4); } return maxsize; @@ -2091,7 +2274,9 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) /* execute the events */ switch (event->type) { case MOUSEMOVE: - + { + const float aspect = BLI_rctf_size_x(&rmd->ar->v2d.cur) / (BLI_rcti_size_x(&rmd->ar->v2d.mask) + 1); + const int snap_size_threshold = (U.widget_unit * 2) / aspect; if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) { delta = event->x - rmd->origx; if (rmd->edge == AE_LEFT_TO_TOPRIGHT) delta = -delta; @@ -2100,6 +2285,13 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) delta /= UI_DPI_FAC; rmd->ar->sizex = rmd->origval + delta; + + if (rmd->ar->type->snap_size) { + short sizex_test = rmd->ar->type->snap_size(rmd->ar, rmd->ar->sizex, 0); + if (ABS(rmd->ar->sizex - sizex_test) < snap_size_threshold) { + rmd->ar->sizex = sizex_test; + } + } CLAMP(rmd->ar->sizex, 0, rmd->maxsize); if (rmd->ar->sizex < UI_UNIT_X) { @@ -2119,6 +2311,13 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) delta /= UI_DPI_FAC; rmd->ar->sizey = rmd->origval + delta; + + if (rmd->ar->type->snap_size) { + short sizey_test = rmd->ar->type->snap_size(rmd->ar, rmd->ar->sizey, 1); + if (ABS(rmd->ar->sizey - sizey_test) < snap_size_threshold) { + rmd->ar->sizey = sizey_test; + } + } CLAMP(rmd->ar->sizey, 0, rmd->maxsize); /* note, 'UI_UNIT_Y/4' means you need to drag the header almost @@ -2138,7 +2337,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); break; - + } case LEFTMOUSE: if (event->val == KM_RELEASE) { @@ -2201,16 +2400,15 @@ static void areas_do_frame_follow(bContext *C, bool middle) bScreen *scr = CTX_wm_screen(C); Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *window; - for (window = wm->windows.first; window; window = window->next) { - ScrArea *sa; - for (sa = window->screen->areabase.first; sa; sa = sa->next) { - ARegion *ar; - for (ar = sa->regionbase.first; ar; ar = ar->next) { + for (wmWindow *window = wm->windows.first; window; window = window->next) { + const bScreen *screen = WM_window_get_active_screen(window); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { /* do follow here if editor type supports it */ if ((scr->redraws_flag & TIME_FOLLOW)) { if ((ar->regiontype == RGN_TYPE_WINDOW && - ELEM(sa->spacetype, SPACE_SEQ, SPACE_TIME, SPACE_IPO, SPACE_ACTION, SPACE_NLA)) || + ELEM(sa->spacetype, SPACE_SEQ, SPACE_IPO, SPACE_ACTION, SPACE_NLA)) || (sa->spacetype == SPACE_CLIP && ar->regiontype == RGN_TYPE_PREVIEW)) { float w = BLI_rctf_size_x(&ar->v2d.cur); @@ -2521,64 +2719,16 @@ static void SCREEN_OT_marker_jump(wmOperatorType *ot) /** \name Set Screen Operator * \{ */ -static bool screen_set_is_ok(bScreen *screen, bScreen *screen_prev) -{ - return ((screen->winid == 0) && - /* in typical usage these should have a nonzero winid - * (all temp screens should be used, or closed & freed). */ - (screen->temp == false) && - (screen->state == SCREENNORMAL) && - (screen != screen_prev) && - (screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT))); -} - /* function to be called outside UI context, or for redo */ static int screen_set_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - bScreen *screen = CTX_wm_screen(C); - bScreen *screen_prev = screen; - - ScrArea *sa = CTX_wm_area(C); - int tot = BLI_listbase_count(&bmain->screen); + WorkSpace *workspace = CTX_wm_workspace(C); int delta = RNA_int_get(op->ptr, "delta"); - /* temp screens are for userpref or render display */ - if (screen->temp || (sa && sa->full && sa->full->temp)) { - return OPERATOR_CANCELLED; - } - - if (delta == 1) { - while (tot--) { - screen = screen->id.next; - if (screen == NULL) screen = bmain->screen.first; - if (screen_set_is_ok(screen, screen_prev)) { - break; - } - } - } - else if (delta == -1) { - while (tot--) { - screen = screen->id.prev; - if (screen == NULL) screen = bmain->screen.last; - if (screen_set_is_ok(screen, screen_prev)) { - break; - } - } - } - else { - screen = NULL; - } - - if (screen && screen_prev != screen) { - /* return to previous state before switching screens */ - if (sa && sa->full) { - ED_screen_full_restore(C, sa); /* may free 'screen_prev' */ - } - - ED_screen_set(C, screen); + if (ED_workspace_layout_cycle(workspace, delta, C)) { return OPERATOR_FINISHED; } + return OPERATOR_CANCELLED; } @@ -2634,6 +2784,15 @@ static int screen_maximize_area_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static int screen_maximize_area_poll(bContext *C) +{ + const bScreen *screen = CTX_wm_screen(C); + const ScrArea *area = CTX_wm_area(C); + return ED_operator_areaactive(C) && + /* Don't allow maximizing global areas but allow minimizing from them. */ + ((screen->state != SCREENNORMAL) || !ED_area_is_global(area)); +} + static void SCREEN_OT_screen_full_area(wmOperatorType *ot) { PropertyRNA *prop; @@ -2643,7 +2802,7 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot) ot->idname = "SCREEN_OT_screen_full_area"; ot->exec = screen_maximize_area_exec; - ot->poll = ED_operator_areaactive; + ot->poll = screen_maximize_area_poll; ot->flag = 0; prop = RNA_def_boolean(ot->srna, "use_hide_panels", false, "Hide Panels", "Hide all the panels"); @@ -2776,9 +2935,9 @@ static void area_join_exit(bContext *C, wmOperator *op) } /* this makes sure aligned edges will result in aligned grabbing */ - removedouble_scredges(CTX_wm_screen(C)); - removenotused_scredges(CTX_wm_screen(C)); - removenotused_scrverts(CTX_wm_screen(C)); + BKE_screen_remove_double_scredges(CTX_wm_screen(C)); + BKE_screen_remove_unused_scredges(CTX_wm_screen(C)); + BKE_screen_remove_unused_scrverts(CTX_wm_screen(C)); } static int area_join_exec(bContext *C, wmOperator *op) @@ -2947,16 +3106,16 @@ static void SCREEN_OT_area_join(wmOperatorType *ot) static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - wmWindow *win = CTX_wm_window(C); - bScreen *sc = CTX_wm_screen(C); + const wmWindow *win = CTX_wm_window(C); + const bScreen *sc = CTX_wm_screen(C); uiPopupMenu *pup; uiLayout *layout; PointerRNA ptr; ScrEdge *actedge; - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); + rcti screen_rect; - actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, event->x, event->y); + WM_window_screen_rect_calc(win, &screen_rect); + actedge = screen_area_map_find_active_scredge(AREAMAP_FROM_SCREEN(sc), &screen_rect, event->x, event->y); if (actedge == NULL) return OPERATOR_CANCELLED; @@ -3360,6 +3519,18 @@ static int region_flip_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } +static int region_flip_poll(bContext *C) +{ + ScrArea *area = CTX_wm_area(C); + + /* don't flip anything around in topbar */ + if (area->spacetype == SPACE_TOPBAR) { + CTX_wm_operator_poll_msg_set(C, "Flipping regions in the Top-bar is not allowed"); + return 0; + } + + return ED_operator_areaactive(C); +} static void SCREEN_OT_region_flip(wmOperatorType *ot) { @@ -3370,7 +3541,7 @@ static void SCREEN_OT_region_flip(wmOperatorType *ot) /* api callbacks */ ot->exec = region_flip_exec; - ot->poll = ED_operator_areaactive; + ot->poll = region_flip_poll; ot->flag = 0; } @@ -3453,6 +3624,8 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN ARegion *ar = CTX_wm_region(C); const char *but_flip_str = (ar->alignment == RGN_ALIGN_TOP) ? IFACE_("Flip to Bottom") : IFACE_("Flip to Top"); + uiItemO(layout, IFACE_("Toggle Header"), ICON_NONE, "SCREEN_OT_header"); + /* default is WM_OP_INVOKE_REGION_WIN, which we don't want here. */ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); @@ -3463,12 +3636,11 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN uiItemS(layout); - /* file browser should be fullscreen all the time, but other regions can be maximized/restored... */ - if (sa->spacetype != SPACE_FILE) { - if (sa->full) - uiItemO(layout, IFACE_("Tile Area"), ICON_NONE, "SCREEN_OT_screen_full_area"); - else - uiItemO(layout, IFACE_("Maximize Area"), ICON_NONE, "SCREEN_OT_screen_full_area"); + /* file browser should be fullscreen all the time, topbar should + * never be. But other regions can be maximized/restored... */ + if (!ELEM(sa->spacetype, SPACE_FILE, SPACE_TOPBAR)) { + const char *but_str = sa->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); + uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); } } @@ -3528,13 +3700,14 @@ static int match_region_with_redraws(int spacetype, int regiontype, int redraws, return 1; break; case SPACE_IPO: - case SPACE_ACTION: case SPACE_NLA: if ((redraws & TIME_ALL_ANIM_WIN) || from_anim_edit) return 1; break; - case SPACE_TIME: - /* if only 1 window or 3d windows, we do timeline too */ + case SPACE_ACTION: + /* if only 1 window or 3d windows, we do timeline too + * NOTE: Now we do do action editor in all these cases, since timeline is here + */ if ((redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN)) || from_anim_edit) return 1; break; @@ -3585,7 +3758,7 @@ static int match_region_with_redraws(int spacetype, int regiontype, int redraws, return 1; } else if (regiontype == RGN_TYPE_HEADER) { - if (spacetype == SPACE_TIME) + if (spacetype == SPACE_ACTION) return 1; } else if (regiontype == RGN_TYPE_PREVIEW) { @@ -3615,6 +3788,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv if (screen->animtimer && screen->animtimer == event->customdata) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + struct Depsgraph *depsgraph = CTX_data_depsgraph(C); wmTimer *wt = screen->animtimer; ScreenAnimData *sad = wt->customdata; wmWindowManager *wm = CTX_wm_manager(C); @@ -3725,10 +3899,12 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv } /* since we follow drawflags, we can't send notifier but tag regions ourselves */ - ED_update_for_newframe(bmain, scene, 1); + ED_update_for_newframe(bmain, depsgraph); for (window = wm->windows.first; window; window = window->next) { - for (sa = window->screen->areabase.first; sa; sa = sa->next) { + const bScreen *win_screen = WM_window_get_active_screen(window); + + for (sa = win_screen->areabase.first; sa; sa = sa->next) { ARegion *ar; for (ar = sa->regionbase.first; ar; ar = ar->next) { bool redraw = false; @@ -3744,7 +3920,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv /* do follow here if editor type supports it */ if ((sad->redraws & TIME_FOLLOW)) { if ((ar->regiontype == RGN_TYPE_WINDOW && - ELEM(sa->spacetype, SPACE_SEQ, SPACE_TIME, SPACE_IPO, SPACE_ACTION, SPACE_NLA)) || + ELEM(sa->spacetype, SPACE_SEQ, SPACE_IPO, SPACE_ACTION, SPACE_NLA)) || (sa->spacetype == SPACE_CLIP && ar->regiontype == RGN_TYPE_PREVIEW)) { float w = BLI_rctf_size_x(&ar->v2d.cur); @@ -3808,11 +3984,11 @@ static void SCREEN_OT_animation_step(wmOperatorType *ot) /* find window that owns the animation timer */ bScreen *ED_screen_animation_playing(const wmWindowManager *wm) { - wmWindow *win; + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); - for (win = wm->windows.first; win; win = win->next) { - if (win->screen->animtimer || win->screen->scrubbing) { - return win->screen; + if (screen->animtimer || screen->scrubbing) { + return screen; } } @@ -3821,11 +3997,11 @@ bScreen *ED_screen_animation_playing(const wmWindowManager *wm) bScreen *ED_screen_animation_no_scrub(const wmWindowManager *wm) { - wmWindow *win; + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); - for (win = wm->windows.first; win; win = win->next) { - if (win->screen->animtimer) { - return win->screen; + if (screen->animtimer) { + return screen; } } @@ -3847,7 +4023,7 @@ int ED_screen_animation_play(bContext *C, int sync, int mode) WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } else { - int refresh = SPACE_TIME; /* these settings are currently only available from a menu in the TimeLine */ + int refresh = SPACE_ACTION; /* these settings are currently only available from a menu in the TimeLine */ if (mode == 1) /* XXX only play audio forwards!? */ BKE_sound_play_scene(scene); @@ -4049,7 +4225,7 @@ static void SCREEN_OT_back_to_previous(struct wmOperatorType *ot) static int userpref_show_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int sizex = 800 * UI_DPI_FAC; - int sizey = 480 * UI_DPI_FAC; + int sizey = 500 * UI_DPI_FAC; /* changes context! */ if (WM_window_open_temp(C, event->x, event->y, sizex, sizey, WM_WINDOW_USERPREFS) != NULL) { @@ -4077,118 +4253,101 @@ static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name New Screen Operator +/** \name Show Drivers Editor Operator * \{ */ -static int screen_new_exec(bContext *C, wmOperator *UNUSED(op)) +static int drivers_editor_show_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Main *bmain = CTX_data_main(C); - wmWindow *win = CTX_wm_window(C); - bScreen *sc = CTX_wm_screen(C); + PointerRNA ptr = {{NULL}}; + PropertyRNA *prop = NULL; + int index = -1; + uiBut *but = NULL; - sc = ED_screen_duplicate(bmain, win, sc); - WM_event_add_notifier(C, NC_SCREEN | ND_SCREENBROWSE, sc); + int sizex = 900 * UI_DPI_FAC; + int sizey = 580 * UI_DPI_FAC; - return OPERATOR_FINISHED; -} + /* Get active property to show driver for + * - Need to grab it first, or else this info disappears + * after we've created the window + */ + but = UI_context_active_but_prop_get(C, &ptr, &prop, &index); -static void SCREEN_OT_new(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "New Screen"; - ot->description = "Add a new screen"; - ot->idname = "SCREEN_OT_new"; + /* changes context! */ + if (WM_window_open_temp(C, event->x, event->y, sizex, sizey, WM_WINDOW_DRIVERS) != NULL) { + /* activate driver F-Curve for the property under the cursor */ + if (but) { + FCurve *fcu; + bool driven, special; + + fcu = rna_get_fcurve_context_ui(C, + &ptr, prop, index, + NULL, NULL, &driven, &special); + if (fcu) { + /* Isolate this F-Curve... */ + bAnimContext ac; + if (ANIM_animdata_get_context(C, &ac)) { + int filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS; + ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_CLEAR); + ANIM_set_active_channel(&ac, ac.data, ac.datatype, filter, fcu, ANIMTYPE_FCURVE); + } + else { + /* Just blindly isolate... This isn't the best, and shouldn't happen, but may be enough... */ + fcu->flag |= (FCURVE_ACTIVE | FCURVE_SELECTED); + } + } + } - /* api callbacks */ - ot->exec = screen_new_exec; - ot->poll = WM_operator_winactive; + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_ERROR, "Failed to open window!"); + return OPERATOR_CANCELLED; + } } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Delete Screen Operator - * \{ */ - -static int screen_delete_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bScreen *sc = CTX_wm_screen(C); - - WM_event_add_notifier(C, NC_SCREEN | ND_SCREENDELETE, sc); - - return OPERATOR_FINISHED; -} -static void SCREEN_OT_delete(wmOperatorType *ot) +static void SCREEN_OT_drivers_editor_show(struct wmOperatorType *ot) { /* identifiers */ - ot->name = "Delete Screen"; - ot->description = "Delete active screen"; - ot->idname = "SCREEN_OT_delete"; + ot->name = "Show Drivers Editor"; + ot->description = "Show drivers editor in a separate window"; + ot->idname = "SCREEN_OT_drivers_editor_show"; /* api callbacks */ - ot->exec = screen_delete_exec; + ot->invoke = drivers_editor_show_invoke; + ot->poll = ED_operator_screenactive; } /** \} */ /* -------------------------------------------------------------------- */ -/** \name New Scene Operator +/** \name New Screen Operator * \{ */ -static int scene_new_exec(bContext *C, wmOperator *op) +static int screen_new_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *newscene, *scene = CTX_data_scene(C); Main *bmain = CTX_data_main(C); - int type = RNA_enum_get(op->ptr, "type"); - - if (type == SCE_COPY_NEW) { - newscene = BKE_scene_add(bmain, DATA_("Scene")); - } - else { /* different kinds of copying */ - newscene = BKE_scene_copy(bmain, scene, type); - - /* these can't be handled in blenkernel currently, so do them here */ - if (type == SCE_COPY_LINK_DATA) { - ED_object_single_users(bmain, newscene, false, true); - } - else if (type == SCE_COPY_FULL) { - ED_editors_flush_edits(C, false); - ED_object_single_users(bmain, newscene, true, true); - } - } - - ED_screen_set_scene(C, CTX_wm_screen(C), newscene); + wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WorkSpaceLayout *layout_old = BKE_workspace_active_layout_get(win->workspace_hook); + WorkSpaceLayout *layout_new; - WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, newscene); + layout_new = ED_workspace_layout_duplicate(bmain, workspace, layout_old, win); + WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTBROWSE, layout_new); return OPERATOR_FINISHED; } -static void SCENE_OT_new(wmOperatorType *ot) +static void SCREEN_OT_new(wmOperatorType *ot) { - static const EnumPropertyItem type_items[] = { - {SCE_COPY_NEW, "NEW", 0, "New", "Add new scene"}, - {SCE_COPY_EMPTY, "EMPTY", 0, "Copy Settings", "Make a copy without any objects"}, - {SCE_COPY_LINK_OB, "LINK_OBJECTS", 0, "Link Objects", "Link to the objects from the current scene"}, - {SCE_COPY_LINK_DATA, "LINK_OBJECT_DATA", 0, "Link Object Data", "Copy objects linked to data from the current scene"}, - {SCE_COPY_FULL, "FULL_COPY", 0, "Full Copy", "Make a full copy of the current scene"}, - {0, NULL, 0, NULL, NULL}}; - /* identifiers */ - ot->name = "New Scene"; - ot->description = "Add new scene by type"; - ot->idname = "SCENE_OT_new"; + ot->name = "New Screen"; + ot->description = "Add a new screen"; + ot->idname = "SCREEN_OT_new"; /* api callbacks */ - ot->exec = scene_new_exec; - ot->invoke = WM_menu_invoke; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", ""); + ot->exec = screen_new_exec; + ot->poll = WM_operator_winactive; } /** \} */ @@ -4197,34 +4356,26 @@ static void SCENE_OT_new(wmOperatorType *ot) /** \name Delete Screen Operator * \{ */ -static int scene_delete_exec(bContext *C, wmOperator *UNUSED(op)) +static int screen_delete_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); - - if (ED_screen_delete_scene(C, scene) == false) { - return OPERATOR_CANCELLED; - } - - if (G.debug & G_DEBUG) - printf("scene delete %p\n", scene); + bScreen *sc = CTX_wm_screen(C); + WorkSpace *workspace = CTX_wm_workspace(C); + WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, sc); - WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, scene); + WM_event_add_notifier(C, NC_SCREEN | ND_LAYOUTDELETE, layout); return OPERATOR_FINISHED; } -static void SCENE_OT_delete(wmOperatorType *ot) +static void SCREEN_OT_delete(wmOperatorType *ot) { /* identifiers */ - ot->name = "Delete Scene"; - ot->description = "Delete active scene"; - ot->idname = "SCENE_OT_delete"; + ot->name = "Delete Screen"; + ot->description = "Delete active screen"; + ot->idname = "SCREEN_OT_delete"; /* api callbacks */ - ot->exec = scene_delete_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->exec = screen_delete_exec; } /** \} */ @@ -4246,7 +4397,7 @@ typedef struct RegionAlphaInfo { #define TIMEOUT 0.2f #define TIMESTEP 0.04f -float ED_region_blend_factor(ARegion *ar) +float ED_region_blend_alpha(ARegion *ar) { /* check parent too */ if (ar->regiontimer == NULL && (ar->alignment & RGN_SPLIT_PREV) && ar->prev) { @@ -4432,11 +4583,10 @@ static int space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent PointerRNA ptr; PropertyRNA *prop; context_cycle_prop_get(CTX_wm_screen(C), CTX_wm_area(C), &ptr, &prop); - const int old_context = RNA_property_enum_get(&ptr, prop); const int new_context = RNA_property_enum_step( - C, &ptr, prop, old_context, - direction == SPACE_CONTEXT_CYCLE_PREV ? -1 : 1); + C, &ptr, prop, old_context, + direction == SPACE_CONTEXT_CYCLE_PREV ? -1 : 1); RNA_property_enum_set(&ptr, prop, new_context); RNA_property_update(C, &ptr, prop); @@ -4463,6 +4613,51 @@ static void SCREEN_OT_space_context_cycle(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Workspace Cycle Operator + * \{ */ + +static int space_workspace_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wmWindow *win = CTX_wm_window(C); + if (WM_window_is_temp_screen(win)) { + return OPERATOR_CANCELLED; + } + + Main *bmain = CTX_data_main(C); + const int direction = RNA_enum_get(op->ptr, "direction"); + WorkSpace *workspace_src = WM_window_get_active_workspace(win); + WorkSpace *workspace_dst = (direction == SPACE_CONTEXT_CYCLE_PREV) ? workspace_src->id.prev : workspace_src->id.next; + if (workspace_dst == NULL) { + workspace_dst = (direction == SPACE_CONTEXT_CYCLE_PREV) ? bmain->workspaces.last : bmain->workspaces.first; + } + if (workspace_src != workspace_dst) { + win->workspace_hook->temp_workspace_store = workspace_dst; + WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_SET, workspace_dst); + win->workspace_hook->temp_workspace_store = NULL; + } + return OPERATOR_FINISHED; +} + +static void SCREEN_OT_workspace_cycle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Cycle Workspace"; + ot->description = "Cycle through workspaces"; + ot->idname = "SCREEN_OT_workspace_cycle"; + + /* api callbacks */ + ot->invoke = space_workspace_cycle_invoke; + ot->poll = ED_operator_screenactive;; + + ot->flag = 0; + + RNA_def_enum(ot->srna, "direction", space_context_cycle_direction, SPACE_CONTEXT_CYCLE_NEXT, "Direction", + "Direction to cycle through"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Assigning Operator Types * \{ */ @@ -4495,8 +4690,10 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_screenshot); WM_operatortype_append(SCREEN_OT_screencast); WM_operatortype_append(SCREEN_OT_userpref_show); + WM_operatortype_append(SCREEN_OT_drivers_editor_show); WM_operatortype_append(SCREEN_OT_region_blend); WM_operatortype_append(SCREEN_OT_space_context_cycle); + WM_operatortype_append(SCREEN_OT_workspace_cycle); /*frame changes*/ WM_operatortype_append(SCREEN_OT_frame_offset); @@ -4511,8 +4708,6 @@ void ED_operatortypes_screen(void) /* new/delete */ WM_operatortype_append(SCREEN_OT_new); WM_operatortype_append(SCREEN_OT_delete); - WM_operatortype_append(SCENE_OT_new); - WM_operatortype_append(SCENE_OT_delete); /* tools shared by more space types */ WM_operatortype_append(ED_OT_undo); @@ -4618,10 +4813,8 @@ void ED_keymap_screen(wmKeyConfig *keyconf) RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_screen_set", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", 1); RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_screen_set", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", -1); - WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", UPARROWKEY, KM_PRESS, KM_CTRL, 0); - WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", DOWNARROWKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", SPACEKEY, KM_PRESS, KM_SHIFT, 0); - kmi = WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", F10KEY, KM_PRESS, KM_ALT, 0); + kmi = WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", SPACEKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "use_hide_panels", true); WM_keymap_add_item(keymap, "SCREEN_OT_screenshot", F3KEY, KM_PRESS, KM_CTRL, 0); @@ -4632,9 +4825,14 @@ void ED_keymap_screen(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "SCREEN_OT_space_context_cycle", TABKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); RNA_enum_set(kmi->ptr, "direction", SPACE_CONTEXT_CYCLE_PREV); + kmi = WM_keymap_add_item(keymap, "SCREEN_OT_workspace_cycle", TABKEY, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "direction", SPACE_CONTEXT_CYCLE_NEXT); + kmi = WM_keymap_add_item(keymap, "SCREEN_OT_workspace_cycle", TABKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "direction", SPACE_CONTEXT_CYCLE_PREV); + /* tests */ WM_keymap_add_item(keymap, "SCREEN_OT_region_quadview", QKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); - WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_history", F3KEY, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_history", RKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); WM_keymap_add_item(keymap, "SCREEN_OT_repeat_last", RKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_verify_item(keymap, "SCREEN_OT_region_flip", F5KEY, KM_PRESS, 0, 0); WM_keymap_verify_item(keymap, "SCREEN_OT_redo_last", F6KEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c new file mode 100644 index 00000000000..d942602c896 --- /dev/null +++ b/source/blender/editors/screen/screen_user_menu.c @@ -0,0 +1,164 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/screen/screen_user_menu.c + * \ingroup spview3d + */ + +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <float.h> + +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "BKE_blender_user_menu.h" +#include "BKE_context.h" +#include "BKE_screen.h" +#include "BKE_idprop.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +/* -------------------------------------------------------------------- */ +/** \name Menu Type + * \{ */ + +bUserMenu *ED_screen_user_menu_find(bContext *C) +{ + SpaceLink *sl = CTX_wm_space_data(C); + const char *context = CTX_data_mode_string(C); + return BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context); +} + +bUserMenu *ED_screen_user_menu_ensure(bContext *C) +{ + SpaceLink *sl = CTX_wm_space_data(C); + const char *context = CTX_data_mode_string(C); + return BKE_blender_user_menu_ensure(&U.user_menus, sl->spacetype, context); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Menu Item + * \{ */ + +bUserMenuItem_Op *ED_screen_user_menu_item_find_operator( + ListBase *lb, + wmOperatorType *ot, IDProperty *prop, short opcontext) +{ + for (bUserMenuItem *umi = lb->first; umi; umi = umi->next) { + if (umi->type == USER_MENU_TYPE_OPERATOR) { + bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi; + if (STREQ(ot->idname, umi_op->opname) && + (opcontext == umi_op->opcontext) && + (IDP_EqualsProperties(prop, umi_op->prop))) + { + return umi_op; + } + } + } + return NULL; +} + +void ED_screen_user_menu_item_add_operator( + ListBase *lb, const char *ui_name, + wmOperatorType *ot, IDProperty *prop, short opcontext) +{ + bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_OPERATOR); + umi_op->opcontext = opcontext; + if (!STREQ(ui_name, ot->name)) { + BLI_strncpy(umi_op->item.ui_name, ui_name, OP_MAX_TYPENAME); + } + BLI_strncpy(umi_op->opname, ot->idname, OP_MAX_TYPENAME); + umi_op->prop = prop ? IDP_CopyProperty(prop) : NULL; +} + +void ED_screen_user_menu_item_remove(ListBase *lb, bUserMenuItem *umi) +{ + BLI_remlink(lb, umi); + BKE_blender_user_menu_item_free(umi); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Menu Definition + * \{ */ + +static void screen_user_menu_draw(const bContext *C, Menu *menu) +{ + SpaceLink *sl = CTX_wm_space_data(C); + const char *context = CTX_data_mode_string(C); + bUserMenu *um_array[] = { + BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context), + (sl->spacetype != SPACE_TOPBAR) ? BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context) : NULL, + }; + for (int um_index = 0; um_index < ARRAY_SIZE(um_array); um_index++) { + bUserMenu *um = um_array[um_index]; + if (um == NULL) { + continue; + } + for (bUserMenuItem *umi = um->items.first; umi; umi = umi->next) { + if (umi->type == USER_MENU_TYPE_OPERATOR) { + bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi; + IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL; + uiItemFullO( + menu->layout, umi_op->opname, umi->ui_name[0] ? umi->ui_name : NULL, + ICON_NONE, prop, umi_op->opcontext, 0, NULL); + } + else if (umi->type == USER_MENU_TYPE_SEP) { + uiItemS(menu->layout); + } + } + } +} + +void ED_screen_user_menu_register(void) +{ + MenuType *mt = MEM_callocN(sizeof(MenuType), __func__); + strcpy(mt->idname, "SCREEN_MT_user_menu"); + strcpy(mt->label, "Quick Favorites"); + strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + mt->draw = screen_user_menu_draw; + WM_menutype_add(mt); +} + +/** \} */ diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 54ff3d51479..e7b075dd5d0 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -54,7 +54,9 @@ #include "BKE_writeavi.h" #include "BIF_gl.h" -#include "BIF_glutil.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" #include "RNA_access.h" #include "RNA_define.h" @@ -273,7 +275,7 @@ static void screenshot_draw(bContext *UNUSED(C), wmOperator *op) /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); - uiDefAutoButsRNA(layout, &ptr, screenshot_draw_check_prop, '\0'); + uiDefAutoButsRNA(layout, &ptr, screenshot_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); } static int screenshot_poll(bContext *C) @@ -451,25 +453,24 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float /* Helper callback for drawing the cursor itself */ static void screencast_draw_cursor(bContext *UNUSED(C), int x, int y, void *UNUSED(p_ptr)) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); - glPushMatrix(); + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); - glTranslatef((float)x, (float)y, 0.0f); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ub(0, 0, 0, 32); + imm_draw_circle_fill_2d(pos, (float)x, (float)y, 20, 40); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_BLEND); - - glColor4ub(0, 0, 0, 32); - glutil_draw_filled_arc(0.0, M_PI * 2.0, 20, 40); + immUniformColor4ub(255, 255, 255, 128); + imm_draw_circle_wire_2d(pos, (float)x, (float)y, 20, 40); - glColor4ub(255, 255, 255, 128); - glutil_draw_lined_arc(0.0, M_PI * 2.0, 20, 40); + immUnbindProgram(); glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); - - glPopMatrix(); } /* Turn brush cursor in 3D view on/off */ diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c new file mode 100644 index 00000000000..a044a7d377a --- /dev/null +++ b/source/blender/editors/screen/workspace_edit.c @@ -0,0 +1,508 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/screen/workspace_edit.c + * \ingroup edscr + */ + +#include <stdlib.h> +#include <string.h> + +#include "BLI_utildefines.h" +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "BKE_appdir.h" +#include "BKE_blendfile.h" +#include "BKE_context.h" +#include "BKE_idcode.h" +#include "BKE_main.h" +#include "BKE_layer.h" +#include "BKE_library.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_workspace.h" + +#include "BLO_readfile.h" + +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" + +#include "ED_object.h" +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "DEG_depsgraph.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "WM_toolsystem.h" + +#include "screen_intern.h" + + +/** \name Workspace API + * + * \brief API for managing workspaces and their data. + * \{ */ + +WorkSpace *ED_workspace_add( + Main *bmain, const char *name, Scene *scene, + ViewLayer *act_view_layer) +{ + WorkSpace *workspace = BKE_workspace_add(bmain, name); + + BKE_workspace_view_layer_set(workspace, act_view_layer, scene); + + return workspace; +} + +/** + * Changes the object mode (if needed) to the one set in \a workspace_new. + * Object mode is still stored on object level. In future it should all be workspace level instead. + */ +static void workspace_change_update_mode( + const WorkSpace *workspace_old, const WorkSpace *workspace_new, + bContext *C, Object *ob_act, ReportList *reports) +{ + UNUSED_VARS(workspace_old, workspace_new, C, ob_act, reports); +#if 0 + eObjectMode mode_old = workspace_old->object_mode; + eObjectMode mode_new = workspace_new->object_mode; + + if (mode_old != mode_new) { + ED_object_mode_compat_set(C, ob_act, mode_new, reports); + ED_object_mode_toggle(C, mode_new); + } +#endif +} + +static void workspace_change_update_view_layer( + WorkSpace *workspace_new, const WorkSpace *workspace_old, + Scene *scene) +{ + if (!BKE_workspace_view_layer_exists(workspace_new, scene)) { + BKE_workspace_view_layer_set(workspace_new, BKE_workspace_view_layer_get(workspace_old, scene), scene); + } +} + +static void workspace_change_update( + WorkSpace *workspace_new, const WorkSpace *workspace_old, + bContext *C, wmWindowManager *wm) +{ + /* needs to be done before changing mode! (to ensure right context) */ + workspace_change_update_view_layer(workspace_new, workspace_old, CTX_data_scene(C)); + workspace_change_update_mode(workspace_old, workspace_new, C, CTX_data_active_object(C), &wm->reports); +} + +static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) +{ + /* return false to stop the iterator if we've found a layout that can be activated */ + return workspace_layout_set_poll(layout) ? false : true; +} + +static WorkSpaceLayout *workspace_change_get_new_layout( + Main *bmain, WorkSpace *workspace_new, wmWindow *win) +{ + /* ED_workspace_duplicate may have stored a layout to activate once the workspace gets activated. */ + WorkSpaceLayout *layout_new; + bScreen *screen_new; + + if (win->workspace_hook->temp_workspace_store) { + layout_new = win->workspace_hook->temp_layout_store; + } + else { + layout_new = BKE_workspace_hook_layout_for_workspace_get(win->workspace_hook, workspace_new); + if (!layout_new) { + layout_new = BKE_workspace_layouts_get(workspace_new)->first; + } + } + screen_new = BKE_workspace_layout_screen_get(layout_new); + + if (screen_new->winid) { + /* screen is already used, try to find a free one */ + WorkSpaceLayout *layout_temp = BKE_workspace_layout_iter_circular( + workspace_new, layout_new, workspace_change_find_new_layout_cb, + NULL, false); + if (!layout_temp) { + /* fallback solution: duplicate layout */ + layout_temp = ED_workspace_layout_duplicate(bmain, workspace_new, layout_new, win); + } + layout_new = layout_temp; + } + + return layout_new; +} + +/** + * \brief Change the active workspace. + * + * Operator call, WM + Window + screen already existed before + * Pretty similar to #ED_screen_change since changing workspace also changes screen. + * + * \warning Do NOT call in area/region queues! + * \returns if workspace changing was successful. + */ +bool ED_workspace_change( + WorkSpace *workspace_new, bContext *C, wmWindowManager *wm, wmWindow *win) +{ + Main *bmain = CTX_data_main(C); + WorkSpace *workspace_old = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout_new = workspace_change_get_new_layout(bmain, workspace_new, win); + bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new); + bScreen *screen_old = BKE_workspace_active_screen_get(win->workspace_hook); + + win->workspace_hook->temp_layout_store = NULL; + if (workspace_old == workspace_new) { + /* Could also return true, everything that needs to be done was done (nothing :P), but nothing changed */ + return false; + } + + screen_new = screen_change_prepare(screen_old, screen_new, bmain, C, win); + BLI_assert(BKE_workspace_layout_screen_get(layout_new) == screen_new); + + if (screen_new) { + WM_window_set_active_layout(win, workspace_new, layout_new); + WM_window_set_active_workspace(win, workspace_new); + + /* update screen *after* changing workspace - which also causes the + * actual screen change and updates context (including CTX_wm_workspace) */ + screen_change_update(C, win, screen_new); + workspace_change_update(workspace_new, workspace_old, C, wm); + + BLI_assert(BKE_workspace_view_layer_exists(workspace_new, CTX_data_scene(C)) != NULL); + BLI_assert(CTX_wm_workspace(C) == workspace_new); + + WM_toolsystem_unlink_all(C, workspace_old); + WM_toolsystem_reinit_all(C, win); + + return true; + } + + return false; +} + +/** + * Duplicate a workspace including its layouts. Does not activate the workspace, but + * it stores the screen-layout to be activated (BKE_workspace_temp_layout_store) + */ +WorkSpace *ED_workspace_duplicate( + WorkSpace *workspace_old, Main *bmain, wmWindow *win) +{ + WorkSpaceLayout *layout_active_old = BKE_workspace_active_layout_get(win->workspace_hook); + ListBase *layouts_old = BKE_workspace_layouts_get(workspace_old); + Scene *scene = WM_window_get_active_scene(win); + WorkSpace *workspace_new = ED_workspace_add( + bmain, workspace_old->id.name + 2, scene, + BKE_workspace_view_layer_get(workspace_old, scene)); + + /* TODO(campbell): tools */ + + for (WorkSpaceLayout *layout_old = layouts_old->first; layout_old; layout_old = layout_old->next) { + WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(bmain, workspace_new, layout_old, win); + + if (layout_active_old == layout_old) { + win->workspace_hook->temp_layout_store = layout_new; + } + } + return workspace_new; +} + +/** + * \return if succeeded. + */ +bool ED_workspace_delete( + WorkSpace *workspace, Main *bmain, bContext *C, wmWindowManager *wm) +{ + ID *workspace_id = (ID *)workspace; + + if (BLI_listbase_is_single(&bmain->workspaces)) { + return false; + } + + for (wmWindow *win = wm->windows.first; win; win = win->next) { + WorkSpace *prev = workspace_id->prev; + WorkSpace *next = workspace_id->next; + + ED_workspace_change((prev != NULL) ? prev : next, C, wm, win); + } + BKE_libblock_free(bmain, workspace_id); + + return true; +} + +/** + * Some editor data may need to be synced with scene data (3D View camera and layers). + * This function ensures data is synced for editors in active layout of \a workspace. + */ +void ED_workspace_scene_data_sync( + WorkSpaceInstanceHook *hook, Scene *scene) +{ + bScreen *screen = BKE_workspace_active_screen_get(hook); + BKE_screen_view3d_scene_sync(screen, scene); +} + +void ED_workspace_view_layer_unset( + const Main *bmain, Scene *scene, + const ViewLayer *layer_unset, ViewLayer *layer_new) +{ + for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) { + if (BKE_workspace_view_layer_get(workspace, scene) == layer_unset) { + BKE_workspace_view_layer_set(workspace, layer_new, scene); + } + } +} + +/** \} Workspace API */ + + +/** \name Workspace Operators + * + * \{ */ + +static int workspace_new_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = ED_workspace_duplicate(WM_window_get_active_workspace(win), bmain, win); + + WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_SET, workspace); + + return OPERATOR_FINISHED; +} + +static void WORKSPACE_OT_workspace_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Workspace"; + ot->description = "Add a new workspace"; + ot->idname = "WORKSPACE_OT_workspace_duplicate"; + + /* api callbacks */ + ot->exec = workspace_new_exec; + ot->poll = WM_operator_winactive; +} + +static int workspace_delete_exec(bContext *C, wmOperator *UNUSED(op)) +{ + wmWindow *win = CTX_wm_window(C); + + WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_DELETE, WM_window_get_active_workspace(win)); + + return OPERATOR_FINISHED; +} + +static void WORKSPACE_OT_workspace_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Workspace"; + ot->description = "Delete the active workspace"; + ot->idname = "WORKSPACE_OT_workspace_delete"; + + /* api callbacks */ + ot->exec = workspace_delete_exec; +} + +static int workspace_append_activate_poll(bContext *C) +{ + wmOperatorType *ot = WM_operatortype_find("WM_OT_append", false); + return WM_operator_poll(C, ot); +} + +static int workspace_append(bContext *C, const char *directory, const char *idname) +{ + wmOperatorType *ot = WM_operatortype_find("WM_OT_append", false); + PointerRNA opptr; + int retval; + + WM_operator_properties_create_ptr(&opptr, ot); + RNA_string_set(&opptr, "directory", directory); + RNA_string_set(&opptr, "filename", idname); + RNA_boolean_set(&opptr, "autoselect", false); + + retval = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &opptr); + + WM_operator_properties_free(&opptr); + + return retval; +} + +static int workspace_append_activate_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + char idname[MAX_ID_NAME - 2], directory[FILE_MAX]; + + if (!RNA_struct_property_is_set(op->ptr, "idname") || + !RNA_struct_property_is_set(op->ptr, "directory")) + { + return OPERATOR_CANCELLED; + } + RNA_string_get(op->ptr, "idname", idname); + RNA_string_get(op->ptr, "directory", directory); + + if (workspace_append(C, directory, idname) != OPERATOR_CANCELLED) { + WorkSpace *appended_workspace = BLI_findstring(&bmain->workspaces, idname, offsetof(ID, name) + 2); + + BLI_assert(appended_workspace != NULL); + /* Changing workspace changes context. Do delayed! */ + WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_SET, appended_workspace); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +static void WORKSPACE_OT_append_activate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Append and Activate Workspace"; + ot->description = "Append a workspace and make it the active one in the current window"; + ot->idname = "WORKSPACE_OT_append_activate"; + + /* api callbacks */ + ot->exec = workspace_append_activate_exec; + ot->poll = workspace_append_activate_poll; + + RNA_def_string(ot->srna, "idname", NULL, MAX_ID_NAME - 2, "Identifier", + "Name of the workspace to append and activate"); + RNA_def_string(ot->srna, "directory", NULL, FILE_MAX, "Directory", + "Path to the library"); +} + +static void workspace_config_file_path_from_folder_id( + const Main *bmain, int folder_id, char *r_path) +{ + const char *app_template = U.app_template[0] ? U.app_template : NULL; + const char * const cfgdir = BKE_appdir_folder_id(folder_id, app_template); + + if (cfgdir) { + BLI_make_file_string(bmain->name, r_path, cfgdir, BLENDER_WORKSPACES_FILE); + } + else { + r_path[0] = '\0'; + } +} + +ATTR_NONNULL(1) +static WorkspaceConfigFileData *workspace_config_file_read( + const Main *bmain, ReportList *reports) +{ + char workspace_config_path[FILE_MAX]; + bool has_path = false; + + workspace_config_file_path_from_folder_id(bmain, BLENDER_USER_CONFIG, workspace_config_path); + if (BLI_exists(workspace_config_path)) { + has_path = true; + } + else { + workspace_config_file_path_from_folder_id(bmain, BLENDER_DATAFILES, workspace_config_path); + if (BLI_exists(workspace_config_path)) { + has_path = true; + } + } + + return has_path ? BKE_blendfile_workspace_config_read(workspace_config_path, reports) : NULL; +} + +static void workspace_append_button( + uiLayout *layout, wmOperatorType *ot_append, const WorkSpace *workspace, const Main *from_main) +{ + const ID *id = (ID *)workspace; + PointerRNA opptr; + char lib_path[FILE_MAX_LIBEXTRA]; + + BLI_path_join( + lib_path, sizeof(lib_path), from_main->name, BKE_idcode_to_name(GS(id->name)), NULL); + + BLI_assert(STREQ(ot_append->idname, "WORKSPACE_OT_append_activate")); + uiItemFullO_ptr( + layout, ot_append, workspace->id.name + 2, ICON_NONE, NULL, + WM_OP_EXEC_DEFAULT, 0, &opptr); + RNA_string_set(&opptr, "idname", id->name + 2); + RNA_string_set(&opptr, "directory", lib_path); +} + +ATTR_NONNULL(1, 2) +static void workspace_config_file_append_buttons( + uiLayout *layout, const Main *bmain, ReportList *reports) +{ + WorkspaceConfigFileData *workspace_config = workspace_config_file_read(bmain, reports); + + if (workspace_config) { + wmOperatorType *ot_append = WM_operatortype_find("WORKSPACE_OT_append_activate", true); + + for (WorkSpace *workspace = workspace_config->workspaces.first; workspace; workspace = workspace->id.next) { + workspace_append_button(layout, ot_append, workspace, workspace_config->main); + } + + BKE_blendfile_workspace_config_data_free(workspace_config); + } +} + +static int workspace_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + const Main *bmain = CTX_data_main(C); + + uiPopupMenu *pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + + uiItemO(layout, "Duplicate Current", ICON_NONE, "WORKSPACE_OT_workspace_duplicate"); + uiItemS(layout); + workspace_config_file_append_buttons(layout, bmain, op->reports); + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +static void WORKSPACE_OT_workspace_add_menu(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Workspace"; + ot->description = "Add a new workspace by duplicating the current one or appending one " + "from the user configuration"; + ot->idname = "WORKSPACE_OT_workspace_add_menu"; + + /* api callbacks */ + ot->invoke = workspace_add_invoke; +} + +void ED_operatortypes_workspace(void) +{ + WM_operatortype_append(WORKSPACE_OT_workspace_duplicate); + WM_operatortype_append(WORKSPACE_OT_workspace_delete); + WM_operatortype_append(WORKSPACE_OT_workspace_add_menu); + WM_operatortype_append(WORKSPACE_OT_append_activate); +} + +/** \} Workspace Operators */ diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c new file mode 100644 index 00000000000..6d504c05dd1 --- /dev/null +++ b/source/blender/editors/screen/workspace_layout_edit.c @@ -0,0 +1,199 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/screen/workspace_layout_edit.c + * \ingroup edscr + */ + +#include <stdlib.h> + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" + +#include "DNA_screen_types.h" +#include "DNA_workspace_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_screen.h" +#include "BKE_workspace.h" + +#include "WM_api.h" + +#include "ED_screen.h" + +#include "screen_intern.h" + +/** + * Empty screen, with 1 dummy area without spacedata. Uses window size. + */ +WorkSpaceLayout *ED_workspace_layout_add( + Main *bmain, + WorkSpace *workspace, + wmWindow *win, + const char *name) +{ + bScreen *screen; + rcti screen_rect; + + WM_window_screen_rect_calc(win, &screen_rect); + screen = screen_add(bmain, name, &screen_rect); + + return BKE_workspace_layout_add(bmain, workspace, screen, name); +} + +WorkSpaceLayout *ED_workspace_layout_duplicate( + Main *bmain, + WorkSpace *workspace, const WorkSpaceLayout *layout_old, + wmWindow *win) +{ + bScreen *screen_old = BKE_workspace_layout_screen_get(layout_old); + const char *name = BKE_workspace_layout_name_get(layout_old); + bScreen *screen_new; + WorkSpaceLayout *layout_new; + + if (BKE_screen_is_fullscreen_area(screen_old)) { + return NULL; /* XXX handle this case! */ + } + + layout_new = ED_workspace_layout_add(bmain, workspace, win, name); + screen_new = BKE_workspace_layout_screen_get(layout_new); + screen_data_copy(screen_new, screen_old); + + return layout_new; +} + +static bool workspace_layout_delete_doit( + WorkSpace *workspace, WorkSpaceLayout *layout_old, WorkSpaceLayout *layout_new, + bContext *C) +{ + Main *bmain = CTX_data_main(C); + wmWindow *win = CTX_wm_window(C); + bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new); + + ED_screen_change(C, screen_new); + + if (BKE_workspace_active_layout_get(win->workspace_hook) != layout_old) { + BKE_workspace_layout_remove(bmain, workspace, layout_old); + return true; + } + + return false; +} + +bool workspace_layout_set_poll(const WorkSpaceLayout *layout) +{ + const bScreen *screen = BKE_workspace_layout_screen_get(layout); + + return ((BKE_screen_is_used(screen) == false) && + /* in typical usage temp screens should have a nonzero winid + * (all temp screens should be used, or closed & freed). */ + (screen->temp == false) && + (BKE_screen_is_fullscreen_area(screen) == false) && + (screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT))); +} + +static WorkSpaceLayout *workspace_layout_delete_find_new(const WorkSpaceLayout *layout_old) +{ + for (WorkSpaceLayout *layout_new = layout_old->prev; layout_new; layout_new = layout_new->next) { + if (workspace_layout_set_poll(layout_new)) { + return layout_new; + } + } + + for (WorkSpaceLayout *layout_new = layout_old->next; layout_new; layout_new = layout_new->next) { + if (workspace_layout_set_poll(layout_new)) { + return layout_new; + } + } + + return NULL; +} + +/** + * \warning Only call outside of area/region loops! + * \return true if succeeded. + */ +bool ED_workspace_layout_delete( + WorkSpace *workspace, WorkSpaceLayout *layout_old, + bContext *C) +{ + const bScreen *screen_old = BKE_workspace_layout_screen_get(layout_old); + WorkSpaceLayout *layout_new; + + BLI_assert(BLI_findindex(BKE_workspace_layouts_get(workspace), layout_old) != -1); + + /* don't allow deleting temp fullscreens for now */ + if (BKE_screen_is_fullscreen_area(screen_old)) { + return false; + } + + /* A layout/screen can only be in use by one window at a time, so as + * long as we are able to find a layout/screen that is unused, we + * can safely assume ours is not in use anywhere an delete it. */ + + layout_new = workspace_layout_delete_find_new(layout_old); + + if (layout_new) { + return workspace_layout_delete_doit(workspace, layout_old, layout_new, C); + } + + return false; +} + +static bool workspace_layout_cycle_iter_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) +{ + /* return false to stop iterator when we have found a layout to activate */ + return !workspace_layout_set_poll(layout); +} + +bool ED_workspace_layout_cycle( + WorkSpace *workspace, const short direction, bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + WorkSpaceLayout *old_layout = BKE_workspace_active_layout_get(win->workspace_hook); + WorkSpaceLayout *new_layout; + const bScreen *old_screen = BKE_workspace_layout_screen_get(old_layout); + ScrArea *sa = CTX_wm_area(C); + + if (old_screen->temp || (sa && sa->full && sa->full->temp)) { + return false; + } + + BLI_assert(ELEM(direction, 1, -1)); + new_layout = BKE_workspace_layout_iter_circular(workspace, old_layout, workspace_layout_cycle_iter_cb, + NULL, (direction == -1) ? true : false); + + if (new_layout && (old_layout != new_layout)) { + bScreen *new_screen = BKE_workspace_layout_screen_get(new_layout); + + if (sa && sa->full) { + /* return to previous state before switching screens */ + ED_screen_full_restore(C, sa); /* may free screen of old_layout */ + } + + ED_screen_change(C, new_screen); + + return true; + } + + return false; +} |