1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2008 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edscr
22 */
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_userdef_types.h"
30
31 #include "BLI_blenlib.h"
32 #include "BLI_linklist_stack.h"
33 #include "BLI_math.h"
34 #include "BLI_rand.h"
35 #include "BLI_utildefines.h"
36
37 #include "BKE_context.h"
38 #include "BKE_global.h"
39 #include "BKE_image.h"
40 #include "BKE_screen.h"
41 #include "BKE_workspace.h"
42
43 #include "RNA_access.h"
44 #include "RNA_types.h"
45
46 #include "WM_api.h"
47 #include "WM_message.h"
48 #include "WM_toolsystem.h"
49 #include "WM_types.h"
50
51 #include "ED_buttons.h"
52 #include "ED_screen.h"
53 #include "ED_screen_types.h"
54 #include "ED_space_api.h"
55 #include "ED_time_scrub_ui.h"
56
57 #include "GPU_framebuffer.h"
58 #include "GPU_immediate.h"
59 #include "GPU_immediate_util.h"
60 #include "GPU_matrix.h"
61 #include "GPU_state.h"
62
63 #include "BLF_api.h"
64
65 #include "IMB_imbuf_types.h"
66 #include "IMB_metadata.h"
67
68 #include "UI_interface.h"
69 #include "UI_interface_icons.h"
70 #include "UI_resources.h"
71 #include "UI_view2d.h"
72
73 #include "screen_intern.h"
74
75 enum RegionEmbossSide {
76 REGION_EMBOSS_LEFT = (1 << 0),
77 REGION_EMBOSS_TOP = (1 << 1),
78 REGION_EMBOSS_BOTTOM = (1 << 2),
79 REGION_EMBOSS_RIGHT = (1 << 3),
80 REGION_EMBOSS_ALL = REGION_EMBOSS_LEFT | REGION_EMBOSS_TOP | REGION_EMBOSS_RIGHT |
81 REGION_EMBOSS_BOTTOM,
82 };
83
84 /* general area and region code */
85
region_draw_emboss(const ARegion * region,const rcti * scirct,int sides)86 static void region_draw_emboss(const ARegion *region, const rcti *scirct, int sides)
87 {
88 /* translate scissor rect to region space */
89 const rcti rect = {.xmin = scirct->xmin - region->winrct.xmin,
90 .xmax = scirct->xmax - region->winrct.xmin,
91 .ymin = scirct->ymin - region->winrct.ymin,
92 .ymax = scirct->ymax - region->winrct.ymin};
93
94 /* set transp line */
95 GPU_blend(GPU_BLEND_ALPHA);
96
97 float color[4] = {0.0f, 0.0f, 0.0f, 0.25f};
98 UI_GetThemeColor3fv(TH_EDITOR_OUTLINE, color);
99
100 GPUVertFormat *format = immVertexFormat();
101 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
102 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
103 immUniformColor4fv(color);
104
105 immBeginAtMost(GPU_PRIM_LINES, 8);
106
107 /* right */
108 if (sides & REGION_EMBOSS_RIGHT) {
109 immVertex2f(pos, rect.xmax, rect.ymax);
110 immVertex2f(pos, rect.xmax, rect.ymin);
111 }
112
113 /* bottom */
114 if (sides & REGION_EMBOSS_BOTTOM) {
115 immVertex2f(pos, rect.xmax, rect.ymin);
116 immVertex2f(pos, rect.xmin, rect.ymin);
117 }
118
119 /* left */
120 if (sides & REGION_EMBOSS_LEFT) {
121 immVertex2f(pos, rect.xmin, rect.ymin);
122 immVertex2f(pos, rect.xmin, rect.ymax);
123 }
124
125 /* top */
126 if (sides & REGION_EMBOSS_TOP) {
127 immVertex2f(pos, rect.xmin, rect.ymax);
128 immVertex2f(pos, rect.xmax, rect.ymax);
129 }
130
131 immEnd();
132 immUnbindProgram();
133
134 GPU_blend(GPU_BLEND_NONE);
135 }
136
ED_region_pixelspace(ARegion * region)137 void ED_region_pixelspace(ARegion *region)
138 {
139 wmOrtho2_region_pixelspace(region);
140 GPU_matrix_identity_set();
141 }
142
143 /* only exported for WM */
ED_region_do_listen(wmWindow * win,ScrArea * area,ARegion * region,wmNotifier * note,const Scene * scene)144 void ED_region_do_listen(
145 wmWindow *win, ScrArea *area, ARegion *region, wmNotifier *note, const Scene *scene)
146 {
147 /* generic notes first */
148 switch (note->category) {
149 case NC_WM:
150 if (note->data == ND_FILEREAD) {
151 ED_region_tag_redraw(region);
152 }
153 break;
154 case NC_WINDOW:
155 ED_region_tag_redraw(region);
156 break;
157 }
158
159 if (region->type && region->type->listener) {
160 region->type->listener(win, area, region, note, scene);
161 }
162 }
163
164 /* only exported for WM */
ED_area_do_listen(wmWindow * win,ScrArea * area,wmNotifier * note,Scene * scene)165 void ED_area_do_listen(wmWindow *win, ScrArea *area, wmNotifier *note, Scene *scene)
166 {
167 /* no generic notes? */
168 if (area->type && area->type->listener) {
169 area->type->listener(win, area, note, scene);
170 }
171 }
172
173 /* only exported for WM */
ED_area_do_refresh(bContext * C,ScrArea * area)174 void ED_area_do_refresh(bContext *C, ScrArea *area)
175 {
176 /* no generic notes? */
177 if (area->type && area->type->refresh) {
178 area->type->refresh(C, area);
179 }
180 area->do_refresh = false;
181 }
182
183 /**
184 * \brief Corner widget use for quitting fullscreen.
185 */
area_draw_azone_fullscreen(short UNUSED (x1),short UNUSED (y1),short x2,short y2,float alpha)186 static void area_draw_azone_fullscreen(
187 short UNUSED(x1), short UNUSED(y1), short x2, short y2, float alpha)
188 {
189 UI_icon_draw_ex(x2 - U.widget_unit,
190 y2 - U.widget_unit,
191 ICON_FULLSCREEN_EXIT,
192 U.inv_dpi_fac,
193 min_ff(alpha, 0.75f),
194 0.0f,
195 NULL,
196 false);
197 }
198
199 /**
200 * \brief Corner widgets use for dragging and splitting the view.
201 */
area_draw_azone(short UNUSED (x1),short UNUSED (y1),short UNUSED (x2),short UNUSED (y2))202 static void area_draw_azone(short UNUSED(x1), short UNUSED(y1), short UNUSED(x2), short UNUSED(y2))
203 {
204 /* No drawing needed since all corners are action zone, and visually distinguishable. */
205 }
206
207 /**
208 * \brief Edge widgets to show hidden panels such as the toolbar and headers.
209 */
draw_azone_arrow(float x1,float y1,float x2,float y2,AZEdge edge)210 static void draw_azone_arrow(float x1, float y1, float x2, float y2, AZEdge edge)
211 {
212 const float size = 0.2f * U.widget_unit;
213 const float l = 1.0f; /* arrow length */
214 const float s = 0.25f; /* arrow thickness */
215 const float hl = l / 2.0f;
216 const float points[6][2] = {
217 {0, -hl}, {l, hl}, {l - s, hl + s}, {0, s + s - hl}, {s - l, hl + s}, {-l, hl}};
218 const float center[2] = {(x1 + x2) / 2, (y1 + y2) / 2};
219
220 int axis;
221 int sign;
222 switch (edge) {
223 case AE_BOTTOM_TO_TOPLEFT:
224 axis = 0;
225 sign = 1;
226 break;
227 case AE_TOP_TO_BOTTOMRIGHT:
228 axis = 0;
229 sign = -1;
230 break;
231 case AE_LEFT_TO_TOPRIGHT:
232 axis = 1;
233 sign = 1;
234 break;
235 case AE_RIGHT_TO_TOPLEFT:
236 axis = 1;
237 sign = -1;
238 break;
239 default:
240 BLI_assert(0);
241 return;
242 }
243
244 GPUVertFormat *format = immVertexFormat();
245 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
246
247 GPU_blend(GPU_BLEND_ALPHA);
248 /* NOTE(fclem): There is something strange going on with Mesa and GPU_SHADER_2D_UNIFORM_COLOR
249 * that causes a crash on some GPUs (see T76113). Using 3D variant avoid the issue. */
250 immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
251 immUniformColor4f(0.8f, 0.8f, 0.8f, 0.4f);
252
253 immBegin(GPU_PRIM_TRI_FAN, 6);
254 for (int i = 0; i < 6; i++) {
255 if (axis == 0) {
256 immVertex2f(pos, center[0] + points[i][0] * size, center[1] + points[i][1] * sign * size);
257 }
258 else {
259 immVertex2f(pos, center[0] + points[i][1] * sign * size, center[1] + points[i][0] * size);
260 }
261 }
262 immEnd();
263
264 immUnbindProgram();
265 GPU_blend(GPU_BLEND_NONE);
266 }
267
region_draw_azone_tab_arrow(ScrArea * area,ARegion * region,AZone * az)268 static void region_draw_azone_tab_arrow(ScrArea *area, ARegion *region, AZone *az)
269 {
270 GPU_blend(GPU_BLEND_ALPHA);
271
272 /* add code to draw region hidden as 'too small' */
273 switch (az->edge) {
274 case AE_TOP_TO_BOTTOMRIGHT:
275 UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
276 break;
277 case AE_BOTTOM_TO_TOPLEFT:
278 UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
279 break;
280 case AE_LEFT_TO_TOPRIGHT:
281 UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
282 break;
283 case AE_RIGHT_TO_TOPLEFT:
284 UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
285 break;
286 }
287
288 /* Workaround for different color spaces between normal areas and the ones using GPUViewports. */
289 float alpha = WM_region_use_viewport(area, region) ? 0.6f : 0.4f;
290 const float color[4] = {0.05f, 0.05f, 0.05f, alpha};
291 UI_draw_roundbox_aa(
292 true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, color);
293
294 draw_azone_arrow((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, az->edge);
295 }
296
area_azone_tag_update(ScrArea * area)297 static void area_azone_tag_update(ScrArea *area)
298 {
299 area->flag |= AREA_FLAG_ACTIONZONES_UPDATE;
300 }
301
region_draw_azones(ScrArea * area,ARegion * region)302 static void region_draw_azones(ScrArea *area, ARegion *region)
303 {
304 if (!area) {
305 return;
306 }
307
308 GPU_line_width(1.0f);
309 GPU_blend(GPU_BLEND_ALPHA);
310
311 GPU_matrix_push();
312 GPU_matrix_translate_2f(-region->winrct.xmin, -region->winrct.ymin);
313
314 LISTBASE_FOREACH (AZone *, az, &area->actionzones) {
315 /* test if action zone is over this region */
316 rcti azrct;
317 BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2);
318
319 if (BLI_rcti_isect(®ion->drawrct, &azrct, NULL)) {
320 if (az->type == AZONE_AREA) {
321 area_draw_azone(az->x1, az->y1, az->x2, az->y2);
322 }
323 else if (az->type == AZONE_REGION) {
324 if (az->region) {
325 /* only display tab or icons when the region is hidden */
326 if (az->region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
327 region_draw_azone_tab_arrow(area, region, az);
328 }
329 }
330 }
331 else if (az->type == AZONE_FULLSCREEN) {
332 if (az->alpha > 0.0f) {
333 area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha);
334 }
335 }
336 }
337 if (!IS_EQF(az->alpha, 0.0f) && ELEM(az->type, AZONE_FULLSCREEN, AZONE_REGION_SCROLL)) {
338 area_azone_tag_update(area);
339 }
340 }
341
342 GPU_matrix_pop();
343
344 GPU_blend(GPU_BLEND_NONE);
345 }
346
region_draw_status_text(ScrArea * area,ARegion * region)347 static void region_draw_status_text(ScrArea *area, ARegion *region)
348 {
349 bool overlap = ED_region_is_overlap(area->spacetype, region->regiontype);
350
351 if (overlap) {
352 GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
353 }
354 else {
355 UI_ThemeClearColor(TH_HEADER);
356 }
357
358 int fontid = BLF_set_default();
359
360 const float width = BLF_width(fontid, region->headerstr, BLF_DRAW_STR_DUMMY_MAX);
361 const float x = UI_UNIT_X;
362 const float y = 0.4f * UI_UNIT_Y;
363
364 if (overlap) {
365 const float pad = 2.0f * UI_DPI_FAC;
366 const float x1 = x - (UI_UNIT_X - pad);
367 const float x2 = x + width + (UI_UNIT_X - pad);
368 const float y1 = pad;
369 const float y2 = region->winy - pad;
370
371 GPU_blend(GPU_BLEND_ALPHA);
372
373 float color[4] = {0.0f, 0.0f, 0.0f, 0.5f};
374 UI_GetThemeColor3fv(TH_BACK, color);
375 UI_draw_roundbox_corner_set(UI_CNR_ALL);
376 UI_draw_roundbox_aa(true, x1, y1, x2, y2, 4.0f, color);
377
378 UI_FontThemeColor(fontid, TH_TEXT);
379 }
380 else {
381 UI_FontThemeColor(fontid, TH_TEXT);
382 }
383
384 BLF_position(fontid, x, y, 0.0f);
385 BLF_draw(fontid, region->headerstr, BLF_DRAW_STR_DUMMY_MAX);
386 }
387
ED_region_do_msg_notify_tag_redraw(bContext * UNUSED (C),wmMsgSubscribeKey * UNUSED (msg_key),wmMsgSubscribeValue * msg_val)388 void ED_region_do_msg_notify_tag_redraw(
389 /* Follow wmMsgNotifyFn spec */
390 bContext *UNUSED(C),
391 wmMsgSubscribeKey *UNUSED(msg_key),
392 wmMsgSubscribeValue *msg_val)
393 {
394 ARegion *region = msg_val->owner;
395 ED_region_tag_redraw(region);
396
397 /* This avoids _many_ situations where header/properties control display settings.
398 * the common case is space properties in the header */
399 if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_UI)) {
400 while (region && region->prev) {
401 region = region->prev;
402 }
403 for (; region; region = region->next) {
404 if (ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS)) {
405 ED_region_tag_redraw(region);
406 }
407 }
408 }
409 }
410
ED_area_do_msg_notify_tag_refresh(bContext * UNUSED (C),wmMsgSubscribeKey * UNUSED (msg_key),wmMsgSubscribeValue * msg_val)411 void ED_area_do_msg_notify_tag_refresh(
412 /* Follow wmMsgNotifyFn spec */
413 bContext *UNUSED(C),
414 wmMsgSubscribeKey *UNUSED(msg_key),
415 wmMsgSubscribeValue *msg_val)
416 {
417 ScrArea *area = msg_val->user_data;
418 ED_area_tag_refresh(area);
419 }
420
ED_area_do_mgs_subscribe_for_tool_header(const struct bContext * UNUSED (C),struct WorkSpace * workspace,struct Scene * UNUSED (scene),struct bScreen * UNUSED (screen),struct ScrArea * UNUSED (area),struct ARegion * region,struct wmMsgBus * mbus)421 void ED_area_do_mgs_subscribe_for_tool_header(
422 /* Follow ARegionType.message_subscribe */
423 const struct bContext *UNUSED(C),
424 struct WorkSpace *workspace,
425 struct Scene *UNUSED(scene),
426 struct bScreen *UNUSED(screen),
427 struct ScrArea *UNUSED(area),
428 struct ARegion *region,
429 struct wmMsgBus *mbus)
430 {
431 BLI_assert(region->regiontype == RGN_TYPE_TOOL_HEADER);
432 wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
433 .owner = region,
434 .user_data = region,
435 .notify = ED_region_do_msg_notify_tag_redraw,
436 };
437 WM_msg_subscribe_rna_prop(
438 mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw);
439 }
440
ED_area_do_mgs_subscribe_for_tool_ui(const struct bContext * UNUSED (C),struct WorkSpace * workspace,struct Scene * UNUSED (scene),struct bScreen * UNUSED (screen),struct ScrArea * UNUSED (area),struct ARegion * region,struct wmMsgBus * mbus)441 void ED_area_do_mgs_subscribe_for_tool_ui(
442 /* Follow ARegionType.message_subscribe */
443 const struct bContext *UNUSED(C),
444 struct WorkSpace *workspace,
445 struct Scene *UNUSED(scene),
446 struct bScreen *UNUSED(screen),
447 struct ScrArea *UNUSED(area),
448 struct ARegion *region,
449 struct wmMsgBus *mbus)
450 {
451 BLI_assert(region->regiontype == RGN_TYPE_UI);
452 const char *panel_category_tool = "Tool";
453 const char *category = UI_panel_category_active_get(region, false);
454
455 bool update_region = false;
456 if (category && STREQ(category, panel_category_tool)) {
457 update_region = true;
458 }
459 else {
460 /* Check if a tool category panel is pinned and visible in another category. */
461 LISTBASE_FOREACH (Panel *, panel, ®ion->panels) {
462 if (UI_panel_is_active(panel) && panel->flag & PNL_PIN &&
463 STREQ(panel->type->category, panel_category_tool)) {
464 update_region = true;
465 break;
466 }
467 }
468 }
469
470 if (update_region) {
471 wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
472 .owner = region,
473 .user_data = region,
474 .notify = ED_region_do_msg_notify_tag_redraw,
475 };
476 WM_msg_subscribe_rna_prop(
477 mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw);
478 }
479 }
480
481 /**
482 * Although there's no general support for minimizing areas, the status-bar can
483 * be snapped to be only a few pixels high. A few pixels rather than 0 so it
484 * can be un-minimized again. We consider it pseudo-minimized and don't draw
485 * it then.
486 */
area_is_pseudo_minimized(const ScrArea * area)487 static bool area_is_pseudo_minimized(const ScrArea *area)
488 {
489 return (area->winx < 3) || (area->winy < 3);
490 }
491
492 /* only exported for WM */
ED_region_do_layout(bContext * C,ARegion * region)493 void ED_region_do_layout(bContext *C, ARegion *region)
494 {
495 /* This is optional, only needed for dynamically sized regions. */
496 ScrArea *area = CTX_wm_area(C);
497 ARegionType *at = region->type;
498
499 if (!at->layout) {
500 return;
501 }
502
503 if (at->do_lock || (area && area_is_pseudo_minimized(area))) {
504 return;
505 }
506
507 region->do_draw |= RGN_DRAWING;
508
509 UI_SetTheme(area ? area->spacetype : 0, at->regionid);
510 at->layout(C, region);
511
512 /* Clear temporary update flag. */
513 region->flag &= ~RGN_FLAG_SEARCH_FILTER_UPDATE;
514 }
515
516 /* only exported for WM */
ED_region_do_draw(bContext * C,ARegion * region)517 void ED_region_do_draw(bContext *C, ARegion *region)
518 {
519 wmWindow *win = CTX_wm_window(C);
520 ScrArea *area = CTX_wm_area(C);
521 ARegionType *at = region->type;
522
523 /* see BKE_spacedata_draw_locks() */
524 if (at->do_lock) {
525 return;
526 }
527
528 region->do_draw |= RGN_DRAWING;
529
530 /* Set viewport, scissor, ortho and region->drawrct. */
531 wmPartialViewport(®ion->drawrct, ®ion->winrct, ®ion->drawrct);
532
533 wmOrtho2_region_pixelspace(region);
534
535 UI_SetTheme(area ? area->spacetype : 0, at->regionid);
536
537 if (area && area_is_pseudo_minimized(area)) {
538 UI_ThemeClearColor(TH_EDITOR_OUTLINE);
539 return;
540 }
541 /* optional header info instead? */
542 if (region->headerstr) {
543 region_draw_status_text(area, region);
544 }
545 else if (at->draw) {
546 at->draw(C, region);
547 }
548
549 /* XXX test: add convention to end regions always in pixel space,
550 * for drawing of borders/gestures etc */
551 ED_region_pixelspace(region);
552
553 /* Remove sRGB override by rebinding the framebuffer. */
554 GPUFrameBuffer *fb = GPU_framebuffer_active_get();
555 GPU_framebuffer_bind(fb);
556
557 ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_PIXEL);
558
559 region_draw_azones(area, region);
560
561 /* for debugging unneeded area redraws and partial redraw */
562 if (G.debug_value == 888) {
563 GPU_blend(GPU_BLEND_ALPHA);
564 GPUVertFormat *format = immVertexFormat();
565 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
566 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
567 immUniformColor4f(BLI_thread_frand(0), BLI_thread_frand(0), BLI_thread_frand(0), 0.1f);
568 immRectf(pos,
569 region->drawrct.xmin - region->winrct.xmin,
570 region->drawrct.ymin - region->winrct.ymin,
571 region->drawrct.xmax - region->winrct.xmin,
572 region->drawrct.ymax - region->winrct.ymin);
573 immUnbindProgram();
574 GPU_blend(GPU_BLEND_NONE);
575 }
576
577 memset(®ion->drawrct, 0, sizeof(region->drawrct));
578
579 UI_blocklist_free_inactive(C, ®ion->uiblocks);
580
581 if (area) {
582 const bScreen *screen = WM_window_get_active_screen(win);
583
584 /* Only region emboss for top-bar */
585 if ((screen->state != SCREENFULL) && ED_area_is_global(area)) {
586 region_draw_emboss(region, ®ion->winrct, (REGION_EMBOSS_LEFT | REGION_EMBOSS_RIGHT));
587 }
588 else if ((region->regiontype == RGN_TYPE_WINDOW) && (region->alignment == RGN_ALIGN_QSPLIT)) {
589
590 /* draw separating lines between the quad views */
591
592 float color[4] = {0.0f, 0.0f, 0.0f, 0.8f};
593 UI_GetThemeColor3fv(TH_EDITOR_OUTLINE, color);
594 GPUVertFormat *format = immVertexFormat();
595 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
596 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
597 immUniformColor4fv(color);
598 GPU_line_width(1.0f);
599 imm_draw_box_wire_2d(pos,
600 0,
601 0,
602 region->winrct.xmax - region->winrct.xmin + 1,
603 region->winrct.ymax - region->winrct.ymin + 1);
604 immUnbindProgram();
605 }
606 }
607
608 /* We may want to detach message-subscriptions from drawing. */
609 {
610 WorkSpace *workspace = CTX_wm_workspace(C);
611 wmWindowManager *wm = CTX_wm_manager(C);
612 bScreen *screen = WM_window_get_active_screen(win);
613 Scene *scene = CTX_data_scene(C);
614 struct wmMsgBus *mbus = wm->message_bus;
615 WM_msgbus_clear_by_owner(mbus, region);
616
617 /* Cheat, always subscribe to this space type properties.
618 *
619 * This covers most cases and avoids copy-paste similar code for each space type.
620 */
621 if (ELEM(
622 region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS, RGN_TYPE_UI, RGN_TYPE_TOOLS)) {
623 SpaceLink *sl = area->spacedata.first;
624
625 PointerRNA ptr;
626 RNA_pointer_create(&screen->id, &RNA_Space, sl, &ptr);
627
628 wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
629 .owner = region,
630 .user_data = region,
631 .notify = ED_region_do_msg_notify_tag_redraw,
632 };
633 /* All properties for this space type. */
634 WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_region_tag_redraw, __func__);
635 }
636
637 ED_region_message_subscribe(C, workspace, scene, screen, area, region, mbus);
638 }
639 }
640
641 /* **********************************
642 * maybe silly, but let's try for now
643 * to keep these tags protected
644 * ********************************** */
645
ED_region_tag_redraw(ARegion * region)646 void ED_region_tag_redraw(ARegion *region)
647 {
648 /* don't tag redraw while drawing, it shouldn't happen normally
649 * but python scripts can cause this to happen indirectly */
650 if (region && !(region->do_draw & RGN_DRAWING)) {
651 /* zero region means full region redraw */
652 region->do_draw &= ~(RGN_DRAW_PARTIAL | RGN_DRAW_NO_REBUILD | RGN_DRAW_EDITOR_OVERLAYS);
653 region->do_draw |= RGN_DRAW;
654 memset(®ion->drawrct, 0, sizeof(region->drawrct));
655 }
656 }
657
ED_region_tag_redraw_cursor(ARegion * region)658 void ED_region_tag_redraw_cursor(ARegion *region)
659 {
660 if (region) {
661 region->do_draw_paintcursor = RGN_DRAW;
662 }
663 }
664
ED_region_tag_redraw_no_rebuild(ARegion * region)665 void ED_region_tag_redraw_no_rebuild(ARegion *region)
666 {
667 if (region && !(region->do_draw & (RGN_DRAWING | RGN_DRAW))) {
668 region->do_draw &= ~(RGN_DRAW_PARTIAL | RGN_DRAW_EDITOR_OVERLAYS);
669 region->do_draw |= RGN_DRAW_NO_REBUILD;
670 memset(®ion->drawrct, 0, sizeof(region->drawrct));
671 }
672 }
673
ED_region_tag_refresh_ui(ARegion * region)674 void ED_region_tag_refresh_ui(ARegion *region)
675 {
676 if (region) {
677 region->do_draw |= RGN_REFRESH_UI;
678 }
679 }
680
681 /**
682 * Tag editor overlays to be redrawn. If in doubt about which parts need to be redrawn (partial
683 * clipping rectangle set), redraw everything.
684 */
ED_region_tag_redraw_editor_overlays(struct ARegion * region)685 void ED_region_tag_redraw_editor_overlays(struct ARegion *region)
686 {
687 if (region && !(region->do_draw & (RGN_DRAWING | RGN_DRAW))) {
688 if (region->do_draw & RGN_DRAW_PARTIAL) {
689 ED_region_tag_redraw(region);
690 }
691 else {
692 region->do_draw |= RGN_DRAW_EDITOR_OVERLAYS;
693 }
694 }
695 }
696
ED_region_tag_redraw_partial(ARegion * region,const rcti * rct,bool rebuild)697 void ED_region_tag_redraw_partial(ARegion *region, const rcti *rct, bool rebuild)
698 {
699 if (region && !(region->do_draw & RGN_DRAWING)) {
700 if (region->do_draw & RGN_DRAW_PARTIAL) {
701 /* Partial redraw already set, expand region. */
702 BLI_rcti_union(®ion->drawrct, rct);
703 if (rebuild) {
704 region->do_draw &= ~RGN_DRAW_NO_REBUILD;
705 }
706 }
707 else if (region->do_draw & (RGN_DRAW | RGN_DRAW_NO_REBUILD)) {
708 /* Full redraw already requested. */
709 if (rebuild) {
710 region->do_draw &= ~RGN_DRAW_NO_REBUILD;
711 }
712 }
713 else {
714 /* No redraw set yet, set partial region. */
715 region->drawrct = *rct;
716 region->do_draw |= RGN_DRAW_PARTIAL;
717 if (!rebuild) {
718 region->do_draw |= RGN_DRAW_NO_REBUILD;
719 }
720 }
721 }
722 }
723
ED_area_tag_redraw(ScrArea * area)724 void ED_area_tag_redraw(ScrArea *area)
725 {
726 if (area) {
727 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
728 ED_region_tag_redraw(region);
729 }
730 }
731 }
732
ED_area_tag_redraw_no_rebuild(ScrArea * area)733 void ED_area_tag_redraw_no_rebuild(ScrArea *area)
734 {
735 if (area) {
736 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
737 ED_region_tag_redraw_no_rebuild(region);
738 }
739 }
740 }
741
ED_area_tag_redraw_regiontype(ScrArea * area,int regiontype)742 void ED_area_tag_redraw_regiontype(ScrArea *area, int regiontype)
743 {
744 if (area) {
745 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
746 if (region->regiontype == regiontype) {
747 ED_region_tag_redraw(region);
748 }
749 }
750 }
751 }
752
ED_area_tag_refresh(ScrArea * area)753 void ED_area_tag_refresh(ScrArea *area)
754 {
755 if (area) {
756 area->do_refresh = true;
757 }
758 }
759
760 /* *************************************************************** */
761
762 /**
763 * Returns the search string if the space type and region type support property search.
764 */
ED_area_region_search_filter_get(const ScrArea * area,const ARegion * region)765 const char *ED_area_region_search_filter_get(const ScrArea *area, const ARegion *region)
766 {
767 /* Only the properties editor has a search string for now. */
768 if (area->spacetype == SPACE_PROPERTIES) {
769 SpaceProperties *sbuts = area->spacedata.first;
770 if (region->regiontype == RGN_TYPE_WINDOW) {
771 return ED_buttons_search_string_get(sbuts);
772 }
773 }
774
775 return NULL;
776 }
777
778 /**
779 * Set the temporary update flag for property search.
780 */
ED_region_search_filter_update(const ScrArea * area,ARegion * region)781 void ED_region_search_filter_update(const ScrArea *area, ARegion *region)
782 {
783 region->flag |= RGN_FLAG_SEARCH_FILTER_UPDATE;
784
785 const char *search_filter = ED_area_region_search_filter_get(area, region);
786 SET_FLAG_FROM_TEST(region->flag,
787 region->regiontype == RGN_TYPE_WINDOW && search_filter[0] != '\0',
788 RGN_FLAG_SEARCH_FILTER_ACTIVE);
789 }
790
791 /* *************************************************************** */
792
793 /* use NULL to disable it */
ED_area_status_text(ScrArea * area,const char * str)794 void ED_area_status_text(ScrArea *area, const char *str)
795 {
796 /* happens when running transform operators in background mode */
797 if (area == NULL) {
798 return;
799 }
800
801 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
802 if (region->regiontype == RGN_TYPE_HEADER) {
803 if (str) {
804 if (region->headerstr == NULL) {
805 region->headerstr = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint");
806 }
807 BLI_strncpy(region->headerstr, str, UI_MAX_DRAW_STR);
808 BLI_str_rstrip(region->headerstr);
809 }
810 else if (region->headerstr) {
811 MEM_freeN(region->headerstr);
812 region->headerstr = NULL;
813 }
814 ED_region_tag_redraw(region);
815 }
816 }
817 }
818
ED_workspace_status_text(bContext * C,const char * str)819 void ED_workspace_status_text(bContext *C, const char *str)
820 {
821 wmWindow *win = CTX_wm_window(C);
822 WorkSpace *workspace = CTX_wm_workspace(C);
823
824 /* Can be NULL when running operators in background mode. */
825 if (workspace == NULL) {
826 return;
827 }
828
829 if (str) {
830 if (workspace->status_text == NULL) {
831 workspace->status_text = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint");
832 }
833 BLI_strncpy(workspace->status_text, str, UI_MAX_DRAW_STR);
834 }
835 else if (workspace->status_text) {
836 MEM_freeN(workspace->status_text);
837 workspace->status_text = NULL;
838 }
839
840 /* Redraw status bar. */
841 LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) {
842 if (area->spacetype == SPACE_STATUSBAR) {
843 ED_area_tag_redraw(area);
844 break;
845 }
846 }
847 }
848
849 /* ************************************************************ */
850
area_azone_init(wmWindow * win,const bScreen * screen,ScrArea * area)851 static void area_azone_init(wmWindow *win, const bScreen *screen, ScrArea *area)
852 {
853 /* reinitialize entirely, regions and fullscreen add azones too */
854 BLI_freelistN(&area->actionzones);
855
856 if (screen->state != SCREENNORMAL) {
857 return;
858 }
859
860 if (U.app_flag & USER_APP_LOCK_UI_LAYOUT) {
861 return;
862 }
863
864 if (ED_area_is_global(area)) {
865 return;
866 }
867
868 if (screen->temp) {
869 return;
870 }
871
872 const float coords[4][4] = {
873 /* Bottom-left. */
874 {area->totrct.xmin - U.pixelsize,
875 area->totrct.ymin - U.pixelsize,
876 area->totrct.xmin + AZONESPOTW,
877 area->totrct.ymin + AZONESPOTH},
878 /* Bottom-right. */
879 {area->totrct.xmax - AZONESPOTW,
880 area->totrct.ymin - U.pixelsize,
881 area->totrct.xmax + U.pixelsize,
882 area->totrct.ymin + AZONESPOTH},
883 /* Top-left. */
884 {area->totrct.xmin - U.pixelsize,
885 area->totrct.ymax - AZONESPOTH,
886 area->totrct.xmin + AZONESPOTW,
887 area->totrct.ymax + U.pixelsize},
888 /* Top-right. */
889 {area->totrct.xmax - AZONESPOTW,
890 area->totrct.ymax - AZONESPOTH,
891 area->totrct.xmax + U.pixelsize,
892 area->totrct.ymax + U.pixelsize},
893 };
894
895 for (int i = 0; i < 4; i++) {
896 /* can't click on bottom corners on OS X, already used for resizing */
897 #ifdef __APPLE__
898 if (!WM_window_is_fullscreen(win) &&
899 ((coords[i][0] == 0 && coords[i][1] == 0) ||
900 (coords[i][0] == WM_window_pixels_x(win) && coords[i][1] == 0))) {
901 continue;
902 }
903 #else
904 (void)win;
905 #endif
906
907 /* set area action zones */
908 AZone *az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
909 BLI_addtail(&(area->actionzones), az);
910 az->type = AZONE_AREA;
911 az->x1 = coords[i][0];
912 az->y1 = coords[i][1];
913 az->x2 = coords[i][2];
914 az->y2 = coords[i][3];
915 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
916 }
917 }
918
fullscreen_azone_init(ScrArea * area,ARegion * region)919 static void fullscreen_azone_init(ScrArea *area, ARegion *region)
920 {
921 if (ED_area_is_global(area) || (region->regiontype != RGN_TYPE_WINDOW)) {
922 return;
923 }
924
925 AZone *az = (AZone *)MEM_callocN(sizeof(AZone), "fullscreen action zone");
926 BLI_addtail(&(area->actionzones), az);
927 az->type = AZONE_FULLSCREEN;
928 az->region = region;
929 az->alpha = 0.0f;
930
931 if (U.uiflag2 & USER_REGION_OVERLAP) {
932 const rcti *rect_visible = ED_region_visible_rect(region);
933 az->x2 = region->winrct.xmin + rect_visible->xmax;
934 az->y2 = region->winrct.ymin + rect_visible->ymax;
935 }
936 else {
937 az->x2 = region->winrct.xmax;
938 az->y2 = region->winrct.ymax;
939 }
940 az->x1 = az->x2 - AZONEFADEOUT;
941 az->y1 = az->y2 - AZONEFADEOUT;
942
943 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
944 }
945
946 #define AZONEPAD_EDGE (0.1f * U.widget_unit)
947 #define AZONEPAD_ICON (0.45f * U.widget_unit)
region_azone_edge(AZone * az,ARegion * region)948 static void region_azone_edge(AZone *az, ARegion *region)
949 {
950 switch (az->edge) {
951 case AE_TOP_TO_BOTTOMRIGHT:
952 az->x1 = region->winrct.xmin;
953 az->y1 = region->winrct.ymax - AZONEPAD_EDGE;
954 az->x2 = region->winrct.xmax;
955 az->y2 = region->winrct.ymax + AZONEPAD_EDGE;
956 break;
957 case AE_BOTTOM_TO_TOPLEFT:
958 az->x1 = region->winrct.xmin;
959 az->y1 = region->winrct.ymin + AZONEPAD_EDGE;
960 az->x2 = region->winrct.xmax;
961 az->y2 = region->winrct.ymin - AZONEPAD_EDGE;
962 break;
963 case AE_LEFT_TO_TOPRIGHT:
964 az->x1 = region->winrct.xmin - AZONEPAD_EDGE;
965 az->y1 = region->winrct.ymin;
966 az->x2 = region->winrct.xmin + AZONEPAD_EDGE;
967 az->y2 = region->winrct.ymax;
968 break;
969 case AE_RIGHT_TO_TOPLEFT:
970 az->x1 = region->winrct.xmax + AZONEPAD_EDGE;
971 az->y1 = region->winrct.ymin;
972 az->x2 = region->winrct.xmax - AZONEPAD_EDGE;
973 az->y2 = region->winrct.ymax;
974 break;
975 }
976 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
977 }
978
979 /* region already made zero sized, in shape of edge */
region_azone_tab_plus(ScrArea * area,AZone * az,ARegion * region)980 static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region)
981 {
982 float edge_offset = 1.0f;
983 const float tab_size_x = 0.7f * U.widget_unit;
984 const float tab_size_y = 0.4f * U.widget_unit;
985
986 int tot = 0;
987 LISTBASE_FOREACH (AZone *, azt, &area->actionzones) {
988 if (azt->edge == az->edge) {
989 tot++;
990 }
991 }
992
993 switch (az->edge) {
994 case AE_TOP_TO_BOTTOMRIGHT: {
995 int add = (region->winrct.ymax == area->totrct.ymin) ? 1 : 0;
996 az->x1 = region->winrct.xmax - ((edge_offset + 1.0f) * tab_size_x);
997 az->y1 = region->winrct.ymax - add;
998 az->x2 = region->winrct.xmax - (edge_offset * tab_size_x);
999 az->y2 = region->winrct.ymax - add + tab_size_y;
1000 break;
1001 }
1002 case AE_BOTTOM_TO_TOPLEFT:
1003 az->x1 = region->winrct.xmax - ((edge_offset + 1.0f) * tab_size_x);
1004 az->y1 = region->winrct.ymin - tab_size_y;
1005 az->x2 = region->winrct.xmax - (edge_offset * tab_size_x);
1006 az->y2 = region->winrct.ymin;
1007 break;
1008 case AE_LEFT_TO_TOPRIGHT:
1009 az->x1 = region->winrct.xmin - tab_size_y;
1010 az->y1 = region->winrct.ymax - ((edge_offset + 1.0f) * tab_size_x);
1011 az->x2 = region->winrct.xmin;
1012 az->y2 = region->winrct.ymax - (edge_offset * tab_size_x);
1013 break;
1014 case AE_RIGHT_TO_TOPLEFT:
1015 az->x1 = region->winrct.xmax;
1016 az->y1 = region->winrct.ymax - ((edge_offset + 1.0f) * tab_size_x);
1017 az->x2 = region->winrct.xmax + tab_size_y;
1018 az->y2 = region->winrct.ymax - (edge_offset * tab_size_x);
1019 break;
1020 }
1021 /* rect needed for mouse pointer test */
1022 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
1023 }
1024
region_azone_edge_poll(const ARegion * region,const bool is_fullscreen)1025 static bool region_azone_edge_poll(const ARegion *region, const bool is_fullscreen)
1026 {
1027 const bool is_hidden = (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
1028
1029 if (is_hidden && is_fullscreen) {
1030 return false;
1031 }
1032 if (!is_hidden && ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
1033 return false;
1034 }
1035
1036 return true;
1037 }
1038
region_azone_edge_init(ScrArea * area,ARegion * region,AZEdge edge,const bool is_fullscreen)1039 static void region_azone_edge_init(ScrArea *area,
1040 ARegion *region,
1041 AZEdge edge,
1042 const bool is_fullscreen)
1043 {
1044 const bool is_hidden = (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
1045
1046 if (!region_azone_edge_poll(region, is_fullscreen)) {
1047 return;
1048 }
1049
1050 AZone *az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
1051 BLI_addtail(&(area->actionzones), az);
1052 az->type = AZONE_REGION;
1053 az->region = region;
1054 az->edge = edge;
1055
1056 if (is_hidden) {
1057 region_azone_tab_plus(area, az, region);
1058 }
1059 else {
1060 region_azone_edge(az, region);
1061 }
1062 }
1063
region_azone_scrollbar_init(ScrArea * area,ARegion * region,AZScrollDirection direction)1064 static void region_azone_scrollbar_init(ScrArea *area,
1065 ARegion *region,
1066 AZScrollDirection direction)
1067 {
1068 rcti scroller_vert = (direction == AZ_SCROLL_VERT) ? region->v2d.vert : region->v2d.hor;
1069 AZone *az = MEM_callocN(sizeof(*az), __func__);
1070
1071 BLI_addtail(&area->actionzones, az);
1072 az->type = AZONE_REGION_SCROLL;
1073 az->region = region;
1074 az->direction = direction;
1075
1076 if (direction == AZ_SCROLL_VERT) {
1077 az->region->v2d.alpha_vert = 0;
1078 }
1079 else if (direction == AZ_SCROLL_HOR) {
1080 az->region->v2d.alpha_hor = 0;
1081 }
1082
1083 BLI_rcti_translate(&scroller_vert, region->winrct.xmin, region->winrct.ymin);
1084 az->x1 = scroller_vert.xmin - AZONEFADEIN;
1085 az->y1 = scroller_vert.ymin - AZONEFADEIN;
1086 az->x2 = scroller_vert.xmax + AZONEFADEIN;
1087 az->y2 = scroller_vert.ymax + AZONEFADEIN;
1088
1089 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
1090 }
1091
region_azones_scrollbars_init(ScrArea * area,ARegion * region)1092 static void region_azones_scrollbars_init(ScrArea *area, ARegion *region)
1093 {
1094 const View2D *v2d = ®ion->v2d;
1095
1096 if ((v2d->scroll & V2D_SCROLL_VERTICAL) && ((v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES) == 0)) {
1097 region_azone_scrollbar_init(area, region, AZ_SCROLL_VERT);
1098 }
1099 if ((v2d->scroll & V2D_SCROLL_HORIZONTAL) &&
1100 ((v2d->scroll & V2D_SCROLL_HORIZONTAL_HANDLES) == 0)) {
1101 region_azone_scrollbar_init(area, region, AZ_SCROLL_HOR);
1102 }
1103 }
1104
1105 /* *************************************************************** */
region_azones_add_edge(ScrArea * area,ARegion * region,const int alignment,const bool is_fullscreen)1106 static void region_azones_add_edge(ScrArea *area,
1107 ARegion *region,
1108 const int alignment,
1109 const bool is_fullscreen)
1110 {
1111
1112 /* edge code (t b l r) is along which area edge azone will be drawn */
1113 if (alignment == RGN_ALIGN_TOP) {
1114 region_azone_edge_init(area, region, AE_BOTTOM_TO_TOPLEFT, is_fullscreen);
1115 }
1116 else if (alignment == RGN_ALIGN_BOTTOM) {
1117 region_azone_edge_init(area, region, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen);
1118 }
1119 else if (alignment == RGN_ALIGN_RIGHT) {
1120 region_azone_edge_init(area, region, AE_LEFT_TO_TOPRIGHT, is_fullscreen);
1121 }
1122 else if (alignment == RGN_ALIGN_LEFT) {
1123 region_azone_edge_init(area, region, AE_RIGHT_TO_TOPLEFT, is_fullscreen);
1124 }
1125 }
1126
region_azones_add(const bScreen * screen,ScrArea * area,ARegion * region)1127 static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *region)
1128 {
1129 const bool is_fullscreen = screen->state == SCREENFULL;
1130
1131 /* Only display tab or icons when the header region is hidden
1132 * (not the tool header - they overlap). */
1133 if (region->regiontype == RGN_TYPE_TOOL_HEADER) {
1134 return;
1135 }
1136
1137 region_azones_add_edge(area, region, RGN_ALIGN_ENUM_FROM_MASK(region->alignment), is_fullscreen);
1138
1139 /* For a split region also continue the azone edge from the next region if this region is aligned
1140 * with the next */
1141 if ((region->alignment & RGN_SPLIT_PREV) && region->prev) {
1142 region_azones_add_edge(
1143 area, region, RGN_ALIGN_ENUM_FROM_MASK(region->prev->alignment), is_fullscreen);
1144 }
1145
1146 if (is_fullscreen) {
1147 fullscreen_azone_init(area, region);
1148 }
1149
1150 region_azones_scrollbars_init(area, region);
1151 }
1152
1153 /* dir is direction to check, not the splitting edge direction! */
rct_fits(const rcti * rect,char dir,int size)1154 static int rct_fits(const rcti *rect, char dir, int size)
1155 {
1156 if (dir == 'h') {
1157 return BLI_rcti_size_x(rect) + 1 - size;
1158 }
1159 /* 'v' */
1160 return BLI_rcti_size_y(rect) + 1 - size;
1161 }
1162
1163 /* *************************************************************** */
1164
1165 /* region should be overlapping */
1166 /* function checks if some overlapping region was defined before - on same place */
region_overlap_fix(ScrArea * area,ARegion * region)1167 static void region_overlap_fix(ScrArea *area, ARegion *region)
1168 {
1169 /* find overlapping previous region on same place */
1170 ARegion *ar1;
1171 int align1 = 0;
1172 const int align = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
1173 for (ar1 = region->prev; ar1; ar1 = ar1->prev) {
1174 if (ar1->flag & RGN_FLAG_HIDDEN) {
1175 continue;
1176 }
1177
1178 if (ar1->overlap && ((ar1->alignment & RGN_SPLIT_PREV) == 0)) {
1179 if (ELEM(ar1->alignment, RGN_ALIGN_FLOAT)) {
1180 continue;
1181 }
1182 align1 = ar1->alignment;
1183 if (BLI_rcti_isect(&ar1->winrct, ®ion->winrct, NULL)) {
1184 if (align1 != align) {
1185 /* Left overlapping right or vice-versa, forbid this! */
1186 region->flag |= RGN_FLAG_TOO_SMALL;
1187 return;
1188 }
1189 /* Else, we have our previous region on same side. */
1190 break;
1191 }
1192 }
1193 }
1194
1195 /* Guard against flags slipping through that would have to be masked out in usages below. */
1196 BLI_assert(align1 == RGN_ALIGN_ENUM_FROM_MASK(align1));
1197
1198 /* translate or close */
1199 if (ar1) {
1200 if (align1 == RGN_ALIGN_LEFT) {
1201 if (region->winrct.xmax + ar1->winx > area->winx - U.widget_unit) {
1202 region->flag |= RGN_FLAG_TOO_SMALL;
1203 return;
1204 }
1205 BLI_rcti_translate(®ion->winrct, ar1->winx, 0);
1206 }
1207 else if (align1 == RGN_ALIGN_RIGHT) {
1208 if (region->winrct.xmin - ar1->winx < U.widget_unit) {
1209 region->flag |= RGN_FLAG_TOO_SMALL;
1210 return;
1211 }
1212 BLI_rcti_translate(®ion->winrct, -ar1->winx, 0);
1213 }
1214 }
1215
1216 /* At this point, 'region' is in its final position and still open.
1217 * Make a final check it does not overlap any previous 'other side' region. */
1218 for (ar1 = region->prev; ar1; ar1 = ar1->prev) {
1219 if (ar1->flag & RGN_FLAG_HIDDEN) {
1220 continue;
1221 }
1222 if (ELEM(ar1->alignment, RGN_ALIGN_FLOAT)) {
1223 continue;
1224 }
1225
1226 if (ar1->overlap && (ar1->alignment & RGN_SPLIT_PREV) == 0) {
1227 if ((ar1->alignment != align) && BLI_rcti_isect(&ar1->winrct, ®ion->winrct, NULL)) {
1228 /* Left overlapping right or vice-versa, forbid this! */
1229 region->flag |= RGN_FLAG_TOO_SMALL;
1230 return;
1231 }
1232 }
1233 }
1234 }
1235
1236 /* overlapping regions only in the following restricted cases */
ED_region_is_overlap(int spacetype,int regiontype)1237 bool ED_region_is_overlap(int spacetype, int regiontype)
1238 {
1239 if (regiontype == RGN_TYPE_HUD) {
1240 return true;
1241 }
1242 if (U.uiflag2 & USER_REGION_OVERLAP) {
1243 if (spacetype == SPACE_NODE) {
1244 if (regiontype == RGN_TYPE_TOOLS) {
1245 return true;
1246 }
1247 }
1248 else if (ELEM(spacetype, SPACE_VIEW3D, SPACE_IMAGE)) {
1249 if (ELEM(regiontype,
1250 RGN_TYPE_TOOLS,
1251 RGN_TYPE_UI,
1252 RGN_TYPE_TOOL_PROPS,
1253 RGN_TYPE_HEADER,
1254 RGN_TYPE_FOOTER)) {
1255 return true;
1256 }
1257 }
1258 }
1259
1260 return false;
1261 }
1262
region_rect_recursive(ScrArea * area,ARegion * region,rcti * remainder,rcti * overlap_remainder,int quad)1263 static void region_rect_recursive(
1264 ScrArea *area, ARegion *region, rcti *remainder, rcti *overlap_remainder, int quad)
1265 {
1266 rcti *remainder_prev = remainder;
1267
1268 if (region == NULL) {
1269 return;
1270 }
1271
1272 int prev_winx = region->winx;
1273 int prev_winy = region->winy;
1274
1275 /* no returns in function, winrct gets set in the end again */
1276 BLI_rcti_init(®ion->winrct, 0, 0, 0, 0);
1277
1278 /* for test; allow split of previously defined region */
1279 if (region->alignment & RGN_SPLIT_PREV) {
1280 if (region->prev) {
1281 remainder = ®ion->prev->winrct;
1282 }
1283 }
1284
1285 int alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
1286
1287 /* set here, assuming userpref switching forces to call this again */
1288 region->overlap = ED_region_is_overlap(area->spacetype, region->regiontype);
1289
1290 /* clear state flags first */
1291 region->flag &= ~(RGN_FLAG_TOO_SMALL | RGN_FLAG_SIZE_CLAMP_X | RGN_FLAG_SIZE_CLAMP_Y);
1292 /* user errors */
1293 if ((region->next == NULL) && !ELEM(alignment, RGN_ALIGN_QSPLIT, RGN_ALIGN_FLOAT)) {
1294 alignment = RGN_ALIGN_NONE;
1295 }
1296
1297 /* If both the ARegion.sizex/y and the prefsize are 0, the region is tagged as too small, even
1298 * before the layout for dynamic regions is created. #wm_draw_window_offscreen() allows the
1299 * layout to be created despite the RGN_FLAG_TOO_SMALL flag being set. But there may still be
1300 * regions that don't have a separate ARegionType.layout callback. For those, set a default
1301 * prefsize so they can become visible. */
1302 if ((region->flag & RGN_FLAG_DYNAMIC_SIZE) && !(region->type->layout)) {
1303 if ((region->sizex == 0) && (region->type->prefsizex == 0)) {
1304 region->type->prefsizex = AREAMINX;
1305 }
1306 if ((region->sizey == 0) && (region->type->prefsizey == 0)) {
1307 region->type->prefsizey = HEADERY;
1308 }
1309 }
1310
1311 /* prefsize, taking into account DPI */
1312 int prefsizex = UI_DPI_FAC *
1313 ((region->sizex > 1) ? region->sizex + 0.5f : region->type->prefsizex);
1314 int prefsizey;
1315
1316 if (region->flag & RGN_FLAG_PREFSIZE_OR_HIDDEN) {
1317 prefsizex = UI_DPI_FAC * region->type->prefsizex;
1318 prefsizey = UI_DPI_FAC * region->type->prefsizey;
1319 }
1320 else if (region->regiontype == RGN_TYPE_HEADER) {
1321 prefsizey = ED_area_headersize();
1322 }
1323 else if (region->regiontype == RGN_TYPE_TOOL_HEADER) {
1324 prefsizey = ED_area_headersize();
1325 }
1326 else if (region->regiontype == RGN_TYPE_FOOTER) {
1327 prefsizey = ED_area_footersize();
1328 }
1329 else if (ED_area_is_global(area)) {
1330 prefsizey = ED_region_global_size_y();
1331 }
1332 else {
1333 prefsizey = UI_DPI_FAC * (region->sizey > 1 ? region->sizey + 0.5f : region->type->prefsizey);
1334 }
1335
1336 if (region->flag & RGN_FLAG_HIDDEN) {
1337 /* hidden is user flag */
1338 }
1339 else if (alignment == RGN_ALIGN_FLOAT) {
1340 /**
1341 * \note Currently this window type is only used for #RGN_TYPE_HUD,
1342 * We expect the panel to resize it's self to be larger.
1343 *
1344 * This aligns to the lower left of the area.
1345 */
1346 const int size_min[2] = {UI_UNIT_X, UI_UNIT_Y};
1347 rcti overlap_remainder_margin = *overlap_remainder;
1348
1349 BLI_rcti_resize(&overlap_remainder_margin,
1350 max_ii(0, BLI_rcti_size_x(overlap_remainder) - UI_UNIT_X / 2),
1351 max_ii(0, BLI_rcti_size_y(overlap_remainder) - UI_UNIT_Y / 2));
1352 region->winrct.xmin = overlap_remainder_margin.xmin + region->runtime.offset_x;
1353 region->winrct.ymin = overlap_remainder_margin.ymin + region->runtime.offset_y;
1354 region->winrct.xmax = region->winrct.xmin + prefsizex - 1;
1355 region->winrct.ymax = region->winrct.ymin + prefsizey - 1;
1356
1357 BLI_rcti_isect(®ion->winrct, &overlap_remainder_margin, ®ion->winrct);
1358
1359 if (BLI_rcti_size_x(®ion->winrct) != prefsizex - 1) {
1360 region->flag |= RGN_FLAG_SIZE_CLAMP_X;
1361 }
1362 if (BLI_rcti_size_y(®ion->winrct) != prefsizey - 1) {
1363 region->flag |= RGN_FLAG_SIZE_CLAMP_Y;
1364 }
1365
1366 /* We need to use a test that wont have been previously clamped. */
1367 rcti winrct_test = {
1368 .xmin = region->winrct.xmin,
1369 .ymin = region->winrct.ymin,
1370 .xmax = region->winrct.xmin + size_min[0],
1371 .ymax = region->winrct.ymin + size_min[1],
1372 };
1373 BLI_rcti_isect(&winrct_test, &overlap_remainder_margin, &winrct_test);
1374 if (BLI_rcti_size_x(&winrct_test) < size_min[0] ||
1375 BLI_rcti_size_y(&winrct_test) < size_min[1]) {
1376 region->flag |= RGN_FLAG_TOO_SMALL;
1377 }
1378 }
1379 else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) {
1380 /* remainder is too small for any usage */
1381 region->flag |= RGN_FLAG_TOO_SMALL;
1382 }
1383 else if (alignment == RGN_ALIGN_NONE) {
1384 /* typically last region */
1385 region->winrct = *remainder;
1386 BLI_rcti_init(remainder, 0, 0, 0, 0);
1387 }
1388 else if (alignment == RGN_ALIGN_TOP || alignment == RGN_ALIGN_BOTTOM) {
1389 rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
1390
1391 if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) {
1392 region->flag |= RGN_FLAG_TOO_SMALL;
1393 }
1394 else {
1395 int fac = rct_fits(winrct, 'v', prefsizey);
1396
1397 if (fac < 0) {
1398 prefsizey += fac;
1399 }
1400
1401 region->winrct = *winrct;
1402
1403 if (alignment == RGN_ALIGN_TOP) {
1404 region->winrct.ymin = region->winrct.ymax - prefsizey + 1;
1405 winrct->ymax = region->winrct.ymin - 1;
1406 }
1407 else {
1408 region->winrct.ymax = region->winrct.ymin + prefsizey - 1;
1409 winrct->ymin = region->winrct.ymax + 1;
1410 }
1411 BLI_rcti_sanitize(winrct);
1412 }
1413 }
1414 else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
1415 rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
1416
1417 if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) {
1418 region->flag |= RGN_FLAG_TOO_SMALL;
1419 }
1420 else {
1421 int fac = rct_fits(winrct, 'h', prefsizex);
1422
1423 if (fac < 0) {
1424 prefsizex += fac;
1425 }
1426
1427 region->winrct = *winrct;
1428
1429 if (alignment == RGN_ALIGN_RIGHT) {
1430 region->winrct.xmin = region->winrct.xmax - prefsizex + 1;
1431 winrct->xmax = region->winrct.xmin - 1;
1432 }
1433 else {
1434 region->winrct.xmax = region->winrct.xmin + prefsizex - 1;
1435 winrct->xmin = region->winrct.xmax + 1;
1436 }
1437 BLI_rcti_sanitize(winrct);
1438 }
1439 }
1440 else if (alignment == RGN_ALIGN_VSPLIT || alignment == RGN_ALIGN_HSPLIT) {
1441 /* percentage subdiv*/
1442 region->winrct = *remainder;
1443
1444 if (alignment == RGN_ALIGN_HSPLIT) {
1445 if (rct_fits(remainder, 'h', prefsizex) > 4) {
1446 region->winrct.xmax = BLI_rcti_cent_x(remainder);
1447 remainder->xmin = region->winrct.xmax + 1;
1448 }
1449 else {
1450 BLI_rcti_init(remainder, 0, 0, 0, 0);
1451 }
1452 }
1453 else {
1454 if (rct_fits(remainder, 'v', prefsizey) > 4) {
1455 region->winrct.ymax = BLI_rcti_cent_y(remainder);
1456 remainder->ymin = region->winrct.ymax + 1;
1457 }
1458 else {
1459 BLI_rcti_init(remainder, 0, 0, 0, 0);
1460 }
1461 }
1462 }
1463 else if (alignment == RGN_ALIGN_QSPLIT) {
1464 region->winrct = *remainder;
1465
1466 /* test if there's still 4 regions left */
1467 if (quad == 0) {
1468 ARegion *artest = region->next;
1469 int count = 1;
1470
1471 while (artest) {
1472 artest->alignment = RGN_ALIGN_QSPLIT;
1473 artest = artest->next;
1474 count++;
1475 }
1476
1477 if (count != 4) {
1478 /* let's stop adding regions */
1479 BLI_rcti_init(remainder, 0, 0, 0, 0);
1480 if (G.debug & G_DEBUG) {
1481 printf("region quadsplit failed\n");
1482 }
1483 }
1484 else {
1485 quad = 1;
1486 }
1487 }
1488 if (quad) {
1489 if (quad == 1) { /* left bottom */
1490 region->winrct.xmax = BLI_rcti_cent_x(remainder);
1491 region->winrct.ymax = BLI_rcti_cent_y(remainder);
1492 }
1493 else if (quad == 2) { /* left top */
1494 region->winrct.xmax = BLI_rcti_cent_x(remainder);
1495 region->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
1496 }
1497 else if (quad == 3) { /* right bottom */
1498 region->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
1499 region->winrct.ymax = BLI_rcti_cent_y(remainder);
1500 }
1501 else { /* right top */
1502 region->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
1503 region->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
1504 BLI_rcti_init(remainder, 0, 0, 0, 0);
1505 }
1506
1507 /* Fix any negative dimensions. This can happen when a quad split 3d view gets to small. (see
1508 * T72200). */
1509 BLI_rcti_sanitize(®ion->winrct);
1510
1511 quad++;
1512 }
1513 }
1514
1515 /* for speedup */
1516 region->winx = BLI_rcti_size_x(®ion->winrct) + 1;
1517 region->winy = BLI_rcti_size_y(®ion->winrct) + 1;
1518
1519 /* if region opened normally, we store this for hide/reveal usage */
1520 /* prevent rounding errors for UI_DPI_FAC mult and divide */
1521 if (region->winx > 1) {
1522 region->sizex = (region->winx + 0.5f) / UI_DPI_FAC;
1523 }
1524 if (region->winy > 1) {
1525 region->sizey = (region->winy + 0.5f) / UI_DPI_FAC;
1526 }
1527
1528 /* exception for multiple overlapping regions on same spot */
1529 if (region->overlap && (alignment != RGN_ALIGN_FLOAT)) {
1530 region_overlap_fix(area, region);
1531 }
1532
1533 /* set winrect for azones */
1534 if (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
1535 region->winrct = (region->overlap) ? *overlap_remainder : *remainder;
1536
1537 switch (alignment) {
1538 case RGN_ALIGN_TOP:
1539 region->winrct.ymin = region->winrct.ymax;
1540 break;
1541 case RGN_ALIGN_BOTTOM:
1542 region->winrct.ymax = region->winrct.ymin;
1543 break;
1544 case RGN_ALIGN_RIGHT:
1545 region->winrct.xmin = region->winrct.xmax;
1546 break;
1547 case RGN_ALIGN_LEFT:
1548 region->winrct.xmax = region->winrct.xmin;
1549 break;
1550 default:
1551 /* prevent winrct to be valid */
1552 region->winrct.xmax = region->winrct.xmin;
1553 break;
1554 }
1555
1556 /* Size on one axis is now 0, the other axis may still be invalid (negative) though. */
1557 BLI_rcti_sanitize(®ion->winrct);
1558 }
1559
1560 /* restore prev-split exception */
1561 if (region->alignment & RGN_SPLIT_PREV) {
1562 if (region->prev) {
1563 remainder = remainder_prev;
1564 region->prev->winx = BLI_rcti_size_x(®ion->prev->winrct) + 1;
1565 region->prev->winy = BLI_rcti_size_y(®ion->prev->winrct) + 1;
1566 }
1567 }
1568
1569 /* After non-overlapping region, all following overlapping regions
1570 * fit within the remaining space again. */
1571 if (!region->overlap) {
1572 *overlap_remainder = *remainder;
1573 }
1574
1575 BLI_assert(BLI_rcti_is_valid(®ion->winrct));
1576
1577 region_rect_recursive(area, region->next, remainder, overlap_remainder, quad);
1578
1579 /* Tag for redraw if size changes. */
1580 if (region->winx != prev_winx || region->winy != prev_winy) {
1581 /* 3D View needs a full rebuild in case a progressive render runs. Rest can live with
1582 * no-rebuild (e.g. Outliner) */
1583 if (area->spacetype == SPACE_VIEW3D) {
1584 ED_region_tag_redraw(region);
1585 }
1586 else {
1587 ED_region_tag_redraw_no_rebuild(region);
1588 }
1589 }
1590
1591 /* Clear, initialize on demand. */
1592 memset(®ion->runtime.visible_rect, 0, sizeof(region->runtime.visible_rect));
1593 }
1594
area_calc_totrct(ScrArea * area,const rcti * window_rect)1595 static void area_calc_totrct(ScrArea *area, const rcti *window_rect)
1596 {
1597 short px = (short)U.pixelsize;
1598
1599 area->totrct.xmin = area->v1->vec.x;
1600 area->totrct.xmax = area->v4->vec.x;
1601 area->totrct.ymin = area->v1->vec.y;
1602 area->totrct.ymax = area->v2->vec.y;
1603
1604 /* scale down totrct by 1 pixel on all sides not matching window borders */
1605 if (area->totrct.xmin > window_rect->xmin) {
1606 area->totrct.xmin += px;
1607 }
1608 if (area->totrct.xmax < (window_rect->xmax - 1)) {
1609 area->totrct.xmax -= px;
1610 }
1611 if (area->totrct.ymin > window_rect->ymin) {
1612 area->totrct.ymin += px;
1613 }
1614 if (area->totrct.ymax < (window_rect->ymax - 1)) {
1615 area->totrct.ymax -= px;
1616 }
1617 /* Although the following asserts are correct they lead to a very unstable Blender.
1618 * And the asserts would fail even in 2.7x
1619 * (they were added in 2.8x as part of the top-bar commit).
1620 * For more details see T54864. */
1621 #if 0
1622 BLI_assert(area->totrct.xmin >= 0);
1623 BLI_assert(area->totrct.xmax >= 0);
1624 BLI_assert(area->totrct.ymin >= 0);
1625 BLI_assert(area->totrct.ymax >= 0);
1626 #endif
1627
1628 /* for speedup */
1629 area->winx = BLI_rcti_size_x(&area->totrct) + 1;
1630 area->winy = BLI_rcti_size_y(&area->totrct) + 1;
1631 }
1632
1633 /* used for area initialize below */
region_subwindow(ARegion * region)1634 static void region_subwindow(ARegion *region)
1635 {
1636 bool hidden = (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) != 0;
1637
1638 if ((region->alignment & RGN_SPLIT_PREV) && region->prev) {
1639 hidden = hidden || (region->prev->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
1640 }
1641
1642 region->visible = !hidden;
1643 }
1644
event_in_markers_region(const ARegion * region,const wmEvent * event)1645 static bool event_in_markers_region(const ARegion *region, const wmEvent *event)
1646 {
1647 rcti rect = region->winrct;
1648 rect.ymax = rect.ymin + UI_MARKER_MARGIN_Y;
1649 return BLI_rcti_isect_pt(&rect, event->x, event->y);
1650 }
1651
1652 /**
1653 * \param region: Region, may be NULL when adding handlers for \a area.
1654 */
ed_default_handlers(wmWindowManager * wm,ScrArea * area,ARegion * region,ListBase * handlers,int flag)1655 static void ed_default_handlers(
1656 wmWindowManager *wm, ScrArea *area, ARegion *region, ListBase *handlers, int flag)
1657 {
1658 BLI_assert(region ? (®ion->handlers == handlers) : (&area->handlers == handlers));
1659
1660 /* note, add-handler checks if it already exists */
1661
1662 /* XXX it would be good to have boundbox checks for some of these... */
1663 if (flag & ED_KEYMAP_UI) {
1664 wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "User Interface", 0, 0);
1665 WM_event_add_keymap_handler(handlers, keymap);
1666
1667 /* user interface widgets */
1668 UI_region_handlers_add(handlers);
1669 }
1670 if (flag & ED_KEYMAP_GIZMO) {
1671 BLI_assert(region && ELEM(region->type->regionid, RGN_TYPE_WINDOW, RGN_TYPE_PREVIEW));
1672 if (region) {
1673 /* Anything else is confusing, only allow this. */
1674 BLI_assert(®ion->handlers == handlers);
1675 if (region->gizmo_map == NULL) {
1676 region->gizmo_map = WM_gizmomap_new_from_type(
1677 &(const struct wmGizmoMapType_Params){area->spacetype, region->type->regionid});
1678 }
1679 WM_gizmomap_add_handlers(region, region->gizmo_map);
1680 }
1681 }
1682 if (flag & ED_KEYMAP_VIEW2D) {
1683 /* 2d-viewport handling+manipulation */
1684 wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "View2D", 0, 0);
1685 WM_event_add_keymap_handler(handlers, keymap);
1686 }
1687 if (flag & ED_KEYMAP_ANIMATION) {
1688 wmKeyMap *keymap;
1689
1690 /* time-markers */
1691 keymap = WM_keymap_ensure(wm->defaultconf, "Markers", 0, 0);
1692 WM_event_add_keymap_handler_poll(handlers, keymap, event_in_markers_region);
1693
1694 /* time-scrub */
1695 keymap = WM_keymap_ensure(wm->defaultconf, "Time Scrub", 0, 0);
1696 WM_event_add_keymap_handler_poll(handlers, keymap, ED_time_scrub_event_in_region);
1697
1698 /* frame changing and timeline operators (for time spaces) */
1699 keymap = WM_keymap_ensure(wm->defaultconf, "Animation", 0, 0);
1700 WM_event_add_keymap_handler(handlers, keymap);
1701 }
1702 if (flag & ED_KEYMAP_TOOL) {
1703 WM_event_add_keymap_handler_dynamic(
1704 ®ion->handlers, WM_event_get_keymap_from_toolsystem_fallback, area);
1705 WM_event_add_keymap_handler_dynamic(
1706 ®ion->handlers, WM_event_get_keymap_from_toolsystem, area);
1707 }
1708 if (flag & ED_KEYMAP_FRAMES) {
1709 /* frame changing/jumping (for all spaces) */
1710 wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Frames", 0, 0);
1711 WM_event_add_keymap_handler(handlers, keymap);
1712 }
1713 if (flag & ED_KEYMAP_HEADER) {
1714 /* standard keymap for headers regions */
1715 wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Region Context Menu", 0, 0);
1716 WM_event_add_keymap_handler(handlers, keymap);
1717 }
1718 if (flag & ED_KEYMAP_FOOTER) {
1719 /* standard keymap for footer regions */
1720 wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Region Context Menu", 0, 0);
1721 WM_event_add_keymap_handler(handlers, keymap);
1722 }
1723 if (flag & ED_KEYMAP_NAVBAR) {
1724 /* standard keymap for Navigation bar regions */
1725 wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Region Context Menu", 0, 0);
1726 WM_event_add_keymap_handler(®ion->handlers, keymap);
1727 }
1728
1729 /* Keep last because of LMB/RMB handling, see: T57527. */
1730 if (flag & ED_KEYMAP_GPENCIL) {
1731 /* grease pencil */
1732 /* NOTE: This is now 4 keymaps - One for basic functionality,
1733 * and others for special stroke modes (edit, paint and sculpt).
1734 *
1735 * For now, it's easier to just include all,
1736 * since you hardly want one without the others.
1737 */
1738 wmKeyMap *keymap_general = WM_keymap_ensure(wm->defaultconf, "Grease Pencil", 0, 0);
1739 WM_event_add_keymap_handler(handlers, keymap_general);
1740
1741 wmKeyMap *keymap_edit = WM_keymap_ensure(
1742 wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
1743 WM_event_add_keymap_handler(handlers, keymap_edit);
1744
1745 wmKeyMap *keymap_paint = WM_keymap_ensure(
1746 wm->defaultconf, "Grease Pencil Stroke Paint Mode", 0, 0);
1747 WM_event_add_keymap_handler(handlers, keymap_paint);
1748
1749 wmKeyMap *keymap_paint_draw = WM_keymap_ensure(
1750 wm->defaultconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0);
1751 WM_event_add_keymap_handler(handlers, keymap_paint_draw);
1752
1753 wmKeyMap *keymap_paint_erase = WM_keymap_ensure(
1754 wm->defaultconf, "Grease Pencil Stroke Paint (Erase)", 0, 0);
1755 WM_event_add_keymap_handler(handlers, keymap_paint_erase);
1756
1757 wmKeyMap *keymap_paint_fill = WM_keymap_ensure(
1758 wm->defaultconf, "Grease Pencil Stroke Paint (Fill)", 0, 0);
1759 WM_event_add_keymap_handler(handlers, keymap_paint_fill);
1760
1761 wmKeyMap *keymap_paint_tint = WM_keymap_ensure(
1762 wm->defaultconf, "Grease Pencil Stroke Paint (Tint)", 0, 0);
1763 WM_event_add_keymap_handler(handlers, keymap_paint_tint);
1764
1765 wmKeyMap *keymap_sculpt = WM_keymap_ensure(
1766 wm->defaultconf, "Grease Pencil Stroke Sculpt Mode", 0, 0);
1767 WM_event_add_keymap_handler(handlers, keymap_sculpt);
1768
1769 wmKeyMap *keymap_vertex = WM_keymap_ensure(
1770 wm->defaultconf, "Grease Pencil Stroke Vertex Mode", 0, 0);
1771 WM_event_add_keymap_handler(handlers, keymap_vertex);
1772
1773 wmKeyMap *keymap_vertex_draw = WM_keymap_ensure(
1774 wm->defaultconf, "Grease Pencil Stroke Vertex (Draw)", 0, 0);
1775 WM_event_add_keymap_handler(handlers, keymap_vertex_draw);
1776
1777 wmKeyMap *keymap_vertex_blur = WM_keymap_ensure(
1778 wm->defaultconf, "Grease Pencil Stroke Vertex (Blur)", 0, 0);
1779 WM_event_add_keymap_handler(handlers, keymap_vertex_blur);
1780
1781 wmKeyMap *keymap_vertex_average = WM_keymap_ensure(
1782 wm->defaultconf, "Grease Pencil Stroke Vertex (Average)", 0, 0);
1783 WM_event_add_keymap_handler(handlers, keymap_vertex_average);
1784
1785 wmKeyMap *keymap_vertex_smear = WM_keymap_ensure(
1786 wm->defaultconf, "Grease Pencil Stroke Vertex (Smear)", 0, 0);
1787 WM_event_add_keymap_handler(handlers, keymap_vertex_smear);
1788
1789 wmKeyMap *keymap_vertex_replace = WM_keymap_ensure(
1790 wm->defaultconf, "Grease Pencil Stroke Vertex (Replace)", 0, 0);
1791 WM_event_add_keymap_handler(handlers, keymap_vertex_replace);
1792
1793 wmKeyMap *keymap_sculpt_smooth = WM_keymap_ensure(
1794 wm->defaultconf, "Grease Pencil Stroke Sculpt (Smooth)", 0, 0);
1795 WM_event_add_keymap_handler(handlers, keymap_sculpt_smooth);
1796
1797 wmKeyMap *keymap_sculpt_thickness = WM_keymap_ensure(
1798 wm->defaultconf, "Grease Pencil Stroke Sculpt (Thickness)", 0, 0);
1799 WM_event_add_keymap_handler(handlers, keymap_sculpt_thickness);
1800
1801 wmKeyMap *keymap_sculpt_strength = WM_keymap_ensure(
1802 wm->defaultconf, "Grease Pencil Stroke Sculpt (Strength)", 0, 0);
1803 WM_event_add_keymap_handler(handlers, keymap_sculpt_strength);
1804
1805 wmKeyMap *keymap_sculpt_grab = WM_keymap_ensure(
1806 wm->defaultconf, "Grease Pencil Stroke Sculpt (Grab)", 0, 0);
1807 WM_event_add_keymap_handler(handlers, keymap_sculpt_grab);
1808
1809 wmKeyMap *keymap_sculpt_push = WM_keymap_ensure(
1810 wm->defaultconf, "Grease Pencil Stroke Sculpt (Push)", 0, 0);
1811 WM_event_add_keymap_handler(handlers, keymap_sculpt_push);
1812
1813 wmKeyMap *keymap_sculpt_twist = WM_keymap_ensure(
1814 wm->defaultconf, "Grease Pencil Stroke Sculpt (Twist)", 0, 0);
1815 WM_event_add_keymap_handler(handlers, keymap_sculpt_twist);
1816
1817 wmKeyMap *keymap_sculpt_pinch = WM_keymap_ensure(
1818 wm->defaultconf, "Grease Pencil Stroke Sculpt (Pinch)", 0, 0);
1819 WM_event_add_keymap_handler(handlers, keymap_sculpt_pinch);
1820
1821 wmKeyMap *keymap_sculpt_randomize = WM_keymap_ensure(
1822 wm->defaultconf, "Grease Pencil Stroke Sculpt (Randomize)", 0, 0);
1823 WM_event_add_keymap_handler(handlers, keymap_sculpt_randomize);
1824
1825 wmKeyMap *keymap_sculpt_clone = WM_keymap_ensure(
1826 wm->defaultconf, "Grease Pencil Stroke Sculpt (Clone)", 0, 0);
1827 WM_event_add_keymap_handler(handlers, keymap_sculpt_clone);
1828
1829 wmKeyMap *keymap_weight = WM_keymap_ensure(
1830 wm->defaultconf, "Grease Pencil Stroke Weight Mode", 0, 0);
1831 WM_event_add_keymap_handler(handlers, keymap_weight);
1832
1833 wmKeyMap *keymap_weight_draw = WM_keymap_ensure(
1834 wm->defaultconf, "Grease Pencil Stroke Weight (Draw)", 0, 0);
1835 WM_event_add_keymap_handler(handlers, keymap_weight_draw);
1836 }
1837 }
1838
ED_area_update_region_sizes(wmWindowManager * wm,wmWindow * win,ScrArea * area)1839 void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *area)
1840 {
1841 if (!(area->flag & AREA_FLAG_REGION_SIZE_UPDATE)) {
1842 return;
1843 }
1844 const bScreen *screen = WM_window_get_active_screen(win);
1845
1846 rcti window_rect;
1847 WM_window_rect_calc(win, &window_rect);
1848 area_calc_totrct(area, &window_rect);
1849
1850 /* region rect sizes */
1851 rcti rect = area->totrct;
1852 rcti overlap_rect = rect;
1853 region_rect_recursive(area, area->regionbase.first, &rect, &overlap_rect, 0);
1854
1855 /* Dynamically sized regions may have changed region sizes, so we have to force azone update. */
1856 area_azone_init(win, screen, area);
1857
1858 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1859 region_subwindow(region);
1860
1861 /* region size may have changed, init does necessary adjustments */
1862 if (region->type->init) {
1863 region->type->init(wm, region);
1864 }
1865
1866 /* Some AZones use View2D data which is only updated in region init, so call that first! */
1867 region_azones_add(screen, area, region);
1868 }
1869 ED_area_azones_update(area, &win->eventstate->x);
1870
1871 area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE;
1872 }
1873
1874 /* called in screen_refresh, or screens_init, also area size changes */
ED_area_init(wmWindowManager * wm,wmWindow * win,ScrArea * area)1875 void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area)
1876 {
1877 WorkSpace *workspace = WM_window_get_active_workspace(win);
1878 const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
1879 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
1880
1881 if (ED_area_is_global(area) && (area->global->flag & GLOBAL_AREA_IS_HIDDEN)) {
1882 return;
1883 }
1884
1885 rcti window_rect;
1886 WM_window_rect_calc(win, &window_rect);
1887
1888 /* set typedefinitions */
1889 area->type = BKE_spacetype_from_id(area->spacetype);
1890
1891 if (area->type == NULL) {
1892 area->spacetype = SPACE_VIEW3D;
1893 area->type = BKE_spacetype_from_id(area->spacetype);
1894 }
1895
1896 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1897 region->type = BKE_regiontype_from_id_or_first(area->type, region->regiontype);
1898 }
1899
1900 /* area sizes */
1901 area_calc_totrct(area, &window_rect);
1902
1903 /* region rect sizes */
1904 rcti rect = area->totrct;
1905 rcti overlap_rect = rect;
1906 region_rect_recursive(area, area->regionbase.first, &rect, &overlap_rect, 0);
1907 area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE;
1908
1909 /* default area handlers */
1910 ed_default_handlers(wm, area, NULL, &area->handlers, area->type->keymapflag);
1911 /* checks spacedata, adds own handlers */
1912 if (area->type->init) {
1913 area->type->init(wm, area);
1914 }
1915
1916 /* clear all azones, add the area triangle widgets */
1917 area_azone_init(win, screen, area);
1918
1919 /* region windows, default and own handlers */
1920 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1921 region_subwindow(region);
1922
1923 if (region->visible) {
1924 /* default region handlers */
1925 ed_default_handlers(wm, area, region, ®ion->handlers, region->type->keymapflag);
1926 /* own handlers */
1927 if (region->type->init) {
1928 region->type->init(wm, region);
1929 }
1930 }
1931 else {
1932 /* prevent uiblocks to run */
1933 UI_blocklist_free(NULL, ®ion->uiblocks);
1934 }
1935
1936 /* Some AZones use View2D data which is only updated in region init, so call that first! */
1937 region_azones_add(screen, area, region);
1938 }
1939
1940 /* Avoid re-initializing tools while resizing the window. */
1941 if ((G.moving & G_TRANSFORM_WM) == 0) {
1942 if ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) {
1943 WM_toolsystem_refresh_screen_area(workspace, view_layer, area);
1944 area->flag |= AREA_FLAG_ACTIVE_TOOL_UPDATE;
1945 }
1946 else {
1947 area->runtime.tool = NULL;
1948 area->runtime.is_tool_set = true;
1949 }
1950 }
1951 }
1952
region_update_rect(ARegion * region)1953 static void region_update_rect(ARegion *region)
1954 {
1955 region->winx = BLI_rcti_size_x(®ion->winrct) + 1;
1956 region->winy = BLI_rcti_size_y(®ion->winrct) + 1;
1957
1958 /* v2d mask is used to subtract scrollbars from a 2d view. Needs initialize here. */
1959 BLI_rcti_init(®ion->v2d.mask, 0, region->winx - 1, 0, region->winy - 1);
1960 }
1961
1962 /**
1963 * Call to move a popup window (keep OpenGL context free!)
1964 */
ED_region_update_rect(ARegion * region)1965 void ED_region_update_rect(ARegion *region)
1966 {
1967 region_update_rect(region);
1968 }
1969
1970 /* externally called for floating regions like menus */
ED_region_floating_init(ARegion * region)1971 void ED_region_floating_init(ARegion *region)
1972 {
1973 BLI_assert(region->alignment == RGN_ALIGN_FLOAT);
1974
1975 /* refresh can be called before window opened */
1976 region_subwindow(region);
1977
1978 region_update_rect(region);
1979 }
1980
ED_region_cursor_set(wmWindow * win,ScrArea * area,ARegion * region)1981 void ED_region_cursor_set(wmWindow *win, ScrArea *area, ARegion *region)
1982 {
1983 if (region != NULL) {
1984 if ((region->gizmo_map != NULL) && WM_gizmomap_cursor_set(region->gizmo_map, win)) {
1985 return;
1986 }
1987 if (area && region->type && region->type->cursor) {
1988 region->type->cursor(win, area, region);
1989 return;
1990 }
1991 }
1992
1993 if (WM_cursor_set_from_tool(win, area, region)) {
1994 return;
1995 }
1996
1997 WM_cursor_set(win, WM_CURSOR_DEFAULT);
1998 }
1999
2000 /* for use after changing visibility of regions */
ED_region_visibility_change_update(bContext * C,ScrArea * area,ARegion * region)2001 void ED_region_visibility_change_update(bContext *C, ScrArea *area, ARegion *region)
2002 {
2003 if (region->flag & RGN_FLAG_HIDDEN) {
2004 WM_event_remove_handlers(C, ®ion->handlers);
2005 }
2006
2007 ED_area_init(CTX_wm_manager(C), CTX_wm_window(C), area);
2008 ED_area_tag_redraw(area);
2009 }
2010
2011 /* for quick toggle, can skip fades */
region_toggle_hidden(bContext * C,ARegion * region,const bool do_fade)2012 void region_toggle_hidden(bContext *C, ARegion *region, const bool do_fade)
2013 {
2014 ScrArea *area = CTX_wm_area(C);
2015
2016 region->flag ^= RGN_FLAG_HIDDEN;
2017
2018 if (do_fade && region->overlap) {
2019 /* starts a timer, and in end calls the stuff below itself (region_sblend_invoke()) */
2020 ED_region_visibility_change_update_animated(C, area, region);
2021 }
2022 else {
2023 ED_region_visibility_change_update(C, area, region);
2024 }
2025 }
2026
2027 /* exported to all editors, uses fading default */
ED_region_toggle_hidden(bContext * C,ARegion * region)2028 void ED_region_toggle_hidden(bContext *C, ARegion *region)
2029 {
2030 region_toggle_hidden(C, region, true);
2031 }
2032
2033 /**
2034 * we swap spaces for fullscreen to keep all allocated data area vertices were set
2035 */
ED_area_data_copy(ScrArea * area_dst,ScrArea * area_src,const bool do_free)2036 void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
2037 {
2038 const char spacetype = area_dst->spacetype;
2039 const short flag_copy = HEADER_NO_PULLDOWN;
2040
2041 area_dst->spacetype = area_src->spacetype;
2042 area_dst->type = area_src->type;
2043
2044 area_dst->flag = (area_dst->flag & ~flag_copy) | (area_src->flag & flag_copy);
2045
2046 /* area */
2047 if (do_free) {
2048 BKE_spacedata_freelist(&area_dst->spacedata);
2049 }
2050 BKE_spacedata_copylist(&area_dst->spacedata, &area_src->spacedata);
2051
2052 /* Note; SPACE_EMPTY is possible on new screens */
2053
2054 /* regions */
2055 if (do_free) {
2056 SpaceType *st = BKE_spacetype_from_id(spacetype);
2057 LISTBASE_FOREACH (ARegion *, region, &area_dst->regionbase) {
2058 BKE_area_region_free(st, region);
2059 }
2060 BLI_freelistN(&area_dst->regionbase);
2061 }
2062 SpaceType *st = BKE_spacetype_from_id(area_src->spacetype);
2063 LISTBASE_FOREACH (ARegion *, region, &area_src->regionbase) {
2064 ARegion *newar = BKE_area_region_copy(st, region);
2065 BLI_addtail(&area_dst->regionbase, newar);
2066 }
2067 }
2068
ED_area_data_swap(ScrArea * area_dst,ScrArea * area_src)2069 void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src)
2070 {
2071 SWAP(char, area_dst->spacetype, area_src->spacetype);
2072 SWAP(SpaceType *, area_dst->type, area_src->type);
2073
2074 SWAP(ListBase, area_dst->spacedata, area_src->spacedata);
2075 SWAP(ListBase, area_dst->regionbase, area_src->regionbase);
2076 }
2077
2078 /* -------------------------------------------------------------------- */
2079 /** \name Region Alignment Syncing for Space Switching
2080 * \{ */
2081
2082 /**
2083 * Store the alignment & other info per region type
2084 * (use as a region-type aligned array).
2085 *
2086 * \note Currently this is only done for headers,
2087 * we might want to do this with the tool-bar in the future too.
2088 */
2089 struct RegionTypeAlignInfo {
2090 struct {
2091 /**
2092 * Values match #ARegion.alignment without flags (see #RGN_ALIGN_ENUM_FROM_MASK).
2093 * store all so we can sync alignment without adding extra checks.
2094 */
2095 short alignment;
2096 /**
2097 * Needed for detecting which header displays the space-type switcher.
2098 */
2099 bool hidden;
2100 } by_type[RGN_TYPE_LEN];
2101 };
2102
region_align_info_from_area(ScrArea * area,struct RegionTypeAlignInfo * r_align_info)2103 static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInfo *r_align_info)
2104 {
2105 for (int index = 0; index < RGN_TYPE_LEN; index++) {
2106 r_align_info->by_type[index].alignment = -1;
2107 /* Default to true, when it doesn't exist - it's effectively hidden. */
2108 r_align_info->by_type[index].hidden = true;
2109 }
2110
2111 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2112 const int index = region->regiontype;
2113 if ((uint)index < RGN_TYPE_LEN) {
2114 r_align_info->by_type[index].alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
2115 r_align_info->by_type[index].hidden = (region->flag & RGN_FLAG_HIDDEN) != 0;
2116 }
2117 }
2118 }
2119
2120 /**
2121 * Keeping alignment between headers keep the space-type selector button in the same place.
2122 * This is complicated by the editor-type selector being placed on the header
2123 * closest to the screen edge which changes based on hidden state.
2124 *
2125 * The tool-header is used when visible, otherwise the header is used.
2126 */
region_alignment_from_header_and_tool_header_state(const struct RegionTypeAlignInfo * region_align_info,const short fallback)2127 static short region_alignment_from_header_and_tool_header_state(
2128 const struct RegionTypeAlignInfo *region_align_info, const short fallback)
2129 {
2130 const short header_alignment = region_align_info->by_type[RGN_TYPE_HEADER].alignment;
2131 const short tool_header_alignment = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].alignment;
2132
2133 const bool header_hidden = region_align_info->by_type[RGN_TYPE_HEADER].hidden;
2134 const bool tool_header_hidden = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].hidden;
2135
2136 if ((tool_header_alignment != -1) &&
2137 /* If tool-header is hidden, use header alignment. */
2138 ((tool_header_hidden == false) ||
2139 /* Don't prioritize the tool-header if both are hidden (behave as if both are visible).
2140 * Without this, switching to a space with headers hidden will flip the alignment
2141 * upon switching to a space with visible headers. */
2142 (header_hidden && tool_header_hidden))) {
2143 return tool_header_alignment;
2144 }
2145 if (header_alignment != -1) {
2146 return header_alignment;
2147 }
2148 return fallback;
2149 }
2150
2151 /**
2152 * Notes on header alignment syncing.
2153 *
2154 * This is as involved as it is because:
2155 *
2156 * - There are currently 3 kinds of headers.
2157 * - All headers can independently visible & flipped to another side
2158 * (except for the tool-header that depends on the header visibility).
2159 * - We don't want the space-switching button to flip when switching spaces.
2160 * From the user perspective it feels like a bug to move the button you click on
2161 * to the opposite side of the area.
2162 * - The space-switcher may be on either the header or the tool-header
2163 * depending on the tool-header visibility.
2164 *
2165 * How this works:
2166 *
2167 * - When headers match on both spaces, we copy the alignment
2168 * from the previous regions to the next regions when syncing.
2169 * - Otherwise detect the _primary_ header (the one that shows the space type)
2170 * and use this to set alignment for the headers in the destination area.
2171 * - Header & tool-header/footer may be on opposite sides, this is preserved when syncing.
2172 */
region_align_info_to_area_for_headers(const struct RegionTypeAlignInfo * region_align_info_src,const struct RegionTypeAlignInfo * region_align_info_dst,ARegion * region_by_type[RGN_TYPE_LEN])2173 static void region_align_info_to_area_for_headers(
2174 const struct RegionTypeAlignInfo *region_align_info_src,
2175 const struct RegionTypeAlignInfo *region_align_info_dst,
2176 ARegion *region_by_type[RGN_TYPE_LEN])
2177 {
2178 /* Abbreviate access. */
2179 const short header_alignment_src = region_align_info_src->by_type[RGN_TYPE_HEADER].alignment;
2180 const short tool_header_alignment_src =
2181 region_align_info_src->by_type[RGN_TYPE_TOOL_HEADER].alignment;
2182
2183 const bool tool_header_hidden_src = region_align_info_src->by_type[RGN_TYPE_TOOL_HEADER].hidden;
2184
2185 const short primary_header_alignment_src = region_alignment_from_header_and_tool_header_state(
2186 region_align_info_src, -1);
2187
2188 /* Neither alignments are usable, don't sync. */
2189 if (primary_header_alignment_src == -1) {
2190 return;
2191 }
2192
2193 const short header_alignment_dst = region_align_info_dst->by_type[RGN_TYPE_HEADER].alignment;
2194 const short tool_header_alignment_dst =
2195 region_align_info_dst->by_type[RGN_TYPE_TOOL_HEADER].alignment;
2196 const short footer_alignment_dst = region_align_info_dst->by_type[RGN_TYPE_FOOTER].alignment;
2197
2198 const bool tool_header_hidden_dst = region_align_info_dst->by_type[RGN_TYPE_TOOL_HEADER].hidden;
2199
2200 /* New synchronized alignments to set (or ignore when left as -1). */
2201 short header_alignment_sync = -1;
2202 short tool_header_alignment_sync = -1;
2203 short footer_alignment_sync = -1;
2204
2205 /* Both source/destination areas have same region configurations regarding headers.
2206 * Simply copy the values. */
2207 if (((header_alignment_src != -1) == (header_alignment_dst != -1)) &&
2208 ((tool_header_alignment_src != -1) == (tool_header_alignment_dst != -1)) &&
2209 (tool_header_hidden_src == tool_header_hidden_dst)) {
2210 if (header_alignment_dst != -1) {
2211 header_alignment_sync = header_alignment_src;
2212 }
2213 if (tool_header_alignment_dst != -1) {
2214 tool_header_alignment_sync = tool_header_alignment_src;
2215 }
2216 }
2217 else {
2218 /* Not an exact match, check the space selector isn't moving. */
2219 const short primary_header_alignment_dst = region_alignment_from_header_and_tool_header_state(
2220 region_align_info_dst, -1);
2221
2222 if (primary_header_alignment_src != primary_header_alignment_dst) {
2223 if ((header_alignment_dst != -1) && (tool_header_alignment_dst != -1)) {
2224 if (header_alignment_dst == tool_header_alignment_dst) {
2225 /* Apply to both. */
2226 tool_header_alignment_sync = primary_header_alignment_src;
2227 header_alignment_sync = primary_header_alignment_src;
2228 }
2229 else {
2230 /* Keep on opposite sides. */
2231 tool_header_alignment_sync = primary_header_alignment_src;
2232 header_alignment_sync = (tool_header_alignment_sync == RGN_ALIGN_BOTTOM) ?
2233 RGN_ALIGN_TOP :
2234 RGN_ALIGN_BOTTOM;
2235 }
2236 }
2237 else {
2238 /* Apply what we can to regions that exist. */
2239 if (header_alignment_dst != -1) {
2240 header_alignment_sync = primary_header_alignment_src;
2241 }
2242 if (tool_header_alignment_dst != -1) {
2243 tool_header_alignment_sync = primary_header_alignment_src;
2244 }
2245 }
2246 }
2247 }
2248
2249 if (footer_alignment_dst != -1) {
2250 if ((header_alignment_dst != -1) && (header_alignment_dst == footer_alignment_dst)) {
2251 /* Apply to both. */
2252 footer_alignment_sync = primary_header_alignment_src;
2253 }
2254 else {
2255 /* Keep on opposite sides. */
2256 footer_alignment_sync = (primary_header_alignment_src == RGN_ALIGN_BOTTOM) ?
2257 RGN_ALIGN_TOP :
2258 RGN_ALIGN_BOTTOM;
2259 }
2260 }
2261
2262 /* Finally apply synchronized flags. */
2263 if (header_alignment_sync != -1) {
2264 ARegion *region = region_by_type[RGN_TYPE_HEADER];
2265 if (region != NULL) {
2266 region->alignment = RGN_ALIGN_ENUM_FROM_MASK(header_alignment_sync) |
2267 RGN_ALIGN_FLAG_FROM_MASK(region->alignment);
2268 }
2269 }
2270
2271 if (tool_header_alignment_sync != -1) {
2272 ARegion *region = region_by_type[RGN_TYPE_TOOL_HEADER];
2273 if (region != NULL) {
2274 region->alignment = RGN_ALIGN_ENUM_FROM_MASK(tool_header_alignment_sync) |
2275 RGN_ALIGN_FLAG_FROM_MASK(region->alignment);
2276 }
2277 }
2278
2279 if (footer_alignment_sync != -1) {
2280 ARegion *region = region_by_type[RGN_TYPE_FOOTER];
2281 if (region != NULL) {
2282 region->alignment = RGN_ALIGN_ENUM_FROM_MASK(footer_alignment_sync) |
2283 RGN_ALIGN_FLAG_FROM_MASK(region->alignment);
2284 }
2285 }
2286 }
2287
region_align_info_to_area(ScrArea * area,const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_LEN])2288 static void region_align_info_to_area(
2289 ScrArea *area, const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_LEN])
2290 {
2291 ARegion *region_by_type[RGN_TYPE_LEN] = {NULL};
2292 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2293 const int index = region->regiontype;
2294 if ((uint)index < RGN_TYPE_LEN) {
2295 region_by_type[index] = region;
2296 }
2297 }
2298
2299 struct RegionTypeAlignInfo region_align_info_dst;
2300 region_align_info_from_area(area, ®ion_align_info_dst);
2301
2302 if ((region_by_type[RGN_TYPE_HEADER] != NULL) ||
2303 (region_by_type[RGN_TYPE_TOOL_HEADER] != NULL)) {
2304 region_align_info_to_area_for_headers(
2305 region_align_info_src, ®ion_align_info_dst, region_by_type);
2306 }
2307
2308 /* Note that we could support other region types. */
2309 }
2310
2311 /** \} */
2312
2313 /* *********** Space switching code *********** */
2314
ED_area_swapspace(bContext * C,ScrArea * sa1,ScrArea * sa2)2315 void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2)
2316 {
2317 ScrArea *tmp = MEM_callocN(sizeof(ScrArea), "addscrarea");
2318 wmWindow *win = CTX_wm_window(C);
2319
2320 ED_area_exit(C, sa1);
2321 ED_area_exit(C, sa2);
2322
2323 ED_area_data_copy(tmp, sa1, false);
2324 ED_area_data_copy(sa1, sa2, true);
2325 ED_area_data_copy(sa2, tmp, true);
2326 ED_area_init(CTX_wm_manager(C), win, sa1);
2327 ED_area_init(CTX_wm_manager(C), win, sa2);
2328
2329 BKE_screen_area_free(tmp);
2330 MEM_freeN(tmp);
2331
2332 /* tell WM to refresh, cursor types etc */
2333 WM_event_add_mousemove(win);
2334
2335 ED_area_tag_redraw(sa1);
2336 ED_area_tag_refresh(sa1);
2337 ED_area_tag_redraw(sa2);
2338 ED_area_tag_refresh(sa2);
2339 }
2340
2341 /**
2342 * \param skip_region_exit: Skip calling area exit callback. Set for opening temp spaces.
2343 */
ED_area_newspace(bContext * C,ScrArea * area,int type,const bool skip_region_exit)2344 void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_region_exit)
2345 {
2346 wmWindow *win = CTX_wm_window(C);
2347
2348 if (area->spacetype != type) {
2349 SpaceLink *slold = area->spacedata.first;
2350 /* store area->type->exit callback */
2351 void *area_exit = area->type ? area->type->exit : NULL;
2352 /* When the user switches between space-types from the type-selector,
2353 * changing the header-type is jarring (especially when using Ctrl-MouseWheel).
2354 *
2355 * However, add-on install for example, forces the header to the top which shouldn't
2356 * be applied back to the previous space type when closing - see: T57724
2357 *
2358 * Newly created windows wont have any space data, use the alignment
2359 * the space type defaults to in this case instead
2360 * (needed for preferences to have space-type on bottom).
2361 */
2362
2363 bool sync_header_alignment = false;
2364 struct RegionTypeAlignInfo region_align_info[RGN_TYPE_LEN];
2365 if ((slold != NULL) && (slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
2366 region_align_info_from_area(area, region_align_info);
2367 sync_header_alignment = true;
2368 }
2369
2370 /* in some cases (opening temp space) we don't want to
2371 * call area exit callback, so we temporarily unset it */
2372 if (skip_region_exit && area->type) {
2373 area->type->exit = NULL;
2374 }
2375
2376 ED_area_exit(C, area);
2377
2378 /* restore old area exit callback */
2379 if (skip_region_exit && area->type) {
2380 area->type->exit = area_exit;
2381 }
2382
2383 SpaceType *st = BKE_spacetype_from_id(type);
2384
2385 area->spacetype = type;
2386 area->type = st;
2387
2388 /* If st->create may be called, don't use context until then. The
2389 * area->type->context() callback has changed but data may be invalid
2390 * (e.g. with properties editor) until space-data is properly created */
2391
2392 /* check previously stored space */
2393 SpaceLink *sl = NULL;
2394 LISTBASE_FOREACH (SpaceLink *, sl_iter, &area->spacedata) {
2395 if (sl_iter->spacetype == type) {
2396 sl = sl_iter;
2397 break;
2398 }
2399 }
2400
2401 /* old spacedata... happened during work on 2.50, remove */
2402 if (sl && BLI_listbase_is_empty(&sl->regionbase)) {
2403 st->free(sl);
2404 BLI_freelinkN(&area->spacedata, sl);
2405 if (slold == sl) {
2406 slold = NULL;
2407 }
2408 sl = NULL;
2409 }
2410
2411 if (sl) {
2412 /* swap regions */
2413 slold->regionbase = area->regionbase;
2414 area->regionbase = sl->regionbase;
2415 BLI_listbase_clear(&sl->regionbase);
2416 /* SPACE_FLAG_TYPE_WAS_ACTIVE is only used to go back to a previously active space that is
2417 * overlapped by temporary ones. It's now properly activated, so the flag should be cleared
2418 * at this point. */
2419 sl->link_flag &= ~SPACE_FLAG_TYPE_WAS_ACTIVE;
2420
2421 /* put in front of list */
2422 BLI_remlink(&area->spacedata, sl);
2423 BLI_addhead(&area->spacedata, sl);
2424 }
2425 else {
2426 /* new space */
2427 if (st) {
2428 /* Don't get scene from context here which may depend on space-data. */
2429 Scene *scene = WM_window_get_active_scene(win);
2430 sl = st->create(area, scene);
2431 BLI_addhead(&area->spacedata, sl);
2432
2433 /* swap regions */
2434 if (slold) {
2435 slold->regionbase = area->regionbase;
2436 }
2437 area->regionbase = sl->regionbase;
2438 BLI_listbase_clear(&sl->regionbase);
2439 }
2440 }
2441
2442 /* Sync header alignment. */
2443 if (sync_header_alignment) {
2444 region_align_info_to_area(area, region_align_info);
2445 }
2446
2447 ED_area_init(CTX_wm_manager(C), win, area);
2448
2449 /* tell WM to refresh, cursor types etc */
2450 WM_event_add_mousemove(win);
2451
2452 /* send space change notifier */
2453 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CHANGED, area);
2454
2455 ED_area_tag_refresh(area);
2456 }
2457
2458 /* also redraw when re-used */
2459 ED_area_tag_redraw(area);
2460 }
2461
area_get_prevspace(ScrArea * area)2462 static SpaceLink *area_get_prevspace(ScrArea *area)
2463 {
2464 SpaceLink *sl = area->spacedata.first;
2465
2466 /* First toggle to the next temporary space in the list. */
2467 for (SpaceLink *sl_iter = sl->next; sl_iter; sl_iter = sl_iter->next) {
2468 if (sl_iter->link_flag & SPACE_FLAG_TYPE_TEMPORARY) {
2469 return sl_iter;
2470 }
2471 }
2472
2473 /* No temporary space, find the item marked as last active. */
2474 for (SpaceLink *sl_iter = sl->next; sl_iter; sl_iter = sl_iter->next) {
2475 if (sl_iter->link_flag & SPACE_FLAG_TYPE_WAS_ACTIVE) {
2476 return sl_iter;
2477 }
2478 }
2479
2480 /* If neither is found, we can just return to the regular previous one. */
2481 return sl->next;
2482 }
2483
ED_area_prevspace(bContext * C,ScrArea * area)2484 void ED_area_prevspace(bContext *C, ScrArea *area)
2485 {
2486 SpaceLink *sl = area->spacedata.first;
2487 SpaceLink *prevspace = sl ? area_get_prevspace(area) : NULL;
2488
2489 if (prevspace) {
2490 ED_area_newspace(C, area, prevspace->spacetype, false);
2491 /* We've exited the space, so it can't be considered temporary anymore. */
2492 sl->link_flag &= ~SPACE_FLAG_TYPE_TEMPORARY;
2493 }
2494 else {
2495 /* no change */
2496 return;
2497 }
2498 /* If this is a stacked fullscreen, changing to previous area exits it (meaning we're still in a
2499 * fullscreen, but not in a stacked one). */
2500 area->flag &= ~AREA_FLAG_STACKED_FULLSCREEN;
2501
2502 ED_area_tag_redraw(area);
2503
2504 /* send space change notifier */
2505 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CHANGED, area);
2506 }
2507
2508 /* returns offset for next button in header */
ED_area_header_switchbutton(const bContext * C,uiBlock * block,int yco)2509 int ED_area_header_switchbutton(const bContext *C, uiBlock *block, int yco)
2510 {
2511 ScrArea *area = CTX_wm_area(C);
2512 bScreen *screen = CTX_wm_screen(C);
2513 PointerRNA areaptr;
2514 int xco = 0.4 * U.widget_unit;
2515
2516 RNA_pointer_create(&(screen->id), &RNA_Area, area, &areaptr);
2517
2518 uiDefButR(block,
2519 UI_BTYPE_MENU,
2520 0,
2521 "",
2522 xco,
2523 yco,
2524 1.6 * U.widget_unit,
2525 U.widget_unit,
2526 &areaptr,
2527 "ui_type",
2528 0,
2529 0.0f,
2530 0.0f,
2531 0.0f,
2532 0.0f,
2533 "");
2534
2535 return xco + 1.7 * U.widget_unit;
2536 }
2537
2538 /************************ standard UI regions ************************/
2539
region_background_color_id(const bContext * C,const ARegion * region)2540 static ThemeColorID region_background_color_id(const bContext *C, const ARegion *region)
2541 {
2542 ScrArea *area = CTX_wm_area(C);
2543
2544 switch (region->regiontype) {
2545 case RGN_TYPE_HEADER:
2546 case RGN_TYPE_TOOL_HEADER:
2547 if (ED_screen_area_active(C) || ED_area_is_global(area)) {
2548 return TH_HEADER;
2549 }
2550 else {
2551 return TH_HEADERDESEL;
2552 }
2553 case RGN_TYPE_PREVIEW:
2554 return TH_PREVIEW_BACK;
2555 default:
2556 return TH_BACK;
2557 }
2558 }
2559
region_clear_color(const bContext * C,const ARegion * region,ThemeColorID colorid)2560 static void region_clear_color(const bContext *C, const ARegion *region, ThemeColorID colorid)
2561 {
2562 if (region->alignment == RGN_ALIGN_FLOAT) {
2563 /* handle our own drawing. */
2564 }
2565 else if (region->overlap) {
2566 /* view should be in pixelspace */
2567 UI_view2d_view_restore(C);
2568
2569 float back[4];
2570 UI_GetThemeColor4fv(colorid, back);
2571 GPU_clear_color(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]);
2572 }
2573 else {
2574 UI_ThemeClearColor(colorid);
2575 }
2576 }
2577
streq_array_any(const char * s,const char * arr[])2578 BLI_INLINE bool streq_array_any(const char *s, const char *arr[])
2579 {
2580 for (uint i = 0; arr[i]; i++) {
2581 if (STREQ(arr[i], s)) {
2582 return true;
2583 }
2584 }
2585 return false;
2586 }
2587
2588 /**
2589 * Builds the panel layout for the input \a panel or type \a pt.
2590 *
2591 * \param panel: The panel to draw. Can be null,
2592 * in which case a panel with the type of \a pt will be created.
2593 * \param unique_panel_str: A unique identifier for the name of the \a uiBlock associated with the
2594 * panel. Used when the panel is an instanced panel so a unique identifier is needed to find the
2595 * correct old \a uiBlock, and NULL otherwise.
2596 */
ed_panel_draw(const bContext * C,ARegion * region,ListBase * lb,PanelType * pt,Panel * panel,int w,int em,char * unique_panel_str,const char * search_filter)2597 static void ed_panel_draw(const bContext *C,
2598 ARegion *region,
2599 ListBase *lb,
2600 PanelType *pt,
2601 Panel *panel,
2602 int w,
2603 int em,
2604 char *unique_panel_str,
2605 const char *search_filter)
2606 {
2607 const uiStyle *style = UI_style_get_dpi();
2608
2609 /* Draw panel. */
2610
2611 char block_name[BKE_ST_MAXNAME + INSTANCED_PANEL_UNIQUE_STR_LEN];
2612 strncpy(block_name, pt->idname, BKE_ST_MAXNAME);
2613 if (unique_panel_str != NULL) {
2614 /* Instanced panels should have already been added at this point. */
2615 strncat(block_name, unique_panel_str, INSTANCED_PANEL_UNIQUE_STR_LEN);
2616 }
2617 uiBlock *block = UI_block_begin(C, region, block_name, UI_EMBOSS);
2618
2619 bool open;
2620 panel = UI_panel_begin(region, lb, block, pt, panel, &open);
2621
2622 const bool search_filter_active = search_filter != NULL && search_filter[0] != '\0';
2623
2624 /* bad fixed values */
2625 int xco, yco, h = 0;
2626 int headerend = w - UI_UNIT_X;
2627
2628 UI_panel_header_buttons_begin(panel);
2629 if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER)) {
2630 /* for preset menu */
2631 panel->layout = UI_block_layout(block,
2632 UI_LAYOUT_HORIZONTAL,
2633 UI_LAYOUT_HEADER,
2634 0,
2635 (UI_UNIT_Y * 1.1f) + style->panelspace,
2636 UI_UNIT_Y,
2637 1,
2638 0,
2639 style);
2640
2641 pt->draw_header_preset(C, panel);
2642
2643 UI_block_apply_search_filter(block, search_filter);
2644 UI_block_layout_resolve(block, &xco, &yco);
2645 UI_block_translate(block, headerend - xco, 0);
2646 panel->layout = NULL;
2647 }
2648
2649 if (pt->draw_header && !(pt->flag & PNL_NO_HEADER)) {
2650 int labelx, labely;
2651 UI_panel_label_offset(block, &labelx, &labely);
2652
2653 /* Unusual case: Use expanding layout (buttons stretch to available width). */
2654 if (pt->flag & PNL_LAYOUT_HEADER_EXPAND) {
2655 uiLayout *layout = UI_block_layout(block,
2656 UI_LAYOUT_VERTICAL,
2657 UI_LAYOUT_PANEL,
2658 labelx,
2659 labely,
2660 headerend - 2 * style->panelspace,
2661 1,
2662 0,
2663 style);
2664 panel->layout = uiLayoutRow(layout, false);
2665 }
2666 /* Regular case: Normal panel with fixed size buttons. */
2667 else {
2668 panel->layout = UI_block_layout(
2669 block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, labelx, labely, UI_UNIT_Y, 1, 0, style);
2670 }
2671
2672 pt->draw_header(C, panel);
2673
2674 UI_block_apply_search_filter(block, search_filter);
2675 UI_block_layout_resolve(block, &xco, &yco);
2676 panel->labelofs = xco - labelx;
2677 panel->layout = NULL;
2678 }
2679 else {
2680 panel->labelofs = 0;
2681 }
2682 UI_panel_header_buttons_end(panel);
2683
2684 if (open || search_filter_active) {
2685 short panelContext;
2686
2687 /* panel context can either be toolbar region or normal panels region */
2688 if (pt->flag & PNL_LAYOUT_VERT_BAR) {
2689 panelContext = UI_LAYOUT_VERT_BAR;
2690 }
2691 else if (region->regiontype == RGN_TYPE_TOOLS) {
2692 panelContext = UI_LAYOUT_TOOLBAR;
2693 }
2694 else {
2695 panelContext = UI_LAYOUT_PANEL;
2696 }
2697
2698 panel->layout = UI_block_layout(block,
2699 UI_LAYOUT_VERTICAL,
2700 panelContext,
2701 (pt->flag & PNL_LAYOUT_VERT_BAR) ? 0 : style->panelspace,
2702 0,
2703 (pt->flag & PNL_LAYOUT_VERT_BAR) ? 0 :
2704 w - 2 * style->panelspace,
2705 em,
2706 0,
2707 style);
2708
2709 pt->draw(C, panel);
2710
2711 UI_block_apply_search_filter(block, search_filter);
2712 UI_block_layout_resolve(block, &xco, &yco);
2713 panel->layout = NULL;
2714
2715 if (yco != 0) {
2716 h = -yco + 2 * style->panelspace;
2717 }
2718 }
2719
2720 UI_block_end(C, block);
2721
2722 /* Draw child panels. */
2723 if (open || search_filter_active) {
2724 LISTBASE_FOREACH (LinkData *, link, &pt->children) {
2725 PanelType *child_pt = link->data;
2726 Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt);
2727
2728 if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) {
2729 ed_panel_draw(C,
2730 region,
2731 &panel->children,
2732 child_pt,
2733 child_panel,
2734 w,
2735 em,
2736 unique_panel_str,
2737 search_filter);
2738 }
2739 }
2740 }
2741
2742 UI_panel_end(panel, w, h);
2743 }
2744
2745 /**
2746 * Check whether a panel should be added to the region's panel layout.
2747 */
panel_add_check(const bContext * C,const WorkSpace * workspace,const char * contexts[],const char * category_override,PanelType * panel_type)2748 static bool panel_add_check(const bContext *C,
2749 const WorkSpace *workspace,
2750 const char *contexts[],
2751 const char *category_override,
2752 PanelType *panel_type)
2753 {
2754 /* Only add top level panels. */
2755 if (panel_type->parent) {
2756 return false;
2757 }
2758 /* Check the category override first. */
2759 if (category_override) {
2760 if (!STREQ(panel_type->category, category_override)) {
2761 return false;
2762 }
2763 }
2764
2765 /* Verify context. */
2766 if (contexts != NULL && panel_type->context[0]) {
2767 if (!streq_array_any(panel_type->context, contexts)) {
2768 return false;
2769 }
2770 }
2771
2772 /* If we're tagged, only use compatible. */
2773 if (panel_type->owner_id[0]) {
2774 if (!BKE_workspace_owner_id_check(workspace, panel_type->owner_id)) {
2775 return false;
2776 }
2777 }
2778
2779 if (LIKELY(panel_type->draw)) {
2780 if (panel_type->poll && !panel_type->poll(C, panel_type)) {
2781 return false;
2782 }
2783 }
2784
2785 return true;
2786 }
2787
region_uses_category_tabs(const ScrArea * area,const ARegion * region)2788 static bool region_uses_category_tabs(const ScrArea *area, const ARegion *region)
2789 {
2790 /* XXX, should use some better check? */
2791 /* For now also has hardcoded check for clip editor until it supports actual toolbar. */
2792 return ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) ||
2793 (region->regiontype == RGN_TYPE_TOOLS && area->spacetype == SPACE_CLIP);
2794 }
2795
region_panels_collect_categories(ARegion * region,LinkNode * panel_types_stack,bool * use_category_tabs)2796 static const char *region_panels_collect_categories(ARegion *region,
2797 LinkNode *panel_types_stack,
2798 bool *use_category_tabs)
2799 {
2800 UI_panel_category_clear_all(region);
2801
2802 /* gather unique categories */
2803 for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) {
2804 PanelType *pt = pt_link->link;
2805 if (pt->category[0]) {
2806 if (!UI_panel_category_find(region, pt->category)) {
2807 UI_panel_category_add(region, pt->category);
2808 }
2809 }
2810 }
2811
2812 if (UI_panel_category_is_visible(region)) {
2813 return UI_panel_category_active_get(region, true);
2814 }
2815
2816 *use_category_tabs = false;
2817 return NULL;
2818 }
2819
2820 /**
2821 * \param contexts: A NULL terminated array of context strings to match against.
2822 * Matching against any of these strings will draw the panel.
2823 * Can be NULL to skip context checks.
2824 */
ED_region_panels_layout_ex(const bContext * C,ARegion * region,ListBase * paneltypes,const char * contexts[],const char * category_override)2825 void ED_region_panels_layout_ex(const bContext *C,
2826 ARegion *region,
2827 ListBase *paneltypes,
2828 const char *contexts[],
2829 const char *category_override)
2830 {
2831 /* collect panels to draw */
2832 WorkSpace *workspace = CTX_wm_workspace(C);
2833 LinkNode *panel_types_stack = NULL;
2834 LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) {
2835 if (panel_add_check(C, workspace, contexts, category_override, pt)) {
2836 BLI_linklist_prepend_alloca(&panel_types_stack, pt);
2837 }
2838 }
2839
2840 region->runtime.category = NULL;
2841
2842 ScrArea *area = CTX_wm_area(C);
2843 View2D *v2d = ®ion->v2d;
2844
2845 bool use_category_tabs = (category_override == NULL) && region_uses_category_tabs(area, region);
2846 /* offset panels for small vertical tab area */
2847 const char *category = NULL;
2848 const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH;
2849 int margin_x = 0;
2850 const bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE;
2851 bool update_tot_size = true;
2852
2853 /* only allow scrolling in vertical direction */
2854 v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y;
2855 v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X);
2856 v2d->scroll &= ~V2D_SCROLL_BOTTOM;
2857 v2d->scroll |= V2D_SCROLL_RIGHT;
2858
2859 /* collect categories */
2860 if (use_category_tabs) {
2861 category = region_panels_collect_categories(region, panel_types_stack, &use_category_tabs);
2862 }
2863 if (use_category_tabs) {
2864 margin_x = category_tabs_width;
2865 }
2866
2867 const int w = BLI_rctf_size_x(&v2d->cur) - margin_x;
2868 /* Works out to 10 * UI_UNIT_X or 20 * UI_UNIT_X. */
2869 const int em = (region->type->prefsizex) ? 10 : 20;
2870
2871 const int w_box_panel = w - UI_PANEL_BOX_STYLE_MARGIN * 2.0f;
2872
2873 /* create panels */
2874 UI_panels_begin(C, region);
2875
2876 /* Get search string for property search. */
2877 const char *search_filter = ED_area_region_search_filter_get(area, region);
2878
2879 /* set view2d view matrix - UI_block_begin() stores it */
2880 UI_view2d_view_ortho(v2d);
2881
2882 bool has_instanced_panel = false;
2883 for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) {
2884 PanelType *pt = pt_link->link;
2885
2886 if (pt->flag & PNL_INSTANCED) {
2887 has_instanced_panel = true;
2888 continue;
2889 }
2890 Panel *panel = UI_panel_find_by_type(®ion->panels, pt);
2891
2892 if (use_category_tabs && pt->category[0] && !STREQ(category, pt->category)) {
2893 if ((panel == NULL) || ((panel->flag & PNL_PIN) == 0)) {
2894 continue;
2895 }
2896 }
2897
2898 if (panel && UI_panel_is_dragging(panel)) {
2899 /* Prevent View2d.tot rectangle size changes while dragging panels. */
2900 update_tot_size = false;
2901 }
2902
2903 ed_panel_draw(C,
2904 region,
2905 ®ion->panels,
2906 pt,
2907 panel,
2908 (pt->flag & PNL_DRAW_BOX) ? w_box_panel : w,
2909 em,
2910 NULL,
2911 search_filter);
2912 }
2913
2914 /* Draw "polyinstantaited" panels that don't have a 1 to 1 correspondence with their types. */
2915 if (has_instanced_panel) {
2916 LISTBASE_FOREACH (Panel *, panel, ®ion->panels) {
2917 if (panel->type == NULL) {
2918 continue; /* Some panels don't have a type. */
2919 }
2920 if (!(panel->type->flag & PNL_INSTANCED)) {
2921 continue;
2922 }
2923 if (use_category_tabs && panel->type->category[0] &&
2924 !STREQ(category, panel->type->category)) {
2925 continue;
2926 }
2927
2928 if (panel && UI_panel_is_dragging(panel)) {
2929 /* Prevent View2d.tot rectangle size changes while dragging panels. */
2930 update_tot_size = false;
2931 }
2932
2933 /* Use a unique identifier for instanced panels, otherwise an old block for a different
2934 * panel of the same type might be found. */
2935 char unique_panel_str[INSTANCED_PANEL_UNIQUE_STR_LEN];
2936 UI_list_panel_unique_str(panel, unique_panel_str);
2937 ed_panel_draw(C,
2938 region,
2939 ®ion->panels,
2940 panel->type,
2941 panel,
2942 (panel->type->flag & PNL_DRAW_BOX) ? w_box_panel : w,
2943 em,
2944 unique_panel_str,
2945 search_filter);
2946 }
2947 }
2948
2949 /* align panels and return size */
2950 int x, y;
2951 UI_panels_end(C, region, &x, &y);
2952
2953 /* before setting the view */
2954 if (region_layout_based) {
2955 /* XXX, only single panel support atm.
2956 * Can't use x/y values calculated above because they're not using the real height of panels,
2957 * instead they calculate offsets for the next panel to start drawing. */
2958 Panel *panel = region->panels.last;
2959 if (panel != NULL) {
2960 const int size_dyn[2] = {
2961 UI_UNIT_X * (UI_panel_is_closed(panel) ? 8 : 14) / UI_DPI_FAC,
2962 UI_panel_size_y(panel) / UI_DPI_FAC,
2963 };
2964 /* region size is layout based and needs to be updated */
2965 if ((region->sizex != size_dyn[0]) || (region->sizey != size_dyn[1])) {
2966 region->sizex = size_dyn[0];
2967 region->sizey = size_dyn[1];
2968 area->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
2969 }
2970 y = fabsf(region->sizey * UI_DPI_FAC - 1);
2971 }
2972 }
2973 else {
2974 /* We always keep the scroll offset -
2975 * so the total view gets increased with the scrolled away part. */
2976 if (v2d->cur.ymax < -FLT_EPSILON) {
2977 /* Clamp to lower view boundary */
2978 if (v2d->tot.ymin < -v2d->winy) {
2979 y = min_ii(y, 0);
2980 }
2981 else {
2982 y = min_ii(y, v2d->cur.ymin);
2983 }
2984 }
2985
2986 y = -y;
2987 }
2988
2989 if (update_tot_size) {
2990 /* this also changes the 'cur' */
2991 UI_view2d_totRect_set(v2d, x, y);
2992 }
2993
2994 if (use_category_tabs) {
2995 region->runtime.category = category;
2996 }
2997 }
2998
ED_region_panels_layout(const bContext * C,ARegion * region)2999 void ED_region_panels_layout(const bContext *C, ARegion *region)
3000 {
3001 ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, NULL, NULL);
3002 }
3003
ED_region_panels_draw(const bContext * C,ARegion * region)3004 void ED_region_panels_draw(const bContext *C, ARegion *region)
3005 {
3006 View2D *v2d = ®ion->v2d;
3007
3008 if (region->alignment != RGN_ALIGN_FLOAT) {
3009 region_clear_color(
3010 C, region, (region->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK);
3011 }
3012
3013 /* reset line width for drawing tabs */
3014 GPU_line_width(1.0f);
3015
3016 /* set the view */
3017 UI_view2d_view_ortho(v2d);
3018
3019 /* View2D matrix might have changed due to dynamic sized regions. */
3020 UI_blocklist_update_window_matrix(C, ®ion->uiblocks);
3021
3022 /* draw panels */
3023 UI_panels_draw(C, region);
3024
3025 /* restore view matrix */
3026 UI_view2d_view_restore(C);
3027
3028 /* Set in layout. */
3029 if (region->runtime.category) {
3030 UI_panel_category_draw_all(region, region->runtime.category);
3031 }
3032
3033 /* scrollers */
3034 bool use_mask = false;
3035 rcti mask;
3036 if (region->runtime.category &&
3037 (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) == RGN_ALIGN_RIGHT)) {
3038 use_mask = true;
3039 UI_view2d_mask_from_win(v2d, &mask);
3040 mask.xmax -= UI_PANEL_CATEGORY_MARGIN_WIDTH;
3041 }
3042 UI_view2d_scrollers_draw(v2d, use_mask ? &mask : NULL);
3043 }
3044
ED_region_panels_ex(const bContext * C,ARegion * region,const char * contexts[])3045 void ED_region_panels_ex(const bContext *C, ARegion *region, const char *contexts[])
3046 {
3047 /* TODO: remove? */
3048 ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, NULL);
3049 ED_region_panels_draw(C, region);
3050 }
3051
ED_region_panels(const bContext * C,ARegion * region)3052 void ED_region_panels(const bContext *C, ARegion *region)
3053 {
3054 /* TODO: remove? */
3055 ED_region_panels_layout(C, region);
3056 ED_region_panels_draw(C, region);
3057 }
3058
ED_region_panels_init(wmWindowManager * wm,ARegion * region)3059 void ED_region_panels_init(wmWindowManager *wm, ARegion *region)
3060 {
3061 UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy);
3062
3063 wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0);
3064 WM_event_add_keymap_handler(®ion->handlers, keymap);
3065 }
3066
3067 /**
3068 * Check whether any of the buttons generated by the \a panel_type's
3069 * layout callbacks match the \a search_filter.
3070 *
3071 * \param panel: If non-NULL, use this instead of adding a new panel for the \a panel_type.
3072 */
panel_property_search(const bContext * C,ARegion * region,const uiStyle * style,Panel * panel,PanelType * panel_type,const char * search_filter)3073 static bool panel_property_search(const bContext *C,
3074 ARegion *region,
3075 const uiStyle *style,
3076 Panel *panel,
3077 PanelType *panel_type,
3078 const char *search_filter)
3079 {
3080 uiBlock *block = UI_block_begin(C, region, panel_type->idname, UI_EMBOSS);
3081 UI_block_set_search_only(block, true);
3082
3083 /* Skip panels that give meaningless search results. */
3084 if (panel_type->flag & PNL_NO_SEARCH) {
3085 return false;
3086 }
3087
3088 if (panel == NULL) {
3089 bool open; /* Dummy variable. */
3090 panel = UI_panel_begin(region, ®ion->panels, block, panel_type, panel, &open);
3091 }
3092
3093 /* Build the layouts. Because they are only used for search,
3094 * they don't need any of the proper style or layout information. */
3095 if (panel->type->draw_header_preset != NULL) {
3096 panel->layout = UI_block_layout(
3097 block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, 0, 0, 0, 0, 0, style);
3098 panel_type->draw_header_preset(C, panel);
3099 }
3100 if (panel->type->draw_header != NULL) {
3101 panel->layout = UI_block_layout(
3102 block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, 0, 0, 0, 0, 0, style);
3103 panel_type->draw_header(C, panel);
3104 }
3105 if (LIKELY(panel->type->draw != NULL)) {
3106 panel->layout = UI_block_layout(
3107 block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 0, 0, 0, style);
3108 panel_type->draw(C, panel);
3109 }
3110
3111 UI_block_layout_free(block);
3112
3113 /* We could check after each layout to increase the likelihood of returning early,
3114 * but that probably wouldn't make much of a difference anyway. */
3115 if (UI_block_apply_search_filter(block, search_filter)) {
3116 return true;
3117 }
3118
3119 LISTBASE_FOREACH (LinkData *, link, &panel_type->children) {
3120 PanelType *panel_type_child = link->data;
3121 if (!panel_type_child->poll || panel_type_child->poll(C, panel_type_child)) {
3122 /* Search for the existing child panel here because it might be an instanced
3123 * child panel with a custom data field that will be needed to build the layout. */
3124 Panel *child_panel = UI_panel_find_by_type(&panel->children, panel_type_child);
3125 if (panel_property_search(C, region, style, child_panel, panel_type_child, search_filter)) {
3126 return true;
3127 }
3128 }
3129 }
3130
3131 return false;
3132 }
3133
3134 /**
3135 * Build the same panel list as #ED_region_panels_layout_ex and checks whether any
3136 * of the panels contain a search result based on the area / region's search filter.
3137 */
ED_region_property_search(const bContext * C,ARegion * region,ListBase * paneltypes,const char * contexts[],const char * category_override)3138 bool ED_region_property_search(const bContext *C,
3139 ARegion *region,
3140 ListBase *paneltypes,
3141 const char *contexts[],
3142 const char *category_override)
3143 {
3144 ScrArea *area = CTX_wm_area(C);
3145 WorkSpace *workspace = CTX_wm_workspace(C);
3146 const uiStyle *style = UI_style_get_dpi();
3147 const char *search_filter = ED_area_region_search_filter_get(area, region);
3148
3149 LinkNode *panel_types_stack = NULL;
3150 LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) {
3151 if (panel_add_check(C, workspace, contexts, category_override, pt)) {
3152 BLI_linklist_prepend_alloca(&panel_types_stack, pt);
3153 }
3154 }
3155
3156 const char *category = NULL;
3157 bool use_category_tabs = (category_override == NULL) && region_uses_category_tabs(area, region);
3158 if (use_category_tabs) {
3159 category = region_panels_collect_categories(region, panel_types_stack, &use_category_tabs);
3160 }
3161
3162 /* Run property search for each panel, stopping if a result is found. */
3163 bool has_result = true;
3164 bool has_instanced_panel = false;
3165 for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) {
3166 PanelType *panel_type = pt_link->link;
3167 /* Note that these checks are duplicated from #ED_region_panels_layout_ex. */
3168 if (panel_type->flag & PNL_INSTANCED) {
3169 has_instanced_panel = true;
3170 continue;
3171 }
3172
3173 if (use_category_tabs) {
3174 if (panel_type->category[0] && !STREQ(category, panel_type->category)) {
3175 continue;
3176 }
3177 }
3178
3179 /* We start property search with an empty panel list, so there's
3180 * no point in trying to find an existing panel with this type. */
3181 has_result = panel_property_search(C, region, style, NULL, panel_type, search_filter);
3182 if (has_result) {
3183 break;
3184 }
3185 }
3186
3187 /* Run property search for instanced panels (created in the layout calls of previous panels). */
3188 if (!has_result && has_instanced_panel) {
3189 LISTBASE_FOREACH (Panel *, panel, ®ion->panels) {
3190 /* Note that these checks are duplicated from #ED_region_panels_layout_ex. */
3191 if (panel->type == NULL || !(panel->type->flag & PNL_INSTANCED)) {
3192 continue;
3193 }
3194 if (use_category_tabs) {
3195 if (panel->type->category[0] && !STREQ(category, panel->type->category)) {
3196 continue;
3197 }
3198 }
3199
3200 has_result = panel_property_search(C, region, style, panel, panel->type, search_filter);
3201 if (has_result) {
3202 break;
3203 }
3204 }
3205 }
3206
3207 /* Free the panels and blocks, as they are only used for search. */
3208 UI_blocklist_free(C, ®ion->uiblocks);
3209 UI_panels_free_instanced(C, region);
3210 BKE_area_region_panels_free(®ion->panels);
3211
3212 return has_result;
3213 }
3214
ED_region_header_layout(const bContext * C,ARegion * region)3215 void ED_region_header_layout(const bContext *C, ARegion *region)
3216 {
3217 const uiStyle *style = UI_style_get_dpi();
3218 bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE;
3219
3220 /* Height of buttons and scaling needed to achieve it. */
3221 const int buttony = min_ii(UI_UNIT_Y, region->winy - 2 * UI_DPI_FAC);
3222 const float buttony_scale = buttony / (float)UI_UNIT_Y;
3223
3224 /* Vertically center buttons. */
3225 int xco = UI_HEADER_OFFSET;
3226 int yco = buttony + (region->winy - buttony) / 2;
3227 int maxco = xco;
3228
3229 /* XXX workaround for 1 px alignment issue. Not sure what causes it...
3230 * Would prefer a proper fix - Julian */
3231 if (!ELEM(CTX_wm_area(C)->spacetype, SPACE_TOPBAR, SPACE_STATUSBAR)) {
3232 yco -= 1;
3233 }
3234
3235 /* set view2d view matrix for scrolling (without scrollers) */
3236 UI_view2d_view_ortho(®ion->v2d);
3237
3238 /* draw all headers types */
3239 LISTBASE_FOREACH (HeaderType *, ht, ®ion->type->headertypes) {
3240 if (ht->poll && !ht->poll(C, ht)) {
3241 continue;
3242 }
3243
3244 uiBlock *block = UI_block_begin(C, region, ht->idname, UI_EMBOSS);
3245 uiLayout *layout = UI_block_layout(
3246 block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, xco, yco, buttony, 1, 0, style);
3247
3248 if (buttony_scale != 1.0f) {
3249 uiLayoutSetScaleY(layout, buttony_scale);
3250 }
3251
3252 Header header = {NULL};
3253 if (ht->draw) {
3254 header.type = ht;
3255 header.layout = layout;
3256 ht->draw(C, &header);
3257 if (ht->next) {
3258 uiItemS(layout);
3259 }
3260
3261 /* for view2d */
3262 xco = uiLayoutGetWidth(layout);
3263 if (xco > maxco) {
3264 maxco = xco;
3265 }
3266 }
3267
3268 UI_block_layout_resolve(block, &xco, &yco);
3269
3270 /* for view2d */
3271 if (xco > maxco) {
3272 maxco = xco;
3273 }
3274
3275 int new_sizex = (maxco + UI_HEADER_OFFSET) / UI_DPI_FAC;
3276
3277 if (region_layout_based && (region->sizex != new_sizex)) {
3278 /* region size is layout based and needs to be updated */
3279 ScrArea *area = CTX_wm_area(C);
3280
3281 region->sizex = new_sizex;
3282 area->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
3283 }
3284
3285 UI_block_end(C, block);
3286
3287 /* In most cases there is only ever one header, it never makes sense to draw more than one
3288 * header in the same region, this results in overlapping buttons, see: T60195. */
3289 break;
3290 }
3291
3292 if (!region_layout_based) {
3293 maxco += UI_HEADER_OFFSET;
3294 }
3295
3296 /* always as last */
3297 UI_view2d_totRect_set(®ion->v2d, maxco, region->winy);
3298
3299 /* restore view matrix */
3300 UI_view2d_view_restore(C);
3301 }
3302
ED_region_header_draw(const bContext * C,ARegion * region)3303 void ED_region_header_draw(const bContext *C, ARegion *region)
3304 {
3305 /* clear */
3306 region_clear_color(C, region, region_background_color_id(C, region));
3307
3308 UI_view2d_view_ortho(®ion->v2d);
3309
3310 /* View2D matrix might have changed due to dynamic sized regions. */
3311 UI_blocklist_update_window_matrix(C, ®ion->uiblocks);
3312
3313 /* draw blocks */
3314 UI_blocklist_draw(C, ®ion->uiblocks);
3315
3316 /* restore view matrix */
3317 UI_view2d_view_restore(C);
3318 }
3319
ED_region_header(const bContext * C,ARegion * region)3320 void ED_region_header(const bContext *C, ARegion *region)
3321 {
3322 /* TODO: remove? */
3323 ED_region_header_layout(C, region);
3324 ED_region_header_draw(C, region);
3325 }
3326
ED_region_header_init(ARegion * region)3327 void ED_region_header_init(ARegion *region)
3328 {
3329 UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy);
3330 }
3331
ED_area_headersize(void)3332 int ED_area_headersize(void)
3333 {
3334 /* Accommodate widget and padding. */
3335 return U.widget_unit + (int)(UI_DPI_FAC * HEADER_PADDING_Y);
3336 }
3337
ED_area_footersize(void)3338 int ED_area_footersize(void)
3339 {
3340 return ED_area_headersize();
3341 }
3342
3343 /**
3344 * \return the final height of a global \a area, accounting for DPI.
3345 */
ED_area_global_size_y(const ScrArea * area)3346 int ED_area_global_size_y(const ScrArea *area)
3347 {
3348 BLI_assert(ED_area_is_global(area));
3349 return round_fl_to_int(area->global->cur_fixed_height * UI_DPI_FAC);
3350 }
ED_area_global_min_size_y(const ScrArea * area)3351 int ED_area_global_min_size_y(const ScrArea *area)
3352 {
3353 BLI_assert(ED_area_is_global(area));
3354 return round_fl_to_int(area->global->size_min * UI_DPI_FAC);
3355 }
ED_area_global_max_size_y(const ScrArea * area)3356 int ED_area_global_max_size_y(const ScrArea *area)
3357 {
3358 BLI_assert(ED_area_is_global(area));
3359 return round_fl_to_int(area->global->size_max * UI_DPI_FAC);
3360 }
3361
ED_area_is_global(const ScrArea * area)3362 bool ED_area_is_global(const ScrArea *area)
3363 {
3364 return area->global != NULL;
3365 }
3366
ED_screen_areas_iter_first(const wmWindow * win,const bScreen * screen)3367 ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen)
3368 {
3369 ScrArea *global_area = win->global_areas.areabase.first;
3370
3371 if (!global_area) {
3372 return screen->areabase.first;
3373 }
3374 if ((global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) {
3375 return global_area;
3376 }
3377 /* Find next visible area. */
3378 return ED_screen_areas_iter_next(screen, global_area);
3379 }
ED_screen_areas_iter_next(const bScreen * screen,const ScrArea * area)3380 ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area)
3381 {
3382 if (area->global == NULL) {
3383 return area->next;
3384 }
3385
3386 for (ScrArea *area_iter = area->next; area_iter; area_iter = area_iter->next) {
3387 if ((area_iter->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) {
3388 return area_iter;
3389 }
3390 }
3391 /* No visible next global area found, start iterating over layout areas. */
3392 return screen->areabase.first;
3393 }
3394
3395 /**
3396 * For now we just assume all global areas are made up out of horizontal bars
3397 * with the same size. A fixed size could be stored in ARegion instead if needed.
3398 *
3399 * \return the DPI aware height of a single bar/region in global areas.
3400 */
ED_region_global_size_y(void)3401 int ED_region_global_size_y(void)
3402 {
3403 return ED_area_headersize(); /* same size as header */
3404 }
3405
ED_region_info_draw_multiline(ARegion * region,const char * text_array[],float fill_color[4],const bool full_redraw)3406 void ED_region_info_draw_multiline(ARegion *region,
3407 const char *text_array[],
3408 float fill_color[4],
3409 const bool full_redraw)
3410 {
3411 const int header_height = UI_UNIT_Y;
3412 const uiStyle *style = UI_style_get_dpi();
3413 int fontid = style->widget.uifont_id;
3414 int scissor[4];
3415 int num_lines = 0;
3416
3417 /* background box */
3418 rcti rect = *ED_region_visible_rect(region);
3419
3420 /* Box fill entire width or just around text. */
3421 if (!full_redraw) {
3422 const char **text = &text_array[0];
3423 while (*text) {
3424 rect.xmax = min_ii(rect.xmax,
3425 rect.xmin + BLF_width(fontid, *text, BLF_DRAW_STR_DUMMY_MAX) +
3426 1.2f * U.widget_unit);
3427 text++;
3428 num_lines++;
3429 }
3430 }
3431 /* Just count the line number. */
3432 else {
3433 const char **text = &text_array[0];
3434 while (*text) {
3435 text++;
3436 num_lines++;
3437 }
3438 }
3439
3440 rect.ymin = rect.ymax - header_height * num_lines;
3441
3442 /* setup scissor */
3443 GPU_scissor_get(scissor);
3444 GPU_scissor(rect.xmin, rect.ymin, BLI_rcti_size_x(&rect) + 1, BLI_rcti_size_y(&rect) + 1);
3445
3446 GPU_blend(GPU_BLEND_ALPHA);
3447 GPUVertFormat *format = immVertexFormat();
3448 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
3449 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3450 immUniformColor4fv(fill_color);
3451 immRecti(pos, rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1);
3452 immUnbindProgram();
3453 GPU_blend(GPU_BLEND_NONE);
3454
3455 /* text */
3456 UI_FontThemeColor(fontid, TH_TEXT_HI);
3457 BLF_clipping(fontid, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3458 BLF_enable(fontid, BLF_CLIPPING);
3459 int offset = num_lines - 1;
3460 {
3461 const char **text = &text_array[0];
3462 while (*text) {
3463 BLF_position(fontid,
3464 rect.xmin + 0.6f * U.widget_unit,
3465 rect.ymin + 0.3f * U.widget_unit + offset * header_height,
3466 0.0f);
3467 BLF_draw(fontid, *text, BLF_DRAW_STR_DUMMY_MAX);
3468 text++;
3469 offset--;
3470 }
3471 }
3472
3473 BLF_disable(fontid, BLF_CLIPPING);
3474
3475 /* restore scissor as it was before */
3476 GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
3477 }
3478
ED_region_info_draw(ARegion * region,const char * text,float fill_color[4],const bool full_redraw)3479 void ED_region_info_draw(ARegion *region,
3480 const char *text,
3481 float fill_color[4],
3482 const bool full_redraw)
3483 {
3484 const char *text_array[2] = {text, NULL};
3485 ED_region_info_draw_multiline(region, text_array, fill_color, full_redraw);
3486 }
3487
3488 #define MAX_METADATA_STR 1024
3489
3490 static const char *meta_data_list[] = {
3491 "File",
3492 "Strip",
3493 "Date",
3494 "RenderTime",
3495 "Note",
3496 "Marker",
3497 "Time",
3498 "Frame",
3499 "Camera",
3500 "Scene",
3501 };
3502
metadata_is_valid(ImBuf * ibuf,char * r_str,short index,int offset)3503 BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset)
3504 {
3505 return (IMB_metadata_get_field(
3506 ibuf->metadata, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) &&
3507 r_str[0]);
3508 }
3509
metadata_is_custom_drawable(const char * field)3510 BLI_INLINE bool metadata_is_custom_drawable(const char *field)
3511 {
3512 /* Metadata field stored by Blender for multilayer EXR images. Is rather
3513 * useless to be viewed all the time. Can still be seen in the Metadata
3514 * panel. */
3515 if (STREQ(field, "BlenderMultiChannel")) {
3516 return false;
3517 }
3518 /* Is almost always has value "scanlineimage", also useless to be seen
3519 * all the time. */
3520 if (STREQ(field, "type")) {
3521 return false;
3522 }
3523 return !BKE_stamp_is_known_field(field);
3524 }
3525
3526 typedef struct MetadataCustomDrawContext {
3527 int fontid;
3528 int xmin, ymin;
3529 int vertical_offset;
3530 int current_y;
3531 } MetadataCustomDrawContext;
3532
metadata_custom_draw_fields(const char * field,const char * value,void * ctx_v)3533 static void metadata_custom_draw_fields(const char *field, const char *value, void *ctx_v)
3534 {
3535 if (!metadata_is_custom_drawable(field)) {
3536 return;
3537 }
3538 MetadataCustomDrawContext *ctx = (MetadataCustomDrawContext *)ctx_v;
3539 char temp_str[MAX_METADATA_STR];
3540 BLI_snprintf(temp_str, MAX_METADATA_STR, "%s: %s", field, value);
3541 BLF_position(ctx->fontid, ctx->xmin, ctx->ymin + ctx->current_y, 0.0f);
3542 BLF_draw(ctx->fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
3543 ctx->current_y += ctx->vertical_offset;
3544 }
3545
metadata_draw_imbuf(ImBuf * ibuf,const rctf * rect,int fontid,const bool is_top)3546 static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const bool is_top)
3547 {
3548 char temp_str[MAX_METADATA_STR];
3549 int ofs_y = 0;
3550 const float height = BLF_height_max(fontid);
3551 const float margin = height / 8;
3552 const float vertical_offset = (height + margin);
3553
3554 /* values taking margins into account */
3555 const float descender = BLF_descender(fontid);
3556 const float xmin = (rect->xmin + margin);
3557 const float xmax = (rect->xmax - margin);
3558 const float ymin = (rect->ymin + margin) - descender;
3559 const float ymax = (rect->ymax - margin) - descender;
3560
3561 if (is_top) {
3562 for (int i = 0; i < 4; i++) {
3563 /* first line */
3564 if (i == 0) {
3565 bool do_newline = false;
3566 int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[0]);
3567 if (metadata_is_valid(ibuf, temp_str, 0, len)) {
3568 BLF_position(fontid, xmin, ymax - vertical_offset, 0.0f);
3569 BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
3570 do_newline = true;
3571 }
3572
3573 len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[1]);
3574 if (metadata_is_valid(ibuf, temp_str, 1, len)) {
3575 int line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
3576 BLF_position(fontid, xmax - line_width, ymax - vertical_offset, 0.0f);
3577 BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
3578 do_newline = true;
3579 }
3580
3581 if (do_newline) {
3582 ofs_y += vertical_offset;
3583 }
3584 } /* Strip */
3585 else if (i == 1 || i == 2) {
3586 int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
3587 if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
3588 BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
3589 BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
3590 ofs_y += vertical_offset;
3591 }
3592 } /* Note (wrapped) */
3593 else if (i == 3) {
3594 int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
3595 if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
3596 struct ResultBLF info;
3597 BLF_enable(fontid, BLF_WORD_WRAP);
3598 BLF_wordwrap(fontid, ibuf->x - (margin * 2));
3599 BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
3600 BLF_draw_ex(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX, &info);
3601 BLF_wordwrap(fontid, 0);
3602 BLF_disable(fontid, BLF_WORD_WRAP);
3603 ofs_y += vertical_offset * info.lines;
3604 }
3605 }
3606 else {
3607 int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
3608 if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
3609 int line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
3610 BLF_position(fontid, xmax - line_width, ymax - vertical_offset - ofs_y, 0.0f);
3611 BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
3612 ofs_y += vertical_offset;
3613 }
3614 }
3615 }
3616 }
3617 else {
3618 MetadataCustomDrawContext ctx;
3619 ctx.fontid = fontid;
3620 ctx.xmin = xmin;
3621 ctx.ymin = ymin;
3622 ctx.current_y = ofs_y;
3623 ctx.vertical_offset = vertical_offset;
3624 IMB_metadata_foreach(ibuf, metadata_custom_draw_fields, &ctx);
3625 int ofs_x = 0;
3626 ofs_y = ctx.current_y;
3627 for (int i = 5; i < 10; i++) {
3628 int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i]);
3629 if (metadata_is_valid(ibuf, temp_str, i, len)) {
3630 BLF_position(fontid, xmin + ofs_x, ymin + ofs_y, 0.0f);
3631 BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
3632
3633 ofs_x += BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX) + UI_UNIT_X;
3634 }
3635 }
3636 }
3637 }
3638
3639 typedef struct MetadataCustomCountContext {
3640 int count;
3641 } MetadataCustomCountContext;
3642
metadata_custom_count_fields(const char * field,const char * UNUSED (value),void * ctx_v)3643 static void metadata_custom_count_fields(const char *field, const char *UNUSED(value), void *ctx_v)
3644 {
3645 if (!metadata_is_custom_drawable(field)) {
3646 return;
3647 }
3648 MetadataCustomCountContext *ctx = (MetadataCustomCountContext *)ctx_v;
3649 ctx->count++;
3650 }
3651
metadata_box_height_get(ImBuf * ibuf,int fontid,const bool is_top)3652 static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top)
3653 {
3654 const float height = BLF_height_max(fontid);
3655 const float margin = (height / 8);
3656 char str[MAX_METADATA_STR] = "";
3657 short count = 0;
3658
3659 if (is_top) {
3660 if (metadata_is_valid(ibuf, str, 0, 0) || metadata_is_valid(ibuf, str, 1, 0)) {
3661 count++;
3662 }
3663 for (int i = 2; i < 5; i++) {
3664 if (metadata_is_valid(ibuf, str, i, 0)) {
3665 if (i == 4) {
3666 struct {
3667 struct ResultBLF info;
3668 rctf rect;
3669 } wrap;
3670
3671 BLF_enable(fontid, BLF_WORD_WRAP);
3672 BLF_wordwrap(fontid, ibuf->x - (margin * 2));
3673 BLF_boundbox_ex(fontid, str, sizeof(str), &wrap.rect, &wrap.info);
3674 BLF_wordwrap(fontid, 0);
3675 BLF_disable(fontid, BLF_WORD_WRAP);
3676
3677 count += wrap.info.lines;
3678 }
3679 else {
3680 count++;
3681 }
3682 }
3683 }
3684 }
3685 else {
3686 for (int i = 5; i < 10; i++) {
3687 if (metadata_is_valid(ibuf, str, i, 0)) {
3688 count = 1;
3689 break;
3690 }
3691 }
3692 MetadataCustomCountContext ctx;
3693 ctx.count = 0;
3694 IMB_metadata_foreach(ibuf, metadata_custom_count_fields, &ctx);
3695 count += ctx.count;
3696 }
3697
3698 if (count) {
3699 return (height + margin) * count;
3700 }
3701
3702 return 0;
3703 }
3704
3705 #undef MAX_METADATA_STR
3706
ED_region_image_metadata_draw(int x,int y,ImBuf * ibuf,const rctf * frame,float zoomx,float zoomy)3707 void ED_region_image_metadata_draw(
3708 int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy)
3709 {
3710 const uiStyle *style = UI_style_get_dpi();
3711
3712 if (!ibuf->metadata) {
3713 return;
3714 }
3715
3716 /* find window pixel coordinates of origin */
3717 GPU_matrix_push();
3718
3719 /* offset and zoom using ogl */
3720 GPU_matrix_translate_2f(x, y);
3721 GPU_matrix_scale_2f(zoomx, zoomy);
3722
3723 BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f * U.pixelsize, U.dpi);
3724
3725 /* *** upper box*** */
3726
3727 /* get needed box height */
3728 float box_y = metadata_box_height_get(ibuf, blf_mono_font, true);
3729
3730 if (box_y) {
3731 /* set up rect */
3732 rctf rect;
3733 BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymax, frame->ymax + box_y);
3734 /* draw top box */
3735 GPUVertFormat *format = immVertexFormat();
3736 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3737 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3738 immUniformThemeColor(TH_METADATA_BG);
3739 immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3740 immUnbindProgram();
3741
3742 BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3743 BLF_enable(blf_mono_font, BLF_CLIPPING);
3744
3745 UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
3746 metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true);
3747
3748 BLF_disable(blf_mono_font, BLF_CLIPPING);
3749 }
3750
3751 /* *** lower box*** */
3752
3753 box_y = metadata_box_height_get(ibuf, blf_mono_font, false);
3754
3755 if (box_y) {
3756 /* set up box rect */
3757 rctf rect;
3758 BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymin - box_y, frame->ymin);
3759 /* draw top box */
3760 GPUVertFormat *format = immVertexFormat();
3761 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3762 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3763 immUniformThemeColor(TH_METADATA_BG);
3764 immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3765 immUnbindProgram();
3766
3767 BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3768 BLF_enable(blf_mono_font, BLF_CLIPPING);
3769
3770 UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
3771 metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false);
3772
3773 BLF_disable(blf_mono_font, BLF_CLIPPING);
3774 }
3775
3776 GPU_matrix_pop();
3777 }
3778
3779 typedef struct MetadataPanelDrawContext {
3780 uiLayout *layout;
3781 } MetadataPanelDrawContext;
3782
metadata_panel_draw_field(const char * field,const char * value,void * ctx_v)3783 static void metadata_panel_draw_field(const char *field, const char *value, void *ctx_v)
3784 {
3785 MetadataPanelDrawContext *ctx = (MetadataPanelDrawContext *)ctx_v;
3786 uiLayout *row = uiLayoutRow(ctx->layout, false);
3787 uiItemL(row, field, ICON_NONE);
3788 uiItemL(row, value, ICON_NONE);
3789 }
3790
ED_region_image_metadata_panel_draw(ImBuf * ibuf,uiLayout * layout)3791 void ED_region_image_metadata_panel_draw(ImBuf *ibuf, uiLayout *layout)
3792 {
3793 MetadataPanelDrawContext ctx;
3794 ctx.layout = layout;
3795 IMB_metadata_foreach(ibuf, metadata_panel_draw_field, &ctx);
3796 }
3797
ED_region_grid_draw(ARegion * region,float zoomx,float zoomy,float x0,float y0)3798 void ED_region_grid_draw(ARegion *region, float zoomx, float zoomy, float x0, float y0)
3799 {
3800 /* the image is located inside (x0, y0), (x0+1, y0+1) as set by view2d */
3801 int x1, y1, x2, y2;
3802 UI_view2d_view_to_region(®ion->v2d, x0, y0, &x1, &y1);
3803 UI_view2d_view_to_region(®ion->v2d, x0 + 1.0f, y0 + 1.0f, &x2, &y2);
3804
3805 GPUVertFormat *format = immVertexFormat();
3806 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3807
3808 float gridcolor[4];
3809 UI_GetThemeColor4fv(TH_GRID, gridcolor);
3810
3811 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3812 /* To fake alpha-blending, color shading is reduced when alpha is nearing 0. */
3813 immUniformThemeColorBlendShade(TH_BACK, TH_GRID, gridcolor[3], 20 * gridcolor[3]);
3814 immRectf(pos, x1, y1, x2, y2);
3815 immUnbindProgram();
3816
3817 /* gridsize adapted to zoom level */
3818 float gridsize = 0.5f * (zoomx + zoomy);
3819 float gridstep = 1.0f / 32.0f;
3820 if (gridsize <= 0.0f) {
3821 return;
3822 }
3823
3824 if (gridsize < 1.0f) {
3825 while (gridsize < 1.0f) {
3826 gridsize *= 4.0f;
3827 gridstep *= 4.0f;
3828 }
3829 }
3830 else {
3831 while (gridsize >= 4.0f) {
3832 gridsize /= 4.0f;
3833 gridstep /= 4.0f;
3834 }
3835 }
3836
3837 float blendfac = 0.25f * gridsize - floorf(0.25f * gridsize);
3838 CLAMP(blendfac, 0.0f, 1.0f);
3839
3840 int count_fine = 1.0f / gridstep;
3841 int count_large = 1.0f / (4.0f * gridstep);
3842
3843 if (count_fine > 0) {
3844 GPU_vertformat_clear(format);
3845 pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3846 uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
3847
3848 immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
3849 immBegin(GPU_PRIM_LINES, 4 * count_fine + 4 * count_large);
3850
3851 float theme_color[3];
3852 UI_GetThemeColorShade3fv(TH_GRID, (int)(20.0f * (1.0f - blendfac)), theme_color);
3853 float fac = 0.0f;
3854
3855 /* the fine resolution level */
3856 for (int i = 0; i < count_fine; i++) {
3857 immAttr3fv(color, theme_color);
3858 immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
3859 immAttr3fv(color, theme_color);
3860 immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
3861 immAttr3fv(color, theme_color);
3862 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
3863 immAttr3fv(color, theme_color);
3864 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
3865 fac += gridstep;
3866 }
3867
3868 if (count_large > 0) {
3869 UI_GetThemeColor3fv(TH_GRID, theme_color);
3870 fac = 0.0f;
3871
3872 /* the large resolution level */
3873 for (int i = 0; i < count_large; i++) {
3874 immAttr3fv(color, theme_color);
3875 immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
3876 immAttr3fv(color, theme_color);
3877 immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
3878 immAttr3fv(color, theme_color);
3879 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
3880 immAttr3fv(color, theme_color);
3881 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
3882 fac += 4.0f * gridstep;
3883 }
3884 }
3885
3886 immEnd();
3887 immUnbindProgram();
3888 }
3889 }
3890
3891 /* If the area has overlapping regions, it returns visible rect for Region *region */
3892 /* rect gets returned in local region coordinates */
region_visible_rect_calc(ARegion * region,rcti * rect)3893 static void region_visible_rect_calc(ARegion *region, rcti *rect)
3894 {
3895 ARegion *arn = region;
3896
3897 /* allow function to be called without area */
3898 while (arn->prev) {
3899 arn = arn->prev;
3900 }
3901
3902 *rect = region->winrct;
3903
3904 /* check if a region overlaps with the current one */
3905 for (; arn; arn = arn->next) {
3906 if (region != arn && arn->overlap) {
3907 if (BLI_rcti_isect(rect, &arn->winrct, NULL)) {
3908 int alignment = RGN_ALIGN_ENUM_FROM_MASK(arn->alignment);
3909
3910 if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
3911 /* Overlap left, also check 1 pixel offset (2 regions on one side). */
3912 if (abs(rect->xmin - arn->winrct.xmin) < 2) {
3913 rect->xmin = arn->winrct.xmax;
3914 }
3915
3916 /* Overlap right. */
3917 if (abs(rect->xmax - arn->winrct.xmax) < 2) {
3918 rect->xmax = arn->winrct.xmin;
3919 }
3920 }
3921 else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
3922 /* Same logic as above for vertical regions. */
3923 if (abs(rect->ymin - arn->winrct.ymin) < 2) {
3924 rect->ymin = arn->winrct.ymax;
3925 }
3926 if (abs(rect->ymax - arn->winrct.ymax) < 2) {
3927 rect->ymax = arn->winrct.ymin;
3928 }
3929 }
3930 else if (alignment == RGN_ALIGN_FLOAT) {
3931 /* Skip floating. */
3932 }
3933 else {
3934 BLI_assert(!"Region overlap with unknown alignment");
3935 }
3936 }
3937 }
3938 }
3939 BLI_rcti_translate(rect, -region->winrct.xmin, -region->winrct.ymin);
3940 }
3941
ED_region_visible_rect(ARegion * region)3942 const rcti *ED_region_visible_rect(ARegion *region)
3943 {
3944 rcti *rect = ®ion->runtime.visible_rect;
3945 if (rect->xmin == 0 && rect->ymin == 0 && rect->xmax == 0 && rect->ymax == 0) {
3946 region_visible_rect_calc(region, rect);
3947 }
3948 return rect;
3949 }
3950
3951 /* Cache display helpers */
3952
ED_region_cache_draw_background(ARegion * region)3953 void ED_region_cache_draw_background(ARegion *region)
3954 {
3955 /* Local coordinate visible rect inside region, to accommodate overlapping ui. */
3956 const rcti *rect_visible = ED_region_visible_rect(region);
3957 const int region_bottom = rect_visible->ymin;
3958
3959 uint pos = GPU_vertformat_attr_add(
3960 immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
3961 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3962 immUniformColor4ub(128, 128, 255, 64);
3963 immRecti(pos, 0, region_bottom, region->winx, region_bottom + 8 * UI_DPI_FAC);
3964 immUnbindProgram();
3965 }
3966
ED_region_cache_draw_curfra_label(const int framenr,const float x,const float y)3967 void ED_region_cache_draw_curfra_label(const int framenr, const float x, const float y)
3968 {
3969 const uiStyle *style = UI_style_get();
3970 int fontid = style->widget.uifont_id;
3971 char numstr[32];
3972 float font_dims[2] = {0.0f, 0.0f};
3973
3974 /* frame number */
3975 BLF_size(fontid, 11.0f * U.pixelsize, U.dpi);
3976 BLI_snprintf(numstr, sizeof(numstr), "%d", framenr);
3977
3978 BLF_width_and_height(fontid, numstr, sizeof(numstr), &font_dims[0], &font_dims[1]);
3979
3980 uint pos = GPU_vertformat_attr_add(
3981 immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
3982 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3983 immUniformThemeColor(TH_CFRAME);
3984 immRecti(pos, x, y, x + font_dims[0] + 6.0f, y + font_dims[1] + 4.0f);
3985 immUnbindProgram();
3986
3987 UI_FontThemeColor(fontid, TH_TEXT);
3988 BLF_position(fontid, x + 2.0f, y + 2.0f, 0.0f);
3989 BLF_draw(fontid, numstr, sizeof(numstr));
3990 }
3991
ED_region_cache_draw_cached_segments(ARegion * region,const int num_segments,const int * points,const int sfra,const int efra)3992 void ED_region_cache_draw_cached_segments(
3993 ARegion *region, const int num_segments, const int *points, const int sfra, const int efra)
3994 {
3995 if (num_segments) {
3996 /* Local coordinate visible rect inside region, to accommodate overlapping ui. */
3997 const rcti *rect_visible = ED_region_visible_rect(region);
3998 const int region_bottom = rect_visible->ymin;
3999
4000 uint pos = GPU_vertformat_attr_add(
4001 immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
4002 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
4003 immUniformColor4ub(128, 128, 255, 128);
4004
4005 for (int a = 0; a < num_segments; a++) {
4006 float x1 = (float)(points[a * 2] - sfra) / (efra - sfra + 1) * region->winx;
4007 float x2 = (float)(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * region->winx;
4008
4009 immRecti(pos, x1, region_bottom, x2, region_bottom + 8 * UI_DPI_FAC);
4010 /* TODO(merwin): use primitive restart to draw multiple rects more efficiently */
4011 }
4012
4013 immUnbindProgram();
4014 }
4015 }
4016
4017 /**
4018 * Generate subscriptions for this region.
4019 */
ED_region_message_subscribe(bContext * C,struct WorkSpace * workspace,struct Scene * scene,struct bScreen * screen,struct ScrArea * area,struct ARegion * region,struct wmMsgBus * mbus)4020 void ED_region_message_subscribe(bContext *C,
4021 struct WorkSpace *workspace,
4022 struct Scene *scene,
4023 struct bScreen *screen,
4024 struct ScrArea *area,
4025 struct ARegion *region,
4026 struct wmMsgBus *mbus)
4027 {
4028 if (region->gizmo_map != NULL) {
4029 WM_gizmomap_message_subscribe(C, region->gizmo_map, region, mbus);
4030 }
4031
4032 if (!BLI_listbase_is_empty(®ion->uiblocks)) {
4033 UI_region_message_subscribe(region, mbus);
4034 }
4035
4036 if (region->type->message_subscribe != NULL) {
4037 region->type->message_subscribe(C, workspace, scene, screen, area, region, mbus);
4038 }
4039 }
4040
ED_region_snap_size_test(const ARegion * region)4041 int ED_region_snap_size_test(const ARegion *region)
4042 {
4043 /* Use a larger value because toggling scrollbars can jump in size. */
4044 const int snap_match_threshold = 16;
4045 if (region->type->snap_size != NULL) {
4046 return ((((region->sizex - region->type->snap_size(region, region->sizex, 0)) <=
4047 snap_match_threshold)
4048 << 0) |
4049 (((region->sizey - region->type->snap_size(region, region->sizey, 1)) <=
4050 snap_match_threshold)
4051 << 1));
4052 }
4053 return 0;
4054 }
4055
ED_region_snap_size_apply(ARegion * region,int snap_flag)4056 bool ED_region_snap_size_apply(ARegion *region, int snap_flag)
4057 {
4058 bool changed = false;
4059 if (region->type->snap_size != NULL) {
4060 if (snap_flag & (1 << 0)) {
4061 short snap_size = region->type->snap_size(region, region->sizex, 0);
4062 if (snap_size != region->sizex) {
4063 region->sizex = snap_size;
4064 changed = true;
4065 }
4066 }
4067 if (snap_flag & (1 << 1)) {
4068 short snap_size = region->type->snap_size(region, region->sizey, 1);
4069 if (snap_size != region->sizey) {
4070 region->sizey = snap_size;
4071 changed = true;
4072 }
4073 }
4074 }
4075 return changed;
4076 }
4077