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