Files
test/source/blender/editors/screen/area.c
Brecht Van Lommel 7a9f64e665 UI: better support for dynamically sized regions in topbar.
Dynamically sized regions in the topbar were flickering due to only updating
their size after redraws. Now there is an optional layout() callback for
all regions in an area to do UI layout first, then refresh the region layout,
and then do the actual drawing for each region.

Task T54753
2018-04-29 22:11:12 +02:00

2887 lines
80 KiB
C

/*
* ***** 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) 2008 Blender Foundation.
* All rights reserved.
*
*
* Contributor(s): Blender Foundation
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/screen/area.c
* \ingroup edscr
*/
#include <string.h>
#include <stdio.h>
#include "MEM_guardedalloc.h"
#include "DNA_userdef_types.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#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_message.h"
#include "ED_screen.h"
#include "ED_screen_types.h"
#include "ED_space_api.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"
#include "IMB_imbuf_types.h"
#include "IMB_metadata.h"
#include "UI_interface.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
#include "UI_view2d.h"
#include "screen_intern.h"
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, int sides)
{
rcti rect;
/* translate scissor rect to region space */
rect.xmin = scirct->xmin - ar->winrct.xmin;
rect.ymin = scirct->ymin - ar->winrct.ymin;
rect.xmax = scirct->xmax - ar->winrct.xmin;
rect.ymax = scirct->ymax - ar->winrct.ymin;
/* set transp line */
glEnable(GL_BLEND);
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);
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);
}
/* left */
if (sides & REGION_EMBOSS_LEFT) {
immVertex2f(pos, rect.xmin, rect.ymin);
immVertex2f(pos, rect.xmin, rect.ymax);
}
/* top */
if (sides & REGION_EMBOSS_TOP) {
immVertex2f(pos, rect.xmin, rect.ymax);
immVertex2f(pos, rect.xmax, 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);
gpuLoadIdentity();
}
/* only exported for WM */
void ED_region_do_listen(bScreen *sc, ScrArea *sa, ARegion *ar, wmNotifier *note, const Scene *scene)
{
/* generic notes first */
switch (note->category) {
case NC_WM:
if (note->data == ND_FILEREAD)
ED_region_tag_redraw(ar);
break;
case NC_WINDOW:
ED_region_tag_redraw(ar);
break;
}
if (ar->type && ar->type->listener)
ar->type->listener(sc, sa, ar, note, scene);
}
/* only exported for WM */
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, scene, workspace);
}
}
/* only exported for WM */
void ED_area_do_refresh(bContext *C, ScrArea *sa)
{
/* no generic notes? */
if (sa->type && sa->type->refresh) {
sa->type->refresh(C, sa);
}
sa->do_refresh = false;
}
/**
* Action zones are only updated if the mouse is inside of them, but in some cases (currently only fullscreen icon)
* it might be needed to update their properties and redraw if the mouse isn't inside.
*/
void ED_area_azones_update(ScrArea *sa, const int mouse_xy[2])
{
AZone *az;
bool changed = false;
for (az = sa->actionzones.first; az; az = az->next) {
if (az->type == AZONE_FULLSCREEN) {
/* only if mouse is not hovering the azone */
if (BLI_rcti_isect_pt_v(&az->rect, mouse_xy) == false) {
az->alpha = 0.0f;
changed = true;
/* can break since currently only this is handled here */
break;
}
}
}
if (changed) {
sa->flag &= ~AREA_FLAG_ACTIONZONES_UPDATE;
ED_area_tag_redraw(sa);
}
}
/**
* \brief Corner widget use for quitting fullscreen.
*/
static void area_draw_azone_fullscreen(short x1, short y1, short x2, short y2, float alpha)
{
int x = x2 - ((float) x2 - x1) * 0.5f / UI_DPI_FAC;
int y = y2 - ((float) y2 - y1) * 0.5f / UI_DPI_FAC;
/* adjust the icon distance from the corner */
x += 36.0f / UI_DPI_FAC;
y += 36.0f / UI_DPI_FAC;
/* draws from the left bottom corner of the icon */
x -= UI_DPI_ICON_SIZE;
y -= UI_DPI_ICON_SIZE;
alpha = min_ff(alpha, 0.75f);
UI_icon_draw_aspect(x, y, ICON_FULLSCREEN_EXIT, 0.7f / UI_DPI_FAC, alpha);
/* debug drawing :
* The click_rect is the same as defined in fullscreen_click_rcti_init
* Keep them both in sync */
if (G.debug_value == 1) {
rcti click_rect;
float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC;
unsigned char alpha_debug = 255 * alpha;
BLI_rcti_init(&click_rect, x, x + icon_size, y, y + icon_size);
Gwn_VertFormat *format = immVertexFormat();
unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
unsigned int color = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
immAttrib4ub(color, 255, 0, 0, alpha_debug);
immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
imm_draw_box_wire_2d(pos, click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax);
immAttrib4ub(color, 0, 255, 255, alpha_debug);
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 UNUSED(x1), short UNUSED(y1), short UNUSED(x2), short UNUSED(y2))
{
/* No drawing needed since all corners are action zone, and visually distinguishable. */
}
static void region_draw_azone_icon(AZone *az)
{
float midx = az->x1 + (az->x2 - az->x1) * 0.5f;
float midy = az->y1 + (az->y2 - az->y1) * 0.5f;
Gwn_VertFormat *format = immVertexFormat();
unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
/* outlined circle */
GPU_enable_program_point_size(); /* TODO: make a fixed-size shader to avoid this */
immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f);
immUniform4f("outlineColor", 0.2f, 0.2f, 0.2f, 0.9f);
immUniform1f("outlineWidth", 1.0f);
immUniform1f("size", 9.5f);
immBegin(GWN_PRIM_POINTS, 1);
immVertex2f(pos, midx, midy);
immEnd();
immUnbindProgram();
GPU_disable_program_point_size();
/* + */
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4f(0.2f, 0.2f, 0.2f, 0.9f);
immBegin(GWN_PRIM_LINES, 4);
immVertex2f(pos, midx, midy - 2);
immVertex2f(pos, midx, midy + 3);
immVertex2f(pos, midx - 2, midy);
immVertex2f(pos, midx + 3, midy);
immEnd();
immUnbindProgram();
}
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;
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)
{
glEnable(GL_BLEND);
/* 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);
break;
case AE_BOTTOM_TO_TOPLEFT:
UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
break;
case AE_LEFT_TO_TOPRIGHT:
UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
break;
case AE_RIGHT_TO_TOPLEFT:
UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
break;
}
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);
draw_azone_plus((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2);
}
static void region_draw_azone_tab(AZone *az)
{
float col[4], black[4] = {0.0f, 0.0f, 0.0f, 0.5f};
glEnable(GL_BLEND);
UI_GetThemeColor3fv(TH_HEADER, col);
col[3] = 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_draw_roundbox_shade_x(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f, col);
UI_draw_roundbox_aa(false, (float)az->x1, 0.3f + (float)az->y1, (float)az->x2, 0.3f + (float)az->y2, 4.0f, black);
break;
case AE_BOTTOM_TO_TOPLEFT:
UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
UI_draw_roundbox_shade_x(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f, col);
UI_draw_roundbox_aa(false, (float)az->x1, 0.3f + (float)az->y1, (float)az->x2, 0.3f + (float)az->y2, 4.0f, black);
break;
case AE_LEFT_TO_TOPRIGHT:
UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
UI_draw_roundbox_shade_x(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f, col);
UI_draw_roundbox_aa(false, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, black);
break;
case AE_RIGHT_TO_TOPLEFT:
UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
UI_draw_roundbox_shade_x(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f, col);
UI_draw_roundbox_aa(false, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, black);
break;
}
glDisable(GL_BLEND);
}
static void region_draw_azone_tria(AZone *az)
{
glEnable(GL_BLEND);
//UI_GetThemeColor3fv(TH_HEADER, col);
float color[4] = {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, color);
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, color);
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, color);
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, color);
break;
}
glDisable(GL_BLEND);
}
static void area_azone_tag_update(ScrArea *sa)
{
sa->flag |= AREA_FLAG_ACTIONZONES_UPDATE;
}
static void region_draw_azones(ScrArea *sa, ARegion *ar)
{
AZone *az;
if (!sa)
return;
glLineWidth(1.0f);
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
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 */
rcti azrct;
BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2);
if (BLI_rcti_isect(&ar->drawrct, &azrct, NULL)) {
if (az->type == AZONE_AREA) {
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);
}
}
}
else if (az->type == AZONE_FULLSCREEN) {
area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha);
if (az->alpha != 0.0f) {
area_azone_tag_update(sa);
}
}
}
}
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 */
void ED_region_do_layout(bContext *C, ARegion *ar)
{
/* This is optional, only needed for dynamically sized regions. */
ScrArea *sa = CTX_wm_area(C);
ARegionType *at = ar->type;
if (!at->layout) {
return;
}
if (at->do_lock) {
return;
}
ar->do_draw |= RGN_DRAWING;
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;
/* see BKE_spacedata_draw_locks() */
if (at->do_lock)
return;
ar->do_draw |= RGN_DRAWING;
/* Set viewport, scissor, ortho and ar->drawrct. */
wmPartialViewport(&ar->drawrct, &ar->winrct, &ar->drawrct);
wmOrtho2_region_pixelspace(ar);
UI_SetTheme(sa ? sa->spacetype : 0, at->regionid);
/* optional header info instead? */
if (ar->headerstr) {
UI_ThemeClearColor(TH_HEADER);
glClear(GL_COLOR_BUFFER_BIT);
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) {
at->draw(C, ar);
}
/* XXX test: add convention to end regions always in pixel space, for drawing of borders/gestures etc */
ED_region_pixelspace(ar);
ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_PIXEL);
region_draw_azones(sa, ar);
/* for debugging unneeded area redraws and partial redraw */
#if 0
glEnable(GL_BLEND);
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
memset(&ar->drawrct, 0, sizeof(ar->drawrct));
UI_blocklist_free_inactive(C, &ar->uiblocks);
if (sa) {
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);
}
}
/* **********************************
* maybe silly, but let's try for now
* to keep these tags protected
* ********************************** */
void ED_region_tag_redraw(ARegion *ar)
{
/* don't tag redraw while drawing, it shouldn't happen normally
* 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;
memset(&ar->drawrct, 0, sizeof(ar->drawrct));
}
}
void ED_region_tag_redraw_overlay(ARegion *ar)
{
if (ar)
ar->do_draw_overlay = RGN_DRAW;
}
void ED_region_tag_refresh_ui(ARegion *ar)
{
if (ar) {
ar->do_draw |= RGN_DRAW_REFRESH_UI;
}
}
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))) {
/* no redraw set yet, set partial region */
ar->do_draw |= RGN_DRAW_PARTIAL;
ar->drawrct = *rct;
}
else if (ar->drawrct.xmin != ar->drawrct.xmax) {
BLI_assert((ar->do_draw & RGN_DRAW_PARTIAL) != 0);
/* partial redraw already set, expand region */
BLI_rcti_union(&ar->drawrct, rct);
}
else {
BLI_assert((ar->do_draw & RGN_DRAW) != 0);
/* Else, full redraw is already requested, nothing to do here. */
}
}
}
void ED_area_tag_redraw(ScrArea *sa)
{
ARegion *ar;
if (sa)
for (ar = sa->regionbase.first; ar; ar = ar->next)
ED_region_tag_redraw(ar);
}
void ED_area_tag_redraw_regiontype(ScrArea *sa, int regiontype)
{
ARegion *ar;
if (sa) {
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->regiontype == regiontype) {
ED_region_tag_redraw(ar);
}
}
}
}
void ED_area_tag_refresh(ScrArea *sa)
{
if (sa)
sa->do_refresh = true;
}
/* *************************************************************** */
/* use NULL to disable it */
void ED_area_headerprint(ScrArea *sa, const char *str)
{
ARegion *ar;
/* happens when running transform operators in backround mode */
if (sa == NULL)
return;
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->regiontype == RGN_TYPE_HEADER) {
if (str) {
if (ar->headerstr == NULL)
ar->headerstr = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint");
BLI_strncpy(ar->headerstr, str, UI_MAX_DRAW_STR);
}
else if (ar->headerstr) {
MEM_freeN(ar->headerstr);
ar->headerstr = NULL;
}
ED_region_tag_redraw(ar);
}
}
}
/* ************************************************************ */
static void area_azone_initialize(wmWindow *win, const bScreen *screen, ScrArea *sa)
{
AZone *az;
/* reinitalize entirely, regions and fullscreen add azones too */
BLI_freelistN(&sa->actionzones);
if (screen->state != SCREENNORMAL) {
return;
}
if (U.app_flag & USER_APP_LOCK_UI_LAYOUT) {
return;
}
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 (!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;
#endif
/* set area action zones */
az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
BLI_addtail(&(sa->actionzones), az);
az->type = AZONE_AREA;
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);
}
}
static void fullscreen_azone_initialize(ScrArea *sa, ARegion *ar)
{
AZone *az;
if (ED_area_is_global(sa) || (ar->regiontype != RGN_TYPE_WINDOW))
return;
az = (AZone *)MEM_callocN(sizeof(AZone), "fullscreen action zone");
BLI_addtail(&(sa->actionzones), az);
az->type = AZONE_FULLSCREEN;
az->ar = ar;
az->alpha = 0.0f;
az->x1 = ar->winrct.xmax - (AZONEFADEOUT - 1);
az->y1 = ar->winrct.ymax - (AZONEFADEOUT - 1);
az->x2 = ar->winrct.xmax;
az->y2 = ar->winrct.ymax;
BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
}
#define AZONEPAD_EDGE (0.1f * U.widget_unit)
#define AZONEPAD_ICON (0.45f * U.widget_unit)
static void region_azone_edge(AZone *az, ARegion *ar)
{
switch (az->edge) {
case AE_TOP_TO_BOTTOMRIGHT:
az->x1 = ar->winrct.xmin;
az->y1 = ar->winrct.ymax - AZONEPAD_EDGE;
az->x2 = ar->winrct.xmax;
az->y2 = ar->winrct.ymax + AZONEPAD_EDGE;
break;
case AE_BOTTOM_TO_TOPLEFT:
az->x1 = ar->winrct.xmin;
az->y1 = ar->winrct.ymin + AZONEPAD_EDGE;
az->x2 = ar->winrct.xmax;
az->y2 = ar->winrct.ymin - AZONEPAD_EDGE;
break;
case AE_LEFT_TO_TOPRIGHT:
az->x1 = ar->winrct.xmin - AZONEPAD_EDGE;
az->y1 = ar->winrct.ymin;
az->x2 = ar->winrct.xmin + AZONEPAD_EDGE;
az->y2 = ar->winrct.ymax;
break;
case AE_RIGHT_TO_TOPLEFT:
az->x1 = ar->winrct.xmax + AZONEPAD_EDGE;
az->y1 = ar->winrct.ymin;
az->x2 = ar->winrct.xmax - AZONEPAD_EDGE;
az->y2 = ar->winrct.ymax;
break;
}
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)
/* region already made zero sized, in shape of edge */
static void region_azone_tab_plus(ScrArea *sa, AZone *az, ARegion *ar)
{
AZone *azt;
int tot = 0, add;
for (azt = sa->actionzones.first; azt; azt = azt->next) {
if (azt->edge == az->edge) tot++;
}
switch (az->edge) {
case AE_TOP_TO_BOTTOMRIGHT:
add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0;
az->x1 = ar->winrct.xmax - 2.5f * AZONEPAD_TAB_PLUSW;
az->y1 = ar->winrct.ymax - add;
az->x2 = ar->winrct.xmax - 1.5f * AZONEPAD_TAB_PLUSW;
az->y2 = ar->winrct.ymax - add + AZONEPAD_TAB_PLUSH;
break;
case AE_BOTTOM_TO_TOPLEFT:
az->x1 = ar->winrct.xmax - 2.5f * AZONEPAD_TAB_PLUSW;
az->y1 = ar->winrct.ymin - AZONEPAD_TAB_PLUSH;
az->x2 = ar->winrct.xmax - 1.5f * AZONEPAD_TAB_PLUSW;
az->y2 = ar->winrct.ymin;
break;
case AE_LEFT_TO_TOPRIGHT:
az->x1 = ar->winrct.xmin - AZONEPAD_TAB_PLUSH;
az->y1 = ar->winrct.ymax - 2.5f * AZONEPAD_TAB_PLUSW;
az->x2 = ar->winrct.xmin;
az->y2 = ar->winrct.ymax - 1.5f * AZONEPAD_TAB_PLUSW;
break;
case AE_RIGHT_TO_TOPLEFT:
az->x1 = ar->winrct.xmax - 1;
az->y1 = ar->winrct.ymax - 2.5f * AZONEPAD_TAB_PLUSW;
az->x2 = ar->winrct.xmax - 1 + AZONEPAD_TAB_PLUSH;
az->y2 = ar->winrct.ymax - 1.5f * AZONEPAD_TAB_PLUSW;
break;
}
/* rect needed for mouse pointer test */
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)
{
AZone *azt;
int tot = 0, add;
for (azt = sa->actionzones.first; azt; azt = azt->next) {
if (azt->edge == az->edge) tot++;
}
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;
}
/* 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)
{
AZone *azt;
int tot = 0, add;
for (azt = sa->actionzones.first; azt; azt = azt->next) {
if (azt->edge == az->edge) tot++;
}
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;
}
/* rect needed for mouse pointer test */
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)
{
AZone *az = NULL;
const bool is_hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) == 0;
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 (!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);
}
}
else {
region_azone_edge(az, ar);
}
}
/* *************************************************************** */
static void region_azone_add(ScrArea *sa, ARegion *ar, const int alignment, const bool is_fullscreen)
{
/* 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);
else if (alignment == RGN_ALIGN_BOTTOM)
region_azone_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);
else if (alignment == RGN_ALIGN_LEFT)
region_azone_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen);
}
/* dir is direction to check, not the splitting edge direction! */
static int rct_fits(const rcti *rect, char dir, int size)
{
if (dir == 'h') {
return BLI_rcti_size_x(rect) + 1 - size;
}
else { /* 'v' */
return BLI_rcti_size_y(rect) + 1 - size;
}
}
/* *************************************************************** */
/* ar should be overlapping */
/* function checks if some overlapping region was defined before - on same place */
static void region_overlap_fix(ScrArea *sa, ARegion *ar)
{
ARegion *ar1;
const int align = ar->alignment & ~RGN_SPLIT_PREV;
int align1 = 0;
/* find overlapping previous region on same place */
for (ar1 = ar->prev; ar1; ar1 = ar1->prev) {
if (ar1->overlap && ((ar1->alignment & RGN_SPLIT_PREV) == 0)) {
align1 = ar1->alignment;
if (BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) {
if (align1 != align) {
/* Left overlapping right or vice-versa, forbid this! */
ar->flag |= RGN_FLAG_TOO_SMALL;
return;
}
/* Else, we have our previous region on same side. */
break;
}
}
}
/* translate or close */
if (ar1) {
if (align1 == RGN_ALIGN_LEFT) {
if (ar->winrct.xmax + ar1->winx > sa->winx - U.widget_unit) {
ar->flag |= RGN_FLAG_TOO_SMALL;
return;
}
else {
BLI_rcti_translate(&ar->winrct, ar1->winx, 0);
}
}
else if (align1 == RGN_ALIGN_RIGHT) {
if (ar->winrct.xmin - ar1->winx < U.widget_unit) {
ar->flag |= RGN_FLAG_TOO_SMALL;
return;
}
else {
BLI_rcti_translate(&ar->winrct, -ar1->winx, 0);
}
}
}
/* 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->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! */
ar->flag |= RGN_FLAG_TOO_SMALL;
return;
}
}
}
}
/* overlapping regions only in the following restricted cases */
static bool region_is_overlap(ScrArea *sa, ARegion *ar)
{
if (U.uiflag2 & USER_REGION_OVERLAP) {
if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_SEQ)) {
if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS))
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))
return 1;
}
}
return 0;
}
static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti *remainder, int quad, bool add_azones)
{
rcti *remainder_prev = remainder;
int prefsizex, prefsizey;
int alignment;
if (ar == NULL)
return;
/* no returns in function, winrct gets set in the end again */
BLI_rcti_init(&ar->winrct, 0, 0, 0, 0);
/* for test; allow split of previously defined region */
if (ar->alignment & RGN_SPLIT_PREV)
if (ar->prev)
remainder = &ar->prev->winrct;
alignment = ar->alignment & ~RGN_SPLIT_PREV;
/* set here, assuming userpref switching forces to call this again */
ar->overlap = region_is_overlap(sa, ar);
/* clear state flags first */
ar->flag &= ~RGN_FLAG_TOO_SMALL;
/* user errors */
if (ar->next == NULL && alignment != RGN_ALIGN_QSPLIT)
alignment = RGN_ALIGN_NONE;
/* 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);
}
else {
prefsizey = UI_DPI_FAC * (ar->sizey > 1 ? ar->sizey + 0.5f : ar->type->prefsizey);
}
if (ar->flag & RGN_FLAG_HIDDEN) {
/* hidden is user flag */
}
else if (alignment == RGN_ALIGN_FLOAT) {
/* XXX floating area region, not handled yet here */
}
else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) {
/* remainder is too small for any usage */
ar->flag |= RGN_FLAG_TOO_SMALL;
}
else if (alignment == RGN_ALIGN_NONE) {
/* typically last region */
ar->winrct = *remainder;
BLI_rcti_init(remainder, 0, 0, 0, 0);
}
else if (alignment == RGN_ALIGN_TOP || alignment == RGN_ALIGN_BOTTOM) {
if (rct_fits(remainder, 'v', prefsizey) < 0) {
ar->flag |= RGN_FLAG_TOO_SMALL;
}
else {
int fac = rct_fits(remainder, 'v', prefsizey);
if (fac < 0)
prefsizey += fac;
ar->winrct = *remainder;
if (alignment == RGN_ALIGN_TOP) {
ar->winrct.ymin = ar->winrct.ymax - prefsizey + 1;
remainder->ymax = ar->winrct.ymin - 1;
}
else {
ar->winrct.ymax = ar->winrct.ymin + prefsizey - 1;
remainder->ymin = ar->winrct.ymax + 1;
}
}
}
else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
if (rct_fits(remainder, 'h', prefsizex) < 0) {
ar->flag |= RGN_FLAG_TOO_SMALL;
}
else {
int fac = rct_fits(remainder, 'h', prefsizex);
if (fac < 0)
prefsizex += fac;
ar->winrct = *remainder;
if (alignment == RGN_ALIGN_RIGHT) {
ar->winrct.xmin = ar->winrct.xmax - prefsizex + 1;
if (ar->overlap == 0)
remainder->xmax = ar->winrct.xmin - 1;
}
else {
ar->winrct.xmax = ar->winrct.xmin + prefsizex - 1;
if (ar->overlap == 0)
remainder->xmin = ar->winrct.xmax + 1;
}
}
}
else if (alignment == RGN_ALIGN_VSPLIT || alignment == RGN_ALIGN_HSPLIT) {
/* percentage subdiv*/
ar->winrct = *remainder;
if (alignment == RGN_ALIGN_HSPLIT) {
if (rct_fits(remainder, 'h', prefsizex) > 4) {
ar->winrct.xmax = BLI_rcti_cent_x(remainder);
remainder->xmin = ar->winrct.xmax + 1;
}
else {
BLI_rcti_init(remainder, 0, 0, 0, 0);
}
}
else {
if (rct_fits(remainder, 'v', prefsizey) > 4) {
ar->winrct.ymax = BLI_rcti_cent_y(remainder);
remainder->ymin = ar->winrct.ymax + 1;
}
else {
BLI_rcti_init(remainder, 0, 0, 0, 0);
}
}
}
else if (alignment == RGN_ALIGN_QSPLIT) {
ar->winrct = *remainder;
/* test if there's still 4 regions left */
if (quad == 0) {
ARegion *artest = ar->next;
int count = 1;
while (artest) {
artest->alignment = RGN_ALIGN_QSPLIT;
artest = artest->next;
count++;
}
if (count != 4) {
/* let's stop adding regions */
BLI_rcti_init(remainder, 0, 0, 0, 0);
if (G.debug & G_DEBUG)
printf("region quadsplit failed\n");
}
else {
quad = 1;
}
}
if (quad) {
if (quad == 1) { /* left bottom */
ar->winrct.xmax = BLI_rcti_cent_x(remainder);
ar->winrct.ymax = BLI_rcti_cent_y(remainder);
}
else if (quad == 2) { /* left top */
ar->winrct.xmax = BLI_rcti_cent_x(remainder);
ar->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
}
else if (quad == 3) { /* right bottom */
ar->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
ar->winrct.ymax = BLI_rcti_cent_y(remainder);
}
else { /* right top */
ar->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
ar->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
BLI_rcti_init(remainder, 0, 0, 0, 0);
}
quad++;
}
}
/* for speedup */
ar->winx = BLI_rcti_size_x(&ar->winrct) + 1;
ar->winy = BLI_rcti_size_y(&ar->winrct) + 1;
/* if region opened normally, we store this for hide/reveal usage */
/* prevent rounding errors for UI_DPI_FAC mult and divide */
if (ar->winx > 1) ar->sizex = (ar->winx + 0.5f) / UI_DPI_FAC;
if (ar->winy > 1) ar->sizey = (ar->winy + 0.5f) / UI_DPI_FAC;
/* exception for multiple overlapping regions on same spot */
if (ar->overlap)
region_overlap_fix(sa, ar);
/* set winrect for azones */
if (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
ar->winrct = *remainder;
switch (alignment) {
case RGN_ALIGN_TOP:
ar->winrct.ymin = ar->winrct.ymax;
break;
case RGN_ALIGN_BOTTOM:
ar->winrct.ymax = ar->winrct.ymin;
break;
case RGN_ALIGN_RIGHT:
ar->winrct.xmin = ar->winrct.xmax;
break;
case RGN_ALIGN_LEFT:
default:
/* prevent winrct to be valid */
ar->winrct.xmax = ar->winrct.xmin;
break;
}
}
/* restore prev-split exception */
if (ar->alignment & RGN_SPLIT_PREV) {
if (ar->prev) {
remainder = remainder_prev;
ar->prev->winx = BLI_rcti_size_x(&ar->prev->winrct) + 1;
ar->prev->winy = BLI_rcti_size_y(&ar->prev->winrct) + 1;
}
}
/* 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 (add_azones) {
const bScreen *screen = WM_window_get_active_screen(win);
if (ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) {
region_azone_add(sa, ar, alignment, false);
}
else {
region_azone_add(sa, ar, alignment, true);
fullscreen_azone_initialize(sa, ar);
}
}
region_rect_recursive(win, sa, ar->next, remainder, quad, add_azones);
}
static void area_calc_totrct(ScrArea *sa, int window_size_x, int window_size_y)
{
short px = (short)U.pixelsize;
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;
/* scale down totrct by 1 pixel on all sides not matching window borders */
if (sa->totrct.xmin > 0) {
sa->totrct.xmin += px;
}
if (sa->totrct.xmax < (window_size_x - 1)) {
sa->totrct.xmax -= px;
}
if (sa->totrct.ymin > 0) {
sa->totrct.ymin += px;
}
if (sa->totrct.ymax < (window_size_y - 1)) {
sa->totrct.ymax -= px;
}
BLI_assert(sa->totrct.xmin >= 0);
BLI_assert(sa->totrct.xmax >= 0);
BLI_assert(sa->totrct.ymin >= 0);
BLI_assert(sa->totrct.ymax >= 0);
/* for speedup */
sa->winx = BLI_rcti_size_x(&sa->totrct) + 1;
sa->winy = BLI_rcti_size_y(&sa->totrct) + 1;
}
/* used for area initialize below */
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));
ar->visible = !hidden;
}
static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *handlers, int flag)
{
/* note, add-handler checks if it already exists */
/* XXX it would be good to have boundbox checks for some of these... */
if (flag & ED_KEYMAP_UI) {
wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "User Interface", 0, 0);
WM_event_add_keymap_handler(handlers, keymap);
/* user interface widgets */
UI_region_handlers_add(handlers);
}
if (flag & ED_KEYMAP_VIEW2D) {
/* 2d-viewport handling+manipulation */
wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "View2D", 0, 0);
WM_event_add_keymap_handler(handlers, keymap);
}
if (flag & ED_KEYMAP_MARKERS) {
/* time-markers */
wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Markers", 0, 0);
/* 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);
}
}
if (flag & ED_KEYMAP_ANIMATION) {
/* frame changing and timeline operators (for time spaces) */
wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Animation", 0, 0);
WM_event_add_keymap_handler(handlers, keymap);
}
if (flag & ED_KEYMAP_FRAMES) {
/* frame changing/jumping (for all spaces) */
wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Frames", 0, 0);
WM_event_add_keymap_handler(handlers, keymap);
}
if (flag & ED_KEYMAP_GPENCIL) {
/* grease pencil */
/* NOTE: This is now 2 keymaps - One for basic functionality,
* and one that only applies when "Edit Mode" is enabled
* for strokes.
*
* For now, it's easier to just include both,
* since you hardly want one without the other.
*/
wmKeyMap *keymap_general = WM_keymap_find(wm->defaultconf, "Grease Pencil", 0, 0);
wmKeyMap *keymap_edit = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_general);
WM_event_add_keymap_handler(handlers, keymap_edit);
}
if (flag & ED_KEYMAP_HEADER) {
/* standard keymap for headers regions */
wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Header", 0, 0);
WM_event_add_keymap_handler(handlers, keymap);
}
}
void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *area)
{
if (!(area->flag & AREA_FLAG_REGION_SIZE_UPDATE)) {
return;
}
const int size_x = WM_window_pixels_x(win);
const int size_y = WM_window_pixels_y(win);
rcti rect;
area_calc_totrct(area, size_x, size_y);
/* region rect sizes */
rect = area->totrct;
region_rect_recursive(win, area, area->regionbase.first, &rect, 0, false);
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)
{
const bScreen *screen = WM_window_get_active_screen(win);
const int window_size_x = WM_window_pixels_x(win);
const int window_size_y = WM_window_pixels_y(win);
ARegion *ar;
rcti rect;
if (ED_area_is_global(sa) && (sa->global->flag & GLOBAL_AREA_IS_HIDDEN)) {
return;
}
/* set typedefinitions */
sa->type = BKE_spacetype_from_id(sa->spacetype);
if (sa->type == NULL) {
sa->spacetype = SPACE_VIEW3D;
sa->type = BKE_spacetype_from_id(sa->spacetype);
}
for (ar = sa->regionbase.first; ar; ar = ar->next)
ar->type = BKE_regiontype_from_id(sa->type, ar->regiontype);
/* area sizes */
area_calc_totrct(sa, window_size_x, window_size_y);
/* clear all azones, add the area triange widgets */
area_azone_initialize(win, screen, sa);
/* region rect sizes */
rect = sa->totrct;
region_rect_recursive(win, sa, sa->regionbase.first, &rect, 0, true);
sa->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE;
/* default area handlers */
ed_default_handlers(wm, sa, &sa->handlers, sa->type->keymapflag);
/* checks spacedata, adds own handlers */
if (sa->type->init)
sa->type->init(wm, sa);
/* region windows, default and own handlers */
for (ar = sa->regionbase.first; ar; ar = ar->next) {
region_subwindow(ar);
if (ar->visible) {
/* default region handlers */
ed_default_handlers(wm, sa, &ar->handlers, ar->type->keymapflag);
/* own handlers */
if (ar->type->init) {
ar->type->init(wm, ar);
}
}
else {
/* prevent uiblocks to run */
UI_blocklist_free(NULL, &ar->uiblocks);
}
}
}
static void region_update_rect(ARegion *ar)
{
ar->winx = BLI_rcti_size_x(&ar->winrct) + 1;
ar->winy = BLI_rcti_size_y(&ar->winrct) + 1;
/* v2d mask is used to subtract scrollbars from a 2d view. Needs initialize here. */
BLI_rcti_init(&ar->v2d.mask, 0, ar->winx - 1, 0, ar->winy -1);
}
/**
* Call to move a popup window (keep OpenGL context free!)
*/
void ED_region_update_rect(bContext *UNUSED(C), ARegion *ar)
{
region_update_rect(ar);
}
/* externally called for floating regions like menus */
void ED_region_init(bContext *UNUSED(C), ARegion *ar)
{
/* refresh can be called before window opened */
region_subwindow(ar);
region_update_rect(ar);
}
void ED_region_cursor_set(wmWindow *win, ScrArea *sa, ARegion *ar)
{
if (ar && sa && ar->type && ar->type->cursor) {
ar->type->cursor(win, sa, ar);
}
else {
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)
{
ScrArea *sa = CTX_wm_area(C);
ar->flag ^= RGN_FLAG_HIDDEN;
if (do_fade && ar->overlap) {
/* starts a timer, and in end calls the stuff below itself (region_sblend_invoke()) */
region_blend_start(C, sa, ar);
}
else {
ED_region_visibility_change_update(C, ar);
}
}
/* exported to all editors, uses fading default */
void ED_region_toggle_hidden(bContext *C, ARegion *ar)
{
region_toggle_hidden(C, ar, true);
}
/**
* we swap spaces for fullscreen to keep all allocated data area vertices were set
*/
void ED_area_data_copy(ScrArea *sa_dst, ScrArea *sa_src, const bool do_free)
{
SpaceType *st;
ARegion *ar;
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->flag = (sa_dst->flag & ~flag_copy) | (sa_src->flag & flag_copy);
/* area */
if (do_free) {
BKE_spacedata_freelist(&sa_dst->spacedata);
}
BKE_spacedata_copylist(&sa_dst->spacedata, &sa_src->spacedata);
/* Note; SPACE_EMPTY is possible on new screens */
/* regions */
if (do_free) {
st = BKE_spacetype_from_id(spacetype);
for (ar = sa_dst->regionbase.first; ar; ar = ar->next)
BKE_area_region_free(st, ar);
BLI_freelistN(&sa_dst->regionbase);
}
st = BKE_spacetype_from_id(sa_src->spacetype);
for (ar = sa_src->regionbase.first; ar; ar = ar->next) {
ARegion *newar = BKE_area_region_copy(st, ar);
BLI_addtail(&sa_dst->regionbase, newar);
}
}
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(ListBase, sa_dst->spacedata, sa_src->spacedata);
SWAP(ListBase, sa_dst->regionbase, sa_src->regionbase);
}
/* *********** Space switching code *********** */
void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2)
{
ScrArea *tmp = MEM_callocN(sizeof(ScrArea), "addscrarea");
ED_area_exit(C, sa1);
ED_area_exit(C, sa2);
ED_area_data_copy(tmp, sa1, false);
ED_area_data_copy(sa1, sa2, true);
ED_area_data_copy(sa2, tmp, true);
ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa1);
ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa2);
BKE_screen_area_free(tmp);
MEM_freeN(tmp);
/* tell WM to refresh, cursor types etc */
WM_event_add_mousemove(C);
ED_area_tag_redraw(sa1);
ED_area_tag_refresh(sa1);
ED_area_tag_redraw(sa2);
ED_area_tag_refresh(sa2);
}
/**
* \param skip_ar_exit Skip calling area exit callback. Set for opening temp spaces.
*/
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;
/* in some cases (opening temp space) we don't want to
* call area exit callback, so we temporarily unset it */
if (skip_ar_exit && sa->type) {
sa->type->exit = NULL;
}
ED_area_exit(C, sa);
/* restore old area exit callback */
if (skip_ar_exit && sa->type) {
sa->type->exit = sa_exit;
}
st = BKE_spacetype_from_id(type);
slold = sa->spacedata.first;
sa->spacetype = 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)
break;
/* old spacedata... happened during work on 2.50, remove */
if (sl && BLI_listbase_is_empty(&sl->regionbase)) {
st->free(sl);
BLI_freelinkN(&sa->spacedata, sl);
if (slold == sl) {
slold = NULL;
}
sl = NULL;
}
if (sl) {
/* swap regions */
slold->regionbase = sa->regionbase;
sa->regionbase = sl->regionbase;
BLI_listbase_clear(&sl->regionbase);
/* put in front of list */
BLI_remlink(&sa->spacedata, sl);
BLI_addhead(&sa->spacedata, sl);
}
else {
/* new space */
if (st) {
/* 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 */
if (slold)
slold->regionbase = sa->regionbase;
sa->regionbase = sl->regionbase;
BLI_listbase_clear(&sl->regionbase);
}
}
ED_area_initialize(CTX_wm_manager(C), win, sa);
/* tell WM to refresh, cursor types etc */
WM_event_add_mousemove(C);
/* send space change notifier */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CHANGED, sa);
ED_area_tag_refresh(sa);
}
/* also redraw when re-used */
ED_area_tag_redraw(sa);
}
void ED_area_prevspace(bContext *C, ScrArea *sa)
{
SpaceLink *sl = sa->spacedata.first;
if (sl && sl->next) {
ED_area_newspace(C, sa, sl->next->spacetype, false);
/* keep old spacedata but move it to end, so calling
* ED_area_prevspace once more won't open it again */
BLI_remlink(&sa->spacedata, sl);
BLI_addtail(&sa->spacedata, sl);
}
else {
/* no change */
return;
}
sa->flag &= ~AREA_FLAG_STACKED_FULLSCREEN;
ED_area_tag_redraw(sa);
/* send space change notifier */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CHANGED, sa);
}
/* returns offset for next button in header */
int ED_area_header_switchbutton(const bContext *C, uiBlock *block, int yco)
{
ScrArea *sa = CTX_wm_area(C);
bScreen *scr = CTX_wm_screen(C);
PointerRNA areaptr;
int xco = 0.4 * U.widget_unit;
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, "");
return xco + 1.7 * U.widget_unit;
}
/************************ standard UI regions ************************/
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->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);
}
}
void ED_region_panels(const bContext *C, ARegion *ar, const char *context, int contextnr, const bool vertical)
{
const WorkSpace *workspace = CTX_wm_workspace(C);
ScrArea *sa = CTX_wm_area(C);
uiStyle *style = UI_style_get_dpi();
uiBlock *block;
PanelType *pt;
Panel *panel;
View2D *v2d = &ar->v2d;
View2DScrollers *scrollers;
int x, y, xco, yco, w, em, triangle;
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? */
/* 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;
BLI_SMALLSTACK_DECLARE(pt_stack, PanelType *);
if (contextnr != -1)
is_context_new = UI_view2d_tab_set(v2d, contextnr);
/* before setting the view */
if (vertical) {
/* only allow scrolling in vertical direction */
v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y;
v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X);
v2d->scroll &= ~(V2D_SCROLL_BOTTOM);
v2d->scroll |= (V2D_SCROLL_RIGHT);
}
else {
/* for now, allow scrolling in both directions (since layouts are optimized for vertical,
* they often don't fit in horizontal layout)
*/
v2d->keepofs &= ~(V2D_LOCKOFS_X | V2D_LOCKOFS_Y | V2D_KEEPOFS_X | V2D_KEEPOFS_Y);
v2d->scroll |= (V2D_SCROLL_BOTTOM);
v2d->scroll &= ~(V2D_SCROLL_RIGHT);
}
scroll = v2d->scroll;
/* collect panels to draw */
for (pt = ar->type->paneltypes.last; pt; pt = pt->prev) {
/* verify context */
if (context && pt->context[0] && !STREQ(context, pt->context)) {
continue;
}
/* If we're tagged, only use compatible. */
if (pt->owner_id[0] && BKE_workspace_owner_id_check(workspace, pt->owner_id) == false) {
continue;
}
/* draw panel */
if (pt->draw && (!pt->poll || pt->poll(C, pt))) {
BLI_SMALLSTACK_PUSH(pt_stack, pt);
}
}
/* collect categories */
if (use_category_tabs) {
UI_panel_category_clear_all(ar);
/* gather unique categories */
BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt)
{
if (pt->category[0]) {
if (!UI_panel_category_find(ar, pt->category)) {
UI_panel_category_add(ar, pt->category);
}
}
}
BLI_SMALLSTACK_ITER_END;
if (!UI_panel_category_is_visible(ar)) {
use_category_tabs = false;
}
else {
category = UI_panel_category_active_get(ar, true);
margin_x = category_tabs_width;
}
}
/* 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;
/* create panels */
UI_panels_begin(C, ar);
/* set view2d view matrix - UI_block_begin() stores it */
UI_view2d_view_ortho(v2d);
BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt)
{
bool open;
panel = UI_panel_find_by_type(ar, pt);
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);
pt->draw_header(C, panel);
UI_block_layout_resolve(block, &xco, &yco);
panel->labelofs = xco - triangle;
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;
yco -= 2 * style->panelspace;
UI_panel_end(block, w, -yco);
}
else {
yco = 0;
UI_panel_end(block, w, 0);
}
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);
}
else {
x = max_ii(x, v2d->cur.xmax);
}
}
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 0
UI_panels_scale(ar, BLI_rctf_size_x(&v2d->cur));
break;
#endif
}
else {
break;
}
}
region_clear_color(C, ar, (ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK);
/* reset line width for drawing tabs */
glLineWidth(1.0f);
/* set the view */
UI_view2d_view_ortho(v2d);
/* draw panels */
UI_panels_draw(C, ar);
/* restore view matrix */
UI_view2d_view_restore(C);
if (use_category_tabs) {
UI_panel_category_draw_all(ar, category);
}
/* scrollers */
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_init(wmWindowManager *wm, ARegion *ar)
{
wmKeyMap *keymap;
UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_PANELS_UI, ar->winx, ar->winy);
keymap = WM_keymap_find(wm->defaultconf, "View2D Buttons List", 0, 0);
WM_event_add_keymap_handler(&ar->handlers, keymap);
}
void ED_region_header_layout(const bContext *C, ARegion *ar)
{
uiStyle *style = UI_style_get_dpi();
uiBlock *block;
uiLayout *layout;
HeaderType *ht;
Header header = {NULL};
int maxco, xco, yco;
int headery = ED_area_headersize();
const int start_ofs = 0.4f * UI_UNIT_X;
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 = start_ofs;
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) {
block = UI_block_begin(C, ar, ht->idname, UI_EMBOSS);
layout = UI_block_layout(block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, xco, yco, UI_UNIT_Y, 1, 0, style);
if (ht->draw) {
header.type = ht;
header.layout = layout;
ht->draw(C, &header);
/* for view2d */
xco = uiLayoutGetWidth(layout);
if (xco > maxco)
maxco = xco;
}
UI_block_layout_resolve(block, &xco, &yco);
/* for view2d */
if (xco > maxco)
maxco = xco;
int new_sizex = (maxco + start_ofs) / 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);
}
/* always as last */
UI_view2d_totRect_set(&ar->v2d, maxco + (region_layout_based ? 0 : UI_UNIT_X + 80), headery);
/* restore view matrix */
UI_view2d_view_restore(C);
}
void ED_region_header_draw(const bContext *C, ARegion *ar)
{
UI_view2d_view_ortho(&ar->v2d);
/* clear */
region_clear_color(C, ar, region_background_color_id(C, ar));
/* 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);
}
/* UI_UNIT_Y is defined as U variable now, depending dpi */
int ED_area_headersize(void)
{
return (int)(HEADERY * UI_DPI_FAC);
}
/**
* \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);
/* 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.ymin = BLI_rcti_size_y(&ar->winrct) - header_height * num_lines;
rect.ymax = BLI_rcti_size_y(&ar->winrct);
/* setup scissor */
glGetIntegerv(GL_SCISSOR_BOX, scissor);
glScissor(rect.xmin, rect.ymin,
BLI_rcti_size_x(&rect) + 1, BLI_rcti_size_y(&rect) + 1);
glEnable(GL_BLEND);
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_FontThemeColor(fontid, TH_TEXT_HI);
BLF_clipping(fontid, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
BLF_enable(fontid, BLF_CLIPPING);
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);
/* restore scissor as it was before */
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[] =
{
"File",
"Strip",
"Date",
"RenderTime",
"Note",
"Marker",
"Time",
"Frame",
"Camera",
"Scene"
};
BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset)
{
return (IMB_metadata_get_field(ibuf->metadata, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) && r_str[0]);
}
static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const bool is_top)
{
char temp_str[MAX_METADATA_STR];
int line_width;
int ofs_y = 0;
short i;
int len;
const float height = BLF_height_max(fontid);
const float margin = height / 8;
const float vertical_offset = (height + margin);
/* values taking margins into account */
const float descender = BLF_descender(fontid);
const float xmin = (rect->xmin + margin);
const float xmax = (rect->xmax - margin);
const float ymin = (rect->ymin + margin) - descender;
const float ymax = (rect->ymax - margin) - descender;
if (is_top) {
for (i = 0; i < 4; i++) {
/* first line */
if (i == 0) {
bool do_newline = false;
len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[0]);
if (metadata_is_valid(ibuf, temp_str, 0, len)) {
BLF_position(fontid, xmin, ymax - vertical_offset, 0.0f);
BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
do_newline = true;
}
len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[1]);
if (metadata_is_valid(ibuf, temp_str, 1, len)) {
line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
BLF_position(fontid, xmax - line_width, ymax - vertical_offset, 0.0f);
BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
do_newline = true;
}
if (do_newline)
ofs_y += vertical_offset;
} /* Strip */
else if (i == 1 || i == 2) {
len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
ofs_y += vertical_offset;
}
} /* Note (wrapped) */
else if (i == 3) {
len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
struct ResultBLF info;
BLF_enable(fontid, BLF_WORD_WRAP);
BLF_wordwrap(fontid, ibuf->x - (margin * 2));
BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
BLF_draw_ex(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX, &info);
BLF_wordwrap(fontid, 0);
BLF_disable(fontid, BLF_WORD_WRAP);
ofs_y += vertical_offset * info.lines;
}
}
else {
len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
BLF_position(fontid, xmax - line_width, ymax - vertical_offset - ofs_y, 0.0f);
BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
ofs_y += vertical_offset;
}
}
}
}
else {
int ofs_x = 0;
for (i = 5; i < 10; i++) {
len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i]);
if (metadata_is_valid(ibuf, temp_str, i, len)) {
BLF_position(fontid, xmin + ofs_x, ymin, 0.0f);
BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
ofs_x += BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX) + UI_UNIT_X;
}
}
}
}
static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top)
{
const float height = BLF_height_max(fontid);
const float margin = (height / 8);
char str[MAX_METADATA_STR] = "";
short i, count = 0;
if (is_top) {
if (metadata_is_valid(ibuf, str, 0, 0) || metadata_is_valid(ibuf, str, 1, 0)) {
count++;
}
for (i = 2; i < 5; i++) {
if (metadata_is_valid(ibuf, str, i, 0)) {
if (i == 4) {
struct {
struct ResultBLF info;
rctf rect;
} wrap;
BLF_enable(fontid, BLF_WORD_WRAP);
BLF_wordwrap(fontid, ibuf->x - (margin * 2));
BLF_boundbox_ex(fontid, str, sizeof(str), &wrap.rect, &wrap.info);
BLF_wordwrap(fontid, 0);
BLF_disable(fontid, BLF_WORD_WRAP);
count += wrap.info.lines;
}
else {
count++;
}
}
}
}
else {
for (i = 5; i < 10; i++) {
if (metadata_is_valid(ibuf, str, i, 0)) {
count = 1;
}
}
}
if (count) {
return (height + margin) * count;
}
return 0;
}
#undef MAX_METADATA_STR
void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy)
{
float box_y;
rctf rect;
uiStyle *style = UI_style_get_dpi();
if (!ibuf->metadata)
return;
/* find window pixel coordinates of origin */
gpuPushMatrix();
/* offset and zoom using ogl */
gpuTranslate2f(x, y);
gpuScale2f(zoomx, zoomy);
BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f * U.pixelsize, U.dpi);
/* *** upper box*** */
/* get needed box height */
box_y = metadata_box_height_get(ibuf, blf_mono_font, true);
if (box_y) {
/* set up rect */
BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymax, frame->ymax + box_y);
/* draw top box */
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_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true);
BLF_disable(blf_mono_font, BLF_CLIPPING);
}
/* *** lower box*** */
box_y = metadata_box_height_get(ibuf, blf_mono_font, false);
if (box_y) {
/* set up box rect */
BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymin - box_y, frame->ymin);
/* draw top box */
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_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false);
BLF_disable(blf_mono_font, BLF_CLIPPING);
}
gpuPopMatrix();
}
void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy)
{
float gridsize, gridstep = 1.0f / 32.0f;
float fac, blendfac;
int x1, y1, x2, y2;
/* the image is located inside (0, 0), (1, 1) as set by view2d */
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);
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);
if (gridsize <= 0.0f)
return;
if (gridsize < 1.0f) {
while (gridsize < 1.0f) {
gridsize *= 4.0f;
gridstep *= 4.0f;
}
}
else {
while (gridsize >= 4.0f) {
gridsize /= 4.0f;
gridstep /= 4.0f;
}
}
blendfac = 0.25f * gridsize - floorf(0.25f * gridsize);
CLAMP(blendfac, 0.0f, 1.0f);
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;
}
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;
}
}
immEnd();
immUnbindProgram();
}
}
/* If the area has overlapping regions, it returns visible rect for Region *ar */
/* rect gets returned in local region coordinates */
void ED_region_visible_rect(ARegion *ar, rcti *rect)
{
ARegion *arn = ar;
/* allow function to be called without area */
while (arn->prev)
arn = arn->prev;
*rect = ar->winrct;
/* check if a region overlaps with the current one */
for (; arn; arn = arn->next) {
if (ar != arn && arn->overlap) {
if (BLI_rcti_isect(rect, &arn->winrct, NULL)) {
/* 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;
}
}
}
BLI_rcti_translate(rect, -ar->winrct.xmin, -ar->winrct.ymin);
}
/* Cache display helpers */
void ED_region_cache_draw_background(const ARegion *ar)
{
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)
{
uiStyle *style = UI_style_get();
int fontid = style->widget.uifont_id;
char numstr[32];
float font_dims[2] = {0.0f, 0.0f};
/* frame number */
BLF_size(fontid, 11.0f * U.pixelsize, U.dpi);
BLI_snprintf(numstr, sizeof(numstr), "%d", framenr);
BLF_width_and_height(fontid, numstr, sizeof(numstr), &font_dims[0], &font_dims[1]);
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_FontThemeColor(fontid, TH_TEXT);
BLF_position(fontid, x + 2.0f, y + 2.0f, 0.0f);
BLF_draw(fontid, numstr, sizeof(numstr));
}
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) {
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);
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;
immRecti(pos, x1, 0, x2, 8 * UI_DPI_FAC);
/* TODO(merwin): use primitive restart to draw multiple rects more efficiently */
}
immUnbindProgram();
}
}
/**
* 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;
}