1aa54bcd8b5b0c2a2267fbb1a75b8489effd3aa7
[blender.git] / source / blender / editors / screen / area.c
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 <string.h>
25 #include <stdio.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_userdef_types.h"
30
31 #include "BLI_blenlib.h"
32 #include "BLI_math.h"
33 #include "BLI_utildefines.h"
34 #include "BLI_linklist_stack.h"
35
36 #include "BKE_context.h"
37 #include "BKE_global.h"
38 #include "BKE_image.h"
39 #include "BKE_screen.h"
40 #include "BKE_workspace.h"
41
42 #include "RNA_access.h"
43 #include "RNA_types.h"
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47 #include "WM_message.h"
48 #include "WM_toolsystem.h"
49
50 #include "ED_screen.h"
51 #include "ED_screen_types.h"
52 #include "ED_space_api.h"
53
54 #include "GPU_immediate.h"
55 #include "GPU_immediate_util.h"
56 #include "GPU_matrix.h"
57 #include "GPU_draw.h"
58 #include "GPU_state.h"
59 #include "GPU_framebuffer.h"
60
61 #include "BLF_api.h"
62
63 #include "IMB_imbuf.h"
64 #include "IMB_imbuf_types.h"
65 #include "IMB_metadata.h"
66
67 #include "UI_interface.h"
68 #include "UI_interface_icons.h"
69 #include "UI_resources.h"
70 #include "UI_view2d.h"
71
72 #include "screen_intern.h"
73
74 enum RegionEmbossSide {
75   REGION_EMBOSS_LEFT = (1 << 0),
76   REGION_EMBOSS_TOP = (1 << 1),
77   REGION_EMBOSS_BOTTOM = (1 << 2),
78   REGION_EMBOSS_RIGHT = (1 << 3),
79   REGION_EMBOSS_ALL = REGION_EMBOSS_LEFT | REGION_EMBOSS_TOP | REGION_EMBOSS_RIGHT |
80                       REGION_EMBOSS_BOTTOM,
81 };
82
83 /* general area and region code */
84
85 static void region_draw_emboss(const ARegion *ar, const rcti *scirct, int sides)
86 {
87   rcti rect;
88
89   /* translate scissor rect to region space */
90   rect.xmin = scirct->xmin - ar->winrct.xmin;
91   rect.ymin = scirct->ymin - ar->winrct.ymin;
92   rect.xmax = scirct->xmax - ar->winrct.xmin;
93   rect.ymax = scirct->ymax - ar->winrct.ymin;
94
95   /* set transp line */
96   GPU_blend(true);
97   GPU_blend_set_func_separate(
98       GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
99
100   float color[4] = {0.0f, 0.0f, 0.0f, 0.25f};
101   UI_GetThemeColor3fv(TH_EDITOR_OUTLINE, color);
102
103   GPUVertFormat *format = immVertexFormat();
104   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
105   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
106   immUniformColor4fv(color);
107
108   immBeginAtMost(GPU_PRIM_LINES, 8);
109
110   /* right */
111   if (sides & REGION_EMBOSS_RIGHT) {
112     immVertex2f(pos, rect.xmax, rect.ymax);
113     immVertex2f(pos, rect.xmax, rect.ymin);
114   }
115
116   /* bottom */
117   if (sides & REGION_EMBOSS_BOTTOM) {
118     immVertex2f(pos, rect.xmax, rect.ymin);
119     immVertex2f(pos, rect.xmin, rect.ymin);
120   }
121
122   /* left */
123   if (sides & REGION_EMBOSS_LEFT) {
124     immVertex2f(pos, rect.xmin, rect.ymin);
125     immVertex2f(pos, rect.xmin, rect.ymax);
126   }
127
128   /* top */
129   if (sides & REGION_EMBOSS_TOP) {
130     immVertex2f(pos, rect.xmin, rect.ymax);
131     immVertex2f(pos, rect.xmax, rect.ymax);
132   }
133
134   immEnd();
135   immUnbindProgram();
136
137   GPU_blend(false);
138   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
139 }
140
141 void ED_region_pixelspace(ARegion *ar)
142 {
143   wmOrtho2_region_pixelspace(ar);
144   GPU_matrix_identity_set();
145 }
146
147 /* only exported for WM */
148 void ED_region_do_listen(
149     wmWindow *win, ScrArea *sa, ARegion *ar, wmNotifier *note, const Scene *scene)
150 {
151   /* generic notes first */
152   switch (note->category) {
153     case NC_WM:
154       if (note->data == ND_FILEREAD)
155         ED_region_tag_redraw(ar);
156       break;
157     case NC_WINDOW:
158       ED_region_tag_redraw(ar);
159       break;
160   }
161
162   if (ar->type && ar->type->listener)
163     ar->type->listener(win, sa, ar, note, scene);
164 }
165
166 /* only exported for WM */
167 void ED_area_do_listen(wmWindow *win, ScrArea *sa, wmNotifier *note, Scene *scene)
168 {
169   /* no generic notes? */
170   if (sa->type && sa->type->listener) {
171     sa->type->listener(win, sa, note, scene);
172   }
173 }
174
175 /* only exported for WM */
176 void ED_area_do_refresh(bContext *C, ScrArea *sa)
177 {
178   /* no generic notes? */
179   if (sa->type && sa->type->refresh) {
180     sa->type->refresh(C, sa);
181   }
182   sa->do_refresh = false;
183 }
184
185 /**
186  * \brief Corner widget use for quitting fullscreen.
187  */
188 static void area_draw_azone_fullscreen(short x1, short y1, short x2, short y2, float alpha)
189 {
190   int x = x2 - ((float)x2 - x1) * 0.5f / UI_DPI_FAC;
191   int y = y2 - ((float)y2 - y1) * 0.5f / UI_DPI_FAC;
192
193   /* adjust the icon distance from the corner */
194   x += 36.0f / UI_DPI_FAC;
195   y += 36.0f / UI_DPI_FAC;
196
197   /* draws from the left bottom corner of the icon */
198   x -= UI_DPI_ICON_SIZE;
199   y -= UI_DPI_ICON_SIZE;
200
201   alpha = min_ff(alpha, 0.75f);
202
203   UI_icon_draw_aspect(x, y, ICON_FULLSCREEN_EXIT, 0.7f / UI_DPI_FAC, alpha, NULL);
204
205   /* debug drawing :
206    * The click_rect is the same as defined in fullscreen_click_rcti_init
207    * Keep them both in sync */
208
209   if (G.debug_value == 101) {
210     rcti click_rect;
211     float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC;
212
213     BLI_rcti_init(&click_rect, x, x + icon_size, y, y + icon_size);
214
215     GPUVertFormat *format = immVertexFormat();
216     uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
217
218     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
219
220     immUniformColor4f(1.0f, 0.0f, 0.0f, alpha);
221     imm_draw_box_wire_2d(pos, click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax);
222
223     immUniformColor4f(0.0f, 1.0f, 1.0f, alpha);
224     immBegin(GPU_PRIM_LINES, 4);
225     immVertex2f(pos, click_rect.xmin, click_rect.ymin);
226     immVertex2f(pos, click_rect.xmax, click_rect.ymax);
227     immVertex2f(pos, click_rect.xmin, click_rect.ymax);
228     immVertex2f(pos, click_rect.xmax, click_rect.ymin);
229     immEnd();
230
231     immUnbindProgram();
232   }
233 }
234
235 /**
236  * \brief Corner widgets use for dragging and splitting the view.
237  */
238 static void area_draw_azone(short UNUSED(x1), short UNUSED(y1), short UNUSED(x2), short UNUSED(y2))
239 {
240   /* No drawing needed since all corners are action zone, and visually distinguishable. */
241 }
242
243 /**
244  * \brief Edge widgets to show hidden panels such as the toolbar and headers.
245  */
246 static void draw_azone_arrow(float x1, float y1, float x2, float y2, AZEdge edge)
247 {
248   const float size = 0.2f * U.widget_unit;
249   const float l = 1.0f;  /* arrow length */
250   const float s = 0.25f; /* arrow thickness */
251   const float hl = l / 2.0f;
252   const float points[6][2] = {
253       {0, -hl}, {l, hl}, {l - s, hl + s}, {0, s + s - hl}, {s - l, hl + s}, {-l, hl}};
254   const float center[2] = {(x1 + x2) / 2, (y1 + y2) / 2};
255
256   int axis;
257   int sign;
258   switch (edge) {
259     case AE_BOTTOM_TO_TOPLEFT:
260       axis = 0;
261       sign = 1;
262       break;
263     case AE_TOP_TO_BOTTOMRIGHT:
264       axis = 0;
265       sign = -1;
266       break;
267     case AE_LEFT_TO_TOPRIGHT:
268       axis = 1;
269       sign = 1;
270       break;
271     case AE_RIGHT_TO_TOPLEFT:
272       axis = 1;
273       sign = -1;
274       break;
275     default:
276       BLI_assert(0);
277       return;
278   }
279
280   GPUVertFormat *format = immVertexFormat();
281   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
282
283   GPU_blend(true);
284   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
285   immUniformColor4f(0.8f, 0.8f, 0.8f, 0.4f);
286
287   immBegin(GPU_PRIM_TRI_FAN, 6);
288   for (int i = 0; i < 6; i++) {
289     if (axis == 0) {
290       immVertex2f(pos, center[0] + points[i][0] * size, center[1] + points[i][1] * sign * size);
291     }
292     else {
293       immVertex2f(pos, center[0] + points[i][1] * sign * size, center[1] + points[i][0] * size);
294     }
295   }
296   immEnd();
297
298   immUnbindProgram();
299   GPU_blend(false);
300 }
301
302 static void region_draw_azone_tab_arrow(AZone *az)
303 {
304   GPU_blend(true);
305
306   /* add code to draw region hidden as 'too small' */
307   switch (az->edge) {
308     case AE_TOP_TO_BOTTOMRIGHT:
309       UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
310       break;
311     case AE_BOTTOM_TO_TOPLEFT:
312       UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
313       break;
314     case AE_LEFT_TO_TOPRIGHT:
315       UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
316       break;
317     case AE_RIGHT_TO_TOPLEFT:
318       UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
319       break;
320   }
321
322   float color[4] = {0.05f, 0.05f, 0.05f, 0.4f};
323   UI_draw_roundbox_aa(
324       true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, color);
325
326   draw_azone_arrow((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, az->edge);
327 }
328
329 static void area_azone_tag_update(ScrArea *sa)
330 {
331   sa->flag |= AREA_FLAG_ACTIONZONES_UPDATE;
332 }
333
334 static void region_draw_azones(ScrArea *sa, ARegion *ar)
335 {
336   AZone *az;
337
338   if (!sa)
339     return;
340
341   GPU_line_width(1.0f);
342   GPU_blend(true);
343   GPU_blend_set_func_separate(
344       GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
345
346   GPU_matrix_push();
347   GPU_matrix_translate_2f(-ar->winrct.xmin, -ar->winrct.ymin);
348
349   for (az = sa->actionzones.first; az; az = az->next) {
350     /* test if action zone is over this region */
351     rcti azrct;
352     BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2);
353
354     if (BLI_rcti_isect(&ar->drawrct, &azrct, NULL)) {
355       if (az->type == AZONE_AREA) {
356         area_draw_azone(az->x1, az->y1, az->x2, az->y2);
357       }
358       else if (az->type == AZONE_REGION) {
359         if (az->ar) {
360           /* only display tab or icons when the region is hidden */
361           if (az->ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
362             region_draw_azone_tab_arrow(az);
363           }
364         }
365       }
366       else if (az->type == AZONE_FULLSCREEN) {
367         area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha);
368       }
369     }
370     if (!IS_EQF(az->alpha, 0.0f) && ELEM(az->type, AZONE_FULLSCREEN, AZONE_REGION_SCROLL)) {
371       area_azone_tag_update(sa);
372     }
373   }
374
375   GPU_matrix_pop();
376
377   GPU_blend(false);
378 }
379
380 static void region_draw_status_text(ScrArea *sa, ARegion *ar)
381 {
382   bool overlap = ED_region_is_overlap(sa->spacetype, ar->regiontype);
383
384   if (overlap) {
385     GPU_clear_color(0.0, 0.0, 0.0, 0.0);
386     glClear(GL_COLOR_BUFFER_BIT);
387   }
388   else {
389     UI_ThemeClearColor(TH_HEADER);
390     glClear(GL_COLOR_BUFFER_BIT);
391   }
392
393   int fontid = BLF_set_default();
394
395   const float width = BLF_width(fontid, ar->headerstr, BLF_DRAW_STR_DUMMY_MAX);
396   const float x = UI_UNIT_X;
397   const float y = 0.4f * UI_UNIT_Y;
398
399   if (overlap) {
400     const float pad = 2.0f * UI_DPI_FAC;
401     const float x1 = x - (UI_UNIT_X - pad);
402     const float x2 = x + width + (UI_UNIT_X - pad);
403     const float y1 = pad;
404     const float y2 = ar->winy - pad;
405
406     GPU_blend_set_func_separate(
407         GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
408
409     float color[4] = {0.0f, 0.0f, 0.0f, 0.5f};
410     UI_GetThemeColor3fv(TH_BACK, color);
411     UI_draw_roundbox_corner_set(UI_CNR_ALL);
412     UI_draw_roundbox_aa(true, x1, y1, x2, y2, 4.0f, color);
413
414     UI_FontThemeColor(fontid, TH_TEXT);
415   }
416   else {
417     UI_FontThemeColor(fontid, TH_TEXT);
418   }
419
420   BLF_position(fontid, x, y, 0.0f);
421   BLF_draw(fontid, ar->headerstr, BLF_DRAW_STR_DUMMY_MAX);
422 }
423
424 /* Follow wmMsgNotifyFn spec */
425 void ED_region_do_msg_notify_tag_redraw(bContext *UNUSED(C),
426                                         wmMsgSubscribeKey *UNUSED(msg_key),
427                                         wmMsgSubscribeValue *msg_val)
428 {
429   ARegion *ar = msg_val->owner;
430   ED_region_tag_redraw(ar);
431
432   /* This avoids _many_ situations where header/properties control display settings.
433    * the common case is space properties in the header */
434   if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_UI)) {
435     while (ar && ar->prev) {
436       ar = ar->prev;
437     }
438     for (; ar; ar = ar->next) {
439       if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS)) {
440         ED_region_tag_redraw(ar);
441       }
442     }
443   }
444 }
445 /* Follow wmMsgNotifyFn spec */
446 void ED_area_do_msg_notify_tag_refresh(bContext *UNUSED(C),
447                                        wmMsgSubscribeKey *UNUSED(msg_key),
448                                        wmMsgSubscribeValue *msg_val)
449 {
450   ScrArea *sa = msg_val->user_data;
451   ED_area_tag_refresh(sa);
452 }
453
454 /**
455  * Although there's no general support for minimizing areas, the status-bar can
456  * be snapped to be only a few pixels high. A few pixels rather than 0 so it
457  * can be un-minimized again. We consider it pseudo-minimized and don't draw
458  * it then.
459  */
460 static bool area_is_pseudo_minimized(const ScrArea *area)
461 {
462   return (area->winx < 3) || (area->winy < 3);
463 }
464
465 /* only exported for WM */
466 void ED_region_do_layout(bContext *C, ARegion *ar)
467 {
468   /* This is optional, only needed for dynamically sized regions. */
469   ScrArea *sa = CTX_wm_area(C);
470   ARegionType *at = ar->type;
471
472   if (!at->layout) {
473     return;
474   }
475
476   if (at->do_lock || (sa && area_is_pseudo_minimized(sa))) {
477     return;
478   }
479
480   ar->do_draw |= RGN_DRAWING;
481
482   UI_SetTheme(sa ? sa->spacetype : 0, at->regionid);
483   at->layout(C, ar);
484 }
485
486 /* only exported for WM */
487 void ED_region_do_draw(bContext *C, ARegion *ar)
488 {
489   wmWindow *win = CTX_wm_window(C);
490   ScrArea *sa = CTX_wm_area(C);
491   ARegionType *at = ar->type;
492
493   /* see BKE_spacedata_draw_locks() */
494   if (at->do_lock)
495     return;
496
497   ar->do_draw |= RGN_DRAWING;
498
499   /* Set viewport, scissor, ortho and ar->drawrct. */
500   wmPartialViewport(&ar->drawrct, &ar->winrct, &ar->drawrct);
501
502   wmOrtho2_region_pixelspace(ar);
503
504   UI_SetTheme(sa ? sa->spacetype : 0, at->regionid);
505
506   if (sa && area_is_pseudo_minimized(sa)) {
507     UI_ThemeClearColor(TH_EDITOR_OUTLINE);
508     glClear(GL_COLOR_BUFFER_BIT);
509     return;
510   }
511   /* optional header info instead? */
512   else if (ar->headerstr) {
513     region_draw_status_text(sa, ar);
514   }
515   else if (at->draw) {
516     at->draw(C, ar);
517   }
518
519   /* XXX test: add convention to end regions always in pixel space, for drawing of borders/gestures etc */
520   ED_region_pixelspace(ar);
521
522   ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_PIXEL);
523
524   region_draw_azones(sa, ar);
525
526   /* for debugging unneeded area redraws and partial redraw */
527 #if 0
528   GPU_blend(true);
529   GPUVertFormat *format = immVertexFormat();
530   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
531   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
532   immUniformColor4f(drand48(), drand48(), drand48(), 0.1f);
533   immRectf(pos,
534            ar->drawrct.xmin - ar->winrct.xmin,
535            ar->drawrct.ymin - ar->winrct.ymin,
536            ar->drawrct.xmax - ar->winrct.xmin,
537            ar->drawrct.ymax - ar->winrct.ymin);
538   immUnbindProgram();
539   GPU_blend(false);
540 #endif
541
542   memset(&ar->drawrct, 0, sizeof(ar->drawrct));
543
544   UI_blocklist_free_inactive(C, &ar->uiblocks);
545
546   if (sa) {
547     const bScreen *screen = WM_window_get_active_screen(win);
548
549     /* Only region emboss for top-bar */
550     if ((screen->state != SCREENFULL) && ED_area_is_global(sa)) {
551       region_draw_emboss(ar, &ar->winrct, (REGION_EMBOSS_LEFT | REGION_EMBOSS_RIGHT));
552     }
553     else if ((ar->regiontype == RGN_TYPE_WINDOW) && (ar->alignment == RGN_ALIGN_QSPLIT)) {
554
555       /* draw separating lines between the quad views */
556
557       float color[4] = {0.0f, 0.0f, 0.0f, 0.8f};
558       UI_GetThemeColor3fv(TH_EDITOR_OUTLINE, color);
559       GPUVertFormat *format = immVertexFormat();
560       uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
561       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
562       immUniformColor4fv(color);
563       GPU_line_width(1.0f);
564       imm_draw_box_wire_2d(
565           pos, 0, 0, ar->winrct.xmax - ar->winrct.xmin + 1, ar->winrct.ymax - ar->winrct.ymin + 1);
566       immUnbindProgram();
567     }
568   }
569
570   /* We may want to detach message-subscriptions from drawing. */
571   {
572     WorkSpace *workspace = CTX_wm_workspace(C);
573     wmWindowManager *wm = CTX_wm_manager(C);
574     bScreen *screen = WM_window_get_active_screen(win);
575     Scene *scene = CTX_data_scene(C);
576     struct wmMsgBus *mbus = wm->message_bus;
577     WM_msgbus_clear_by_owner(mbus, ar);
578
579     /* Cheat, always subscribe to this space type properties.
580      *
581      * This covers most cases and avoids copy-paste similar code for each space type.
582      */
583     if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS, RGN_TYPE_UI, RGN_TYPE_TOOLS)) {
584       SpaceLink *sl = sa->spacedata.first;
585
586       PointerRNA ptr;
587       RNA_pointer_create(&screen->id, &RNA_Space, sl, &ptr);
588
589       wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
590           .owner = ar,
591           .user_data = ar,
592           .notify = ED_region_do_msg_notify_tag_redraw,
593       };
594       /* All properties for this space type. */
595       WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_region_tag_redraw, __func__);
596     }
597
598     ED_region_message_subscribe(C, workspace, scene, screen, sa, ar, mbus);
599   }
600 }
601
602 /* **********************************
603  * maybe silly, but let's try for now
604  * to keep these tags protected
605  * ********************************** */
606
607 void ED_region_tag_redraw(ARegion *ar)
608 {
609   /* don't tag redraw while drawing, it shouldn't happen normally
610    * but python scripts can cause this to happen indirectly */
611   if (ar && !(ar->do_draw & RGN_DRAWING)) {
612     /* zero region means full region redraw */
613     ar->do_draw &= ~(RGN_DRAW_PARTIAL | RGN_DRAW_NO_REBUILD);
614     ar->do_draw |= RGN_DRAW;
615     memset(&ar->drawrct, 0, sizeof(ar->drawrct));
616   }
617 }
618
619 void ED_region_tag_redraw_overlay(ARegion *ar)
620 {
621   if (ar)
622     ar->do_draw_overlay = RGN_DRAW;
623 }
624
625 void ED_region_tag_redraw_no_rebuild(ARegion *ar)
626 {
627   if (ar && !(ar->do_draw & (RGN_DRAWING | RGN_DRAW))) {
628     ar->do_draw &= ~RGN_DRAW_PARTIAL;
629     ar->do_draw |= RGN_DRAW_NO_REBUILD;
630     memset(&ar->drawrct, 0, sizeof(ar->drawrct));
631   }
632 }
633
634 void ED_region_tag_refresh_ui(ARegion *ar)
635 {
636   if (ar) {
637     ar->do_draw |= RGN_DRAW_REFRESH_UI;
638   }
639 }
640
641 void ED_region_tag_redraw_partial(ARegion *ar, const rcti *rct)
642 {
643   if (ar && !(ar->do_draw & RGN_DRAWING)) {
644     if (!(ar->do_draw & (RGN_DRAW | RGN_DRAW_NO_REBUILD | RGN_DRAW_PARTIAL))) {
645       /* no redraw set yet, set partial region */
646       ar->do_draw |= RGN_DRAW_PARTIAL;
647       ar->drawrct = *rct;
648     }
649     else if (ar->drawrct.xmin != ar->drawrct.xmax) {
650       BLI_assert((ar->do_draw & RGN_DRAW_PARTIAL) != 0);
651       /* partial redraw already set, expand region */
652       BLI_rcti_union(&ar->drawrct, rct);
653     }
654     else {
655       BLI_assert((ar->do_draw & (RGN_DRAW | RGN_DRAW_NO_REBUILD)) != 0);
656       /* Else, full redraw is already requested, nothing to do here. */
657     }
658   }
659 }
660
661 void ED_area_tag_redraw(ScrArea *sa)
662 {
663   ARegion *ar;
664
665   if (sa)
666     for (ar = sa->regionbase.first; ar; ar = ar->next)
667       ED_region_tag_redraw(ar);
668 }
669
670 void ED_area_tag_redraw_no_rebuild(ScrArea *sa)
671 {
672   ARegion *ar;
673
674   if (sa)
675     for (ar = sa->regionbase.first; ar; ar = ar->next)
676       ED_region_tag_redraw_no_rebuild(ar);
677 }
678
679 void ED_area_tag_redraw_regiontype(ScrArea *sa, int regiontype)
680 {
681   ARegion *ar;
682
683   if (sa) {
684     for (ar = sa->regionbase.first; ar; ar = ar->next) {
685       if (ar->regiontype == regiontype) {
686         ED_region_tag_redraw(ar);
687       }
688     }
689   }
690 }
691
692 void ED_area_tag_refresh(ScrArea *sa)
693 {
694   if (sa)
695     sa->do_refresh = true;
696 }
697
698 /* *************************************************************** */
699
700 /* use NULL to disable it */
701 void ED_area_status_text(ScrArea *sa, const char *str)
702 {
703   ARegion *ar;
704
705   /* happens when running transform operators in background mode */
706   if (sa == NULL)
707     return;
708
709   for (ar = sa->regionbase.first; ar; ar = ar->next) {
710     if (ar->regiontype == RGN_TYPE_HEADER) {
711       if (str) {
712         if (ar->headerstr == NULL)
713           ar->headerstr = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint");
714         BLI_strncpy(ar->headerstr, str, UI_MAX_DRAW_STR);
715         BLI_str_rstrip(ar->headerstr);
716       }
717       else if (ar->headerstr) {
718         MEM_freeN(ar->headerstr);
719         ar->headerstr = NULL;
720       }
721       ED_region_tag_redraw(ar);
722     }
723   }
724 }
725
726 void ED_workspace_status_text(bContext *C, const char *str)
727 {
728   wmWindow *win = CTX_wm_window(C);
729   WorkSpace *workspace = CTX_wm_workspace(C);
730
731   /* Can be NULL when running operators in background mode. */
732   if (workspace == NULL)
733     return;
734
735   if (str) {
736     if (workspace->status_text == NULL)
737       workspace->status_text = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint");
738     BLI_strncpy(workspace->status_text, str, UI_MAX_DRAW_STR);
739   }
740   else if (workspace->status_text) {
741     MEM_freeN(workspace->status_text);
742     workspace->status_text = NULL;
743   }
744
745   /* Redraw status bar. */
746   for (ScrArea *sa = win->global_areas.areabase.first; sa; sa = sa->next) {
747     if (sa->spacetype == SPACE_STATUSBAR) {
748       ED_area_tag_redraw(sa);
749       break;
750     }
751   }
752 }
753
754 /* ************************************************************ */
755
756 static void area_azone_initialize(wmWindow *win, const bScreen *screen, ScrArea *sa)
757 {
758   AZone *az;
759
760   /* reinitialize entirely, regions and fullscreen add azones too */
761   BLI_freelistN(&sa->actionzones);
762
763   if (screen->state != SCREENNORMAL) {
764     return;
765   }
766
767   if (U.app_flag & USER_APP_LOCK_UI_LAYOUT) {
768     return;
769   }
770
771   if (ED_area_is_global(sa)) {
772     return;
773   }
774
775   if (screen->temp) {
776     return;
777   }
778
779   float coords[4][4] = {
780       /* Bottom-left. */
781       {sa->totrct.xmin - U.pixelsize,
782        sa->totrct.ymin - U.pixelsize,
783        sa->totrct.xmin + AZONESPOTW,
784        sa->totrct.ymin + AZONESPOTH},
785       /* Bottom-right. */
786       {sa->totrct.xmax - AZONESPOTW,
787        sa->totrct.ymin - U.pixelsize,
788        sa->totrct.xmax + U.pixelsize,
789        sa->totrct.ymin + AZONESPOTH},
790       /* Top-left. */
791       {sa->totrct.xmin - U.pixelsize,
792        sa->totrct.ymax - AZONESPOTH,
793        sa->totrct.xmin + AZONESPOTW,
794        sa->totrct.ymax + U.pixelsize},
795       /* Top-right. */
796       {sa->totrct.xmax - AZONESPOTW,
797        sa->totrct.ymax - AZONESPOTH,
798        sa->totrct.xmax + U.pixelsize,
799        sa->totrct.ymax + U.pixelsize},
800   };
801
802   for (int i = 0; i < 4; i++) {
803     /* can't click on bottom corners on OS X, already used for resizing */
804 #ifdef __APPLE__
805     if (!WM_window_is_fullscreen(win) &&
806         ((coords[i][0] == 0 && coords[i][1] == 0) ||
807          (coords[i][0] == WM_window_pixels_x(win) && coords[i][1] == 0))) {
808       continue;
809     }
810 #else
811     (void)win;
812 #endif
813
814     /* set area action zones */
815     az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
816     BLI_addtail(&(sa->actionzones), az);
817     az->type = AZONE_AREA;
818     az->x1 = coords[i][0];
819     az->y1 = coords[i][1];
820     az->x2 = coords[i][2];
821     az->y2 = coords[i][3];
822     BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
823   }
824 }
825
826 static void fullscreen_azone_initialize(ScrArea *sa, ARegion *ar)
827 {
828   AZone *az;
829
830   if (ED_area_is_global(sa) || (ar->regiontype != RGN_TYPE_WINDOW))
831     return;
832
833   az = (AZone *)MEM_callocN(sizeof(AZone), "fullscreen action zone");
834   BLI_addtail(&(sa->actionzones), az);
835   az->type = AZONE_FULLSCREEN;
836   az->ar = ar;
837   az->alpha = 0.0f;
838
839   az->x1 = ar->winrct.xmax - (AZONEFADEOUT - 1);
840   az->y1 = ar->winrct.ymax - (AZONEFADEOUT - 1);
841   az->x2 = ar->winrct.xmax;
842   az->y2 = ar->winrct.ymax;
843   BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
844 }
845
846 #define AZONEPAD_EDGE (0.1f * U.widget_unit)
847 #define AZONEPAD_ICON (0.45f * U.widget_unit)
848 static void region_azone_edge(AZone *az, ARegion *ar)
849 {
850   switch (az->edge) {
851     case AE_TOP_TO_BOTTOMRIGHT:
852       az->x1 = ar->winrct.xmin;
853       az->y1 = ar->winrct.ymax - AZONEPAD_EDGE;
854       az->x2 = ar->winrct.xmax;
855       az->y2 = ar->winrct.ymax + AZONEPAD_EDGE;
856       break;
857     case AE_BOTTOM_TO_TOPLEFT:
858       az->x1 = ar->winrct.xmin;
859       az->y1 = ar->winrct.ymin + AZONEPAD_EDGE;
860       az->x2 = ar->winrct.xmax;
861       az->y2 = ar->winrct.ymin - AZONEPAD_EDGE;
862       break;
863     case AE_LEFT_TO_TOPRIGHT:
864       az->x1 = ar->winrct.xmin - AZONEPAD_EDGE;
865       az->y1 = ar->winrct.ymin;
866       az->x2 = ar->winrct.xmin + AZONEPAD_EDGE;
867       az->y2 = ar->winrct.ymax;
868       break;
869     case AE_RIGHT_TO_TOPLEFT:
870       az->x1 = ar->winrct.xmax + AZONEPAD_EDGE;
871       az->y1 = ar->winrct.ymin;
872       az->x2 = ar->winrct.xmax - AZONEPAD_EDGE;
873       az->y2 = ar->winrct.ymax;
874       break;
875   }
876
877   BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
878 }
879
880 /* region already made zero sized, in shape of edge */
881 static void region_azone_tab_plus(ScrArea *sa, AZone *az, ARegion *ar)
882 {
883   AZone *azt;
884   int tot = 0, add;
885   /* Edge offset multiplied by the  */
886
887   float edge_offset = 1.0f;
888   const float tab_size_x = 0.7f * U.widget_unit;
889   const float tab_size_y = 0.4f * U.widget_unit;
890
891   for (azt = sa->actionzones.first; azt; azt = azt->next) {
892     if (azt->edge == az->edge)
893       tot++;
894   }
895
896   switch (az->edge) {
897     case AE_TOP_TO_BOTTOMRIGHT:
898       add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0;
899       az->x1 = ar->winrct.xmax - ((edge_offset + 1.0f) * tab_size_x);
900       az->y1 = ar->winrct.ymax - add;
901       az->x2 = ar->winrct.xmax - (edge_offset * tab_size_x);
902       az->y2 = ar->winrct.ymax - add + tab_size_y;
903       break;
904     case AE_BOTTOM_TO_TOPLEFT:
905       az->x1 = ar->winrct.xmax - ((edge_offset + 1.0f) * tab_size_x);
906       az->y1 = ar->winrct.ymin - tab_size_y;
907       az->x2 = ar->winrct.xmax - (edge_offset * tab_size_x);
908       az->y2 = ar->winrct.ymin;
909       break;
910     case AE_LEFT_TO_TOPRIGHT:
911       az->x1 = ar->winrct.xmin - tab_size_y;
912       az->y1 = ar->winrct.ymax - ((edge_offset + 1.0f) * tab_size_x);
913       az->x2 = ar->winrct.xmin;
914       az->y2 = ar->winrct.ymax - (edge_offset * tab_size_x);
915       break;
916     case AE_RIGHT_TO_TOPLEFT:
917       az->x1 = ar->winrct.xmax;
918       az->y1 = ar->winrct.ymax - ((edge_offset + 1.0f) * tab_size_x);
919       az->x2 = ar->winrct.xmax + tab_size_y;
920       az->y2 = ar->winrct.ymax - (edge_offset * tab_size_x);
921       break;
922   }
923   /* rect needed for mouse pointer test */
924   BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
925 }
926
927 static bool region_azone_edge_poll(const ARegion *ar, const bool is_fullscreen)
928 {
929   const bool is_hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
930
931   if (is_hidden && is_fullscreen) {
932     return false;
933   }
934   if (!is_hidden && ar->regiontype == RGN_TYPE_HEADER) {
935     return false;
936   }
937
938   return true;
939 }
940
941 static void region_azone_edge_initialize(ScrArea *sa,
942                                          ARegion *ar,
943                                          AZEdge edge,
944                                          const bool is_fullscreen)
945 {
946   AZone *az = NULL;
947   const bool is_hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
948
949   if (!region_azone_edge_poll(ar, is_fullscreen)) {
950     return;
951   }
952
953   az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
954   BLI_addtail(&(sa->actionzones), az);
955   az->type = AZONE_REGION;
956   az->ar = ar;
957   az->edge = edge;
958
959   if (is_hidden) {
960     region_azone_tab_plus(sa, az, ar);
961   }
962   else {
963     region_azone_edge(az, ar);
964   }
965 }
966
967 static void region_azone_scrollbar_initialize(ScrArea *sa,
968                                               ARegion *ar,
969                                               AZScrollDirection direction)
970 {
971   rcti scroller_vert = (direction == AZ_SCROLL_VERT) ? ar->v2d.vert : ar->v2d.hor;
972   AZone *az = MEM_callocN(sizeof(*az), __func__);
973
974   BLI_addtail(&sa->actionzones, az);
975   az->type = AZONE_REGION_SCROLL;
976   az->ar = ar;
977   az->direction = direction;
978
979   if (direction == AZ_SCROLL_VERT) {
980     az->ar->v2d.alpha_vert = 0;
981   }
982   else if (direction == AZ_SCROLL_HOR) {
983     az->ar->v2d.alpha_hor = 0;
984   }
985
986   BLI_rcti_translate(&scroller_vert, ar->winrct.xmin, ar->winrct.ymin);
987   az->x1 = scroller_vert.xmin - AZONEFADEIN;
988   az->y1 = scroller_vert.ymin - AZONEFADEIN;
989   az->x2 = scroller_vert.xmax + AZONEFADEIN;
990   az->y2 = scroller_vert.ymax + AZONEFADEIN;
991
992   BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
993 }
994
995 static void region_azones_scrollbars_initialize(ScrArea *sa, ARegion *ar)
996 {
997   const View2D *v2d = &ar->v2d;
998
999   if ((v2d->scroll & V2D_SCROLL_VERTICAL) && ((v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) == 0)) {
1000     region_azone_scrollbar_initialize(sa, ar, AZ_SCROLL_VERT);
1001   }
1002   if ((v2d->scroll & V2D_SCROLL_HORIZONTAL) &&
1003       ((v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) == 0)) {
1004     region_azone_scrollbar_initialize(sa, ar, AZ_SCROLL_HOR);
1005   }
1006 }
1007
1008 /* *************************************************************** */
1009
1010 static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar, const int alignment)
1011 {
1012   const bool is_fullscreen = screen->state == SCREENFULL;
1013
1014   /* edge code (t b l r) is along which area edge azone will be drawn */
1015   if (alignment == RGN_ALIGN_TOP)
1016     region_azone_edge_initialize(sa, ar, AE_BOTTOM_TO_TOPLEFT, is_fullscreen);
1017   else if (alignment == RGN_ALIGN_BOTTOM)
1018     region_azone_edge_initialize(sa, ar, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen);
1019   else if (alignment == RGN_ALIGN_RIGHT)
1020     region_azone_edge_initialize(sa, ar, AE_LEFT_TO_TOPRIGHT, is_fullscreen);
1021   else if (alignment == RGN_ALIGN_LEFT)
1022     region_azone_edge_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen);
1023
1024   if (is_fullscreen) {
1025     fullscreen_azone_initialize(sa, ar);
1026   }
1027
1028   region_azones_scrollbars_initialize(sa, ar);
1029 }
1030
1031 /* dir is direction to check, not the splitting edge direction! */
1032 static int rct_fits(const rcti *rect, char dir, int size)
1033 {
1034   if (dir == 'h') {
1035     return BLI_rcti_size_x(rect) + 1 - size;
1036   }
1037   else { /* 'v' */
1038     return BLI_rcti_size_y(rect) + 1 - size;
1039   }
1040 }
1041
1042 /* *************************************************************** */
1043
1044 /* ar should be overlapping */
1045 /* function checks if some overlapping region was defined before - on same place */
1046 static void region_overlap_fix(ScrArea *sa, ARegion *ar)
1047 {
1048   ARegion *ar1;
1049   const int align = ar->alignment & ~RGN_SPLIT_PREV;
1050   int align1 = 0;
1051
1052   /* find overlapping previous region on same place */
1053   for (ar1 = ar->prev; ar1; ar1 = ar1->prev) {
1054     if (ar1->flag & (RGN_FLAG_HIDDEN)) {
1055       continue;
1056     }
1057
1058     if (ar1->overlap && ((ar1->alignment & RGN_SPLIT_PREV) == 0)) {
1059       if (ELEM(ar1->alignment, RGN_ALIGN_FLOAT)) {
1060         continue;
1061       }
1062       align1 = ar1->alignment;
1063       if (BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) {
1064         if (align1 != align) {
1065           /* Left overlapping right or vice-versa, forbid this! */
1066           ar->flag |= RGN_FLAG_TOO_SMALL;
1067           return;
1068         }
1069         /* Else, we have our previous region on same side. */
1070         break;
1071       }
1072     }
1073   }
1074
1075   /* translate or close */
1076   if (ar1) {
1077     if (align1 == RGN_ALIGN_LEFT) {
1078       if (ar->winrct.xmax + ar1->winx > sa->winx - U.widget_unit) {
1079         ar->flag |= RGN_FLAG_TOO_SMALL;
1080         return;
1081       }
1082       else {
1083         BLI_rcti_translate(&ar->winrct, ar1->winx, 0);
1084       }
1085     }
1086     else if (align1 == RGN_ALIGN_RIGHT) {
1087       if (ar->winrct.xmin - ar1->winx < U.widget_unit) {
1088         ar->flag |= RGN_FLAG_TOO_SMALL;
1089         return;
1090       }
1091       else {
1092         BLI_rcti_translate(&ar->winrct, -ar1->winx, 0);
1093       }
1094     }
1095   }
1096
1097   /* At this point, 'ar' is in its final position and still open.
1098    * Make a final check it does not overlap any previous 'other side' region. */
1099   for (ar1 = ar->prev; ar1; ar1 = ar1->prev) {
1100     if (ar1->flag & (RGN_FLAG_HIDDEN)) {
1101       continue;
1102     }
1103     if (ELEM(ar1->alignment, RGN_ALIGN_FLOAT)) {
1104       continue;
1105     }
1106
1107     if (ar1->overlap && (ar1->alignment & RGN_SPLIT_PREV) == 0) {
1108       if ((ar1->alignment != align) && BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) {
1109         /* Left overlapping right or vice-versa, forbid this! */
1110         ar->flag |= RGN_FLAG_TOO_SMALL;
1111         return;
1112       }
1113     }
1114   }
1115 }
1116
1117 /* overlapping regions only in the following restricted cases */
1118 bool ED_region_is_overlap(int spacetype, int regiontype)
1119 {
1120   if (regiontype == RGN_TYPE_HUD) {
1121     return true;
1122   }
1123   if (U.uiflag2 & USER_REGION_OVERLAP) {
1124     if (spacetype == SPACE_NODE) {
1125       if (regiontype == RGN_TYPE_TOOLS) {
1126         return true;
1127       }
1128     }
1129     else if (ELEM(spacetype, SPACE_VIEW3D, SPACE_IMAGE)) {
1130       if (ELEM(regiontype,
1131                RGN_TYPE_TOOLS,
1132                RGN_TYPE_UI,
1133                RGN_TYPE_TOOL_PROPS,
1134                RGN_TYPE_HEADER,
1135                RGN_TYPE_FOOTER)) {
1136         return true;
1137       }
1138     }
1139   }
1140
1141   return false;
1142 }
1143
1144 static void region_rect_recursive(
1145     ScrArea *sa, ARegion *ar, rcti *remainder, rcti *overlap_remainder, int quad)
1146 {
1147   rcti *remainder_prev = remainder;
1148
1149   if (ar == NULL)
1150     return;
1151
1152   int prev_winx = ar->winx;
1153   int prev_winy = ar->winy;
1154
1155   /* no returns in function, winrct gets set in the end again */
1156   BLI_rcti_init(&ar->winrct, 0, 0, 0, 0);
1157
1158   /* for test; allow split of previously defined region */
1159   if (ar->alignment & RGN_SPLIT_PREV)
1160     if (ar->prev)
1161       remainder = &ar->prev->winrct;
1162
1163   int alignment = ar->alignment & ~RGN_SPLIT_PREV;
1164
1165   /* set here, assuming userpref switching forces to call this again */
1166   ar->overlap = ED_region_is_overlap(sa->spacetype, ar->regiontype);
1167
1168   /* clear state flags first */
1169   ar->flag &= ~(RGN_FLAG_TOO_SMALL | RGN_FLAG_SIZE_CLAMP_X | RGN_FLAG_SIZE_CLAMP_Y);
1170   /* user errors */
1171   if ((ar->next == NULL) && !ELEM(alignment, RGN_ALIGN_QSPLIT, RGN_ALIGN_FLOAT)) {
1172     alignment = RGN_ALIGN_NONE;
1173   }
1174
1175   /* prefsize, taking into account DPI */
1176   int prefsizex = UI_DPI_FAC * ((ar->sizex > 1) ? ar->sizex + 0.5f : ar->type->prefsizex);
1177   int prefsizey;
1178
1179   if (ar->flag & RGN_FLAG_PREFSIZE_OR_HIDDEN) {
1180     prefsizex = UI_DPI_FAC * ar->type->prefsizex;
1181     prefsizey = UI_DPI_FAC * ar->type->prefsizey;
1182   }
1183   else if (ar->regiontype == RGN_TYPE_HEADER) {
1184     prefsizey = ED_area_headersize();
1185   }
1186   else if (ar->regiontype == RGN_TYPE_FOOTER) {
1187     prefsizey = ED_area_footersize();
1188   }
1189   else if (ED_area_is_global(sa)) {
1190     prefsizey = ED_region_global_size_y();
1191   }
1192   else if (ar->regiontype == RGN_TYPE_UI && sa->spacetype == SPACE_FILE) {
1193     prefsizey = UI_UNIT_Y * 2 + (UI_UNIT_Y / 2);
1194   }
1195   else {
1196     prefsizey = UI_DPI_FAC * (ar->sizey > 1 ? ar->sizey + 0.5f : ar->type->prefsizey);
1197   }
1198
1199   if (ar->flag & RGN_FLAG_HIDDEN) {
1200     /* hidden is user flag */
1201   }
1202   else if (alignment == RGN_ALIGN_FLOAT) {
1203     /**
1204      * \note Currently this window type is only used for #RGN_TYPE_HUD,
1205      * We expect the panel to resize it's self to be larger.
1206      *
1207      * This aligns to the lower left of the area.
1208      */
1209     const int size_min[2] = {UI_UNIT_X, UI_UNIT_Y};
1210     rcti overlap_remainder_margin = *overlap_remainder;
1211     BLI_rcti_resize(&overlap_remainder_margin,
1212                     max_ii(0, BLI_rcti_size_x(overlap_remainder) - UI_UNIT_X / 2),
1213                     max_ii(0, BLI_rcti_size_y(overlap_remainder) - UI_UNIT_Y / 2));
1214     ar->winrct.xmin = overlap_remainder_margin.xmin;
1215     ar->winrct.ymin = overlap_remainder_margin.ymin;
1216     ar->winrct.xmax = ar->winrct.xmin + prefsizex - 1;
1217     ar->winrct.ymax = ar->winrct.ymin + prefsizey - 1;
1218
1219     BLI_rcti_isect(&ar->winrct, &overlap_remainder_margin, &ar->winrct);
1220
1221     if (BLI_rcti_size_x(&ar->winrct) != prefsizex - 1) {
1222       ar->flag |= RGN_FLAG_SIZE_CLAMP_X;
1223     }
1224     if (BLI_rcti_size_y(&ar->winrct) != prefsizey - 1) {
1225       ar->flag |= RGN_FLAG_SIZE_CLAMP_Y;
1226     }
1227
1228     /* We need to use a test that wont have been previously clamped. */
1229     rcti winrct_test = {
1230         .xmin = ar->winrct.xmin,
1231         .ymin = ar->winrct.ymin,
1232         .xmax = ar->winrct.xmin + size_min[0],
1233         .ymax = ar->winrct.ymin + size_min[1],
1234     };
1235     BLI_rcti_isect(&winrct_test, &overlap_remainder_margin, &winrct_test);
1236     if (BLI_rcti_size_x(&winrct_test) < size_min[0] ||
1237         BLI_rcti_size_y(&winrct_test) < size_min[1]) {
1238       ar->flag |= RGN_FLAG_TOO_SMALL;
1239     }
1240   }
1241   else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) {
1242     /* remainder is too small for any usage */
1243     ar->flag |= RGN_FLAG_TOO_SMALL;
1244   }
1245   else if (alignment == RGN_ALIGN_NONE) {
1246     /* typically last region */
1247     ar->winrct = *remainder;
1248     BLI_rcti_init(remainder, 0, 0, 0, 0);
1249   }
1250   else if (alignment == RGN_ALIGN_TOP || alignment == RGN_ALIGN_BOTTOM) {
1251     rcti *winrct = (ar->overlap) ? overlap_remainder : remainder;
1252
1253     if (rct_fits(winrct, 'v', prefsizey) < 0) {
1254       ar->flag |= RGN_FLAG_TOO_SMALL;
1255     }
1256     else {
1257       int fac = rct_fits(winrct, 'v', prefsizey);
1258
1259       if (fac < 0)
1260         prefsizey += fac;
1261
1262       ar->winrct = *winrct;
1263
1264       if (alignment == RGN_ALIGN_TOP) {
1265         ar->winrct.ymin = ar->winrct.ymax - prefsizey + 1;
1266         winrct->ymax = ar->winrct.ymin - 1;
1267       }
1268       else {
1269         ar->winrct.ymax = ar->winrct.ymin + prefsizey - 1;
1270         winrct->ymin = ar->winrct.ymax + 1;
1271       }
1272     }
1273   }
1274   else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
1275     rcti *winrct = (ar->overlap) ? overlap_remainder : remainder;
1276
1277     if (rct_fits(winrct, 'h', prefsizex) < 0) {
1278       ar->flag |= RGN_FLAG_TOO_SMALL;
1279     }
1280     else {
1281       int fac = rct_fits(winrct, 'h', prefsizex);
1282
1283       if (fac < 0)
1284         prefsizex += fac;
1285
1286       ar->winrct = *winrct;
1287
1288       if (alignment == RGN_ALIGN_RIGHT) {
1289         ar->winrct.xmin = ar->winrct.xmax - prefsizex + 1;
1290         winrct->xmax = ar->winrct.xmin - 1;
1291       }
1292       else {
1293         ar->winrct.xmax = ar->winrct.xmin + prefsizex - 1;
1294         winrct->xmin = ar->winrct.xmax + 1;
1295       }
1296     }
1297   }
1298   else if (alignment == RGN_ALIGN_VSPLIT || alignment == RGN_ALIGN_HSPLIT) {
1299     /* percentage subdiv*/
1300     ar->winrct = *remainder;
1301
1302     if (alignment == RGN_ALIGN_HSPLIT) {
1303       if (rct_fits(remainder, 'h', prefsizex) > 4) {
1304         ar->winrct.xmax = BLI_rcti_cent_x(remainder);
1305         remainder->xmin = ar->winrct.xmax + 1;
1306       }
1307       else {
1308         BLI_rcti_init(remainder, 0, 0, 0, 0);
1309       }
1310     }
1311     else {
1312       if (rct_fits(remainder, 'v', prefsizey) > 4) {
1313         ar->winrct.ymax = BLI_rcti_cent_y(remainder);
1314         remainder->ymin = ar->winrct.ymax + 1;
1315       }
1316       else {
1317         BLI_rcti_init(remainder, 0, 0, 0, 0);
1318       }
1319     }
1320   }
1321   else if (alignment == RGN_ALIGN_QSPLIT) {
1322     ar->winrct = *remainder;
1323
1324     /* test if there's still 4 regions left */
1325     if (quad == 0) {
1326       ARegion *artest = ar->next;
1327       int count = 1;
1328
1329       while (artest) {
1330         artest->alignment = RGN_ALIGN_QSPLIT;
1331         artest = artest->next;
1332         count++;
1333       }
1334
1335       if (count != 4) {
1336         /* let's stop adding regions */
1337         BLI_rcti_init(remainder, 0, 0, 0, 0);
1338         if (G.debug & G_DEBUG)
1339           printf("region quadsplit failed\n");
1340       }
1341       else {
1342         quad = 1;
1343       }
1344     }
1345     if (quad) {
1346       if (quad == 1) { /* left bottom */
1347         ar->winrct.xmax = BLI_rcti_cent_x(remainder);
1348         ar->winrct.ymax = BLI_rcti_cent_y(remainder);
1349       }
1350       else if (quad == 2) { /* left top */
1351         ar->winrct.xmax = BLI_rcti_cent_x(remainder);
1352         ar->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
1353       }
1354       else if (quad == 3) { /* right bottom */
1355         ar->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
1356         ar->winrct.ymax = BLI_rcti_cent_y(remainder);
1357       }
1358       else { /* right top */
1359         ar->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
1360         ar->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
1361         BLI_rcti_init(remainder, 0, 0, 0, 0);
1362       }
1363
1364       quad++;
1365     }
1366   }
1367
1368   /* for speedup */
1369   ar->winx = BLI_rcti_size_x(&ar->winrct) + 1;
1370   ar->winy = BLI_rcti_size_y(&ar->winrct) + 1;
1371
1372   /* if region opened normally, we store this for hide/reveal usage */
1373   /* prevent rounding errors for UI_DPI_FAC mult and divide */
1374   if (ar->winx > 1)
1375     ar->sizex = (ar->winx + 0.5f) / UI_DPI_FAC;
1376   if (ar->winy > 1)
1377     ar->sizey = (ar->winy + 0.5f) / UI_DPI_FAC;
1378
1379   /* exception for multiple overlapping regions on same spot */
1380   if (ar->overlap && (alignment != RGN_ALIGN_FLOAT)) {
1381     region_overlap_fix(sa, ar);
1382   }
1383
1384   /* set winrect for azones */
1385   if (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
1386     ar->winrct = (ar->overlap) ? *overlap_remainder : *remainder;
1387
1388     switch (alignment) {
1389       case RGN_ALIGN_TOP:
1390         ar->winrct.ymin = ar->winrct.ymax;
1391         break;
1392       case RGN_ALIGN_BOTTOM:
1393         ar->winrct.ymax = ar->winrct.ymin;
1394         break;
1395       case RGN_ALIGN_RIGHT:
1396         ar->winrct.xmin = ar->winrct.xmax;
1397         break;
1398       case RGN_ALIGN_LEFT:
1399       default:
1400         /* prevent winrct to be valid */
1401         ar->winrct.xmax = ar->winrct.xmin;
1402         break;
1403     }
1404   }
1405
1406   /* restore prev-split exception */
1407   if (ar->alignment & RGN_SPLIT_PREV) {
1408     if (ar->prev) {
1409       remainder = remainder_prev;
1410       ar->prev->winx = BLI_rcti_size_x(&ar->prev->winrct) + 1;
1411       ar->prev->winy = BLI_rcti_size_y(&ar->prev->winrct) + 1;
1412     }
1413   }
1414
1415   /* After non-overlapping region, all following overlapping regions
1416    * fit within the remaining space again. */
1417   if (!ar->overlap) {
1418     *overlap_remainder = *remainder;
1419   }
1420
1421   region_rect_recursive(sa, ar->next, remainder, overlap_remainder, quad);
1422
1423   /* Tag for redraw if size changes. */
1424   if (ar->winx != prev_winx || ar->winy != prev_winy) {
1425     ED_region_tag_redraw(ar);
1426   }
1427 }
1428
1429 static void area_calc_totrct(ScrArea *sa, const rcti *window_rect)
1430 {
1431   short px = (short)U.pixelsize;
1432
1433   sa->totrct.xmin = sa->v1->vec.x;
1434   sa->totrct.xmax = sa->v4->vec.x;
1435   sa->totrct.ymin = sa->v1->vec.y;
1436   sa->totrct.ymax = sa->v2->vec.y;
1437
1438   /* scale down totrct by 1 pixel on all sides not matching window borders */
1439   if (sa->totrct.xmin > window_rect->xmin) {
1440     sa->totrct.xmin += px;
1441   }
1442   if (sa->totrct.xmax < (window_rect->xmax - 1)) {
1443     sa->totrct.xmax -= px;
1444   }
1445   if (sa->totrct.ymin > window_rect->ymin) {
1446     sa->totrct.ymin += px;
1447   }
1448   if (sa->totrct.ymax < (window_rect->ymax - 1)) {
1449     sa->totrct.ymax -= px;
1450   }
1451   /* Although the following asserts are correct they lead to a very unstable Blender.
1452    * And the asserts would fail even in 2.7x (they were added in 2.8x as part of the top-bar commit).
1453    * For more details see T54864. */
1454 #if 0
1455   BLI_assert(sa->totrct.xmin >= 0);
1456   BLI_assert(sa->totrct.xmax >= 0);
1457   BLI_assert(sa->totrct.ymin >= 0);
1458   BLI_assert(sa->totrct.ymax >= 0);
1459 #endif
1460
1461   /* for speedup */
1462   sa->winx = BLI_rcti_size_x(&sa->totrct) + 1;
1463   sa->winy = BLI_rcti_size_y(&sa->totrct) + 1;
1464 }
1465
1466 /* used for area initialize below */
1467 static void region_subwindow(ARegion *ar)
1468 {
1469   bool hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) != 0;
1470
1471   if ((ar->alignment & RGN_SPLIT_PREV) && ar->prev)
1472     hidden = hidden || (ar->prev->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
1473
1474   ar->visible = !hidden;
1475 }
1476
1477 /**
1478  * \param ar: Region, may be NULL when adding handlers for \a sa.
1479  */
1480 static void ed_default_handlers(
1481     wmWindowManager *wm, ScrArea *sa, ARegion *ar, ListBase *handlers, int flag)
1482 {
1483   BLI_assert(ar ? (&ar->handlers == handlers) : (&sa->handlers == handlers));
1484
1485   /* note, add-handler checks if it already exists */
1486
1487   /* XXX it would be good to have boundbox checks for some of these... */
1488   if (flag & ED_KEYMAP_UI) {
1489     wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "User Interface", 0, 0);
1490     WM_event_add_keymap_handler(handlers, keymap);
1491
1492     /* user interface widgets */
1493     UI_region_handlers_add(handlers);
1494   }
1495   if (flag & ED_KEYMAP_GIZMO) {
1496     BLI_assert(ar && ar->type->regionid == RGN_TYPE_WINDOW);
1497     if (ar) {
1498       /* Anything else is confusing, only allow this. */
1499       BLI_assert(&ar->handlers == handlers);
1500       if (ar->gizmo_map == NULL) {
1501         ar->gizmo_map = WM_gizmomap_new_from_type(
1502             &(const struct wmGizmoMapType_Params){sa->spacetype, ar->type->regionid});
1503       }
1504       WM_gizmomap_add_handlers(ar, ar->gizmo_map);
1505     }
1506   }
1507   if (flag & ED_KEYMAP_TOOL) {
1508     WM_event_add_keymap_handler_dynamic(&ar->handlers, WM_event_get_keymap_from_toolsystem, sa);
1509   }
1510   if (flag & ED_KEYMAP_VIEW2D) {
1511     /* 2d-viewport handling+manipulation */
1512     wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "View2D", 0, 0);
1513     WM_event_add_keymap_handler(handlers, keymap);
1514   }
1515   if (flag & ED_KEYMAP_MARKERS) {
1516     /* time-markers */
1517     wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Markers", 0, 0);
1518
1519     /* use a boundbox restricted map */
1520     /* same local check for all areas */
1521     static rcti rect = {0, 10000, 0, -1};
1522     rect.ymax = UI_MARKER_MARGIN_Y;
1523     BLI_assert(ar->type->regionid == RGN_TYPE_WINDOW);
1524     WM_event_add_keymap_handler_bb(handlers, keymap, &rect, &ar->winrct);
1525   }
1526   if (flag & ED_KEYMAP_ANIMATION) {
1527     /* frame changing and timeline operators (for time spaces) */
1528     wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Animation", 0, 0);
1529     WM_event_add_keymap_handler(handlers, keymap);
1530   }
1531   if (flag & ED_KEYMAP_FRAMES) {
1532     /* frame changing/jumping (for all spaces) */
1533     wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Frames", 0, 0);
1534     WM_event_add_keymap_handler(handlers, keymap);
1535   }
1536   if (flag & ED_KEYMAP_HEADER) {
1537     /* standard keymap for headers regions */
1538     wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Header", 0, 0);
1539     WM_event_add_keymap_handler(handlers, keymap);
1540   }
1541   if (flag & ED_KEYMAP_FOOTER) {
1542     /* standard keymap for footer regions */
1543     wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Footer", 0, 0);
1544     WM_event_add_keymap_handler(handlers, keymap);
1545   }
1546
1547   /* Keep last because of LMB/RMB handling, see: T57527. */
1548   if (flag & ED_KEYMAP_GPENCIL) {
1549     /* grease pencil */
1550     /* NOTE: This is now 4 keymaps - One for basic functionality,
1551      *       and others for special stroke modes (edit, paint and sculpt).
1552      *
1553      *       For now, it's easier to just include all,
1554      *       since you hardly want one without the others.
1555      */
1556     wmKeyMap *keymap_general = WM_keymap_ensure(wm->defaultconf, "Grease Pencil", 0, 0);
1557     WM_event_add_keymap_handler(handlers, keymap_general);
1558
1559     wmKeyMap *keymap_edit = WM_keymap_ensure(
1560         wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
1561     WM_event_add_keymap_handler(handlers, keymap_edit);
1562
1563     wmKeyMap *keymap_paint = WM_keymap_ensure(
1564         wm->defaultconf, "Grease Pencil Stroke Paint Mode", 0, 0);
1565     WM_event_add_keymap_handler(handlers, keymap_paint);
1566
1567     wmKeyMap *keymap_paint_draw = WM_keymap_ensure(
1568         wm->defaultconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0);
1569     WM_event_add_keymap_handler(handlers, keymap_paint_draw);
1570
1571     wmKeyMap *keymap_paint_erase = WM_keymap_ensure(
1572         wm->defaultconf, "Grease Pencil Stroke Paint (Erase)", 0, 0);
1573     WM_event_add_keymap_handler(handlers, keymap_paint_erase);
1574
1575     wmKeyMap *keymap_paint_fill = WM_keymap_ensure(
1576         wm->defaultconf, "Grease Pencil Stroke Paint (Fill)", 0, 0);
1577     WM_event_add_keymap_handler(handlers, keymap_paint_fill);
1578
1579     wmKeyMap *keymap_sculpt = WM_keymap_ensure(
1580         wm->defaultconf, "Grease Pencil Stroke Sculpt Mode", 0, 0);
1581     WM_event_add_keymap_handler(handlers, keymap_sculpt);
1582   }
1583 }
1584
1585 void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *area)
1586 {
1587   rcti rect, overlap_rect;
1588   rcti window_rect;
1589
1590   if (!(area->flag & AREA_FLAG_REGION_SIZE_UPDATE)) {
1591     return;
1592   }
1593   const bScreen *screen = WM_window_get_active_screen(win);
1594
1595   WM_window_rect_calc(win, &window_rect);
1596   area_calc_totrct(area, &window_rect);
1597
1598   /* region rect sizes */
1599   rect = area->totrct;
1600   overlap_rect = rect;
1601   region_rect_recursive(area, area->regionbase.first, &rect, &overlap_rect, 0);
1602
1603   /* Dynamically sized regions may have changed region sizes, so we have to force azone update. */
1604   area_azone_initialize(win, screen, area);
1605
1606   for (ARegion *ar = area->regionbase.first; ar; ar = ar->next) {
1607     region_subwindow(ar);
1608
1609     /* region size may have changed, init does necessary adjustments */
1610     if (ar->type->init) {
1611       ar->type->init(wm, ar);
1612     }
1613
1614     /* Some AZones use View2D data which is only updated in region init, so call that first! */
1615     region_azones_add(screen, area, ar, ar->alignment & ~RGN_SPLIT_PREV);
1616   }
1617   ED_area_azones_update(area, &win->eventstate->x);
1618
1619   area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE;
1620 }
1621
1622 /* called in screen_refresh, or screens_init, also area size changes */
1623 void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa)
1624 {
1625   WorkSpace *workspace = WM_window_get_active_workspace(win);
1626   const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
1627   ViewLayer *view_layer = WM_window_get_active_view_layer(win);
1628   ARegion *ar;
1629   rcti rect, overlap_rect;
1630   rcti window_rect;
1631
1632   if (ED_area_is_global(sa) && (sa->global->flag & GLOBAL_AREA_IS_HIDDEN)) {
1633     return;
1634   }
1635   WM_window_rect_calc(win, &window_rect);
1636
1637   /* set typedefinitions */
1638   sa->type = BKE_spacetype_from_id(sa->spacetype);
1639
1640   if (sa->type == NULL) {
1641     sa->spacetype = SPACE_VIEW3D;
1642     sa->type = BKE_spacetype_from_id(sa->spacetype);
1643   }
1644
1645   for (ar = sa->regionbase.first; ar; ar = ar->next)
1646     ar->type = BKE_regiontype_from_id_or_first(sa->type, ar->regiontype);
1647
1648   /* area sizes */
1649   area_calc_totrct(sa, &window_rect);
1650
1651   /* region rect sizes */
1652   rect = sa->totrct;
1653   overlap_rect = rect;
1654   region_rect_recursive(sa, sa->regionbase.first, &rect, &overlap_rect, 0);
1655   sa->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE;
1656
1657   /* default area handlers */
1658   ed_default_handlers(wm, sa, NULL, &sa->handlers, sa->type->keymapflag);
1659   /* checks spacedata, adds own handlers */
1660   if (sa->type->init)
1661     sa->type->init(wm, sa);
1662
1663   /* clear all azones, add the area triangle widgets */
1664   area_azone_initialize(win, screen, sa);
1665
1666   /* region windows, default and own handlers */
1667   for (ar = sa->regionbase.first; ar; ar = ar->next) {
1668     region_subwindow(ar);
1669
1670     if (ar->visible) {
1671       /* default region handlers */
1672       ed_default_handlers(wm, sa, ar, &ar->handlers, ar->type->keymapflag);
1673       /* own handlers */
1674       if (ar->type->init) {
1675         ar->type->init(wm, ar);
1676       }
1677     }
1678     else {
1679       /* prevent uiblocks to run */
1680       UI_blocklist_free(NULL, &ar->uiblocks);
1681     }
1682
1683     /* Some AZones use View2D data which is only updated in region init, so call that first! */
1684     region_azones_add(screen, sa, ar, ar->alignment & ~RGN_SPLIT_PREV);
1685   }
1686
1687   /* Avoid re-initializing tools while resizing the window. */
1688   if ((G.moving & G_TRANSFORM_WM) == 0) {
1689     if ((1 << sa->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) {
1690       WM_toolsystem_refresh_screen_area(workspace, view_layer, sa);
1691       sa->flag |= AREA_FLAG_ACTIVE_TOOL_UPDATE;
1692     }
1693     else {
1694       sa->runtime.tool = NULL;
1695       sa->runtime.is_tool_set = true;
1696     }
1697   }
1698 }
1699
1700 static void region_update_rect(ARegion *ar)
1701 {
1702   ar->winx = BLI_rcti_size_x(&ar->winrct) + 1;
1703   ar->winy = BLI_rcti_size_y(&ar->winrct) + 1;
1704
1705   /* v2d mask is used to subtract scrollbars from a 2d view. Needs initialize here. */
1706   BLI_rcti_init(&ar->v2d.mask, 0, ar->winx - 1, 0, ar->winy - 1);
1707 }
1708
1709 /**
1710  * Call to move a popup window (keep OpenGL context free!)
1711  */
1712 void ED_region_update_rect(ARegion *ar)
1713 {
1714   region_update_rect(ar);
1715 }
1716
1717 /* externally called for floating regions like menus */
1718 void ED_region_init(ARegion *ar)
1719 {
1720   /* refresh can be called before window opened */
1721   region_subwindow(ar);
1722
1723   region_update_rect(ar);
1724 }
1725
1726 void ED_region_cursor_set(wmWindow *win, ScrArea *sa, ARegion *ar)
1727 {
1728   if (ar && sa && ar->type && ar->type->cursor) {
1729     ar->type->cursor(win, sa, ar);
1730   }
1731   else {
1732     if (WM_cursor_set_from_tool(win, sa, ar)) {
1733       return;
1734     }
1735     WM_cursor_set(win, CURSOR_STD);
1736   }
1737 }
1738
1739 /* for use after changing visibility of regions */
1740 void ED_region_visibility_change_update(bContext *C, ScrArea *sa, ARegion *ar)
1741 {
1742   if (ar->flag & RGN_FLAG_HIDDEN)
1743     WM_event_remove_handlers(C, &ar->handlers);
1744
1745   ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
1746   ED_area_tag_redraw(sa);
1747 }
1748
1749 /* for quick toggle, can skip fades */
1750 void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade)
1751 {
1752   ScrArea *sa = CTX_wm_area(C);
1753
1754   ar->flag ^= RGN_FLAG_HIDDEN;
1755
1756   if (do_fade && ar->overlap) {
1757     /* starts a timer, and in end calls the stuff below itself (region_sblend_invoke()) */
1758     ED_region_visibility_change_update_animated(C, sa, ar);
1759   }
1760   else {
1761     ED_region_visibility_change_update(C, sa, ar);
1762   }
1763 }
1764
1765 /* exported to all editors, uses fading default */
1766 void ED_region_toggle_hidden(bContext *C, ARegion *ar)
1767 {
1768   region_toggle_hidden(C, ar, true);
1769 }
1770
1771 /**
1772  * we swap spaces for fullscreen to keep all allocated data area vertices were set
1773  */
1774 void ED_area_data_copy(ScrArea *sa_dst, ScrArea *sa_src, const bool do_free)
1775 {
1776   SpaceType *st;
1777   ARegion *ar;
1778   const char spacetype = sa_dst->spacetype;
1779   const short flag_copy = HEADER_NO_PULLDOWN;
1780
1781   sa_dst->spacetype = sa_src->spacetype;
1782   sa_dst->type = sa_src->type;
1783
1784   sa_dst->flag = (sa_dst->flag & ~flag_copy) | (sa_src->flag & flag_copy);
1785
1786   /* area */
1787   if (do_free) {
1788     BKE_spacedata_freelist(&sa_dst->spacedata);
1789   }
1790   BKE_spacedata_copylist(&sa_dst->spacedata, &sa_src->spacedata);
1791
1792   /* Note; SPACE_EMPTY is possible on new screens */
1793
1794   /* regions */
1795   if (do_free) {
1796     st = BKE_spacetype_from_id(spacetype);
1797     for (ar = sa_dst->regionbase.first; ar; ar = ar->next)
1798       BKE_area_region_free(st, ar);
1799     BLI_freelistN(&sa_dst->regionbase);
1800   }
1801   st = BKE_spacetype_from_id(sa_src->spacetype);
1802   for (ar = sa_src->regionbase.first; ar; ar = ar->next) {
1803     ARegion *newar = BKE_area_region_copy(st, ar);
1804     BLI_addtail(&sa_dst->regionbase, newar);
1805   }
1806 }
1807
1808 void ED_area_data_swap(ScrArea *sa_dst, ScrArea *sa_src)
1809 {
1810   SWAP(char, sa_dst->spacetype, sa_src->spacetype);
1811   SWAP(SpaceType *, sa_dst->type, sa_src->type);
1812
1813   SWAP(ListBase, sa_dst->spacedata, sa_src->spacedata);
1814   SWAP(ListBase, sa_dst->regionbase, sa_src->regionbase);
1815 }
1816
1817 /* *********** Space switching code *********** */
1818
1819 void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2)
1820 {
1821   ScrArea *tmp = MEM_callocN(sizeof(ScrArea), "addscrarea");
1822
1823   ED_area_exit(C, sa1);
1824   ED_area_exit(C, sa2);
1825
1826   ED_area_data_copy(tmp, sa1, false);
1827   ED_area_data_copy(sa1, sa2, true);
1828   ED_area_data_copy(sa2, tmp, true);
1829   ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa1);
1830   ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa2);
1831
1832   BKE_screen_area_free(tmp);
1833   MEM_freeN(tmp);
1834
1835   /* tell WM to refresh, cursor types etc */
1836   WM_event_add_mousemove(C);
1837
1838   ED_area_tag_redraw(sa1);
1839   ED_area_tag_refresh(sa1);
1840   ED_area_tag_redraw(sa2);
1841   ED_area_tag_refresh(sa2);
1842 }
1843
1844 /**
1845  * \param skip_ar_exit: Skip calling area exit callback. Set for opening temp spaces.
1846  */
1847 void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exit)
1848 {
1849   wmWindow *win = CTX_wm_window(C);
1850
1851   if (sa->spacetype != type) {
1852     SpaceType *st;
1853     SpaceLink *slold;
1854     SpaceLink *sl;
1855     /* store sa->type->exit callback */
1856     void *sa_exit = sa->type ? sa->type->exit : NULL;
1857     /* When the user switches between space-types from the type-selector,
1858      * changing the header-type is jarring (especially when using Ctrl-MouseWheel).
1859      *
1860      * However, add-on install for example, forces the header to the top which shouldn't
1861      * be applied back to the previous space type when closing - see: T57724
1862      *
1863      * Newly created windows wont have any space data, use the alignment
1864      * the space type defaults to in this case instead
1865      * (needed for preferences to have space-type on bottom).
1866      */
1867     int header_alignment = ED_area_header_alignment_or_fallback(sa, -1);
1868     const bool sync_header_alignment = ((header_alignment != -1) &&
1869                                         (sa->flag & AREA_FLAG_TEMP_TYPE) == 0);
1870
1871     /* in some cases (opening temp space) we don't want to
1872      * call area exit callback, so we temporarily unset it */
1873     if (skip_ar_exit && sa->type) {
1874       sa->type->exit = NULL;
1875     }
1876
1877     ED_area_exit(C, sa);
1878
1879     /* restore old area exit callback */
1880     if (skip_ar_exit && sa->type) {
1881       sa->type->exit = sa_exit;
1882     }
1883
1884     st = BKE_spacetype_from_id(type);
1885     slold = sa->spacedata.first;
1886
1887     sa->spacetype = type;
1888     sa->type = st;
1889
1890     /* If st->new may be called, don't use context until then. The
1891      * sa->type->context() callback has changed but data may be invalid
1892      * (e.g. with properties editor) until space-data is properly created */
1893
1894     /* check previously stored space */
1895     for (sl = sa->spacedata.first; sl; sl = sl->next)
1896       if (sl->spacetype == type)
1897         break;
1898
1899     /* old spacedata... happened during work on 2.50, remove */
1900     if (sl && BLI_listbase_is_empty(&sl->regionbase)) {
1901       st->free(sl);
1902       BLI_freelinkN(&sa->spacedata, sl);
1903       if (slold == sl) {
1904         slold = NULL;
1905       }
1906       sl = NULL;
1907     }
1908
1909     if (sl) {
1910       /* swap regions */
1911       slold->regionbase = sa->regionbase;
1912       sa->regionbase = sl->regionbase;
1913       BLI_listbase_clear(&sl->regionbase);
1914
1915       /* put in front of list */
1916       BLI_remlink(&sa->spacedata, sl);
1917       BLI_addhead(&sa->spacedata, sl);
1918     }
1919     else {
1920       /* new space */
1921       if (st) {
1922         /* Don't get scene from context here which may depend on space-data. */
1923         Scene *scene = WM_window_get_active_scene(win);
1924         sl = st->new (sa, scene);
1925         BLI_addhead(&sa->spacedata, sl);
1926
1927         /* swap regions */
1928         if (slold)
1929           slold->regionbase = sa->regionbase;
1930         sa->regionbase = sl->regionbase;
1931         BLI_listbase_clear(&sl->regionbase);
1932       }
1933     }
1934
1935     /* Sync header alignment. */
1936     if (sync_header_alignment) {
1937       /* Spaces with footer. */
1938       if (st->spaceid == SPACE_TEXT) {
1939         for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
1940           if (ar->regiontype == RGN_TYPE_HEADER) {
1941             ar->alignment = header_alignment;
1942           }
1943           if (ar->regiontype == RGN_TYPE_FOOTER) {
1944             int footer_alignment = (header_alignment == RGN_ALIGN_BOTTOM) ? RGN_ALIGN_TOP :
1945                                                                             RGN_ALIGN_BOTTOM;
1946             ar->alignment = footer_alignment;
1947             break;
1948           }
1949         }
1950       }
1951       else {
1952         for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
1953           if (ar->regiontype == RGN_TYPE_HEADER) {
1954             ar->alignment = header_alignment;
1955             break;
1956           }
1957         }
1958       }
1959     }
1960
1961     ED_area_initialize(CTX_wm_manager(C), win, sa);
1962
1963     /* tell WM to refresh, cursor types etc */
1964     WM_event_add_mousemove(C);
1965
1966     /* send space change notifier */
1967     WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CHANGED, sa);
1968
1969     ED_area_tag_refresh(sa);
1970   }
1971
1972   /* also redraw when re-used */
1973   ED_area_tag_redraw(sa);
1974 }
1975
1976 void ED_area_prevspace(bContext *C, ScrArea *sa)
1977 {
1978   SpaceLink *sl = sa->spacedata.first;
1979
1980   if (sl && sl->next) {
1981     ED_area_newspace(C, sa, sl->next->spacetype, false);
1982
1983     /* keep old spacedata but move it to end, so calling
1984      * ED_area_prevspace once more won't open it again */
1985     BLI_remlink(&sa->spacedata, sl);
1986     BLI_addtail(&sa->spacedata, sl);
1987   }
1988   else {
1989     /* no change */
1990     return;
1991   }
1992   sa->flag &= ~(AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE);
1993
1994   ED_area_tag_redraw(sa);
1995
1996   /* send space change notifier */
1997   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CHANGED, sa);
1998 }
1999
2000 /* returns offset for next button in header */
2001 int ED_area_header_switchbutton(const bContext *C, uiBlock *block, int yco)
2002 {
2003   ScrArea *sa = CTX_wm_area(C);
2004   bScreen *scr = CTX_wm_screen(C);
2005   PointerRNA areaptr;
2006   int xco = 0.4 * U.widget_unit;
2007
2008   RNA_pointer_create(&(scr->id), &RNA_Area, sa, &areaptr);
2009
2010   uiDefButR(block,
2011             UI_BTYPE_MENU,
2012             0,
2013             "",
2014             xco,
2015             yco,
2016             1.6 * U.widget_unit,
2017             U.widget_unit,
2018             &areaptr,
2019             "ui_type",
2020             0,
2021             0.0f,
2022             0.0f,
2023             0.0f,
2024             0.0f,
2025             "");
2026
2027   return xco + 1.7 * U.widget_unit;
2028 }
2029
2030 /************************ standard UI regions ************************/
2031
2032 static ThemeColorID region_background_color_id(const bContext *C, const ARegion *region)
2033 {
2034   ScrArea *area = CTX_wm_area(C);
2035
2036   switch (region->regiontype) {
2037     case RGN_TYPE_HEADER:
2038       if (ED_screen_area_active(C) || ED_area_is_global(area)) {
2039         return TH_HEADER;
2040       }
2041       else {
2042         return TH_HEADERDESEL;
2043       }
2044     case RGN_TYPE_PREVIEW:
2045       return TH_PREVIEW_BACK;
2046     default:
2047       return TH_BACK;
2048   }
2049 }
2050
2051 static void region_clear_color(const bContext *C, const ARegion *ar, ThemeColorID colorid)
2052 {
2053   if (ar->alignment == RGN_ALIGN_FLOAT) {
2054     /* handle our own drawing. */
2055   }
2056   else if (ar->overlap) {
2057     /* view should be in pixelspace */
2058     UI_view2d_view_restore(C);
2059
2060     float back[4];
2061     UI_GetThemeColor4fv(colorid, back);
2062     GPU_clear_color(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]);
2063     GPU_clear(GPU_COLOR_BIT);
2064   }
2065   else {
2066     UI_ThemeClearColor(colorid);
2067     GPU_clear(GPU_COLOR_BIT);
2068   }
2069 }
2070
2071 BLI_INLINE bool streq_array_any(const char *s, const char *arr[])
2072 {
2073   for (uint i = 0; arr[i]; i++) {
2074     if (STREQ(arr[i], s)) {
2075       return true;
2076     }
2077   }
2078   return false;
2079 }
2080
2081 static void ed_panel_draw(const bContext *C,
2082                           ScrArea *sa,
2083                           ARegion *ar,
2084                           ListBase *lb,
2085                           PanelType *pt,
2086                           Panel *panel,
2087                           int w,
2088                           int em,
2089                           bool vertical)
2090 {
2091   uiStyle *style = UI_style_get_dpi();
2092
2093   /* draw panel */
2094   uiBlock *block = UI_block_begin(C, ar, pt->idname, UI_EMBOSS);
2095
2096   bool open;
2097   panel = UI_panel_begin(sa, ar, lb, block, pt, panel, &open);
2098
2099   /* bad fixed values */
2100   int xco, yco, h = 0;
2101
2102   if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) {
2103     /* for preset menu */
2104     panel->layout = UI_block_layout(block,
2105                                     UI_LAYOUT_HORIZONTAL,
2106                                     UI_LAYOUT_HEADER,
2107                                     0,
2108                                     (UI_UNIT_Y * 1.1f) + style->panelspace,
2109                                     UI_UNIT_Y,
2110                                     1,
2111                                     0,
2112                                     style);
2113
2114     pt->draw_header_preset(C, panel);
2115
2116     int headerend = w - UI_UNIT_X;
2117
2118     UI_block_layout_resolve(block, &xco, &yco);
2119     UI_block_translate(block, headerend - xco, 0);
2120     panel->layout = NULL;
2121   }
2122
2123   if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) {
2124     int labelx, labely;
2125     UI_panel_label_offset(block, &labelx, &labely);
2126
2127     /* for enabled buttons */
2128     panel->layout = UI_block_layout(
2129         block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, labelx, labely, UI_UNIT_Y, 1, 0, style);
2130
2131     pt->draw_header(C, panel);
2132
2133     UI_block_layout_resolve(block, &xco, &yco);
2134     panel->labelofs = xco - labelx;
2135     panel->layout = NULL;
2136   }
2137   else {
2138     panel->labelofs = 0;
2139   }
2140
2141   if (open) {
2142     short panelContext;
2143
2144     /* panel context can either be toolbar region or normal panels region */
2145     if (pt->flag & PNL_LAYOUT_VERT_BAR) {
2146       panelContext = UI_LAYOUT_VERT_BAR;
2147     }
2148     else if (ar->regiontype == RGN_TYPE_TOOLS) {
2149       panelContext = UI_LAYOUT_TOOLBAR;
2150     }
2151     else {
2152       panelContext = UI_LAYOUT_PANEL;
2153     }
2154
2155     panel->layout = UI_block_layout(block,
2156                                     UI_LAYOUT_VERTICAL,
2157                                     panelContext,
2158                                     (pt->flag & PNL_LAYOUT_VERT_BAR) ? 0 : style->panelspace,
2159                                     0,
2160                                     (pt->flag & PNL_LAYOUT_VERT_BAR) ? 0 :
2161                                                                        w - 2 * style->panelspace,
2162                                     em,
2163                                     0,
2164                                     style);
2165
2166     pt->draw(C, panel);
2167
2168     UI_block_layout_resolve(block, &xco, &yco);
2169     panel->layout = NULL;
2170
2171     if (yco != 0) {
2172       h = -yco + 2 * style->panelspace;
2173     }
2174   }
2175
2176   UI_block_end(C, block);
2177
2178   /* Draw child panels. */
2179   if (open) {
2180     for (LinkData *link = pt->children.first; link; link = link->next) {
2181       PanelType *child_pt = link->data;
2182       Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt);
2183
2184       if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) {
2185         ed_panel_draw(C, sa, ar, &panel->children, child_pt, child_panel, w, em, vertical);
2186       }
2187     }
2188   }
2189
2190   UI_panel_end(block, w, h);
2191 }
2192
2193 /**
2194  * \param contexts: A NULL terminated array of context strings to match against.
2195  * Matching against any of these strings will draw the panel.
2196  * Can be NULL to skip context checks.
2197  */
2198 void ED_region_panels_layout_ex(
2199     const bContext *C, ARegion *ar, const char *contexts[], int contextnr, const bool vertical)
2200 {
2201   ar->runtime.category = NULL;
2202
2203   const WorkSpace *workspace = CTX_wm_workspace(C);
2204   ScrArea *sa = CTX_wm_area(C);
2205   PanelType *pt;
2206   View2D *v2d = &ar->v2d;
2207   int x, y, w, em;
2208   bool is_context_new = 0;
2209   int scroll;
2210
2211   /* XXX, should use some better check? */
2212   /* For now also has hardcoded check for clip editor until it supports actual toolbar. */
2213   bool use_category_tabs = ((1 << ar->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) ||
2214                            (ar->regiontype == RGN_TYPE_TOOLS && sa->spacetype == SPACE_CLIP);
2215   /* offset panels for small vertical tab area */
2216   const char *category = NULL;
2217   const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH;
2218   int margin_x = 0;
2219   const bool region_layout_based = ar->flag & RGN_FLAG_DYNAMIC_SIZE;
2220
2221   BLI_SMALLSTACK_DECLARE(pt_stack, PanelType *);
2222
2223   if (contextnr != -1)
2224     is_context_new = UI_view2d_tab_set(v2d, contextnr);
2225
2226   /* before setting the view */
2227   if (vertical) {
2228     /* only allow scrolling in vertical direction */
2229     v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y;
2230     v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X);
2231     v2d->scroll &= ~(V2D_SCROLL_BOTTOM);
2232     v2d->scroll |= (V2D_SCROLL_RIGHT);
2233   }
2234   else {
2235     /* for now, allow scrolling in both directions (since layouts are optimized for vertical,
2236      * they often don't fit in horizontal layout)
2237      */
2238     v2d->keepofs &= ~(V2D_LOCKOFS_X | V2D_LOCKOFS_Y | V2D_KEEPOFS_X | V2D_KEEPOFS_Y);
2239     v2d->scroll |= (V2D_SCROLL_BOTTOM);
2240     v2d->scroll &= ~(V2D_SCROLL_RIGHT);
2241   }
2242
2243   scroll = v2d->scroll;
2244
2245   /* collect panels to draw */
2246   for (pt = ar->type->paneltypes.last; pt; pt = pt->prev) {
2247     /* Only draw top level panels. */
2248     if (pt->parent) {
2249       continue;
2250     }
2251
2252     /* verify context */
2253     if (contexts && pt->context[0] && !streq_array_any(pt->context, contexts)) {
2254       continue;
2255     }
2256
2257     /* If we're tagged, only use compatible. */
2258     if (pt->owner_id[0] && BKE_workspace_owner_id_check(workspace, pt->owner_id) == false) {
2259       continue;
2260     }
2261
2262     /* draw panel */
2263     if (pt->draw && (!pt->poll || pt->poll(C, pt))) {
2264       BLI_SMALLSTACK_PUSH(pt_stack, pt);
2265     }
2266   }
2267
2268   /* collect categories */
2269   if (use_category_tabs) {
2270     UI_panel_category_clear_all(ar);
2271
2272     /* gather unique categories */
2273     BLI_SMALLSTACK_ITER_BEGIN (pt_stack, pt) {
2274       if (pt->category[0]) {
2275         if (!UI_panel_category_find(ar, pt->category)) {
2276           UI_panel_category_add(ar, pt->category);
2277         }
2278       }
2279     }
2280     BLI_SMALLSTACK_ITER_END;
2281
2282     if (!UI_panel_category_is_visible(ar)) {
2283       use_category_tabs = false;
2284     }
2285     else {
2286       category = UI_panel_category_active_get(ar, true);
2287       margin_x = category_tabs_width;
2288     }
2289   }
2290
2291   if (vertical) {
2292     w = BLI_rctf_size_x(&v2d->cur);
2293     em = (ar->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */
2294   }
2295   else {
2296     w = UI_PANEL_WIDTH;
2297     em = (ar->type->prefsizex) ? 10 : 20;
2298   }
2299
2300   w -= margin_x;
2301
2302   /* create panels */
2303   UI_panels_begin(C, ar);
2304
2305   /* set view2d view matrix  - UI_block_begin() stores it */
2306   UI_view2d_view_ortho(v2d);
2307
2308   BLI_SMALLSTACK_ITER_BEGIN (pt_stack, pt) {
2309     Panel *panel = UI_panel_find_by_type(&ar->panels, pt);
2310
2311     if (use_category_tabs && pt->category[0] && !STREQ(category, pt->category)) {
2312       if ((panel == NULL) || ((panel->flag & PNL_PIN) == 0)) {
2313         continue;
2314       }
2315     }
2316
2317     ed_panel_draw(C, sa, ar, &ar->panels, pt, panel, w, em, vertical);
2318   }
2319   BLI_SMALLSTACK_ITER_END;
2320
2321   /* align panels and return size */
2322   UI_panels_end(C, ar, &x, &y);
2323
2324   /* before setting the view */
2325   if (region_layout_based) {
2326     /* XXX, only single panel support atm.
2327      * Can't use x/y values calculated above because they're not using the real height of panels,
2328      * instead they calculate offsets for the next panel to start drawing. */
2329     Panel *panel = ar->panels.last;
2330     if (panel != NULL) {
2331       int size_dyn[2] = {
2332           UI_UNIT_X * ((panel->flag & PNL_CLOSED) ? 8 : 14) / UI_DPI_FAC,
2333           UI_panel_size_y(panel) / UI_DPI_FAC,
2334       };
2335       /* region size is layout based and needs to be updated */
2336       if ((ar->sizex != size_dyn[0]) || (ar->sizey != size_dyn[1])) {
2337         ar->sizex = size_dyn[0];
2338         ar->sizey = size_dyn[1];
2339         sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
2340       }
2341       y = ABS(ar->sizey * UI_DPI_FAC - 1);
2342     }
2343   }
2344   else if (vertical) {
2345     /* we always keep the scroll offset - so the total view gets increased with the scrolled away part */
2346     if (v2d->cur.ymax < -FLT_EPSILON) {
2347       /* Clamp to lower view boundary */
2348       if (v2d->tot.ymin < -v2d->winy) {
2349         y = min_ii(y, 0);
2350       }
2351       else {
2352         y = min_ii(y, v2d->cur.ymin);
2353       }
2354     }
2355
2356     y = -y;
2357   }
2358   else {
2359     /* don't jump back when panels close or hide */
2360     if (!is_context_new) {
2361       if (v2d->tot.xmax > v2d->winx) {
2362         x = max_ii(x, 0);
2363       }
2364       else {
2365         x = max_ii(x, v2d->cur.xmax);
2366       }
2367     }
2368
2369     y = -y;
2370   }
2371
2372   /* this also changes the 'cur' */
2373   UI_view2d_totRect_set(v2d, x, y);
2374
2375   if (scroll != v2d->scroll) {
2376     /* Note: this code scales fine, but because of rounding differences, positions of elements
2377      * flip +1 or -1 pixel compared to redoing the entire layout again.
2378      * Leaving in commented code for future tests */
2379 #if 0
2380     UI_panels_scale(ar, BLI_rctf_size_x(&v2d->cur));
2381     break;
2382 #endif
2383   }
2384
2385   if (use_category_tabs) {
2386     ar->runtime.category = category;
2387   }
2388 }
2389 void ED_region_panels_layout(const bContext *C, ARegion *ar)
2390 {
2391   ED_region_panels_layout_ex(C, ar, NULL, -1, true);
2392 }
2393
2394 void ED_region_panels_draw(const bContext *C, ARegion *ar)
2395 {
2396   View2D *v2d = &ar->v2d;
2397
2398   if (ar->alignment != RGN_ALIGN_FLOAT) {
2399     region_clear_color(
2400         C, ar, (ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK);
2401   }
2402
2403   /* reset line width for drawing tabs */
2404   GPU_line_width(1.0f);
2405
2406   /* set the view */
2407   UI_view2d_view_ortho(v2d);
2408
2409   /* View2D matrix might have changed due to dynamic sized regions. */
2410   UI_blocklist_update_window_matrix(C, &ar->uiblocks);
2411
2412   /* draw panels */
2413   UI_panels_draw(C, ar);
2414
2415   /* restore view matrix */
2416   UI_view2d_view_restore(C);
2417
2418   /* Set in layout. */
2419   if (ar->runtime.category) {
2420     UI_panel_category_draw_all(ar, ar->runtime.category);
2421   }
2422
2423   /* scrollers */
2424   const rcti *mask = NULL;
2425   rcti mask_buf;
2426   if (ar->runtime.category && (ar->alignment == RGN_ALIGN_RIGHT)) {
2427     UI_view2d_mask_from_win(v2d, &mask_buf);
2428     mask_buf.xmax -= UI_PANEL_CATEGORY_MARGIN_WIDTH;
2429     mask = &mask_buf;
2430   }
2431   View2DScrollers *scrollers = UI_view2d_scrollers_calc(
2432       C, v2d, mask, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
2433   UI_view2d_scrollers_draw(C, v2d, scrollers);
2434   UI_view2d_scrollers_free(scrollers);
2435 }
2436
2437 void ED_region_panels_ex(
2438     const bContext *C, ARegion *ar, const char *contexts[], int contextnr, const bool vertical)
2439 {
2440   /* TODO: remove? */
2441   ED_region_panels_layout_ex(C, ar, contexts, contextnr, vertical);
2442   ED_region_panels_draw(C, ar);
2443 }
2444
2445 void ED_region_panels(const bContext *C, ARegion *ar)
2446 {
2447   /* TODO: remove? */
2448   ED_region_panels_layout(C, ar);
2449   ED_region_panels_draw(C, ar);
2450 }
2451
2452 void ED_region_panels_init(wmWindowManager *wm, ARegion *ar)
2453 {
2454   wmKeyMap *keymap;
2455
2456   UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_PANELS_UI, ar->winx, ar->winy);
2457
2458   keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0);
2459   WM_event_add_keymap_handler(&ar->handlers, keymap);
2460 }
2461
2462 void ED_region_header_layout(const bContext *C, ARegion *ar)
2463 {
2464   uiStyle *style = UI_style_get_dpi();
2465   uiBlock *block;
2466   uiLayout *layout;
2467   HeaderType *ht;
2468   Header header = {NULL};
2469   bool region_layout_based = ar->flag & RGN_FLAG_DYNAMIC_SIZE;
2470
2471   /* Height of buttons and scaling needed to achieve it. */
2472   const int buttony = min_ii(UI_UNIT_Y, ar->winy - 2 * UI_DPI_FAC);
2473   const float buttony_scale = buttony / (float)UI_UNIT_Y;
2474
2475   /* Vertically center buttons. */
2476   int xco = UI_HEADER_OFFSET;
2477   int yco = buttony + (ar->winy - buttony) / 2;
2478   int maxco = xco;
2479
2480   /* XXX workaround for 1 px alignment issue. Not sure what causes it... Would prefer a proper fix - Julian */
2481   if (!ELEM(CTX_wm_area(C)->spacetype, SPACE_TOPBAR, SPACE_STATUSBAR)) {
2482     yco -= 1;
2483   }
2484
2485   /* set view2d view matrix for scrolling (without scrollers) */
2486   UI_view2d_view_ortho(&ar->v2d);
2487
2488   /* draw all headers types */
2489   for (ht = ar->type->headertypes.first; ht; ht = ht->next) {
2490     if (ht->poll && !ht->poll(C, ht)) {
2491       continue;
2492     }
2493
2494     block = UI_block_begin(C, ar, ht->idname, UI_EMBOSS);
2495     layout = UI_block_layout(
2496         block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, xco, yco, buttony, 1, 0, style);
2497
2498     if (buttony_scale != 1.0f) {
2499       uiLayoutSetScaleY(layout, buttony_scale);
2500     }
2501
2502     if (ht->draw) {
2503       header.type = ht;
2504       header.layout = layout;
2505       ht->draw(C, &header);
2506       if (ht->next) {
2507         uiItemS(layout);
2508       }
2509
2510       /* for view2d */
2511       xco = uiLayoutGetWidth(layout);
2512       if (xco > maxco)
2513         maxco = xco;
2514     }
2515
2516     UI_block_layout_resolve(block, &xco, &yco);
2517
2518     /* for view2d */
2519     if (xco > maxco)
2520       maxco = xco;
2521
2522     int new_sizex = (maxco + UI_HEADER_OFFSET) / UI_DPI_FAC;
2523
2524     if (region_layout_based && (ar->sizex != new_sizex)) {
2525       /* region size is layout based and needs to be updated */
2526       ScrArea *sa = CTX_wm_area(C);
2527
2528       ar->sizex = new_sizex;
2529       sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
2530     }
2531
2532     UI_block_end(C, block);
2533   }
2534
2535   if (!region_layout_based) {
2536     maxco += UI_HEADER_OFFSET;
2537   }
2538
2539   /* always as last  */
2540   UI_view2d_totRect_set(&ar->v2d, maxco, ar->winy);
2541
2542   /* restore view matrix */
2543   UI_view2d_view_restore(C);
2544 }
2545
2546 void ED_region_header_draw(const bContext *C, ARegion *ar)
2547 {
2548   /* clear */
2549   region_clear_color(C, ar, region_background_color_id(C, ar));
2550
2551   UI_view2d_view_ortho(&ar->v2d);
2552
2553   /* View2D matrix might have changed due to dynamic sized regions. */
2554   UI_blocklist_update_window_matrix(C, &ar->uiblocks);
2555
2556   /* draw blocks */
2557   UI_blocklist_draw(C, &ar->uiblocks);
2558
2559   /* restore view matrix */
2560   UI_view2d_view_restore(C);
2561 }
2562
2563 void ED_region_header(const bContext *C, ARegion *ar)
2564 {
2565   /* TODO: remove? */
2566   ED_region_header_layout(C, ar);
2567   ED_region_header_draw(C, ar);
2568 }
2569
2570 void ED_region_header_init(ARegion *ar)
2571 {
2572   UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy);
2573 }
2574
2575 int ED_area_headersize(void)
2576 {
2577   /* Accomodate widget and padding. */
2578   return U.widget_unit + (int)(UI_DPI_FAC * HEADER_PADDING_Y);
2579 }
2580
2581 int ED_area_header_alignment_or_fallback(const ScrArea *area, int fallback)
2582 {
2583   for (ARegion *ar = area->regionbase.first; ar; ar = ar->next) {
2584     if (ar->regiontype == RGN_TYPE_HEADER) {
2585       return ar->alignment;
2586     }
2587   }
2588   return fallback;
2589 }
2590
2591 int ED_area_header_alignment(const ScrArea *area)
2592 {
2593   return ED_area_header_alignment_or_fallback(
2594       area, (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP);
2595 }
2596
2597 int ED_area_footersize(void)
2598 {
2599   return ED_area_headersize();
2600 }
2601
2602 int ED_area_footer_alignment_or_fallback(const ScrArea *area, int fallback)
2603 {
2604   for (ARegion *ar = area->regionbase.first; ar; ar = ar->next) {
2605     if (ar->regiontype == RGN_TYPE_FOOTER) {
2606       return ar->alignment;
2607     }
2608   }
2609   return fallback;
2610 }
2611
2612 int ED_area_footer_alignment(const ScrArea *area)
2613 {
2614   return ED_area_footer_alignment_or_fallback(
2615       area, (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_TOP : RGN_ALIGN_BOTTOM);
2616 }
2617
2618 /**
2619  * \return the final height of a global \a area, accounting for DPI.
2620  */
2621 int ED_area_global_size_y(const ScrArea *area)
2622 {
2623   BLI_assert(ED_area_is_global(area));
2624   return round_fl_to_int(area->global->cur_fixed_height * UI_DPI_FAC);
2625 }
2626 int ED_area_global_min_size_y(const ScrArea *area)
2627 {
2628   BLI_assert(ED_area_is_global(area));
2629   return round_fl_to_int(area->global->size_min * UI_DPI_FAC);
2630 }
2631 int ED_area_global_max_size_y(const ScrArea *area)
2632 {
2633   BLI_assert(ED_area_is_global(area));
2634   return round_fl_to_int(area->global->size_max * UI_DPI_FAC);
2635 }
2636
2637 bool ED_area_is_global(const ScrArea *area)
2638 {
2639   return area->global != NULL;
2640 }
2641
2642 ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen)
2643 {
2644   ScrArea *global_area = win->global_areas.areabase.first;
2645
2646   if (!global_area) {
2647     return screen->areabase.first;
2648   }
2649   else if ((global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) {
2650     return global_area;
2651   }
2652   /* Find next visible area. */
2653   return ED_screen_areas_iter_next(screen, global_area);
2654 }
2655 ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area)
2656 {
2657   if (area->global) {
2658     for (ScrArea *area_iter = area->next; area_iter; area_iter = area_iter->next) {
2659       if ((area_iter->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) {
2660         return area_iter;
2661       }
2662     }
2663     /* No visible next global area found, start iterating over layout areas. */
2664     return screen->areabase.first;
2665   }
2666
2667   return area->next;
2668 }
2669
2670 /**
2671  * For now we just assume all global areas are made up out of horizontal bars
2672  * with the same size. A fixed size could be stored in ARegion instead if needed.
2673  *
2674  * \return the DPI aware height of a single bar/region in global areas.
2675  */
2676 int ED_region_global_size_y(void)
2677 {
2678   return ED_area_headersize(); /* same size as header */
2679 }
2680
2681 void ED_region_info_draw_multiline(ARegion *ar,
2682                                    const char *text_array[],
2683                                    float fill_color[4],
2684                                    const bool full_redraw)
2685 {
2686   const int header_height = UI_UNIT_Y;
2687   uiStyle *style = UI_style_get_dpi();
2688   int fontid = style->widget.uifont_id;
2689   int scissor[4];
2690   rcti rect;
2691   int num_lines = 0;
2692
2693   /* background box */
2694   ED_region_visible_rect(ar, &rect);
2695
2696   /* Box fill entire width or just around text. */
2697   if (!full_redraw) {
2698     const char **text = &text_array[0];
2699     while (*text) {
2700       rect.xmax = min_ii(rect.xmax,
2701                          rect.xmin + BLF_width(fontid, *text, BLF_DRAW_STR_DUMMY_MAX) +
2702                              1.2f * U.widget_unit);
2703       text++;
2704       num_lines++;
2705     }
2706   }
2707   /* Just count the line number. */
2708   else {
2709     const char **text = &text_array[0];
2710     while (*text) {
2711       text++;
2712       num_lines++;
2713     }
2714   }
2715
2716   rect.ymin = rect.ymax - header_height * num_lines;
2717
2718   /* setup scissor */
2719   GPU_scissor_get_i(scissor);
2720   GPU_scissor(rect.xmin, rect.ymin, BLI_rcti_size_x(&rect) + 1, BLI_rcti_size_y(&rect) + 1);
2721
2722   GPU_blend(true);
2723   GPU_blend_set_func_separate(
2724       GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
2725   GPUVertFormat *format = immVertexFormat();
2726   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2727   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2728   immUniformColor4fv(fill_color);
2729   immRecti(pos, rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1);
2730   immUnbindProgram();
2731   GPU_blend(false);
2732
2733   /* text */
2734   UI_FontThemeColor(fontid, TH_TEXT_HI);
2735   BLF_clipping(fontid, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
2736   BLF_enable(fontid, BLF_CLIPPING);
2737   int offset = num_lines - 1;
2738   {
2739     const char **text = &text_array[0];
2740     while (*text) {
2741       BLF_position(fontid,
2742                    rect.xmin + 0.6f * U.widget_unit,
2743                    rect.ymin + 0.3f * U.widget_unit + offset * header_height,
2744                    0.0f);
2745       BLF_draw(fontid, *text, BLF_DRAW_STR_DUMMY_MAX);
2746       text++;
2747       offset--;
2748     }
2749   }
2750
2751   BLF_disable(fontid, BLF_CLIPPING);
2752
2753   /* restore scissor as it was before */
2754   GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
2755 }
2756
2757 void ED_region_info_draw(ARegion *ar,
2758                          const char *text,
2759                          float fill_color[4],
2760                          const bool full_redraw)
2761 {
2762   ED_region_info_draw_multiline(ar, (const char * [2]){text, NULL}, fill_color, full_redraw);
2763 }
2764
2765 #define MAX_METADATA_STR 1024
2766
2767 static const char *meta_data_list[] = {
2768     "File",
2769     "Strip",
2770     "Date",
2771     "RenderTime",
2772     "Note",
2773     "Marker",
2774     "Time",
2775     "Frame",
2776     "Camera",
2777     "Scene",
2778 };
2779
2780 BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset)
2781 {
2782   return (IMB_metadata_get_field(
2783               ibuf->metadata, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) &&
2784           r_str[0]);
2785 }
2786
2787 BLI_INLINE bool metadata_is_custom_drawable(const char *field)
2788 {
2789   /* Metadata field stored by Blender for multilayer EXR images. Is rather
2790    * useless to be viewed all the time. Can still be seen in the Metadata
2791    * panel. */
2792   if (STREQ(field, "BlenderMultiChannel")) {
2793     return false;
2794   }
2795   /* Is almost always has value "scanlineimage", also useless to be seen
2796    * all the time. */
2797   if (STREQ(field, "type")) {
2798     return false;
2799   }
2800   return !BKE_stamp_is_known_field(field);
2801 }
2802
2803 typedef struct MetadataCustomDrawContext {
2804   int fontid;
2805   int xmin, ymin;
2806   int vertical_offset;
2807   int current_y;
2808 } MetadataCustomDrawContext;
2809
2810 static void metadata_custom_draw_fields(const char *field, const char *value, void *ctx_v)
2811 {
2812   if (!metadata_is_custom_drawable(field)) {
2813     return;
2814   }
2815   MetadataCustomDrawContext *ctx = (MetadataCustomDrawContext *)ctx_v;
2816   char temp_str[MAX_METADATA_STR];
2817   BLI_snprintf(temp_str, MAX_METADATA_STR, "%s: %s", field, value);
2818   BLF_position(ctx->fontid, ctx->xmin, ctx->ymin + ctx->current_y, 0.0f);
2819   BLF_draw(ctx->fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2820   ctx->current_y += ctx->vertical_offset;
2821 }
2822
2823 static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const bool is_top)
2824 {
2825   char temp_str[MAX_METADATA_STR];
2826   int line_width;
2827   int ofs_y = 0;
2828   short i;
2829   int len;
2830   const float height = BLF_height_max(fontid);
2831   const float margin = height / 8;
2832   const float vertical_offset = (height + margin);
2833
2834   /* values taking margins into account */
2835   const float descender = BLF_descender(fontid);
2836   const float xmin = (rect->xmin + margin);
2837   const float xmax = (rect->xmax - margin);
2838   const float ymin = (rect->ymin + margin) - descender;
2839   const float ymax = (rect->ymax - margin) - descender;
2840
2841   if (is_top) {
2842     for (i = 0; i < 4; i++) {
2843       /* first line */
2844       if (i == 0) {
2845         bool do_newline = false;
2846         len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[0]);
2847         if (metadata_is_valid(ibuf, temp_str, 0, len)) {
2848           BLF_position(fontid, xmin, ymax - vertical_offset, 0.0f);
2849           BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2850           do_newline = true;
2851         }
2852
2853         len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[1]);
2854         if (metadata_is_valid(ibuf, temp_str, 1, len)) {
2855           line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2856           BLF_position(fontid, xmax - line_width, ymax - vertical_offset, 0.0f);
2857           BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2858           do_newline = true;
2859         }
2860
2861         if (do_newline)
2862           ofs_y += vertical_offset;
2863       } /* Strip */
2864       else if (i == 1 || i == 2) {
2865         len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
2866         if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
2867           BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
2868           BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2869           ofs_y += vertical_offset;
2870         }
2871       } /* Note (wrapped) */
2872       else if (i == 3) {
2873         len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
2874         if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
2875           struct ResultBLF info;
2876           BLF_enable(fontid, BLF_WORD_WRAP);
2877           BLF_wordwrap(fontid, ibuf->x - (margin * 2));
2878           BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
2879           BLF_draw_ex(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX, &info);
2880           BLF_wordwrap(fontid, 0);
2881           BLF_disable(fontid, BLF_WORD_WRAP);
2882           ofs_y += vertical_offset * info.lines;
2883         }
2884       }
2885       else {
2886         len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
2887         if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
2888           line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2889           BLF_position(fontid, xmax - line_width, ymax - vertical_offset - ofs_y, 0.0f);
2890           BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2891           ofs_y += vertical_offset;
2892         }
2893       }
2894     }
2895   }
2896   else {
2897     MetadataCustomDrawContext ctx;
2898     ctx.fontid = fontid;
2899     ctx.xmin = xmin;
2900     ctx.ymin = ymin;
2901     ctx.vertical_offset = vertical_offset;
2902     ctx.current_y = ofs_y;
2903     ctx.vertical_offset = vertical_offset;
2904     IMB_metadata_foreach(ibuf, metadata_custom_draw_fields, &ctx);
2905     int ofs_x = 0;
2906     ofs_y = ctx.current_y;
2907     for (i = 5; i < 10; i++) {
2908       len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i]);
2909       if (metadata_is_valid(ibuf, temp_str, i, len)) {
2910         BLF_position(fontid, xmin + ofs_x, ymin + ofs_y, 0.0f);
2911         BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2912
2913         ofs_x += BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX) + UI_UNIT_X;
2914       }
2915     }
2916   }
2917 }
2918
2919 typedef struct MetadataCustomCountContext {
2920   int count;
2921 } MetadataCustomCountContext;
2922
2923 static void metadata_custom_count_fields(const char *field, const char *UNUSED(value), void *ctx_v)
2924 {
2925   if (!metadata_is_custom_drawable(field)) {
2926     return;
2927   }
2928   MetadataCustomCountContext *ctx = (MetadataCustomCountContext *)ctx_v;
2929   ctx->count++;
2930 }
2931
2932 static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top)
2933 {
2934   const float height = BLF_height_max(fontid);
2935   const float margin = (height / 8);
2936   char str[MAX_METADATA_STR] = "";
2937   short i, count = 0;
2938
2939   if (is_top) {
2940     if (metadata_is_valid(ibuf, str, 0, 0) || metadata_is_valid(ibuf, str, 1, 0)) {
2941       count++;
2942     }
2943     for (i = 2; i < 5; i++) {
2944       if (metadata_is_valid(ibuf, str, i, 0)) {
2945         if (i == 4) {
2946           struct {
2947             struct ResultBLF info;
2948             rctf rect;
2949           } wrap;
2950
2951           BLF_enable(fontid, BLF_WORD_WRAP);
2952           BLF_wordwrap(fontid, ibuf->x - (margin * 2));
2953           BLF_boundbox_ex(fontid, str, sizeof(str), &wrap.rect, &wrap.info);
2954           BLF_wordwrap(fontid, 0);
2955           BLF_disable(fontid, BLF_WORD_WRAP);
2956
2957           count += wrap.info.lines;
2958         }
2959         else {
2960           count++;
2961         }
2962       }
2963     }
2964   }
2965   else {
2966     for (i = 5; i < 10; i++) {
2967       if (metadata_is_valid(ibuf, str, i, 0)) {
2968         count = 1;
2969         break;
2970       }
2971     }
2972     MetadataCustomCountContext ctx;
2973     ctx.count = 0;
2974     IMB_metadata_foreach(ibuf, metadata_custom_count_fields, &ctx);
2975     count += ctx.count;
2976   }
2977
2978   if (count) {
2979     return (height + margin) * count;
2980   }
2981
2982   return 0;
2983 }
2984
2985 #undef MAX_METADATA_STR
2986
2987 void ED_region_image_metadata_draw(
2988     int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy)
2989 {
2990   float box_y;
2991   rctf rect;
2992   uiStyle *style = UI_style_get_dpi();
2993
2994   if (!ibuf->metadata)
2995     return;
2996
2997   /* find window pixel coordinates of origin */
2998   GPU_matrix_push();
2999
3000   /* offset and zoom using ogl */
3001   GPU_matrix_translate_2f(x, y);
3002   GPU_matrix_scale_2f(zoomx, zoomy);
3003
3004   BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f * U.pixelsize, U.dpi);
3005
3006   /* *** upper box*** */
3007
3008   /* get needed box height */
3009   box_y = metadata_box_height_get(ibuf, blf_mono_font, true);
3010
3011   if (box_y) {
3012     /* set up rect */
3013     BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymax, frame->ymax + box_y);
3014     /* draw top box */
3015     GPUVertFormat *format = immVertexFormat();
3016     uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3017     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3018     immUniformThemeColor(TH_METADATA_BG);
3019     immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3020     immUnbindProgram();
3021
3022     BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3023     BLF_enable(blf_mono_font, BLF_CLIPPING);
3024
3025     UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
3026     metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true);
3027
3028     BLF_disable(blf_mono_font, BLF_CLIPPING);
3029   }
3030
3031   /* *** lower box*** */
3032
3033   box_y = metadata_box_height_get(ibuf, blf_mono_font, false);
3034
3035   if (box_y) {
3036     /* set up box rect */
3037     BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymin - box_y, frame->ymin);
3038     /* draw top box */
3039     GPUVertFormat *format = immVertexFormat();
3040     uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3041     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3042     immUniformThemeColor(TH_METADATA_BG);
3043     immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3044     immUnbindProgram();
3045
3046     BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
3047     BLF_enable(blf_mono_font, BLF_CLIPPING);
3048
3049     UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
3050     metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false);
3051
3052     BLF_disable(blf_mono_font, BLF_CLIPPING);
3053   }
3054
3055   GPU_matrix_pop();
3056 }
3057
3058 typedef struct MetadataPanelDrawContext {
3059   uiLayout *layout;
3060 } MetadataPanelDrawContext;
3061
3062 static void metadata_panel_draw_field(const char *field, const char *value, void *ctx_v)
3063 {
3064   MetadataPanelDrawContext *ctx = (MetadataPanelDrawContext *)ctx_v;
3065   uiLayout *row = uiLayoutRow(ctx->layout, false);
3066   uiItemL(row, field, ICON_NONE);
3067   uiItemL(row, value, ICON_NONE);
3068 }
3069
3070 void ED_region_image_metadata_panel_draw(ImBuf *ibuf, uiLayout *layout)
3071 {
3072   MetadataPanelDrawContext ctx;
3073   ctx.layout = layout;
3074   IMB_metadata_foreach(ibuf, metadata_panel_draw_field, &ctx);
3075 }
3076
3077 void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy)
3078 {
3079   float gridsize, gridstep = 1.0f / 32.0f;
3080   float fac, blendfac;
3081   int x1, y1, x2, y2;
3082
3083   /* the image is located inside (0, 0), (1, 1) as set by view2d */
3084   UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &x1, &y1);
3085   UI_view2d_view_to_region(&ar->v2d, 1.0f, 1.0f, &x2, &y2);
3086
3087   GPUVertFormat *format = immVertexFormat();
3088   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3089
3090   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3091   immUniformThemeColorShade(TH_BACK, 20);
3092   immRectf(pos, x1, y1, x2, y2);
3093   immUnbindProgram();
3094
3095   /* gridsize adapted to zoom level */
3096   gridsize = 0.5f * (zoomx + zoomy);
3097   if (gridsize <= 0.0f)
3098     return;
3099
3100   if (gridsize < 1.0f) {
3101     while (gridsize < 1.0f) {
3102       gridsize *= 4.0f;
3103       gridstep *= 4.0f;
3104     }
3105   }
3106   else {
3107     while (gridsize >= 4.0f) {
3108       gridsize /= 4.0f;
3109       gridstep /= 4.0f;
3110     }
3111   }
3112
3113   blendfac = 0.25f * gridsize - floorf(0.25f * gridsize);
3114   CLAMP(blendfac, 0.0f, 1.0f);
3115
3116   int count_fine = 1.0f / gridstep;
3117   int count_large = 1.0f / (4.0f * gridstep);
3118
3119   if (count_fine > 0) {
3120     GPU_vertformat_clear(format);
3121     pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3122     unsigned color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
3123
3124     immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
3125     immBegin(GPU_PRIM_LINES, 4 * count_fine + 4 * count_large);
3126
3127     float theme_color[3];
3128     UI_GetThemeColorShade3fv(TH_BACK, (int)(20.0f * (1.0f - blendfac)), theme_color);
3129     fac = 0.0f;
3130
3131     /* the fine resolution level */
3132     for (int i = 0; i < count_fine; i++) {
3133       immAttr3fv(color, theme_color);
3134       immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
3135       immAttr3fv(color, theme_color);
3136       immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
3137       immAttr3fv(color, theme_color);
3138       immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
3139       immAttr3fv(color, theme_color);
3140       immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
3141       fac += gridstep;
3142     }
3143
3144     if (count_large > 0) {
3145       UI_GetThemeColor3fv(TH_BACK, theme_color);
3146       fac = 0.0f;
3147
3148       /* the large resolution level */
3149       for (int i = 0; i < count_large; i++) {
3150         immAttr3fv(color, theme_color);
3151         immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
3152         immAttr3fv(color, theme_color);
3153         immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
3154         immAttr3fv(color, theme_color);
3155         immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
3156         immAttr3fv(color, theme_color);
3157         immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
3158         fac += 4.0f * gridstep;
3159       }
3160     }
3161
3162     immEnd();
3163     immUnbindProgram();
3164   }
3165 }
3166
3167 /* If the area has overlapping regions, it returns visible rect for Region *ar */
3168 /* rect gets returned in local region coordinates */
3169 void ED_region_visible_rect(ARegion *ar, rcti *rect)
3170 {
3171   ARegion *arn = ar;
3172
3173   /* allow function to be called without area */
3174   while (arn->prev)
3175     arn = arn->prev;
3176
3177   *rect = ar->winrct;
3178
3179   /* check if a region overlaps with the current one */
3180   for (; arn; arn = arn->next) {
3181     if (ar != arn && arn->overlap) {
3182       if (BLI_rcti_isect(rect, &arn->winrct, NULL)) {
3183         if (ELEM(arn->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
3184           /* Overlap left, also check 1 pixel offset (2 regions on one side). */
3185           if (ABS(rect->xmin - arn->winrct.xmin) < 2) {
3186             rect->xmin = arn->winrct.xmax;
3187           }
3188
3189           /* Overlap right. */
3190           if (ABS(rect->xmax - arn->winrct.xmax) < 2) {
3191             rect->xmax = arn->winrct.xmin;
3192           }
3193         }
3194         else if (ELEM(arn->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
3195           /* Same logic as above for vertical regions. */
3196           if (ABS(rect->ymin - arn->winrct.ymin) < 2) {
3197             rect->ymin = arn->winrct.ymax;
3198           }
3199           if (ABS(rect->ymax - arn->winrct.ymax) < 2) {
3200             rect->ymax = arn->winrct.ymin;
3201           }
3202         }
3203         else if (arn->alignment == RGN_ALIGN_FLOAT) {
3204           /* Skip floating. */
3205         }
3206         else {
3207           BLI_assert(!"Region overlap with unknown alignment");
3208         }
3209       }
3210     }
3211   }
3212   BLI_rcti_translate(rect, -ar->winrct.xmin, -ar->winrct.ymin);
3213 }
3214
3215 /* Cache display helpers */
3216
3217 void ED_region_cache_draw_background(const ARegion *ar)
3218 {
3219   uint pos = GPU_vertformat_attr_add(
3220       immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
3221   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3222   immUniformColor4ub(128, 128, 255, 64);
3223   immRecti(pos, 0, 0, ar->winx, 8 * UI_DPI_FAC);
3224   immUnbindProgram();
3225 }
3226
3227 void ED_region_cache_draw_curfra_label(const int framenr, const float x, const float y)
3228 {
3229   uiStyle *style = UI_style_get();
3230   int fontid = style->widget.uifont_id;
3231   char numstr[32];
3232   float font_dims[2] = {0.0f, 0.0f};
3233
3234   /* frame number */
3235   BLF_size(fontid, 11.0f * U.pixelsize, U.dpi);
3236   BLI_snprintf(numstr, sizeof(numstr), "%d", framenr);
3237
3238   BLF_width_and_height(fontid, numstr, sizeof(numstr), &font_dims[0], &font_dims[1]);
3239
3240   uint pos = GPU_vertformat_attr_add(
3241       immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
3242   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3243   immUniformThemeColor(TH_CFRAME);
3244   immRecti(pos, x, y, x + font_dims[0] + 6.0f, y + font_dims[1] + 4.0f);
3245   immUnbindProgram();
3246
3247   UI_FontThemeColor(fontid, TH_TEXT);
3248   BLF_position(fontid, x + 2.0f, y + 2.0f, 0.0f);
3249   BLF_draw(fontid, numstr, sizeof(numstr));
3250 }
3251
3252 void ED_region_cache_draw_cached_segments(
3253     const ARegion *ar, const int num_segments, const int *points, const int sfra, const int efra)
3254 {
3255   if (num_segments) {
3256     uint pos = GPU_vertformat_attr_add(
3257         immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
3258     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3259     immUniformColor4ub(128, 128, 255, 128);
3260
3261     for (int a = 0; a < num_segments; a++) {
3262       float x1 = (float)(points[a * 2] - sfra) / (efra - sfra + 1) * ar->winx;
3263       float x2 = (float)(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * ar->winx;
3264
3265       immRecti(pos, x1, 0, x2, 8 * UI_DPI_FAC);
3266       /* TODO(merwin): use primitive restart to draw multiple rects more efficiently */
3267     }
3268
3269     immUnbindProgram();
3270   }
3271 }
3272
3273 /**
3274  * Generate subscriptions for this region.
3275  */
3276 void ED_region_message_subscribe(bContext *C,
3277                                  struct WorkSpace *workspace,
3278                                  struct Scene *scene,
3279                                  struct bScreen *screen,
3280                                  struct ScrArea *sa,
3281                                  struct ARegion *ar,
3282                                  struct wmMsgBus *mbus)
3283 {
3284   if (ar->gizmo_map != NULL) {
3285     WM_gizmomap_message_subscribe(C, ar->gizmo_map, ar, mbus);
3286   }
3287
3288   if (!BLI_listbase_is_empty(&ar->uiblocks)) {
3289     UI_region_message_subscribe(ar, mbus);
3290   }
3291
3292   if (ar->type->message_subscribe != NULL) {
3293     ar->type->message_subscribe(C, workspace, scene, screen, sa, ar, mbus);
3294   }
3295 }
3296
3297 int ED_region_snap_size_test(const ARegion *ar)
3298 {
3299   /* Use a larger value because toggling scrollbars can jump in size. */
3300   const int snap_match_threshold = 16;
3301   if (ar->type->snap_size != NULL) {
3302     return ((((ar->sizex - ar->type->snap_size(ar, ar->sizex, 0)) <= snap_match_threshold) << 0) |
3303             (((ar->sizey - ar->type->snap_size(ar, ar->sizey, 1)) <= snap_match_threshold) << 1));
3304   }
3305   return 0;
3306 }
3307
3308 bool ED_region_snap_size_apply(ARegion *ar, int snap_flag)
3309 {
3310   bool changed = false;
3311   if (ar->type->snap_size != NULL) {
3312     if (snap_flag & (1 << 0)) {
3313       short snap_size = ar->type->snap_size(ar, ar->sizex, 0);
3314       if (snap_size != ar->sizex) {
3315         ar->sizex = snap_size;
3316         changed = true;
3317       }
3318     }
3319     if (snap_flag & (1 << 1)) {
3320       short snap_size = ar->type->snap_size(ar, ar->sizey, 1);
3321       if (snap_size != ar->sizey) {
3322         ar->sizey = snap_size;
3323         changed = true;
3324       }
3325     }
3326   }
3327   return changed;
3328 }