Blender V5.0
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
8
9#include <algorithm>
10#include <cstdio>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_userdef_types.h"
16
17#include "BLI_linklist.h"
18#include "BLI_listbase.h"
19#include "BLI_math_vector.h"
20#include "BLI_rand.hh"
21#include "BLI_string.h"
22#include "BLI_string_utf8.h"
23#include "BLI_string_utils.hh"
24#include "BLI_utildefines.h"
25
26#include "BKE_context.hh"
27#include "BKE_global.hh"
28#include "BKE_screen.hh"
29#include "BKE_workspace.hh"
30
31#include "RNA_access.hh"
32
33#include "WM_api.hh"
34#include "WM_message.hh"
35#include "WM_toolsystem.hh"
36#include "WM_types.hh"
37
38#include "ED_asset_shelf.hh"
39#include "ED_buttons.hh"
40#include "ED_screen.hh"
41#include "ED_screen_types.hh"
42#include "ED_space_api.hh"
43#include "ED_time_scrub_ui.hh"
44
45#include "GPU_framebuffer.hh"
46#include "GPU_immediate.hh"
47#include "GPU_immediate_util.hh"
48#include "GPU_matrix.hh"
49#include "GPU_platform.hh"
50#include "GPU_state.hh"
51
52#include "BLF_api.hh"
53
54#include "IMB_metadata.hh"
55
56#include "UI_interface.hh"
57#include "UI_interface_icons.hh"
59#include "UI_resources.hh"
60#include "UI_view2d.hh"
61
62#include "screen_intern.hh"
63
64/* general area and region code */
65
66static void region_draw_gradient(const ARegion *region)
67{
68 if (region->v2d.cur.xmax >= region->v2d.tot.xmax) {
69 /* No overflow. */
70 return;
71 }
72
73 float opaque[4];
75 float transparent[4];
76 UI_GetThemeColor3fv(TH_HEADER, transparent);
77 transparent[3] = 0.0f;
78
79 rctf rect{};
80 rect.xmax = BLI_rcti_size_x(&region->winrct) + 1;
81 rect.xmin = rect.xmax - (25.0f * UI_SCALE_FAC);
82 rect.ymin = 0.0f;
83 rect.ymax = BLI_rcti_size_y(&region->winrct) + 1;
84 UI_draw_roundbox_4fv_ex(&rect, opaque, transparent, 0.0f, nullptr, 0.0f, 0.0f);
85}
86
92
94{
95 ARegion *region = params->region;
96 const wmNotifier *notifier = params->notifier;
97
98 /* generic notes first */
99 switch (notifier->category) {
100 case NC_WM:
101 if (notifier->data == ND_FILEREAD) {
102 ED_region_tag_redraw(region);
103 }
104 break;
105 case NC_WINDOW:
106 ED_region_tag_redraw(region);
107 break;
108 }
109
110 if (region->runtime->type && region->runtime->type->listener) {
111 region->runtime->type->listener(params);
112 }
113
114 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
115 UI_block_listen(block, params);
116 }
117
118 LISTBASE_FOREACH (uiList *, list, &region->ui_lists) {
119 if (list->type && list->type->listener) {
120 list->type->listener(list, params);
121 }
122 }
123}
124
126{
127 /* no generic notes? */
128 if (params->area->type && params->area->type->listener) {
129 params->area->type->listener(params);
130 }
131}
132
134{
135 /* no generic notes? */
136 if (area->type && area->type->refresh) {
137 area->type->refresh(C, area);
138 }
139 area->do_refresh = false;
140}
141
145static void area_draw_azone_fullscreen(short /*x1*/, short /*y1*/, short x2, short y2, float alpha)
146{
147 UI_icon_draw_ex(x2 - U.widget_unit,
148 y2 - U.widget_unit,
149 ICON_FULLSCREEN_EXIT,
151 min_ff(alpha, 0.75f),
152 0.0f,
153 nullptr,
154 false,
156}
157
161static void area_draw_azone(ScrArea *area, ARegion *region, AZone *az)
162{
163 if (region->regiontype != RGN_TYPE_HEADER || !(U.uiflag & USER_AREA_CORNER_HANDLE)) {
164 return;
165 }
166
167 if (az->x1 < area->totrct.xmin + 1) {
168 if ((region->alignment == RGN_ALIGN_TOP && az->y2 > area->totrct.ymax - 1) ||
169 (region->alignment == RGN_ALIGN_BOTTOM && az->y1 < area->totrct.ymin + 1))
170 {
172 float(az->x1) + UI_SCALE_FAC, float(az->y1) + (6.0f * UI_SCALE_FAC), ICON_GRIP_V, 0.4f);
173 }
174 }
175}
176
180static void draw_azone_arrow(float x1, float y1, float x2, float y2, AZEdge edge)
181{
182 const float size = 0.2f * U.widget_unit;
183 const float l = 1.0f; /* arrow length */
184 const float s = 0.25f; /* arrow thickness */
185 const float hl = l / 2.0f;
186 const float points[6][2] = {
187 {0, -hl}, {l, hl}, {l - s, hl + s}, {0, s + s - hl}, {s - l, hl + s}, {-l, hl}};
188 const float center[2] = {(x1 + x2) / 2, (y1 + y2) / 2};
189
190 int axis;
191 int sign;
192 switch (edge) {
194 axis = 0;
195 sign = 1;
196 break;
198 axis = 0;
199 sign = -1;
200 break;
202 axis = 1;
203 sign = 1;
204 break;
206 axis = 1;
207 sign = -1;
208 break;
209 default:
210 BLI_assert(0);
211 return;
212 }
213
215 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
216
219 immUniformColor4f(0.8f, 0.8f, 0.8f, 0.4f);
220
222 for (int i = 0; i < 6; i++) {
223 if (axis == 0) {
224 immVertex2f(pos, center[0] + points[i][0] * size, center[1] + points[i][1] * sign * size);
225 }
226 else {
227 immVertex2f(pos, center[0] + points[i][1] * sign * size, center[1] + points[i][0] * size);
228 }
229 }
230 immEnd();
231
234}
235
236static void region_draw_azone_tab_arrow(ScrArea *area, ARegion *region, AZone *az)
237{
239
240 /* add code to draw region hidden as 'too small' */
241 switch (az->edge) {
244 break;
247 break;
250 break;
253 break;
254 }
255
256 /* Workaround for different color spaces between normal areas and the ones using GPUViewports. */
257 float alpha = WM_region_use_viewport(area, region) ? 0.6f : 0.4f;
258 const float color[4] = {0.05f, 0.05f, 0.05f, alpha};
259 rctf rect{};
260 /* Hit size is a bit larger than visible background. */
261 rect.xmin = float(az->x1) + U.pixelsize;
262 rect.xmax = float(az->x2) - U.pixelsize;
263 rect.ymin = float(az->y1) + U.pixelsize;
264 rect.ymax = float(az->y2) - U.pixelsize;
265 UI_draw_roundbox_aa(&rect, true, 4.0f, color);
266
267 draw_azone_arrow(float(az->x1), float(az->y1), float(az->x2), float(az->y2), az->edge);
268}
269
271{
273}
274
275static void region_draw_azones(ScrArea *area, ARegion *region)
276{
277 if (!area) {
278 return;
279 }
280
281 GPU_line_width(1.0f);
283
285 GPU_matrix_translate_2f(-region->winrct.xmin, -region->winrct.ymin);
286
287 LISTBASE_FOREACH (AZone *, az, &area->actionzones) {
288 /* test if action zone is over this region */
289 rcti azrct;
290 BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2);
291
292 if (BLI_rcti_isect(&region->runtime->drawrct, &azrct, nullptr)) {
293 if (az->type == AZONE_AREA) {
294 area_draw_azone(area, region, az);
295 }
296 else if (az->type == AZONE_REGION) {
297 if (az->region && !(az->region->flag & RGN_FLAG_POLL_FAILED)) {
298 /* only display tab or icons when the region is hidden */
299 if (az->region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
300 region_draw_azone_tab_arrow(area, region, az);
301 }
302 }
303 }
304 else if (az->type == AZONE_FULLSCREEN) {
305 if (az->alpha > 0.0f) {
306 area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha);
307 }
308 }
309 }
310 if (!IS_EQF(az->alpha, 0.0f) && ELEM(az->type, AZONE_FULLSCREEN, AZONE_REGION_SCROLL)) {
312 }
313 }
314
316
318}
319
320static void region_draw_status_text(ScrArea * /*area*/, ARegion *region)
321{
322 float header_color[4];
323 UI_GetThemeColor4fv(TH_HEADER, header_color);
324
325 /* Clear the region from the buffer. */
326 GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
327
328 /* Fill with header color when the region is not overlapped. */
329 if (!region->overlap) {
330 const rctf rect = {0.0f, float(region->winx), 0.0f, float(region->winy)};
331 UI_draw_roundbox_3fv_alpha(&rect, true, 0.0f, header_color, 1.0f);
332 }
333
334 const int fontid = BLF_set_default();
335 float x = 12.0f * UI_SCALE_FAC;
336 const float y = 0.4f * UI_UNIT_Y;
337 const float width = BLF_width(fontid, region->runtime->headerstr, BLF_DRAW_STR_DUMMY_MAX);
339
340 /* Draw a background behind the text for extra contrast. */
341 if (region->overlap) {
342 /* Center the text horizontally. */
343 x = (region->winx - width) / 2.0f;
344 const float pad = 5.0f * UI_SCALE_FAC;
345 const float x1 = x - pad;
346 const float x2 = x + width + pad;
347 const float y1 = 3.0f * UI_SCALE_FAC;
348 const float y2 = region->winy - (4.0f * UI_SCALE_FAC);
349 /* Ensure header_color is not too transparent. */
350 header_color[3] = std::max(header_color[3], 0.6f);
352 const rctf rect = {x1, x2, y1, y2};
353 UI_draw_roundbox_4fv(&rect, true, 4.0f * UI_SCALE_FAC, header_color);
354 }
355
356 UI_FontThemeColor(fontid, TH_TEXT);
357 BLF_position(fontid, x, y, 0.0f);
358 BLF_draw(fontid, region->runtime->headerstr, BLF_DRAW_STR_DUMMY_MAX);
359}
360
362 /* Follow wmMsgNotifyFn spec */
363 bContext * /*C*/,
364 wmMsgSubscribeKey * /*msg_key*/,
365 wmMsgSubscribeValue *msg_val)
366{
367 ARegion *region = static_cast<ARegion *>(msg_val->owner);
368 ED_region_tag_redraw(region);
369
370 /* This avoids _many_ situations where header/properties control display settings.
371 * the common case is space properties in the header */
373 while (region && region->prev) {
374 region = region->prev;
375 }
376 for (; region; region = region->next) {
378 ED_region_tag_redraw(region);
379 }
380 }
381 }
382}
383
385 /* Follow wmMsgNotifyFn spec */
386 bContext * /*C*/,
387 wmMsgSubscribeKey * /*msg_key*/,
388 wmMsgSubscribeValue *msg_val)
389{
390 ScrArea *area = static_cast<ScrArea *>(msg_val->user_data);
392}
393
395{
396 wmMsgBus *mbus = params->message_bus;
397 WorkSpace *workspace = params->workspace;
398 ARegion *region = params->region;
399
401 wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
402 msg_sub_value_region_tag_redraw.owner = region;
403 msg_sub_value_region_tag_redraw.user_data = region;
404 msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
406 mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw);
407}
408
410{
411 wmMsgBus *mbus = params->message_bus;
412 WorkSpace *workspace = params->workspace;
413 ARegion *region = params->region;
414
416 const char *panel_category_tool = "Tool";
417 const char *category = UI_panel_category_active_get(region, false);
418
419 bool update_region = false;
420 if (category && STREQ(category, panel_category_tool)) {
421 update_region = true;
422 }
423 else {
424 /* Check if a tool category panel is pinned and visible in another category. */
425 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
426 if (UI_panel_is_active(panel) && panel->flag & PNL_PIN &&
427 STREQ(panel->type->category, panel_category_tool))
428 {
429 update_region = true;
430 break;
431 }
432 }
433 }
434
435 if (update_region) {
436 wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
437 msg_sub_value_region_tag_redraw.owner = region;
438 msg_sub_value_region_tag_redraw.user_data = region;
439 msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
441 mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw);
442 }
443}
444
451static bool area_is_pseudo_minimized(const ScrArea *area)
452{
453 return (area->winx < 3) || (area->winy < 3);
454}
455
457{
458 /* This is optional, only needed for dynamically sized regions. */
459 ScrArea *area = CTX_wm_area(C);
460 ARegionType *at = region->runtime->type;
461
462 if (!at->layout) {
463 return;
464 }
465
466 if (at->do_lock || (area && area_is_pseudo_minimized(area))) {
467 return;
468 }
469
470 region->runtime->do_draw |= RGN_DRAWING;
471
472 UI_SetTheme(area ? area->spacetype : 0, at->regionid);
473 at->layout(C, region);
474
475 /* Clear temporary update flag. */
477}
478
480{
481 using namespace blender;
482 wmWindow *win = CTX_wm_window(C);
483 ScrArea *area = CTX_wm_area(C);
484 ARegionType *at = region->runtime->type;
485
486 /* see BKE_spacedata_draw_locks() */
487 if (at->do_lock) {
488 return;
489 }
490
491 region->runtime->do_draw |= RGN_DRAWING;
492
493 /* Set viewport, scissor, ortho and region->runtime->drawrct. */
494 wmPartialViewport(&region->runtime->drawrct, &region->winrct, &region->runtime->drawrct);
495
497
498 UI_SetTheme(area ? area->spacetype : 0, at->regionid);
499
500 if (area && area_is_pseudo_minimized(area)) {
502 return;
503 }
504 /* optional header info instead? */
505 if (region->runtime->headerstr) {
506 region_draw_status_text(area, region);
507 }
508 else if (at->draw) {
509 at->draw(C, region);
510 }
511
512 /* XXX test: add convention to end regions always in pixel space,
513 * for drawing of borders/gestures etc */
514 ED_region_pixelspace(region);
515
516 /* Remove sRGB override by rebinding the framebuffer. */
519
521
522 region_draw_azones(area, region);
523
524 /* for debugging unneeded area redraws and partial redraw */
525 if (G.debug_value == 888) {
528 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
531 immUniformColor4f(rng.get_float(), rng.get_float(), rng.get_float(), 0.1f);
533 region->runtime->drawrct.xmin - region->winrct.xmin,
534 region->runtime->drawrct.ymin - region->winrct.ymin,
535 region->runtime->drawrct.xmax - region->winrct.xmin,
536 region->runtime->drawrct.ymax - region->winrct.ymin);
539 }
540
541 region->runtime->drawrct = rcti{};
542
544
545 if (area) {
546 const bScreen *screen = WM_window_get_active_screen(win);
547
548 /* Only region gradient for Top Bar. */
549 if ((screen->state != SCREENFULL) && area->spacetype == SPACE_TOPBAR &&
550 region->regiontype == RGN_TYPE_HEADER)
551 {
552 region_draw_gradient(region);
553 }
554 else if ((region->regiontype == RGN_TYPE_WINDOW) && (region->alignment == RGN_ALIGN_QSPLIT)) {
555
556 /* draw separating lines between the quad views */
557
558 float color[4] = {0.0f, 0.0f, 0.0f, 0.8f};
561 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
564 GPU_line_width(1.0f);
566 0,
567 0,
568 region->winrct.xmax - region->winrct.xmin + 1,
569 region->winrct.ymax - region->winrct.ymin + 1);
571 }
572 }
573
574 /* We may want to detach message-subscriptions from drawing. */
575 {
576 WorkSpace *workspace = CTX_wm_workspace(C);
579 Scene *scene = CTX_data_scene(C);
580 wmMsgBus *mbus = wm->runtime->message_bus;
581 WM_msgbus_clear_by_owner(mbus, region);
582
583 /* Cheat, always subscribe to this space type properties.
584 *
585 * This covers most cases and avoids copy-paste similar code for each space type.
586 */
588 {
589 SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
590
591 PointerRNA ptr = RNA_pointer_create_discrete(&screen->id, &RNA_Space, sl);
592
593 /* All properties for this space type. */
594 wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
595 msg_sub_value_region_tag_redraw.owner = region;
596 msg_sub_value_region_tag_redraw.user_data = region;
597 msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
598 WM_msg_subscribe_rna(mbus, &ptr, nullptr, &msg_sub_value_region_tag_redraw, __func__);
599 }
600
601 wmRegionMessageSubscribeParams message_subscribe_params{};
602 message_subscribe_params.context = C;
603 message_subscribe_params.message_bus = mbus;
604 message_subscribe_params.workspace = workspace;
605 message_subscribe_params.scene = scene;
606 message_subscribe_params.screen = screen;
607 message_subscribe_params.area = area;
608 message_subscribe_params.region = region;
609 ED_region_message_subscribe(&message_subscribe_params);
610 }
611}
612
613/* **********************************
614 * maybe silly, but let's try for now
615 * to keep these tags protected
616 * ********************************** */
617
619{
620 /* don't tag redraw while drawing, it shouldn't happen normally
621 * but python scripts can cause this to happen indirectly */
622 if (region && !(region->runtime->do_draw & RGN_DRAWING)) {
623 /* zero region means full region redraw */
624 region->runtime->do_draw &= ~(RGN_DRAW_PARTIAL | RGN_DRAW_NO_REBUILD |
626 region->runtime->do_draw |= RGN_DRAW;
627 region->runtime->drawrct = rcti{};
628 }
629}
630
632{
633 if (region) {
634 region->runtime->do_draw_paintcursor = RGN_DRAW;
635 }
636}
637
639{
640 if (region && !(region->runtime->do_draw & (RGN_DRAWING | RGN_DRAW))) {
641 region->runtime->do_draw &= ~(RGN_DRAW_PARTIAL | RGN_DRAW_EDITOR_OVERLAYS);
642 region->runtime->do_draw |= RGN_DRAW_NO_REBUILD;
643 region->runtime->drawrct = rcti{};
644 }
645}
646
648{
649 if (region) {
650 region->runtime->do_draw |= RGN_REFRESH_UI;
651 }
652}
653
655{
656 if (region && !(region->runtime->do_draw & (RGN_DRAWING | RGN_DRAW))) {
657 if (region->runtime->do_draw & RGN_DRAW_PARTIAL) {
658 ED_region_tag_redraw(region);
659 }
660 else {
661 region->runtime->do_draw |= RGN_DRAW_EDITOR_OVERLAYS;
662 }
663 }
664}
665
666void ED_region_tag_redraw_partial(ARegion *region, const rcti *rct, bool rebuild)
667{
668 if (region && !(region->runtime->do_draw & RGN_DRAWING)) {
669 if (region->runtime->do_draw & RGN_DRAW_PARTIAL) {
670 /* Partial redraw already set, expand region. */
671 BLI_rcti_union(&region->runtime->drawrct, rct);
672 if (rebuild) {
673 region->runtime->do_draw &= ~RGN_DRAW_NO_REBUILD;
674 }
675 }
676 else if (region->runtime->do_draw & (RGN_DRAW | RGN_DRAW_NO_REBUILD)) {
677 /* Full redraw already requested. */
678 if (rebuild) {
679 region->runtime->do_draw &= ~RGN_DRAW_NO_REBUILD;
680 }
681 }
682 else {
683 /* No redraw set yet, set partial region. */
684 region->runtime->drawrct = *rct;
685 region->runtime->do_draw |= RGN_DRAW_PARTIAL;
686 if (!rebuild) {
687 region->runtime->do_draw |= RGN_DRAW_NO_REBUILD;
688 }
689 }
690 }
691}
692
694{
695 if (area) {
696 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
697 ED_region_tag_redraw(region);
698 }
699 }
700}
701
703{
704 if (area) {
705 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
707 }
708 }
709}
710
711void ED_area_tag_redraw_regiontype(ScrArea *area, int regiontype)
712{
713 if (area) {
714 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
715 if (region->regiontype == regiontype) {
716 ED_region_tag_redraw(region);
717 }
718 }
719 }
720}
721
723{
724 if (area) {
725 area->do_refresh = true;
726 }
727}
728
730{
731 if (!area || (area->flag & AREA_FLAG_REGION_SIZE_UPDATE)) {
732 return;
733 }
734
736
737 /* Floating regions don't affect other regions, so the following can be skipped. */
738 if (changed_region->alignment == RGN_ALIGN_FLOAT) {
739 return;
740 }
741
742 /* Tag the following regions for redraw, since the size change of this region may affect the
743 * available space for them. */
744 for (ARegion *following_region = changed_region->next; following_region;
745 following_region = following_region->next)
746 {
747 /* Overlapping and non-overlapping regions don't affect each others space. So layout changes
748 * of one don't require redrawing the other. */
749 if (changed_region->overlap != following_region->overlap) {
750 continue;
751 }
752 /* Floating regions don't affect space of other regions. */
753 if (following_region->alignment == RGN_ALIGN_FLOAT) {
754 continue;
755 }
756 ED_region_tag_redraw(following_region);
757 }
758}
759
760/* *************************************************************** */
761
762int ED_area_max_regionsize(const ScrArea *area, const ARegion *scale_region, const AZEdge edge)
763{
764 int dist;
765
766 /* regions in regions. */
767 if (scale_region->alignment & RGN_SPLIT_PREV) {
768 const int align = RGN_ALIGN_ENUM_FROM_MASK(scale_region->alignment);
769
770 if (ELEM(align, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
771 ARegion *region = scale_region->prev;
772 dist = region->winy + scale_region->winy - U.pixelsize;
773 }
774 else /* if (ELEM(align, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) */ {
775 ARegion *region = scale_region->prev;
776 dist = region->winx + scale_region->winx - U.pixelsize;
777 }
778 }
779 else {
781 dist = BLI_rcti_size_x(&area->totrct);
782 }
783 else { /* AE_BOTTOM_TO_TOPLEFT, AE_TOP_TO_BOTTOMRIGHT */
784 dist = BLI_rcti_size_y(&area->totrct);
785 }
786
787 /* Subtract the width of regions on opposite side
788 * prevents dragging regions into other opposite regions. */
789 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
790 if (region == scale_region) {
791 continue;
792 }
793
794 if (scale_region->alignment == RGN_ALIGN_LEFT && region->alignment == RGN_ALIGN_RIGHT) {
795 dist -= region->winx;
796 }
797 else if (scale_region->alignment == RGN_ALIGN_RIGHT && region->alignment == RGN_ALIGN_LEFT) {
798 dist -= region->winx;
799 }
800 else if (scale_region->alignment == RGN_ALIGN_TOP &&
801 (region->alignment == RGN_ALIGN_BOTTOM || ELEM(region->regiontype,
806 {
807 dist -= region->winy;
808 }
809 else if (scale_region->alignment == RGN_ALIGN_BOTTOM &&
810 (region->alignment == RGN_ALIGN_TOP || ELEM(region->regiontype,
815 {
816 dist -= region->winy;
817 }
818 }
819 }
820
821 dist /= UI_SCALE_FAC;
822 return dist;
823}
824
825const char *ED_area_region_search_filter_get(const ScrArea *area, const ARegion *region)
826{
827 /* Only the properties editor has a search string for now. */
828 if (area->spacetype == SPACE_PROPERTIES) {
829 SpaceProperties *sbuts = static_cast<SpaceProperties *>(area->spacedata.first);
830 if (region->regiontype == RGN_TYPE_WINDOW) {
831 return ED_buttons_search_string_get(sbuts);
832 }
833 }
834
835 return nullptr;
836}
837
839{
841
842 const char *search_filter = ED_area_region_search_filter_get(area, region);
843 SET_FLAG_FROM_TEST(region->flag,
844 region->regiontype == RGN_TYPE_WINDOW && search_filter &&
845 search_filter[0] != '\0',
847}
848
849/* *************************************************************** */
850
851void ED_area_status_text(ScrArea *area, const char *str)
852{
853 /* happens when running transform operators in background mode */
854 if (area == nullptr) {
855 return;
856 }
857
858 ARegion *ar = nullptr;
859
860 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
861 if (region->regiontype == RGN_TYPE_HEADER && region->runtime->visible) {
862 ar = region;
863 }
864 else if (region->regiontype == RGN_TYPE_TOOL_HEADER && region->runtime->visible) {
865 /* Prefer tool header when we also have a header. */
866 ar = region;
867 break;
868 }
869 }
870
871 if (ar) {
872 if (str) {
873 if (ar->runtime->headerstr == nullptr) {
874 ar->runtime->headerstr = MEM_malloc_arrayN<char>(UI_MAX_DRAW_STR, "headerprint");
875 }
877 BLI_str_rstrip(ar->runtime->headerstr);
878 }
879 else {
880 MEM_SAFE_FREE(ar->runtime->headerstr);
881 }
883 }
884}
885
886/* *************************************************************** */
887
888static void ed_workspace_status_item(WorkSpace *workspace,
889 std::string text,
890 const int icon,
891 const float space_factor = 0.0f,
892 const bool inverted = false)
893{
894 /* Can be nullptr when running operators in background mode. */
895 if (workspace == nullptr) {
896 return;
897 }
898
900 item.text = std::move(text);
901 item.icon = icon;
902 item.space_factor = space_factor;
903 item.inverted = inverted;
904 workspace->runtime->status.append(std::move(item));
905}
906
907static void ed_workspace_status_space(WorkSpace *workspace, const float space_factor)
908{
909 ed_workspace_status_item(workspace, {}, ICON_NONE, space_factor);
910}
911
921
922/* -------------------------------------------------------------------- */
925
926static constexpr float STATUS_BEFORE_TEXT = 0.17f;
927static constexpr float STATUS_AFTER_TEXT = 0.90f;
928static constexpr float STATUS_MOUSE_ICON_PAD = -0.68f;
929
930static void ed_workspace_status_text_item(WorkSpace *workspace, std::string text)
931{
932 if (!text.empty()) {
934 ed_workspace_status_item(workspace, std::move(text), ICON_NONE);
936 }
937}
938
940 const int icon,
941 const bool inverted = false)
942{
943 if (icon) {
944 ed_workspace_status_item(workspace, {}, icon, 0.0f, inverted);
945 if (icon >= ICON_MOUSE_LMB && icon <= ICON_MOUSE_MMB_SCROLL) {
946 /* Negative space after narrow mice icons. */
948 }
949 }
950}
951
953
954/* -------------------------------------------------------------------- */
957
958void WorkspaceStatus::item(std::string text, const int icon1, const int icon2)
959{
960 ed_workspace_status_icon_item(workspace_, icon1);
961 ed_workspace_status_icon_item(workspace_, icon2);
962 ed_workspace_status_text_item(workspace_, std::move(text));
963}
964
966{
967 ed_workspace_status_space(workspace_, factor);
968}
969
970void WorkspaceStatus::range(std::string text, const int icon1, const int icon2)
971{
972 ed_workspace_status_item(workspace_, {}, icon1);
973 ed_workspace_status_item(workspace_, "-", ICON_NONE);
974 ed_workspace_status_space(workspace_, -0.5f);
975 ed_workspace_status_item(workspace_, {}, icon2);
976 ed_workspace_status_text_item(workspace_, std::move(text));
977}
978
979void WorkspaceStatus::item_bool(std::string text,
980 const bool inverted,
981 const int icon1,
982 const int icon2)
983{
984 ed_workspace_status_icon_item(workspace_, icon1, inverted);
985 ed_workspace_status_icon_item(workspace_, icon2, inverted);
986 ed_workspace_status_text_item(workspace_, std::move(text));
987}
988
989void WorkspaceStatus::opmodal(std::string text,
990 const wmOperatorType *ot,
991 const int propvalue,
992 const bool inverted)
993{
994 wmKeyMap *keymap = WM_keymap_active(wm_, ot->modalkeymap);
995 if (keymap) {
996 const wmKeyMapItem *kmi = WM_modalkeymap_find_propvalue(keymap, propvalue);
997 if (kmi) {
998#ifdef WITH_HEADLESS
999 int icon = 0;
1000#else
1001 int icon = UI_icon_from_event_type(kmi->type, kmi->val);
1002#endif
1003 if (kmi->shift == KM_MOD_HELD) {
1004 ed_workspace_status_item(workspace_, {}, ICON_EVENT_SHIFT, 0.0f, inverted);
1005 }
1006 if (kmi->ctrl == KM_MOD_HELD) {
1007 ed_workspace_status_item(workspace_, {}, ICON_EVENT_CTRL, 0.0f, inverted);
1008 }
1009 if (kmi->alt == KM_MOD_HELD) {
1010 ed_workspace_status_item(workspace_, {}, ICON_EVENT_ALT, 0.0f, inverted);
1011 }
1012 if (kmi->oskey == KM_MOD_HELD) {
1013 ed_workspace_status_item(workspace_, {}, ICON_EVENT_OS, 0.0f, inverted);
1014 }
1015 if (!ELEM(kmi->hyper, KM_NOTHING, KM_ANY)) {
1016 ed_workspace_status_item(workspace_, {}, ICON_EVENT_HYPER, 0.0f, inverted);
1017 }
1018 ed_workspace_status_icon_item(workspace_, icon, inverted);
1019 ed_workspace_status_text_item(workspace_, std::move(text));
1020 }
1021 }
1022}
1023
1025{
1027 status.item(str ? str : "", ICON_NONE);
1028}
1029
1031
1032/* ************************************************************ */
1033
1034static void area_azone_init(const wmWindow *win, const bScreen *screen, ScrArea *area)
1035{
1036 /* reinitialize entirely, regions and full-screen add azones too */
1037 BLI_freelistN(&area->actionzones);
1038
1039 if (screen->state != SCREENNORMAL) {
1040 return;
1041 }
1042
1043 if (U.app_flag & USER_APP_LOCK_CORNER_SPLIT) {
1044 return;
1045 }
1046
1047 if (ED_area_is_global(area)) {
1048 return;
1049 }
1050
1051 if (screen->temp) {
1052 return;
1053 }
1054
1055 /* Use a taller zone on the left side, the height of
1056 * the header, to make them easier to hit. The others
1057 * on the right are shorter to not interfere with
1058 * scroll bars. */
1059
1060 const float coords[4][4] = {
1061 /* Bottom-left. */
1062 {area->totrct.xmin - U.pixelsize,
1063 area->totrct.ymin - U.pixelsize,
1065 float(area->totrct.ymin + ED_area_headersize())},
1066 /* Bottom-right. */
1068 area->totrct.ymin - U.pixelsize,
1069 area->totrct.xmax + U.pixelsize,
1070 area->totrct.ymin + UI_AZONESPOTH},
1071 /* Top-left. */
1072 {area->totrct.xmin - U.pixelsize,
1075 area->totrct.ymax + U.pixelsize},
1076 /* Top-right. */
1078 area->totrct.ymax - UI_AZONESPOTH,
1079 area->totrct.xmax + U.pixelsize,
1080 area->totrct.ymax + U.pixelsize},
1081 };
1082
1083 for (int i = 0; i < 4; i++) {
1084 /* can't click on bottom corners on OS X, already used for resizing */
1085#ifdef __APPLE__
1086 if (!WM_window_is_fullscreen(win) &&
1087 ((coords[i][0] == 0 && coords[i][1] == 0) ||
1088 (coords[i][0] == WM_window_native_pixel_x(win) && coords[i][1] == 0)))
1089 {
1090 continue;
1091 }
1092#else
1093 (void)win;
1094#endif
1095
1096 /* set area action zones */
1097 AZone *az = MEM_callocN<AZone>("actionzone");
1098 BLI_addtail(&(area->actionzones), az);
1099 az->type = AZONE_AREA;
1100 az->x1 = coords[i][0];
1101 az->y1 = coords[i][1];
1102 az->x2 = coords[i][2];
1103 az->y2 = coords[i][3];
1104 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
1105 }
1106}
1107
1108static void fullscreen_azone_init(ScrArea *area, ARegion *region)
1109{
1111 return;
1112 }
1113
1114 AZone *az = MEM_callocN<AZone>("fullscreen action zone");
1115 BLI_addtail(&(area->actionzones), az);
1116 az->type = AZONE_FULLSCREEN;
1117 az->region = region;
1118 az->alpha = 0.0f;
1119
1120 if (U.uiflag2 & USER_REGION_OVERLAP) {
1121 const rcti *rect_visible = ED_region_visible_rect(region);
1122 az->x2 = region->winrct.xmin + rect_visible->xmax;
1123 az->y2 = region->winrct.ymin + rect_visible->ymax;
1124 }
1125 else {
1126 az->x2 = region->winrct.xmax;
1127 az->y2 = region->winrct.ymax;
1128 }
1129 az->x1 = az->x2 - AZONEFADEOUT;
1130 az->y1 = az->y2 - AZONEFADEOUT;
1131
1132 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
1133}
1134
1140static bool region_background_is_transparent(const ScrArea *area, const ARegion *region)
1141{
1142 /* Ensure the right theme is active, may not be the case on startup, for example. */
1143 bThemeState theme_state;
1144 UI_Theme_Store(&theme_state);
1145 UI_SetTheme(area->spacetype, region->regiontype);
1146
1147 uchar back[4];
1149
1150 UI_Theme_Restore(&theme_state);
1151
1152 return back[3] < 50;
1153}
1154
1155static void region_azone_edge(const ScrArea *area, AZone *az, const ARegion *region)
1156{
1157 /* Narrow regions like headers need a smaller hit-space that
1158 * does not interfere with content. */
1159 const bool is_narrow = RGN_TYPE_IS_HEADER_ANY(region->regiontype);
1160 const bool transparent = !is_narrow && region->overlap &&
1162
1163 /* Only scale the padding inside the region, not outside. */
1164 const float aspect = BLI_rctf_size_y(&region->v2d.cur) /
1165 (BLI_rcti_size_y(&region->v2d.mask) + 1);
1166
1167 /* Different padding inside and outside the region. */
1168 const int pad_out = (is_narrow ? 2.0f : 3.0f) * UI_SCALE_FAC;
1169 const int pad_in = (is_narrow ? 1.0f : (transparent ? 8.0f : 4.0f)) * UI_SCALE_FAC / aspect;
1170
1171 switch (az->edge) {
1173 az->x1 = region->winrct.xmin;
1174 az->y1 = region->winrct.ymax + pad_out;
1175 az->x2 = region->winrct.xmax;
1176 az->y2 = region->winrct.ymax - pad_in;
1177 break;
1179 az->x1 = region->winrct.xmin;
1180 az->y1 = region->winrct.ymin + pad_out;
1181 az->x2 = region->winrct.xmax;
1182 az->y2 = region->winrct.ymin - pad_in;
1183 break;
1185 az->x1 = region->winrct.xmin - pad_out;
1186 az->y1 = region->winrct.ymin;
1187 az->x2 = region->winrct.xmin + pad_in;
1188 az->y2 = region->winrct.ymax;
1189 break;
1191 az->x1 = region->winrct.xmax - pad_in;
1192 az->y1 = region->winrct.ymin;
1193 az->x2 = region->winrct.xmax + pad_out;
1194 az->y2 = region->winrct.ymax;
1195 break;
1196 }
1197 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
1198}
1199
1200/* region already made zero sized, in shape of edge */
1201static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region)
1202{
1203 float edge_offset = 1.0f;
1204 const float tab_size_x = 1.0f * U.widget_unit;
1205 const float tab_size_y = 0.5f * U.widget_unit;
1206
1207 switch (az->edge) {
1208 case AE_TOP_TO_BOTTOMRIGHT: {
1209 int add = (region->winrct.ymax == area->totrct.ymin) ? 1 : 0;
1210 az->x1 = region->winrct.xmax - ((edge_offset + 1.0f) * tab_size_x);
1211 az->y1 = region->winrct.ymax - U.pixelsize;
1212 az->x2 = region->winrct.xmax - (edge_offset * tab_size_x);
1213 az->y2 = region->winrct.ymax - add + tab_size_y;
1214 break;
1215 }
1217 az->x1 = region->winrct.xmax - ((edge_offset + 1.0f) * tab_size_x);
1218 az->y1 = region->winrct.ymin - tab_size_y;
1219 az->x2 = region->winrct.xmax - (edge_offset * tab_size_x);
1220 az->y2 = region->winrct.ymin + U.pixelsize;
1221 break;
1223 az->x1 = region->winrct.xmin - tab_size_y;
1224 az->y1 = region->winrct.ymax - ((edge_offset + 1.0f) * tab_size_x);
1225 az->x2 = region->winrct.xmin + U.pixelsize;
1226 az->y2 = region->winrct.ymax - (edge_offset * tab_size_x);
1227 break;
1229 az->x1 = region->winrct.xmax - U.pixelsize;
1230 az->y1 = region->winrct.ymax - ((edge_offset + 1.0f) * tab_size_x);
1231 az->x2 = region->winrct.xmax + tab_size_y;
1232 az->y2 = region->winrct.ymax - (edge_offset * tab_size_x);
1233 break;
1234 }
1235 /* rect needed for mouse pointer test */
1236 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
1237}
1238
1239static bool region_azone_edge_poll(const ScrArea *area,
1240 const ARegion *region,
1241 const bool is_fullscreen)
1242{
1243 if (region->flag & RGN_FLAG_POLL_FAILED) {
1244 return false;
1245 }
1246
1247 if (area->winy < int(float(ED_area_headersize()) * 1.5f)) {
1248 return false;
1249 }
1250
1251 /* Don't use edge if the region hides with previous region which is now hidden. See #116196. */
1252 if ((region->alignment & (RGN_SPLIT_PREV | RGN_ALIGN_HIDE_WITH_PREV) && region->prev) &&
1254 {
1255 return false;
1256 }
1257
1258 const bool is_hidden = (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
1259
1260 if (is_hidden && is_fullscreen) {
1261 return false;
1262 }
1263 if (!is_hidden && ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
1264 return false;
1265 }
1266 if (!is_hidden && region->regiontype == RGN_TYPE_NAV_BAR && area->spacetype == SPACE_PROPERTIES)
1267 {
1268 return false;
1269 }
1270
1271 if (is_hidden && (U.app_flag & USER_APP_HIDE_REGION_TOGGLE)) {
1272 return false;
1273 }
1274
1275 if (!is_hidden && (U.app_flag & USER_APP_LOCK_EDGE_RESIZE)) {
1276 return false;
1277 }
1278
1279 return true;
1280}
1281
1283 ARegion *region,
1284 AZEdge edge,
1285 const bool is_fullscreen)
1286{
1287 const bool is_hidden = (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
1288
1289 if (!region_azone_edge_poll(area, region, is_fullscreen)) {
1290 return;
1291 }
1292
1293 AZone *az = MEM_callocN<AZone>("actionzone");
1294 BLI_addtail(&(area->actionzones), az);
1295 az->type = AZONE_REGION;
1296 az->region = region;
1297 az->edge = edge;
1298
1299 if (is_hidden) {
1300 region_azone_tab_plus(area, az, region);
1301 }
1302 else {
1303 region_azone_edge(area, az, region);
1304 }
1305}
1306
1308 ARegion *region,
1309 AZScrollDirection direction)
1310{
1311 AZone *az = MEM_callocN<AZone>(__func__);
1312
1313 BLI_addtail(&area->actionzones, az);
1315 az->region = region;
1316 az->direction = direction;
1317
1318 if (direction == AZ_SCROLL_VERT) {
1319 az->region->v2d.alpha_vert = 0;
1320 }
1321 else if (direction == AZ_SCROLL_HOR) {
1322 az->region->v2d.alpha_hor = 0;
1323 }
1324
1325 /* No need to specify rect for scrollbar az. For intersection we'll test against the area around
1326 * the region's scroller instead, in `area_actionzone_get_rect`. */
1327}
1328
1330{
1331 const View2D *v2d = &region->v2d;
1332
1333 if (v2d->scroll & V2D_SCROLL_VERTICAL) {
1335 }
1336 if (v2d->scroll & V2D_SCROLL_HORIZONTAL) {
1338 }
1339}
1340
1341/* *************************************************************** */
1343 ARegion *region,
1344 const int alignment,
1345 const bool is_fullscreen)
1346{
1347
1348 /* edge code (t b l r) is along which area edge azone will be drawn */
1349 if (alignment == RGN_ALIGN_TOP) {
1350 region_azone_edge_init(area, region, AE_BOTTOM_TO_TOPLEFT, is_fullscreen);
1351 }
1352 else if (alignment == RGN_ALIGN_BOTTOM) {
1353 region_azone_edge_init(area, region, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen);
1354 }
1355 else if (alignment == RGN_ALIGN_RIGHT) {
1356 region_azone_edge_init(area, region, AE_LEFT_TO_TOPRIGHT, is_fullscreen);
1357 }
1358 else if (alignment == RGN_ALIGN_LEFT) {
1359 region_azone_edge_init(area, region, AE_RIGHT_TO_TOPLEFT, is_fullscreen);
1360 }
1361}
1362
1363static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *region)
1364{
1365 const bool is_fullscreen = screen->state == SCREENFULL;
1366
1367 /* Only display tab or icons when the header region is hidden
1368 * (not the tool header - they overlap). */
1369 if (region->regiontype == RGN_TYPE_TOOL_HEADER) {
1370 return;
1371 }
1372
1373 region_azones_add_edge(area, region, RGN_ALIGN_ENUM_FROM_MASK(region->alignment), is_fullscreen);
1374
1375 /* For a split region also continue the azone edge from the next region if this region is aligned
1376 * with the next */
1377 if ((region->alignment & RGN_SPLIT_PREV) && region->prev) {
1379 area, region, RGN_ALIGN_ENUM_FROM_MASK(region->prev->alignment), is_fullscreen);
1380 }
1381
1382 if (is_fullscreen) {
1383 fullscreen_azone_init(area, region);
1384 }
1385
1386 region_azones_scrollbars_init(area, region);
1387}
1388
1389/* dir is direction to check, not the splitting edge direction! */
1390static int rct_fits(const rcti *rect, const eScreenAxis dir_axis, int size)
1391{
1392 if (dir_axis == SCREEN_AXIS_H) {
1393 return BLI_rcti_size_x(rect) + 1 - size;
1394 }
1395 /* Vertical. */
1396 return BLI_rcti_size_y(rect) + 1 - size;
1397}
1398
1399/* *************************************************************** */
1400
1401/* region should be overlapping */
1402/* function checks if some overlapping region was defined before - on same place */
1403static void region_overlap_fix(ScrArea *area, ARegion *region)
1404{
1405 /* find overlapping previous region on same place */
1406 ARegion *region_iter;
1407 int align1 = 0;
1408 const int align = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
1409 for (region_iter = region->prev; region_iter; region_iter = region_iter->prev) {
1410 if (region_iter->flag & (RGN_FLAG_POLL_FAILED | RGN_FLAG_HIDDEN)) {
1411 continue;
1412 }
1413 if (!region_iter->overlap || (region_iter->alignment & RGN_SPLIT_PREV)) {
1414 continue;
1415 }
1416
1417 const int align_iter = RGN_ALIGN_ENUM_FROM_MASK(region_iter->alignment);
1418 if (ELEM(align_iter, RGN_ALIGN_FLOAT)) {
1419 continue;
1420 }
1421
1422 align1 = align_iter;
1423 if (BLI_rcti_isect(&region_iter->winrct, &region->winrct, nullptr)) {
1424 if (align1 != align) {
1425 /* Left overlapping right or vice-versa, forbid this! */
1426 region->flag |= RGN_FLAG_TOO_SMALL;
1427 return;
1428 }
1429 /* Else, we have our previous region on same side. */
1430 break;
1431 }
1432 }
1433
1434 /* Guard against flags slipping through that would have to be masked out in usages below. */
1435 BLI_assert(align1 == RGN_ALIGN_ENUM_FROM_MASK(align1));
1436
1437 /* translate or close */
1438 if (region_iter) {
1439 if (align1 == RGN_ALIGN_LEFT) {
1440 if (region->winrct.xmax + region_iter->winx > area->winx - U.widget_unit) {
1441 region->flag |= RGN_FLAG_TOO_SMALL;
1442 return;
1443 }
1444 BLI_rcti_translate(&region->winrct, region_iter->winx, 0);
1445 }
1446 else if (align1 == RGN_ALIGN_RIGHT) {
1447 if (region->winrct.xmin - region_iter->winx < U.widget_unit) {
1448 region->flag |= RGN_FLAG_TOO_SMALL;
1449 return;
1450 }
1451 BLI_rcti_translate(&region->winrct, -region_iter->winx, 0);
1452 }
1453 }
1454
1455 /* At this point, 'region' is in its final position and still open.
1456 * Make a final check it does not overlap any previous 'other side' region. */
1457 for (region_iter = region->prev; region_iter; region_iter = region_iter->prev) {
1458 if (region_iter->flag & (RGN_FLAG_POLL_FAILED | RGN_FLAG_HIDDEN)) {
1459 continue;
1460 }
1461 if (!region_iter->overlap || (region_iter->alignment & RGN_SPLIT_PREV)) {
1462 continue;
1463 }
1464
1465 const int align_iter = RGN_ALIGN_ENUM_FROM_MASK(region_iter->alignment);
1466 if (ELEM(align_iter, RGN_ALIGN_FLOAT)) {
1467 continue;
1468 }
1469
1470 if ((align_iter != align) && BLI_rcti_isect(&region_iter->winrct, &region->winrct, nullptr)) {
1471 /* Left overlapping right or vice-versa, forbid this! */
1472 region->flag |= RGN_FLAG_TOO_SMALL;
1473 return;
1474 }
1475 }
1476}
1477
1478bool ED_region_is_overlap(int spacetype, int regiontype)
1479{
1480 if (regiontype == RGN_TYPE_HUD) {
1481 return true;
1482 }
1483 if (U.uiflag2 & USER_REGION_OVERLAP) {
1484 if (spacetype == SPACE_NODE) {
1485 if (ELEM(regiontype,
1490 {
1491 return true;
1492 }
1493 }
1494 else if (spacetype == SPACE_VIEW3D) {
1495 if (regiontype == RGN_TYPE_HEADER) {
1496 /* Do not treat as overlapped if no transparency. */
1497 bTheme *theme = UI_GetTheme();
1498 return theme->space_view3d.header[3] != 255;
1499 }
1500 if (ELEM(regiontype,
1508 {
1509 return true;
1510 }
1511 }
1512 else if (spacetype == SPACE_IMAGE) {
1513 if (ELEM(regiontype,
1521 {
1522 return true;
1523 }
1524 }
1525 }
1526
1527 return false;
1528}
1529
1531 ScrArea *area, ARegion *region, rcti *remainder, rcti *overlap_remainder, int quad)
1532{
1533 using namespace blender::ed;
1534 rcti *remainder_prev = remainder;
1535
1536 if (region == nullptr) {
1537 return;
1538 }
1539
1540 int prev_winx = region->winx;
1541 int prev_winy = region->winy;
1542
1543 /* no returns in function, winrct gets set in the end again */
1544 BLI_rcti_init(&region->winrct, 0, 0, 0, 0);
1545
1546 /* for test; allow split of previously defined region */
1547 if (region->alignment & RGN_SPLIT_PREV) {
1548 if (region->prev) {
1549 remainder = &region->prev->winrct;
1550 }
1551 }
1552
1553 int alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
1554
1555 /* set here, assuming userpref switching forces to call this again */
1556 region->overlap = ED_region_is_overlap(area->spacetype, region->regiontype);
1557
1558 /* clear state flags first */
1560 /* user errors */
1561 if ((region->next == nullptr) && !ELEM(alignment, RGN_ALIGN_QSPLIT, RGN_ALIGN_FLOAT)) {
1562 alignment = RGN_ALIGN_NONE;
1563 }
1564
1565 /* If both the #ARegion.sizex/y and the #ARegionType.prefsizex/y are 0, the region is tagged as
1566 * too small, even before the layout for dynamically sized regions is created.
1567 * #wm_draw_window_offscreen() allows the layout to be created despite the #RGN_FLAG_TOO_SMALL
1568 * flag being set. But there may still be regions that don't have a separate #ARegionType.layout
1569 * callback. For those, set a default #ARegionType.prefsizex/y so they can become visible. */
1570 if ((region->flag & RGN_FLAG_DYNAMIC_SIZE) && !(region->runtime->type->layout)) {
1571 if ((region->sizex == 0) && (region->runtime->type->prefsizex == 0)) {
1572 region->runtime->type->prefsizex = AREAMINX;
1573 }
1574 if ((region->sizey == 0) && (region->runtime->type->prefsizey == 0)) {
1575 region->runtime->type->prefsizey = HEADERY;
1576 }
1577 }
1578
1579 /* `prefsizex/y`, taking into account DPI. */
1580 int prefsizex = UI_SCALE_FAC *
1581 ((region->sizex > 1) ? region->sizex + 0.5f : region->runtime->type->prefsizex);
1582 int prefsizey;
1583
1584 if (region->regiontype == RGN_TYPE_HEADER) {
1585 prefsizey = ED_area_headersize();
1586 }
1587 else if (region->regiontype == RGN_TYPE_TOOL_HEADER) {
1588 prefsizey = ED_area_headersize();
1589 }
1590 else if (region->regiontype == RGN_TYPE_FOOTER) {
1591 prefsizey = ED_area_footersize();
1592 }
1593 else if (region->regiontype == RGN_TYPE_ASSET_SHELF) {
1594 prefsizey = region->sizey > 1 ? (UI_SCALE_FAC * (region->sizey + 0.5f)) :
1596 }
1597 else if (region->regiontype == RGN_TYPE_ASSET_SHELF_HEADER) {
1599 }
1600 else if (ED_area_is_global(area)) {
1601 prefsizey = ED_region_global_size_y();
1602 }
1603 else {
1604 prefsizey = UI_SCALE_FAC *
1605 (region->sizey > 1 ? region->sizey + 0.5f : region->runtime->type->prefsizey);
1606 }
1607
1608 if (region->flag & (RGN_FLAG_POLL_FAILED | RGN_FLAG_HIDDEN)) {
1609 /* hidden is user flag */
1610 }
1611 else if (alignment == RGN_ALIGN_FLOAT) {
1618 const int size_min[2] = {UI_UNIT_X, UI_UNIT_Y};
1619 rcti overlap_remainder_margin = *overlap_remainder;
1620
1621 BLI_rcti_resize(&overlap_remainder_margin,
1622 max_ii(0, BLI_rcti_size_x(overlap_remainder) - UI_UNIT_X / 2),
1623 max_ii(0, BLI_rcti_size_y(overlap_remainder) - UI_UNIT_Y / 2));
1624 region->winrct.xmin = overlap_remainder_margin.xmin + region->runtime->offset_x;
1625 region->winrct.ymin = overlap_remainder_margin.ymin + region->runtime->offset_y;
1626 region->winrct.xmax = region->winrct.xmin + prefsizex - 1;
1627 region->winrct.ymax = region->winrct.ymin + prefsizey - 1;
1628
1629 BLI_rcti_isect(&region->winrct, &overlap_remainder_margin, &region->winrct);
1630
1631 if (BLI_rcti_size_x(&region->winrct) != prefsizex - 1) {
1632 region->flag |= RGN_FLAG_SIZE_CLAMP_X;
1633 }
1634 if (BLI_rcti_size_y(&region->winrct) != prefsizey - 1) {
1635 region->flag |= RGN_FLAG_SIZE_CLAMP_Y;
1636 }
1637
1638 /* We need to use a test that won't have been previously clamped. */
1639 rcti winrct_test{};
1640 winrct_test.xmin = region->winrct.xmin;
1641 winrct_test.ymin = region->winrct.ymin;
1642 winrct_test.xmax = region->winrct.xmin + size_min[0];
1643 winrct_test.ymax = region->winrct.ymin + size_min[1];
1644
1645 BLI_rcti_isect(&winrct_test, &overlap_remainder_margin, &winrct_test);
1646 if (BLI_rcti_size_x(&winrct_test) < size_min[0] || BLI_rcti_size_y(&winrct_test) < size_min[1])
1647 {
1648 region->flag |= RGN_FLAG_TOO_SMALL;
1649 }
1650 }
1651 else if (rct_fits(remainder, SCREEN_AXIS_V, 1) < 0 || rct_fits(remainder, SCREEN_AXIS_H, 1) < 0)
1652 {
1653 /* remainder is too small for any usage */
1654 region->flag |= RGN_FLAG_TOO_SMALL;
1655 }
1656 else if (alignment == RGN_ALIGN_NONE) {
1657 /* typically last region */
1658 region->winrct = *remainder;
1659 BLI_rcti_init(remainder, 0, 0, 0, 0);
1660 }
1661 else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
1662 rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
1663
1664 if ((prefsizey == 0) || (rct_fits(winrct, SCREEN_AXIS_V, prefsizey) < (U.pixelsize * -2))) {
1665 region->flag |= RGN_FLAG_TOO_SMALL;
1666 }
1667 else {
1668 int fac = rct_fits(winrct, SCREEN_AXIS_V, prefsizey);
1669
1670 if (fac < 0) {
1671 prefsizey += fac;
1672 }
1673
1674 region->winrct = *winrct;
1675
1676 if (alignment == RGN_ALIGN_TOP) {
1677 region->winrct.ymin = region->winrct.ymax - prefsizey + 1;
1678 winrct->ymax = region->winrct.ymin - 1;
1679 }
1680 else {
1681 region->winrct.ymax = region->winrct.ymin + prefsizey - 1;
1682 winrct->ymin = region->winrct.ymax + 1;
1683 }
1684 BLI_rcti_sanitize(winrct);
1685 }
1686 }
1687 else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
1688 rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
1689
1690 if ((prefsizex == 0) || (rct_fits(winrct, SCREEN_AXIS_H, prefsizex) < 0)) {
1691 region->flag |= RGN_FLAG_TOO_SMALL;
1692 }
1693 else {
1694 int fac = rct_fits(winrct, SCREEN_AXIS_H, prefsizex);
1695
1696 if (fac < 0) {
1697 prefsizex += fac;
1698 }
1699
1700 region->winrct = *winrct;
1701
1702 if (alignment == RGN_ALIGN_RIGHT) {
1703 region->winrct.xmin = region->winrct.xmax - prefsizex + 1;
1704 winrct->xmax = region->winrct.xmin - 1;
1705 }
1706 else {
1707 region->winrct.xmax = region->winrct.xmin + prefsizex - 1;
1708 winrct->xmin = region->winrct.xmax + 1;
1709 }
1710 BLI_rcti_sanitize(winrct);
1711 }
1712 }
1713 else if (ELEM(alignment, RGN_ALIGN_VSPLIT, RGN_ALIGN_HSPLIT)) {
1714 /* Percentage subdiv. */
1715 region->winrct = *remainder;
1716
1717 if (alignment == RGN_ALIGN_HSPLIT) {
1718 if (rct_fits(remainder, SCREEN_AXIS_H, prefsizex) > 4) {
1719 region->winrct.xmax = BLI_rcti_cent_x(remainder);
1720 remainder->xmin = region->winrct.xmax + 1;
1721 }
1722 else {
1723 BLI_rcti_init(remainder, 0, 0, 0, 0);
1724 }
1725 }
1726 else {
1727 if (rct_fits(remainder, SCREEN_AXIS_V, prefsizey) > 4) {
1728 region->winrct.ymax = BLI_rcti_cent_y(remainder);
1729 remainder->ymin = region->winrct.ymax + 1;
1730 }
1731 else {
1732 BLI_rcti_init(remainder, 0, 0, 0, 0);
1733 }
1734 }
1735 }
1736 else if (alignment == RGN_ALIGN_QSPLIT) {
1737 region->winrct = *remainder;
1738
1739 /* test if there's still 4 regions left */
1740 if (quad == 0) {
1741 ARegion *region_test = region->next;
1742 int count = 1;
1743
1744 while (region_test) {
1745 region_test->alignment = RGN_ALIGN_QSPLIT;
1746 region_test = region_test->next;
1747 count++;
1748 }
1749
1750 if (count != 4) {
1751 /* let's stop adding regions */
1752 BLI_rcti_init(remainder, 0, 0, 0, 0);
1753 if (G.debug & G_DEBUG) {
1754 printf("region quadsplit failed\n");
1755 }
1756 }
1757 else {
1758 quad = 1;
1759 }
1760 }
1761 if (quad) {
1762 if (quad == 1) { /* left bottom */
1763 region->winrct.xmax = BLI_rcti_cent_x(remainder);
1764 region->winrct.ymax = BLI_rcti_cent_y(remainder);
1765 }
1766 else if (quad == 2) { /* left top */
1767 region->winrct.xmax = BLI_rcti_cent_x(remainder);
1768 region->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
1769 }
1770 else if (quad == 3) { /* right bottom */
1771 region->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
1772 region->winrct.ymax = BLI_rcti_cent_y(remainder);
1773 }
1774 else { /* right top */
1775 region->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
1776 region->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
1777 BLI_rcti_init(remainder, 0, 0, 0, 0);
1778 }
1779
1780 /* Fix any negative dimensions. This can happen when a quad split 3d view gets too small.
1781 * (see #72200). */
1782 BLI_rcti_sanitize(&region->winrct);
1783
1784 quad++;
1785 }
1786 }
1787
1788 /* for speedup */
1789 region->winx = BLI_rcti_size_x(&region->winrct) + 1;
1790 region->winy = BLI_rcti_size_y(&region->winrct) + 1;
1791
1792 if (region->winy <= U.border_width && !(region->flag & RGN_FLAG_HIDDEN)) {
1793 /* Don't draw when just a couple pixels tall. #143617. */
1794 region->flag |= RGN_FLAG_TOO_SMALL;
1795 }
1796
1797 /* If region opened normally, we store this for hide/reveal usage. */
1798 /* Prevent rounding errors for UI_SCALE_FAC multiply and divide. */
1799 if (region->winx > 1) {
1800 region->sizex = (region->winx + 0.5f) / UI_SCALE_FAC;
1801 }
1802 if (region->winy > 1) {
1803 region->sizey = (region->winy + 0.5f) / UI_SCALE_FAC;
1804 }
1805
1806 /* exception for multiple overlapping regions on same spot */
1807 if (region->overlap && (alignment != RGN_ALIGN_FLOAT)) {
1808 region_overlap_fix(area, region);
1809 }
1810
1811 /* Set `region->winrct` for action-zones. */
1812 if (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
1813 region->winrct = (region->overlap) ? *overlap_remainder : *remainder;
1814
1815 switch (alignment) {
1816 case RGN_ALIGN_TOP:
1817 region->winrct.ymin = region->winrct.ymax;
1818 break;
1819 case RGN_ALIGN_BOTTOM:
1820 region->winrct.ymax = region->winrct.ymin;
1821 break;
1822 case RGN_ALIGN_RIGHT:
1823 region->winrct.xmin = region->winrct.xmax;
1824 break;
1825 case RGN_ALIGN_LEFT:
1826 region->winrct.xmax = region->winrct.xmin;
1827 break;
1828 default:
1829 /* prevent winrct to be valid */
1830 region->winrct.xmax = region->winrct.xmin;
1831 break;
1832 }
1833
1834 /* Size on one axis is now 0, the other axis may still be invalid (negative) though. */
1835 BLI_rcti_sanitize(&region->winrct);
1836 }
1837
1838 /* restore prev-split exception */
1839 if (region->alignment & RGN_SPLIT_PREV) {
1840 if (region->prev) {
1841 remainder = remainder_prev;
1842 region->prev->winx = BLI_rcti_size_x(&region->prev->winrct) + 1;
1843 region->prev->winy = BLI_rcti_size_y(&region->prev->winrct) + 1;
1844 }
1845 }
1846
1847 /* After non-overlapping region, all following overlapping regions
1848 * fit within the remaining space again. */
1849 if (!region->overlap) {
1850 *overlap_remainder = *remainder;
1851 }
1852
1854
1855 region_rect_recursive(area, region->next, remainder, overlap_remainder, quad);
1856
1857 /* Tag for redraw if size changes. */
1858 if (region->winx != prev_winx || region->winy != prev_winy) {
1859 /* 3D View needs a full rebuild in case a progressive render runs. Rest can live with
1860 * no-rebuild (e.g. Outliner) */
1861 if (area->spacetype == SPACE_VIEW3D) {
1862 ED_region_tag_redraw(region);
1863 }
1864 else {
1866 }
1867 }
1868
1869 /* Clear, initialize on demand. */
1870 region->runtime->visible_rect = rcti{};
1871}
1872
1873static void area_calc_totrct(const bScreen *screen, ScrArea *area, const rcti *window_rect)
1874{
1875 /* Padding around each area, except at window edges. */
1876 const short px = short(std::max(float(U.border_width) * UI_SCALE_FAC, UI_SCALE_FAC));
1877
1878 /* Padding at window edges. Cannot be less than border width. */
1879 const short px_edge = short(std::min(UI_SCALE_FAC * 2.0f, float(U.border_width) * UI_SCALE_FAC));
1880
1881 area->totrct.xmin = area->v1->vec.x;
1882 area->totrct.xmax = area->v4->vec.x;
1883 area->totrct.ymin = area->v1->vec.y;
1884 area->totrct.ymax = area->v2->vec.y;
1885
1886 /* Scale down totrct by the border size on all sides not at window edges. */
1887 if (!ED_area_is_global(area) && screen->state != SCREENFULL && !(screen->temp) &&
1889 {
1890 area->totrct.xmin += (area->totrct.xmin > window_rect->xmin) ? px : px_edge;
1891 area->totrct.xmax -= (area->totrct.xmax < (window_rect->xmax - 1)) ? px : px_edge;
1892 area->totrct.ymin += (area->totrct.ymin > window_rect->ymin) ? px : px_edge;
1893
1894 if (area->totrct.ymax < (window_rect->ymax - 1)) {
1895 area->totrct.ymax -= px;
1896 }
1897 else if (!BLI_listbase_is_single(&screen->areabase) || screen->state == SCREENMAXIMIZED) {
1898 /* Small gap below Top Bar. */
1899 area->totrct.ymax -= U.pixelsize;
1900 }
1901 else {
1902 area->totrct.ymax -= px_edge;
1903 }
1904 }
1905 /* Although the following asserts are correct they lead to a very unstable Blender.
1906 * And the asserts would fail even in 2.7x
1907 * (they were added in 2.8x as part of the top-bar commit).
1908 * For more details see #54864. */
1909#if 0
1910 BLI_assert(area->totrct.xmin >= 0);
1911 BLI_assert(area->totrct.xmax >= 0);
1912 BLI_assert(area->totrct.ymin >= 0);
1913 BLI_assert(area->totrct.ymax >= 0);
1914#endif
1915
1916 /* for speedup */
1917 area->winx = BLI_rcti_size_x(&area->totrct) + 1;
1918 area->winy = BLI_rcti_size_y(&area->totrct) + 1;
1919}
1920
1925{
1926 bool hidden = (region->flag & (RGN_FLAG_POLL_FAILED | RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) !=
1927 0;
1928
1929 if ((region->alignment & (RGN_SPLIT_PREV | RGN_ALIGN_HIDE_WITH_PREV)) && region->prev) {
1930 hidden = hidden || (region->prev->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
1931 }
1932
1933 region->runtime->visible = !hidden;
1934}
1935
1940 wmWindowManager *wm, ScrArea *area, ARegion *region, ListBase *handlers, int flag)
1941{
1942 BLI_assert(region ? (&region->runtime->handlers == handlers) : (&area->handlers == handlers));
1943
1944 /* NOTE: add-handler checks if it already exists. */
1945
1946 /* XXX: it would be good to have bound-box checks for some of these. */
1947 if (flag & ED_KEYMAP_UI) {
1948 wmKeyMap *keymap = WM_keymap_ensure(
1949 wm->runtime->defaultconf, "User Interface", SPACE_EMPTY, RGN_TYPE_WINDOW);
1950 WM_event_add_keymap_handler(handlers, keymap);
1951
1954
1955 /* user interface widgets */
1956 UI_region_handlers_add(handlers);
1957 }
1958 if (flag & ED_KEYMAP_GIZMO) {
1959 BLI_assert(region && ELEM(region->runtime->type->regionid, RGN_TYPE_WINDOW, RGN_TYPE_PREVIEW));
1960 if (region) {
1961 /* Anything else is confusing, only allow this. */
1962 BLI_assert(&region->runtime->handlers == handlers);
1963 if (region->runtime->gizmo_map == nullptr) {
1965 params.spaceid = area->spacetype;
1966 params.regionid = region->runtime->type->regionid;
1967 region->runtime->gizmo_map = WM_gizmomap_new_from_type(&params);
1968 }
1969 WM_gizmomap_add_handlers(region, region->runtime->gizmo_map);
1970 }
1971 }
1972 if (flag & ED_KEYMAP_VIEW2D) {
1973 /* 2d-viewport handling+manipulation */
1974 wmKeyMap *keymap = WM_keymap_ensure(
1975 wm->runtime->defaultconf, "View2D", SPACE_EMPTY, RGN_TYPE_WINDOW);
1976 WM_event_add_keymap_handler(handlers, keymap);
1977 }
1978 if (flag & ED_KEYMAP_ANIMATION) {
1979 wmKeyMap *keymap;
1980
1981 /* time-markers */
1982 keymap = WM_keymap_ensure(wm->runtime->defaultconf, "Markers", SPACE_EMPTY, RGN_TYPE_WINDOW);
1984
1985 /* time-scrub */
1986 keymap = WM_keymap_ensure(
1987 wm->runtime->defaultconf, "Time Scrub", SPACE_EMPTY, RGN_TYPE_WINDOW);
1989
1990 /* frame changing and timeline operators (for time spaces) */
1991 keymap = WM_keymap_ensure(wm->runtime->defaultconf, "Animation", SPACE_EMPTY, RGN_TYPE_WINDOW);
1992 WM_event_add_keymap_handler(handlers, keymap);
1993 }
1994 if (flag & ED_KEYMAP_TOOL) {
1995 if (flag & ED_KEYMAP_GIZMO) {
1998 }
1999 else {
2001 &region->runtime->handlers, WM_event_get_keymap_from_toolsystem, area);
2002 }
2003 }
2004 if (flag & ED_KEYMAP_FRAMES) {
2005 /* frame changing/jumping (for all spaces) */
2006 wmKeyMap *keymap = WM_keymap_ensure(
2007 wm->runtime->defaultconf, "Frames", SPACE_EMPTY, RGN_TYPE_WINDOW);
2008 WM_event_add_keymap_handler(handlers, keymap);
2009 }
2010 if (flag & ED_KEYMAP_HEADER) {
2011 /* standard keymap for headers regions */
2012 wmKeyMap *keymap = WM_keymap_ensure(
2013 wm->runtime->defaultconf, "Region Context Menu", SPACE_EMPTY, RGN_TYPE_WINDOW);
2014 WM_event_add_keymap_handler(handlers, keymap);
2015 }
2016 if (flag & ED_KEYMAP_FOOTER) {
2017 /* standard keymap for footer regions */
2018 wmKeyMap *keymap = WM_keymap_ensure(
2019 wm->runtime->defaultconf, "Region Context Menu", SPACE_EMPTY, RGN_TYPE_WINDOW);
2020 WM_event_add_keymap_handler(handlers, keymap);
2021 }
2022 if (flag & ED_KEYMAP_NAVBAR) {
2023 /* standard keymap for Navigation bar regions */
2024 wmKeyMap *keymap = WM_keymap_ensure(
2025 wm->runtime->defaultconf, "Region Context Menu", SPACE_EMPTY, RGN_TYPE_WINDOW);
2026 WM_event_add_keymap_handler(&region->runtime->handlers, keymap);
2027 }
2029 /* standard keymap for asset shelf regions */
2030 wmKeyMap *keymap = WM_keymap_ensure(
2031 wm->runtime->defaultconf, "Asset Shelf", SPACE_EMPTY, RGN_TYPE_WINDOW);
2032 WM_event_add_keymap_handler(&region->runtime->handlers, keymap);
2033 }
2034
2035 /* Keep last because of LMB/RMB handling, see: #57527. */
2036 if (flag & ED_KEYMAP_GPENCIL) {
2037 /* grease pencil */
2038 {
2039 wmKeyMap *keymap = WM_keymap_ensure(
2040 wm->runtime->defaultconf, "Grease Pencil", SPACE_EMPTY, RGN_TYPE_WINDOW);
2041 WM_event_add_keymap_handler(handlers, keymap);
2042 }
2043 }
2044}
2045
2047{
2048 if (!(area->flag & AREA_FLAG_REGION_SIZE_UPDATE)) {
2049 return;
2050 }
2051 const bScreen *screen = WM_window_get_active_screen(win);
2052
2053 rcti window_rect;
2054 WM_window_screen_rect_calc(win, &window_rect);
2055 area_calc_totrct(screen, area, &window_rect);
2056
2057 /* region rect sizes */
2058 rcti rect = area->totrct;
2059 rcti overlap_rect = rect;
2061 area, static_cast<ARegion *>(area->regionbase.first), &rect, &overlap_rect, 0);
2062
2063 /* Dynamically sized regions may have changed region sizes, so we have to force azone update. */
2064 area_azone_init(win, screen, area);
2065
2066 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2067 if (region->flag & RGN_FLAG_POLL_FAILED) {
2068 continue;
2069 }
2071
2072 /* region size may have changed, init does necessary adjustments */
2073 if (region->runtime->type->init) {
2074 region->runtime->type->init(wm, region);
2075 }
2076
2077 /* Some AZones use View2D data which is only updated in region init, so call that first! */
2078 region_azones_add(screen, area, region);
2079 }
2080 ED_area_azones_update(area, win->eventstate->xy);
2081
2083}
2084
2086{
2087 return area_getorientation(a, b) != -1;
2088}
2089
2093static void area_init_type_fallback(ScrArea *area, eSpace_Type space_type)
2094{
2095 BLI_assert(area->type == nullptr);
2096 area->spacetype = space_type;
2097 area->type = BKE_spacetype_from_id(area->spacetype);
2098
2099 SpaceLink *sl = nullptr;
2100 LISTBASE_FOREACH (SpaceLink *, sl_iter, &area->spacedata) {
2101 if (sl_iter->spacetype == space_type) {
2102 sl = sl_iter;
2103 break;
2104 }
2105 }
2106 if (sl) {
2107 SpaceLink *sl_old = static_cast<SpaceLink *>(area->spacedata.first);
2108 if (LIKELY(sl != sl_old)) {
2109 BLI_remlink(&area->spacedata, sl);
2110 BLI_addhead(&area->spacedata, sl);
2111
2112 /* swap regions */
2113 sl_old->regionbase = area->regionbase;
2114 area->regionbase = sl->regionbase;
2116 }
2117 }
2118 else {
2119 screen_area_spacelink_add(nullptr, area, space_type);
2120 }
2121}
2122
2124{
2125 area->type = BKE_spacetype_from_id(area->spacetype);
2126
2127 if (area->type == nullptr) {
2129 BLI_assert(area->type != nullptr);
2130 }
2131
2132 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2133 region->runtime->type = BKE_regiontype_from_id(area->type, region->regiontype);
2134 /* Invalid region types may be stored in files (e.g. for new files), but they should be handled
2135 * on file read already, see #BKE_screen_area_blend_read_lib(). */
2136 BLI_assert_msg(region->runtime->type != nullptr, "Region type not valid for this space type");
2137 }
2138}
2139
2140void ED_area_init(bContext *C, const wmWindow *win, ScrArea *area)
2141{
2143 WorkSpace *workspace = WM_window_get_active_workspace(win);
2145 const Scene *scene = WM_window_get_active_scene(win);
2146 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
2147
2148 if (ED_area_is_global(area) && (area->global->flag & GLOBAL_AREA_IS_HIDDEN)) {
2149 return;
2150 }
2151
2152 rcti window_rect;
2153 WM_window_screen_rect_calc(win, &window_rect);
2154
2156
2157 /* area sizes */
2158 area_calc_totrct(screen, area, &window_rect);
2159
2160 area_regions_poll(C, screen, area);
2161
2162 /* region rect sizes */
2163 rcti rect = area->totrct;
2164 rcti overlap_rect = rect;
2166 area, static_cast<ARegion *>(area->regionbase.first), &rect, &overlap_rect, 0);
2168
2169 /* default area handlers */
2170 ed_default_handlers(wm, area, nullptr, &area->handlers, area->type->keymapflag);
2171 /* checks spacedata, adds own handlers */
2172 if (area->type->init) {
2173 area->type->init(wm, area);
2174 }
2175
2176 /* clear all azones, add the area triangle widgets */
2177 area_azone_init(win, screen, area);
2178
2179 /* region windows, default and own handlers */
2180 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2182
2183 if (region->runtime->visible) {
2184 /* default region handlers */
2186 wm, area, region, &region->runtime->handlers, region->runtime->type->keymapflag);
2187 /* own handlers */
2188 if (region->runtime->type->init) {
2189 region->runtime->type->init(wm, region);
2190 }
2191 }
2192 else {
2193 /* prevent uiblocks to run */
2194 UI_blocklist_free(nullptr, region);
2195 }
2196
2197 /* Some AZones use View2D data which is only updated in region init, so call that first! */
2198 region_azones_add(screen, area, region);
2199 }
2200
2201 /* Avoid re-initializing tools while resizing areas & regions. */
2202 if ((G.moving & G_TRANSFORM_WM) == 0) {
2203 if ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) {
2204 if (WM_toolsystem_refresh_screen_area(workspace, scene, view_layer, area) ||
2205 /* When the tool is null it may not be initialized.
2206 * This happens when switching to a new area, see: #126990.
2207 *
2208 * NOTE(@ideasman42): There is a possible down-side here: when refreshing
2209 * tools results in a null value, refreshing won't be skipped here as intended.
2210 * As it happens, spaces that use tools will practically always have a default tool. */
2211 (area->runtime.tool == nullptr))
2212 {
2213 /* Only re-initialize as needed to prevent redundant updates as they
2214 * can cause gizmos to flicker when the flag is set continuously, see: #126525. */
2216 }
2217 }
2218 else {
2219 area->runtime.tool = nullptr;
2220 area->runtime.is_tool_set = true;
2221 }
2222 }
2223}
2224
2226{
2227 area->flag |= AREA_FLAG_OFFSCREEN;
2228 area->type = BKE_spacetype_from_id(area->spacetype);
2229 /* Off screen areas are only ever created at run-time,
2230 * so there is no reason for the type to be unknown. */
2231 BLI_assert(area->type != nullptr);
2232
2233 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2234 region->runtime->type = BKE_regiontype_from_id(area->type, region->regiontype);
2235 }
2236}
2237
2239{
2240 ScrArea *area = MEM_callocN<ScrArea>(__func__);
2241 area->spacetype = space_type;
2242
2244 area_offscreen_init(area);
2245
2246 return area;
2247}
2248
2250{
2251 if (area->type && area->type->exit) {
2252 area->type->exit(wm, area);
2253 }
2254
2255 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2256 if (region->runtime->type && region->runtime->type->exit) {
2257 region->runtime->type->exit(wm, region);
2258 }
2259
2260 WM_event_modal_handler_region_replace(win, region, nullptr);
2261 WM_draw_region_free(region);
2262 region->runtime->visible = false;
2263
2264 MEM_SAFE_FREE(region->runtime->headerstr);
2265
2266 if (region->runtime->regiontimer) {
2267 WM_event_timer_remove(wm, win, region->runtime->regiontimer);
2268 region->runtime->regiontimer = nullptr;
2269 }
2270
2271 if (wm->runtime->message_bus) {
2272 WM_msgbus_clear_by_owner(wm->runtime->message_bus, region);
2273 }
2274 }
2275
2276 WM_event_modal_handler_area_replace(win, area, nullptr);
2277}
2278
2280{
2281 area_offscreen_exit(wm, win, area);
2282
2284 MEM_freeN(area);
2285}
2286
2287static void region_update_rect(ARegion *region)
2288{
2289 region->winx = BLI_rcti_size_x(&region->winrct) + 1;
2290 region->winy = BLI_rcti_size_y(&region->winrct) + 1;
2291
2292 /* v2d mask is used to subtract scrollbars from a 2d view. Needs initialize here. */
2293 BLI_rcti_init(&region->v2d.mask, 0, region->winx - 1, 0, region->winy - 1);
2294}
2295
2297{
2298 region_update_rect(region);
2299}
2300
2302{
2304
2305 /* refresh can be called before window opened */
2307
2308 region_update_rect(region);
2309}
2310
2312{
2313 if (region != nullptr) {
2314 if ((region->runtime->gizmo_map != nullptr) &&
2315 WM_gizmomap_cursor_set(region->runtime->gizmo_map, win))
2316 {
2317 return;
2318 }
2319 if (area && region->runtime->type && region->runtime->type->cursor) {
2320 region->runtime->type->cursor(win, area, region);
2321 return;
2322 }
2323 }
2324
2325 if (WM_cursor_set_from_tool(win, area, region)) {
2326 return;
2327 }
2328
2330}
2331
2333 bContext *C, ScrArea *area, ARegion *region, bool is_hidden, bool do_init)
2334{
2335 if (is_hidden) {
2336 WM_event_remove_handlers(C, &region->runtime->handlers);
2337 /* Needed to close any open pop-overs which would otherwise remain open,
2338 * crashing on attempting to refresh. See: #93410.
2339 *
2340 * When #ED_area_init frees buttons via #UI_blocklist_free a nullptr context
2341 * is passed, causing the free not to remove menus or their handlers. */
2343 }
2344
2345 if (do_init) {
2346 ED_area_init(C, CTX_wm_window(C), area);
2347 ED_area_tag_redraw(area);
2348 }
2349}
2350
2352{
2353 const bool is_hidden = region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_POLL_FAILED);
2354 const bool do_init = true;
2355 ED_region_visibility_change_update_ex(C, area, region, is_hidden, do_init);
2356}
2357
2358void region_toggle_hidden(bContext *C, ARegion *region, const bool do_fade)
2359{
2360 ScrArea *area = CTX_wm_area(C);
2361
2362 region->flag ^= RGN_FLAG_HIDDEN;
2363
2364 if (do_fade && region->overlap && !(U.uiflag & USER_REDUCE_MOTION)) {
2365 /* starts a timer, and in end calls the stuff below itself (region_sblend_invoke()) */
2367 }
2368 else {
2370 }
2371}
2372
2374{
2375 region_toggle_hidden(C, region, true);
2376}
2377
2378void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
2379{
2380 const char spacetype = area_dst->spacetype;
2381 const short flag_copy = HEADER_NO_PULLDOWN;
2382
2383 area_dst->spacetype = area_src->spacetype;
2384 area_dst->type = area_src->type;
2385
2386 area_dst->flag = (area_dst->flag & ~flag_copy) | (area_src->flag & flag_copy);
2387
2388 /* area */
2389 if (do_free) {
2391 }
2392 BKE_spacedata_copylist(&area_dst->spacedata, &area_src->spacedata);
2393
2394 /* NOTE: SPACE_EMPTY is possible on new screens. */
2395
2396 /* regions */
2397 if (do_free) {
2398 SpaceType *st = BKE_spacetype_from_id(spacetype);
2399 LISTBASE_FOREACH (ARegion *, region, &area_dst->regionbase) {
2400 BKE_area_region_free(st, region);
2401 }
2402 BLI_freelistN(&area_dst->regionbase);
2403 }
2404 SpaceType *st = BKE_spacetype_from_id(area_src->spacetype);
2405 LISTBASE_FOREACH (ARegion *, region, &area_src->regionbase) {
2406 ARegion *newar = BKE_area_region_copy(st, region);
2407 BLI_addtail(&area_dst->regionbase, newar);
2408 }
2409}
2410
2411void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src)
2412{
2413 std::swap(area_dst->spacetype, area_src->spacetype);
2414 std::swap(area_dst->type, area_src->type);
2415
2416 std::swap(area_dst->spacedata, area_src->spacedata);
2417 std::swap(area_dst->regionbase, area_src->regionbase);
2418}
2419
2420/* -------------------------------------------------------------------- */
2423
2432 struct {
2443};
2444
2446{
2447 for (int index = 0; index < RGN_TYPE_NUM; index++) {
2448 r_align_info->by_type[index].alignment = -1;
2449 /* Default to true, when it doesn't exist - it's effectively hidden. */
2450 r_align_info->by_type[index].hidden = true;
2451 }
2452
2453 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2454 if (region->flag & RGN_FLAG_POLL_FAILED) {
2455 continue;
2456 }
2457
2458 const int index = region->regiontype;
2459 if (uint(index) < RGN_TYPE_NUM) {
2460 r_align_info->by_type[index].alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
2461 r_align_info->by_type[index].hidden = (region->flag & RGN_FLAG_HIDDEN) != 0;
2462 }
2463 }
2464}
2465
2474 const RegionTypeAlignInfo *region_align_info, const short fallback)
2475{
2476 const short header_alignment = region_align_info->by_type[RGN_TYPE_HEADER].alignment;
2477 const short tool_header_alignment = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].alignment;
2478
2479 const bool header_hidden = region_align_info->by_type[RGN_TYPE_HEADER].hidden;
2480 const bool tool_header_hidden = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].hidden;
2481
2482 if ((tool_header_alignment != -1) &&
2483 /* If tool-header is hidden, use header alignment. */
2484 ((tool_header_hidden == false) ||
2485 /* Don't prioritize the tool-header if both are hidden (behave as if both are visible).
2486 * Without this, switching to a space with headers hidden will flip the alignment
2487 * upon switching to a space with visible headers. */
2488 (header_hidden && tool_header_hidden)))
2489 {
2490 return tool_header_alignment;
2491 }
2492 if (header_alignment != -1) {
2493 return header_alignment;
2494 }
2495 return fallback;
2496}
2497
2520static void region_align_info_to_area_for_headers(const RegionTypeAlignInfo *region_align_info_src,
2521 const RegionTypeAlignInfo *region_align_info_dst,
2522 ARegion *region_by_type[RGN_TYPE_NUM])
2523{
2524 /* Abbreviate access. */
2525 const short header_alignment_src = region_align_info_src->by_type[RGN_TYPE_HEADER].alignment;
2526 const short tool_header_alignment_src =
2527 region_align_info_src->by_type[RGN_TYPE_TOOL_HEADER].alignment;
2528
2529 const bool tool_header_hidden_src = region_align_info_src->by_type[RGN_TYPE_TOOL_HEADER].hidden;
2530
2531 const short primary_header_alignment_src = region_alignment_from_header_and_tool_header_state(
2532 region_align_info_src, -1);
2533
2534 /* Neither alignments are usable, don't sync. */
2535 if (primary_header_alignment_src == -1) {
2536 return;
2537 }
2538
2539 const short header_alignment_dst = region_align_info_dst->by_type[RGN_TYPE_HEADER].alignment;
2540 const short tool_header_alignment_dst =
2541 region_align_info_dst->by_type[RGN_TYPE_TOOL_HEADER].alignment;
2542 const short footer_alignment_dst = region_align_info_dst->by_type[RGN_TYPE_FOOTER].alignment;
2543
2544 const bool tool_header_hidden_dst = region_align_info_dst->by_type[RGN_TYPE_TOOL_HEADER].hidden;
2545
2546 /* New synchronized alignments to set (or ignore when left as -1). */
2547 short header_alignment_sync = -1;
2548 short tool_header_alignment_sync = -1;
2549 short footer_alignment_sync = -1;
2550
2551 /* Both source/destination areas have same region configurations regarding headers.
2552 * Simply copy the values. */
2553 if (((header_alignment_src != -1) == (header_alignment_dst != -1)) &&
2554 ((tool_header_alignment_src != -1) == (tool_header_alignment_dst != -1)) &&
2555 (tool_header_hidden_src == tool_header_hidden_dst))
2556 {
2557 if (header_alignment_dst != -1) {
2558 header_alignment_sync = header_alignment_src;
2559 }
2560 if (tool_header_alignment_dst != -1) {
2561 tool_header_alignment_sync = tool_header_alignment_src;
2562 }
2563 }
2564 else {
2565 /* Not an exact match, check the space selector isn't moving. */
2566 const short primary_header_alignment_dst = region_alignment_from_header_and_tool_header_state(
2567 region_align_info_dst, -1);
2568
2569 if (primary_header_alignment_src != primary_header_alignment_dst) {
2570 if ((header_alignment_dst != -1) && (tool_header_alignment_dst != -1)) {
2571 if (header_alignment_dst == tool_header_alignment_dst) {
2572 /* Apply to both. */
2573 tool_header_alignment_sync = primary_header_alignment_src;
2574 header_alignment_sync = primary_header_alignment_src;
2575 }
2576 else {
2577 /* Keep on opposite sides. */
2578 tool_header_alignment_sync = primary_header_alignment_src;
2579 header_alignment_sync = (tool_header_alignment_sync == RGN_ALIGN_BOTTOM) ?
2582 }
2583 }
2584 else {
2585 /* Apply what we can to regions that exist. */
2586 if (header_alignment_dst != -1) {
2587 header_alignment_sync = primary_header_alignment_src;
2588 }
2589 if (tool_header_alignment_dst != -1) {
2590 tool_header_alignment_sync = primary_header_alignment_src;
2591 }
2592 }
2593 }
2594 }
2595
2596 if (footer_alignment_dst != -1) {
2597 if ((header_alignment_dst != -1) && (header_alignment_dst == footer_alignment_dst)) {
2598 /* Apply to both. */
2599 footer_alignment_sync = primary_header_alignment_src;
2600 }
2601 else {
2602 /* Keep on opposite sides. */
2603 footer_alignment_sync = (primary_header_alignment_src == RGN_ALIGN_BOTTOM) ?
2606 }
2607 }
2608
2609 /* Finally apply synchronized flags. */
2610 if (header_alignment_sync != -1) {
2611 ARegion *region = region_by_type[RGN_TYPE_HEADER];
2612 if (region != nullptr) {
2613 region->alignment = RGN_ALIGN_ENUM_FROM_MASK(header_alignment_sync) |
2615 }
2616 }
2617
2618 if (tool_header_alignment_sync != -1) {
2619 ARegion *region = region_by_type[RGN_TYPE_TOOL_HEADER];
2620 if (region != nullptr) {
2621 region->alignment = RGN_ALIGN_ENUM_FROM_MASK(tool_header_alignment_sync) |
2623 }
2624 }
2625
2626 if (footer_alignment_sync != -1) {
2627 ARegion *region = region_by_type[RGN_TYPE_FOOTER];
2628 if (region != nullptr) {
2629 region->alignment = RGN_ALIGN_ENUM_FROM_MASK(footer_alignment_sync) |
2631 }
2632 }
2633}
2634
2636 ScrArea *area, const RegionTypeAlignInfo region_align_info_src[RGN_TYPE_NUM])
2637{
2638 ARegion *region_by_type[RGN_TYPE_NUM] = {nullptr};
2639 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2640 const int index = region->regiontype;
2641 if (uint(index) < RGN_TYPE_NUM) {
2642 region_by_type[index] = region;
2643 }
2644 }
2645
2646 RegionTypeAlignInfo region_align_info_dst;
2647 region_align_info_from_area(area, &region_align_info_dst);
2648
2649 if ((region_by_type[RGN_TYPE_HEADER] != nullptr) ||
2650 (region_by_type[RGN_TYPE_TOOL_HEADER] != nullptr))
2651 {
2653 region_align_info_src, &region_align_info_dst, region_by_type);
2654 }
2655
2656 /* Note that we could support other region types. */
2657}
2658
2660
2661/* *********** Space switching code *********** */
2662
2664{
2665 ScrArea *tmp = MEM_callocN<ScrArea>(__func__);
2666 wmWindow *win = CTX_wm_window(C);
2667
2668 ED_area_exit(C, sa1);
2669 ED_area_exit(C, sa2);
2670
2671 ED_area_data_copy(tmp, sa1, false);
2672 ED_area_data_copy(sa1, sa2, true);
2673 ED_area_data_copy(sa2, tmp, true);
2674 ED_area_init(C, win, sa1);
2675 ED_area_init(C, win, sa2);
2676
2678 MEM_delete(tmp);
2679
2680 /* The areas being swapped could be between different windows,
2681 * so clear screen active region pointers. This is set later
2682 * through regular operations. #141313. */
2684 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
2685 if (bScreen *screen = WM_window_get_active_screen(win)) {
2686 screen->active_region = nullptr;
2687 }
2688 }
2689
2690 /* tell WM to refresh, cursor types etc */
2692
2693 ED_area_tag_redraw(sa1);
2695 ED_area_tag_redraw(sa2);
2697}
2698
2699void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_region_exit)
2700{
2701 wmWindow *win = CTX_wm_window(C);
2702 SpaceType *st = BKE_spacetype_from_id(type);
2703
2704 if (area->spacetype != type) {
2705 SpaceLink *slold = static_cast<SpaceLink *>(area->spacedata.first);
2706 /* store area->type->exit callback */
2707 void (*area_exit)(wmWindowManager *, ScrArea *) = area->type ? area->type->exit : nullptr;
2708 /* When the user switches between space-types from the type-selector,
2709 * changing the header-type is jarring (especially when using Ctrl-MouseWheel).
2710 *
2711 * However, add-on install for example, forces the header to the top which shouldn't
2712 * be applied back to the previous space type when closing - see: #57724
2713 *
2714 * Newly-created windows won't have any space data, use the alignment
2715 * the space type defaults to in this case instead
2716 * (needed for preferences to have space-type on bottom).
2717 */
2718
2719 bool sync_header_alignment = false;
2720 RegionTypeAlignInfo region_align_info[RGN_TYPE_NUM];
2721 if ((slold != nullptr) && (slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
2722 region_align_info_from_area(area, region_align_info);
2723 sync_header_alignment = true;
2724 }
2725
2726 /* in some cases (opening temp space) we don't want to
2727 * call area exit callback, so we temporarily unset it */
2728 if (skip_region_exit && area->type) {
2729 area->type->exit = nullptr;
2730 }
2731
2732 ED_area_exit(C, area);
2733
2734 /* restore old area exit callback */
2735 if (skip_region_exit && area->type) {
2736 area->type->exit = area_exit;
2737 }
2738
2739 area->spacetype = type;
2740 area->type = st;
2741
2742 /* If st->create may be called, don't use context until then. The
2743 * area->type->context() callback has changed but data may be invalid
2744 * (e.g. with properties editor) until space-data is properly created */
2745
2746 /* check previously stored space */
2747 SpaceLink *sl = nullptr;
2748 LISTBASE_FOREACH (SpaceLink *, sl_iter, &area->spacedata) {
2749 if (sl_iter->spacetype == type) {
2750 sl = sl_iter;
2751 break;
2752 }
2753 }
2754
2755 /* old spacedata... happened during work on 2.50, remove */
2756 if (sl && BLI_listbase_is_empty(&sl->regionbase)) {
2757 st->free(sl);
2758 BLI_freelinkN(&area->spacedata, sl);
2759 if (slold == sl) {
2760 slold = nullptr;
2761 }
2762 sl = nullptr;
2763 }
2764
2765 if (sl) {
2766 /* swap regions */
2767 slold->regionbase = area->regionbase;
2768 area->regionbase = sl->regionbase;
2770 /* SPACE_FLAG_TYPE_WAS_ACTIVE is only used to go back to a previously active space that is
2771 * overlapped by temporary ones. It's now properly activated, so the flag should be cleared
2772 * at this point. */
2774
2775 /* put in front of list */
2776 BLI_remlink(&area->spacedata, sl);
2777 BLI_addhead(&area->spacedata, sl);
2778 }
2779 else {
2780 /* new space */
2781 if (st) {
2782 /* Don't get scene from context here which may depend on space-data. */
2783 Scene *scene = WM_window_get_active_scene(win);
2784 sl = st->create(area, scene);
2785 BLI_addhead(&area->spacedata, sl);
2786
2787 /* swap regions */
2788 if (slold) {
2789 slold->regionbase = area->regionbase;
2790 }
2791 area->regionbase = sl->regionbase;
2793 }
2794 }
2795
2796 /* Sync header alignment. */
2797 if (sync_header_alignment) {
2798 region_align_info_to_area(area, region_align_info);
2799 }
2800
2801 ED_area_init(C, win, area);
2802
2803 /* tell WM to refresh, cursor types etc */
2805
2806 /* send space change notifier */
2808
2809 ED_area_tag_refresh(area);
2810 }
2811
2812 /* Set area space subtype if applicable. */
2813 if (st && st->space_subtype_item_extend != nullptr) {
2814 if (area->butspacetype_subtype == -1) {
2815 /* Indication (probably from space_type_set_or_cycle) to ignore the
2816 * area's current subtype and use last-used, as saved in the space. */
2817 area->butspacetype_subtype = st->space_subtype_get(area);
2818 }
2819 st->space_subtype_set(area, area->butspacetype_subtype);
2820 }
2821
2822 /* Whether setting a subtype or not we need to clear this value. Not just unneeded
2823 * but can interfere with the next change. Operations can change the type without
2824 * specifying a subtype (assumed zero) and we don't want to use the old subtype. */
2825 area->butspacetype_subtype = 0;
2826
2827 if (BLI_listbase_is_single(&CTX_wm_screen(C)->areabase)) {
2828 /* If there is only one area update the window title. */
2830 }
2831
2832 /* See #WM_capabilities_flag code-comments for details on the background check. */
2833 if (!G.background) {
2834 /* If window decoration styles are supported, send a notification to re-apply them. */
2837 }
2838 }
2839
2840 /* also redraw when re-used */
2841 ED_area_tag_redraw(area);
2842}
2843
2845{
2846 SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
2847
2848 /* First toggle to the next temporary space in the list. */
2849 for (SpaceLink *sl_iter = sl->next; sl_iter; sl_iter = sl_iter->next) {
2850 if (sl_iter->link_flag & SPACE_FLAG_TYPE_TEMPORARY) {
2851 return sl_iter;
2852 }
2853 }
2854
2855 /* No temporary space, find the item marked as last active. */
2856 for (SpaceLink *sl_iter = sl->next; sl_iter; sl_iter = sl_iter->next) {
2857 if (sl_iter->link_flag & SPACE_FLAG_TYPE_WAS_ACTIVE) {
2858 return sl_iter;
2859 }
2860 }
2861
2862 /* If neither is found, we can just return to the regular previous one. */
2863 return sl->next;
2864}
2865
2867{
2868 SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
2869 SpaceLink *prevspace = sl ? area_get_prevspace(area) : nullptr;
2870
2871 if (prevspace) {
2872 /* Specify that we want last-used if there are subtypes. */
2873 area->butspacetype_subtype = -1;
2874 ED_area_newspace(C, area, prevspace->spacetype, false);
2875 /* We've exited the space, so it can't be considered temporary anymore. */
2877 }
2878 else {
2879 /* no change */
2880 return;
2881 }
2882 /* If this is a stacked full-screen, changing to previous area exits it (meaning we're still in a
2883 * full-screen, but not in a stacked one). */
2885
2886 ED_area_tag_redraw(area);
2887
2888 /* send space change notifier */
2890}
2891
2892int ED_area_header_switchbutton(const bContext *C, uiBlock *block, int yco)
2893{
2894 ScrArea *area = CTX_wm_area(C);
2895 bScreen *screen = CTX_wm_screen(C);
2896 int xco = 0.4 * U.widget_unit;
2897
2898 PointerRNA areaptr = RNA_pointer_create_discrete(&(screen->id), &RNA_Area, area);
2899
2900 uiDefButR(block,
2902 0,
2903 "",
2904 xco,
2905 yco,
2906 1.6 * U.widget_unit,
2907 U.widget_unit,
2908 &areaptr,
2909 "ui_type",
2910 0,
2911 0.0f,
2912 0.0f,
2913 "");
2914
2915 return xco + 1.7 * U.widget_unit;
2916}
2917
2918/************************ standard UI regions ************************/
2919
2920static ThemeColorID region_background_color_id(const bContext * /*C*/, const ARegion *region)
2921{
2922 switch (region->regiontype) {
2923 case RGN_TYPE_HEADER:
2925 return TH_HEADER;
2926 case RGN_TYPE_PREVIEW:
2927 return TH_PREVIEW_BACK;
2928 default:
2929 return TH_BACK;
2930 }
2931}
2932
2933void ED_region_clear(const bContext *C, const ARegion *region, const int /*ThemeColorID*/ colorid)
2934{
2935 if (region->overlap) {
2936 /* view should be in pixelspace */
2938
2939 float back[4];
2940 UI_GetThemeColor4fv(colorid, back);
2941 GPU_clear_color(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]);
2942 }
2943 else {
2944 UI_ThemeClearColor(colorid);
2945 }
2946}
2947
2949{
2950 /* view should be in pixelspace */
2952
2953 GPU_clear_color(0, 0, 0, 0);
2954}
2955
2956BLI_INLINE bool streq_array_any(const char *s, const char *arr[])
2957{
2958 for (uint i = 0; arr[i]; i++) {
2959 if (STREQ(arr[i], s)) {
2960 return true;
2961 }
2962 }
2963 return false;
2964}
2965
2975static void ed_panel_draw(const bContext *C,
2976 ARegion *region,
2977 ListBase *lb,
2978 PanelType *pt,
2979 Panel *panel,
2980 int w,
2981 int em,
2982 char *unique_panel_str,
2983 const char *search_filter,
2984 blender::wm::OpCallContext op_context)
2985{
2986 const uiStyle *style = UI_style_get_dpi();
2987
2988 /* Draw panel. */
2990 if (unique_panel_str) {
2991 /* Instanced panels should have already been added at this point. */
2992 BLI_string_join(block_name, sizeof(block_name), pt->idname, unique_panel_str);
2993 }
2994 else {
2995 STRNCPY_UTF8(block_name, pt->idname);
2996 }
2997 uiBlock *block = UI_block_begin(C, region, block_name, blender::ui::EmbossType::Emboss);
2998
2999 bool open;
3000 panel = UI_panel_begin(region, lb, block, pt, panel, &open);
3001 panel->runtime->layout_panels.clear();
3002
3003 const bool search_filter_active = search_filter != nullptr && search_filter[0] != '\0';
3004
3005 /* bad fixed values */
3006 blender::int2 co = {0, 0};
3007 int h = 0;
3008 int headerend = w - UI_UNIT_X;
3009
3011 if (pt->draw_header_preset && !(pt->flag & PANEL_TYPE_NO_HEADER)) {
3012 /* for preset menu */
3013 panel->layout = &blender::ui::block_layout(block,
3016 0,
3017 (UI_UNIT_Y * 1.1f) + style->panelspace,
3018 UI_UNIT_Y,
3019 1,
3020 0,
3021 style);
3022
3023 panel->layout->operator_context_set(op_context);
3024
3025 pt->draw_header_preset(C, panel);
3026
3027 UI_block_apply_search_filter(block, search_filter);
3029 UI_block_translate(block, headerend - co.x, 0);
3030 panel->layout = nullptr;
3031 }
3032
3033 if (pt->draw_header && !(pt->flag & PANEL_TYPE_NO_HEADER)) {
3034 int labelx, labely;
3035 UI_panel_label_offset(block, &labelx, &labely);
3036
3037 /* Unusual case: Use expanding layout (buttons stretch to available width). */
3038 if (pt->flag & PANEL_TYPE_HEADER_EXPAND) {
3039 uiLayout &layout = blender::ui::block_layout(block,
3042 labelx,
3043 labely,
3044 headerend - 2 * style->panelspace,
3045 1,
3046 0,
3047 style);
3048 panel->layout = &layout.row(false);
3049 }
3050 /* Regular case: Normal panel with fixed size buttons. */
3051 else {
3052 panel->layout = &blender::ui::block_layout(block,
3055 labelx,
3056 labely,
3057 UI_UNIT_Y,
3058 1,
3059 0,
3060 style);
3061 }
3062
3063 panel->layout->operator_context_set(op_context);
3064
3065 pt->draw_header(C, panel);
3066
3067 UI_block_apply_search_filter(block, search_filter);
3069 panel->labelofs = co.x - labelx;
3070 panel->layout = nullptr;
3071 }
3072 else {
3073 panel->labelofs = 0;
3074 }
3076
3077 if (open || search_filter_active) {
3078 blender::ui::LayoutType panelContext;
3079
3080 /* panel context can either be toolbar region or normal panels region */
3081 if (pt->flag & PANEL_TYPE_LAYOUT_VERT_BAR) {
3083 }
3084 else if (region->regiontype == RGN_TYPE_TOOLS) {
3085 panelContext = blender::ui::LayoutType::Toolbar;
3086 }
3087 else {
3088 panelContext = blender::ui::LayoutType::Panel;
3089 }
3090
3092 block,
3094 panelContext,
3095 (pt->flag & PANEL_TYPE_LAYOUT_VERT_BAR) ? 0 : style->panelspace,
3096 0,
3097 (pt->flag & PANEL_TYPE_LAYOUT_VERT_BAR) ? 0 : w - 2 * style->panelspace,
3098 em,
3099 0,
3100 style);
3101
3102 panel->layout->operator_context_set(op_context);
3103
3104 pt->draw(C, panel);
3105
3106 const bool ends_with_layout_panel_header = uiLayoutEndsWithPanelHeader(*panel->layout);
3107
3108 UI_block_apply_search_filter(block, search_filter);
3110 panel->layout = nullptr;
3111
3112 if (co.y != 0) {
3113 h = -co.y;
3114 h += style->panelspace;
3115 if (!ends_with_layout_panel_header) {
3116 /* Last layout panel header ends together with the panel. */
3117 h += style->panelspace;
3118 }
3119 }
3120 }
3121
3122 UI_block_end(C, block);
3123
3124 /* Draw child panels. */
3125 if (open || search_filter_active) {
3126 LISTBASE_FOREACH (LinkData *, link, &pt->children) {
3127 PanelType *child_pt = static_cast<PanelType *>(link->data);
3128 Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt);
3129
3130 if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) {
3132 region,
3133 &panel->children,
3134 child_pt,
3135 child_panel,
3136 w,
3137 em,
3138 unique_panel_str,
3139 search_filter,
3140 op_context);
3141 }
3142 }
3143 }
3144
3145 UI_panel_end(panel, w, h);
3146}
3147
3151static bool panel_add_check(const bContext *C,
3152 const WorkSpace *workspace,
3153 const char *contexts[],
3154 const char *category_override,
3155 PanelType *panel_type)
3156{
3157 /* Only add top level panels. */
3158 if (panel_type->parent) {
3159 return false;
3160 }
3161 /* Check the category override first. */
3162 if (category_override) {
3163 if (!STREQ(panel_type->category, category_override)) {
3164 return false;
3165 }
3166 }
3167
3168 /* Verify context. */
3169 if (contexts != nullptr && panel_type->context[0]) {
3170 if (!streq_array_any(panel_type->context, contexts)) {
3171 return false;
3172 }
3173 }
3174
3175 /* If we're tagged, only use compatible. */
3176 if (panel_type->owner_id[0]) {
3177 if (!BKE_workspace_owner_id_check(workspace, panel_type->owner_id)) {
3178 return false;
3179 }
3180 }
3181
3182 if (LIKELY(panel_type->draw)) {
3183 if (panel_type->poll && !panel_type->poll(C, panel_type)) {
3184 return false;
3185 }
3186 }
3187
3188 return true;
3189}
3190
3191static bool region_uses_category_tabs(const ScrArea *area, const ARegion *region)
3192{
3193 /* XXX, should use some better check? */
3194 /* For now also has hardcoded check for clip editor until it supports actual toolbar. */
3195 return ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) ||
3196 (region->regiontype == RGN_TYPE_TOOLS && area->spacetype == SPACE_CLIP);
3197}
3198
3200 LinkNode *panel_types_stack,
3201 bool *use_category_tabs)
3202{
3204
3205 /* gather unique categories */
3206 for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) {
3207 PanelType *pt = static_cast<PanelType *>(pt_link->link);
3208 if (pt->category[0]) {
3209 if (!UI_panel_category_find(region, pt->category)) {
3210 UI_panel_category_add(region, pt->category);
3211 }
3212 }
3213 }
3214
3215 if (UI_panel_category_is_visible(region)) {
3216 return UI_panel_category_active_get(region, true);
3217 }
3218
3219 *use_category_tabs = false;
3220 return nullptr;
3221}
3222
3224 const PanelType *panel_type,
3225 const int max_width)
3226{
3227 /* With a background, we want some extra padding. */
3228 return UI_panel_should_show_background(region, panel_type) ?
3229 max_width - UI_PANEL_MARGIN_X * 2.0f :
3230 max_width;
3231}
3232
3234 ARegion *region,
3235 ListBase *paneltypes,
3236 blender::wm::OpCallContext op_context,
3237 const char *contexts[],
3238 const char *category_override)
3239{
3240 /* collect panels to draw */
3241 WorkSpace *workspace = CTX_wm_workspace(C);
3242 LinkNode *panel_types_stack = nullptr;
3243 LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) {
3244 if (panel_add_check(C, workspace, contexts, category_override, pt)) {
3245 BLI_linklist_prepend_alloca(&panel_types_stack, pt);
3246 }
3247 }
3248
3249 region->runtime->category = nullptr;
3250
3251 ScrArea *area = CTX_wm_area(C);
3252 View2D *v2d = &region->v2d;
3253
3254 bool use_category_tabs = (category_override == nullptr) &&
3255 region_uses_category_tabs(area, region);
3256 /* offset panels for small vertical tab area */
3257 const char *category = nullptr;
3258 const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH;
3259 int margin_x = 0;
3260 const bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE;
3261 bool update_tot_size = true;
3262
3263 /* only allow scrolling in vertical direction */
3265 v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X);
3266 v2d->scroll &= ~V2D_SCROLL_BOTTOM;
3267
3268 if (region->alignment & RGN_ALIGN_LEFT) {
3269 region->v2d.scroll &= ~V2D_SCROLL_RIGHT;
3270 region->v2d.scroll |= V2D_SCROLL_LEFT;
3271 }
3272 else {
3273 region->v2d.scroll &= ~V2D_SCROLL_LEFT;
3274 region->v2d.scroll |= V2D_SCROLL_RIGHT;
3275 }
3276
3277 /* collect categories */
3278 if (use_category_tabs) {
3279 category = region_panels_collect_categories(region, panel_types_stack, &use_category_tabs);
3280 }
3281 if (use_category_tabs) {
3282 margin_x = category_tabs_width;
3283 }
3284
3285 const int max_panel_width = BLI_rctf_size_x(&v2d->cur) - margin_x;
3286 /* Works out to 10 * UI_UNIT_X or 20 * UI_UNIT_X. */
3287 const int em = (region->runtime->type->prefsizex) ? 10 : 20;
3288
3289 /* create panels */
3290 UI_panels_begin(C, region);
3291
3292 /* Get search string for property search. */
3293 const char *search_filter = ED_area_region_search_filter_get(area, region);
3294
3295 /* set view2d view matrix - UI_block_begin() stores it */
3297
3298 bool has_instanced_panel = false;
3299 for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) {
3300 PanelType *pt = static_cast<PanelType *>(pt_link->link);
3301
3302 if (pt->flag & PANEL_TYPE_INSTANCED) {
3303 has_instanced_panel = true;
3304 continue;
3305 }
3306 Panel *panel = UI_panel_find_by_type(&region->panels, pt);
3307
3308 if (use_category_tabs && pt->category[0] && !STREQ(category, pt->category)) {
3309 if ((panel == nullptr) || ((panel->flag & PNL_PIN) == 0)) {
3310 continue;
3311 }
3312 }
3313 const int width = panel_draw_width_from_max_width_get(region, pt, max_panel_width);
3314
3315 if (panel && UI_panel_is_dragging(panel)) {
3316 /* Prevent View2d.tot rectangle size changes while dragging panels. */
3317 update_tot_size = false;
3318 }
3319
3321 C, region, &region->panels, pt, panel, width, em, nullptr, search_filter, op_context);
3322 }
3323
3324 /* Draw "poly-instantiated" panels that don't have a 1 to 1 correspondence with their types. */
3325 if (has_instanced_panel) {
3326 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
3327 if (panel->type == nullptr) {
3328 continue; /* Some panels don't have a type. */
3329 }
3330 if (!(panel->type->flag & PANEL_TYPE_INSTANCED)) {
3331 continue;
3332 }
3333 if (use_category_tabs && panel->type->category[0] && !STREQ(category, panel->type->category))
3334 {
3335 continue;
3336 }
3337 if (!panel_add_check(C, workspace, contexts, category_override, panel->type)) {
3338 continue;
3339 }
3340
3341 const int width = panel_draw_width_from_max_width_get(region, panel->type, max_panel_width);
3342
3343 if (UI_panel_is_dragging(panel)) {
3344 /* Prevent View2d.tot rectangle size changes while dragging panels. */
3345 update_tot_size = false;
3346 }
3347
3348 /* Use a unique identifier for instanced panels, otherwise an old block for a different
3349 * panel of the same type might be found. */
3350 char unique_panel_str[INSTANCED_PANEL_UNIQUE_STR_SIZE];
3351 UI_list_panel_unique_str(panel, unique_panel_str);
3353 region,
3354 &region->panels,
3355 panel->type,
3356 panel,
3357 width,
3358 em,
3359 unique_panel_str,
3360 search_filter,
3361 op_context);
3362 }
3363 }
3364
3365 /* align panels and return size */
3366 int x, y;
3367 UI_panels_end(C, region, &x, &y);
3368
3369 /* before setting the view */
3370 if (region_layout_based) {
3371 /* XXX, only single panel support at the moment.
3372 * Can't use x/y values calculated above because they're not using the real height of panels,
3373 * instead they calculate offsets for the next panel to start drawing. */
3374 Panel *panel = static_cast<Panel *>(region->panels.last);
3375 if (panel != nullptr) {
3376 const int size_dyn[2] = {
3377 int(UI_UNIT_X * (UI_panel_is_closed(panel) ? 8 : 14) / UI_SCALE_FAC),
3378 int(UI_panel_size_y(panel) / UI_SCALE_FAC),
3379 };
3380 /* region size is layout based and needs to be updated */
3381 if ((region->sizex != size_dyn[0]) || (region->sizey != size_dyn[1])) {
3382 region->sizex = size_dyn[0];
3383 region->sizey = size_dyn[1];
3384 ED_area_tag_region_size_update(area, region);
3385 }
3386 y = fabsf(region->sizey * UI_SCALE_FAC - 1);
3387 }
3388 }
3389 else {
3390 /* We always keep the scroll offset -
3391 * so the total view gets increased with the scrolled away part. */
3392 if (v2d->cur.ymax < -FLT_EPSILON) {
3393 /* Clamp to lower view boundary */
3394 if (v2d->tot.ymin < -v2d->winy) {
3395 y = min_ii(y, 0);
3396 }
3397 else {
3398 y = min_ii(y, v2d->cur.ymin);
3399 }
3400 }
3401
3402 y = -y;
3403 }
3404
3405 UI_blocklist_update_view_for_buttons(C, &region->runtime->uiblocks);
3406
3407 if (update_tot_size) {
3408 /* this also changes the 'cur' */
3409 UI_view2d_totRect_set(v2d, x, y);
3410 }
3411
3412 if (use_category_tabs) {
3413 region->runtime->category = category;
3414 }
3415}
3416
3418{
3419 if (!(region->flag & RGN_FLAG_INDICATE_OVERFLOW)) {
3420 return;
3421 }
3422
3423 const bool is_overlap = ED_region_is_overlap(area->spacetype, region->regiontype);
3424 const bool is_header = ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER);
3425 const bool narrow = region->v2d.scroll & (V2D_SCROLL_VERTICAL | V2D_SCROLL_HORIZONTAL);
3426 const float gradient_width = (narrow ? 4.0f : 16.0f) * UI_SCALE_FAC;
3427 const float transition = 20.0f * UI_SCALE_FAC;
3428
3429 float opaque[4];
3430 if (narrow) {
3432 opaque[3] = 0.2f;
3433 }
3434 else {
3436 opaque[3] = 1.0f;
3437 if (!is_header) {
3438 mul_v3_fl(opaque, 0.85f);
3439 }
3440 }
3441
3442 if (!mask) {
3443 mask = &region->v2d.mask;
3444 }
3445
3446 int width = BLI_rcti_size_x(mask) + 1;
3447 int height = BLI_rcti_size_y(mask) + 1;
3448 float offset_x = mask->xmin;
3449 float offset_y = mask->ymin;
3450
3451 if (is_overlap) {
3452 if (is_header) {
3453 offset_y += 3.0f * UI_SCALE_FAC;
3454 }
3455 else if (region->panels.first) {
3456 offset_x = UI_PANEL_MARGIN_X;
3457 width -= (2 * UI_PANEL_MARGIN_X);
3458 }
3459 }
3460
3461 rctf rect{};
3462 float transparent[4];
3463 copy_v3_v3(transparent, opaque);
3464 transparent[3] = 0.0f;
3465 float grad_color[4];
3466
3467 if (region->v2d.cur.xmax < region->v2d.tot.xmax &&
3468 !(area->spacetype == SPACE_OUTLINER && region->regiontype == RGN_TYPE_WINDOW))
3469 {
3470 /* Right Edge. */
3471 rect.xmax = offset_x + width;
3472 rect.xmin = offset_x + rect.xmax - gradient_width;
3473 rect.ymin = offset_y;
3474 rect.ymax = height;
3475 copy_v4_v4(grad_color, opaque);
3476 grad_color[3] *= std::min((region->v2d.tot.xmax - region->v2d.cur.xmax) / transition, 1.0f);
3477 UI_draw_roundbox_4fv_ex(&rect, grad_color, transparent, 0.0f, nullptr, 0.0f, 0.0f);
3478 }
3479 if (region->v2d.cur.xmin > region->v2d.tot.xmin) {
3480 /* Left Edge. */
3481 rect.xmin = offset_x;
3482 if (is_header && (U.uiflag & USER_AREA_CORNER_HANDLE)) {
3483 rect.xmin += 12.0f * UI_SCALE_FAC;
3484 }
3485 rect.xmax = rect.xmin + gradient_width;
3486 rect.ymin = offset_y;
3487 rect.ymax = height;
3488 copy_v4_v4(grad_color, opaque);
3489 grad_color[3] *= std::min((region->v2d.cur.xmin - region->v2d.tot.xmin) / transition, 1.0f);
3490 UI_draw_roundbox_4fv_ex(&rect, transparent, grad_color, 0.0f, nullptr, 0.0f, 0.0f);
3491 if (is_header && (U.uiflag & USER_AREA_CORNER_HANDLE)) {
3492 rect.xmin = 0.0f;
3493 rect.xmax = 12.0f * UI_SCALE_FAC;
3494 UI_draw_roundbox_4fv_ex(&rect, grad_color, nullptr, 0.0f, nullptr, 0.0f, 0.0f);
3495 }
3496 }
3497 if (region->v2d.cur.ymax < region->v2d.tot.ymax) {
3498 /* Top Edge. */
3499 rect.xmin = offset_x;
3500 rect.xmax = offset_x + width;
3501 rect.ymax = offset_y + height;
3502 rect.ymin = rect.ymax - gradient_width;
3503 copy_v4_v4(grad_color, opaque);
3504 grad_color[3] *= std::min((region->v2d.tot.ymax - region->v2d.cur.ymax) / transition, 1.0f);
3505 UI_draw_roundbox_4fv_ex(&rect, grad_color, transparent, 1.0f, nullptr, 0.0f, 0.0f);
3506 }
3507 if (region->v2d.cur.ymin > region->v2d.tot.ymin) {
3508 /* Bottom Edge. */
3509 rect.xmin = offset_x;
3510 rect.xmax = offset_x + width;
3511 rect.ymin = offset_y;
3512 rect.ymax = rect.ymin + gradient_width;
3513 copy_v4_v4(grad_color, opaque);
3514 grad_color[3] *= std::min((region->v2d.cur.ymin - region->v2d.tot.ymin) / transition, 1.0f);
3515 UI_draw_roundbox_4fv_ex(&rect, transparent, grad_color, 1.0f, nullptr, 0.0f, 0.0f);
3516 }
3517}
3518
3520{
3522 region,
3523 &region->runtime->type->paneltypes,
3525 nullptr,
3526 nullptr);
3527}
3528
3530{
3531 View2D *v2d = &region->v2d;
3532 const float aspect = BLI_rctf_size_y(&region->v2d.cur) /
3533 (BLI_rcti_size_y(&region->v2d.mask) + 1);
3534
3535 if (region->alignment != RGN_ALIGN_FLOAT) {
3537 region,
3538 (region->runtime->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK :
3539 TH_BACK);
3540 }
3541
3542 /* reset line width for drawing tabs */
3543 GPU_line_width(1.0f);
3544
3545 /* set the view */
3547
3548 /* View2D matrix might have changed due to dynamic sized regions. */
3549 UI_blocklist_update_window_matrix(C, &region->runtime->uiblocks);
3550
3551 /* draw panels if they are large enough. */
3552 const bool has_catgories = (region->panels_category_active.first != nullptr);
3553 const short min_draw_size = has_catgories ? short(UI_PANEL_CATEGORY_MIN_WIDTH) + 20 :
3554 std::min(region->runtime->type->prefsizex, 20);
3555 if (region->winx >= (min_draw_size * UI_SCALE_FAC / aspect)) {
3556 UI_panels_draw(C, region);
3557 }
3558
3559 /* restore view matrix */
3561
3562 /* Set in layout. */
3563 if (region->runtime->category) {
3564 UI_panel_category_draw_all(region, region->runtime->category);
3565 }
3566
3567 /* scrollers */
3568 bool use_mask = false;
3569 rcti mask;
3570 const short alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
3571 if (region->runtime->category && ELEM(alignment, RGN_ALIGN_RIGHT, RGN_ALIGN_LEFT) &&
3573 {
3574 use_mask = true;
3576 const int category_width = round_fl_to_int(UI_view2d_scale_get_x(&region->v2d) *
3578 if (alignment == RGN_ALIGN_RIGHT) {
3579 mask.xmax -= category_width;
3580 }
3581 else if (alignment == RGN_ALIGN_LEFT) {
3582 mask.xmin += category_width;
3583 }
3584 }
3585
3586 ED_region_draw_overflow_indication(CTX_wm_area(C), region, use_mask ? &mask : nullptr);
3587
3588 /* Hide scrollbars below a threshold. */
3589 int min_width = UI_panel_category_is_visible(region) ? 60.0f * UI_SCALE_FAC / aspect :
3590 40.0f * UI_SCALE_FAC / aspect;
3591 if (BLI_rcti_size_x(&region->winrct) <= min_width) {
3593 }
3594
3595 UI_view2d_scrollers_draw(v2d, use_mask ? &mask : nullptr);
3596}
3597
3599 ARegion *region,
3600 blender::wm::OpCallContext op_context,
3601 const char *contexts[])
3602{
3603 /* TODO: remove? */
3605 C, region, &region->runtime->type->paneltypes, op_context, contexts, nullptr);
3606 ED_region_panels_draw(C, region);
3607}
3608
3609void ED_region_panels(const bContext *C, ARegion *region)
3610{
3611 /* TODO: remove? */
3612 ED_region_panels_layout(C, region);
3613 ED_region_panels_draw(C, region);
3614}
3615
3617{
3618 UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy);
3619
3620 /* Place scroll bars to the left if left-aligned, right if right-aligned. */
3621 if (region->alignment & RGN_ALIGN_LEFT) {
3622 region->v2d.scroll &= ~V2D_SCROLL_RIGHT;
3623 region->v2d.scroll |= V2D_SCROLL_LEFT;
3624 }
3625 else if (region->alignment & RGN_ALIGN_RIGHT) {
3626 region->v2d.scroll &= ~V2D_SCROLL_LEFT;
3627 region->v2d.scroll |= V2D_SCROLL_RIGHT;
3628 }
3629
3630 wmKeyMap *keymap = WM_keymap_ensure(
3631 wm->runtime->defaultconf, "View2D Buttons List", SPACE_EMPTY, RGN_TYPE_WINDOW);
3632 WM_event_add_keymap_handler(&region->runtime->handlers, keymap);
3633}
3634
3642 ARegion *region,
3643 const uiStyle *style,
3644 Panel *panel,
3645 PanelType *panel_type,
3646 const char *search_filter)
3647{
3648 uiBlock *block = UI_block_begin(C, region, panel_type->idname, blender::ui::EmbossType::Emboss);
3649 UI_block_set_search_only(block, true);
3650
3651 /* Skip panels that give meaningless search results. */
3652 if (panel_type->flag & PANEL_TYPE_NO_SEARCH) {
3653 return false;
3654 }
3655
3656 if (panel == nullptr) {
3657 bool open; /* Dummy variable. */
3658 panel = UI_panel_begin(region, &region->panels, block, panel_type, panel, &open);
3659 }
3660
3661 /* Build the layouts. Because they are only used for search,
3662 * they don't need any of the proper style or layout information. */
3663 if (panel->type->draw_header_preset != nullptr) {
3664 panel->layout = &blender::ui::block_layout(block,
3667 0,
3668 0,
3669 0,
3670 0,
3671 0,
3672 style);
3673 panel_type->draw_header_preset(C, panel);
3674 }
3675 if (panel->type->draw_header != nullptr) {
3676 panel->layout = &blender::ui::block_layout(block,
3679 0,
3680 0,
3681 0,
3682 0,
3683 0,
3684 style);
3685 panel_type->draw_header(C, panel);
3686 }
3687 if (LIKELY(panel->type->draw != nullptr)) {
3688 panel->layout = &blender::ui::block_layout(block,
3691 0,
3692 0,
3693 0,
3694 0,
3695 0,
3696 style);
3697 panel_type->draw(C, panel);
3698 }
3699
3701
3702 /* We could check after each layout to increase the likelihood of returning early,
3703 * but that probably wouldn't make much of a difference anyway. */
3704 if (UI_block_apply_search_filter(block, search_filter)) {
3705 return true;
3706 }
3707
3708 LISTBASE_FOREACH (LinkData *, link, &panel_type->children) {
3709 PanelType *panel_type_child = static_cast<PanelType *>(link->data);
3710 if (!panel_type_child->poll || panel_type_child->poll(C, panel_type_child)) {
3711 /* Search for the existing child panel here because it might be an instanced
3712 * child panel with a custom data field that will be needed to build the layout. */
3713 Panel *child_panel = UI_panel_find_by_type(&panel->children, panel_type_child);
3714 if (panel_property_search(C, region, style, child_panel, panel_type_child, search_filter)) {
3715 return true;
3716 }
3717 }
3718 }
3719
3720 return false;
3721}
3722
3724 ARegion *region,
3725 ListBase *paneltypes,
3726 const char *contexts[],
3727 const char *category_override)
3728{
3729 ScrArea *area = CTX_wm_area(C);
3730 WorkSpace *workspace = CTX_wm_workspace(C);
3731 const uiStyle *style = UI_style_get_dpi();
3732 const char *search_filter = ED_area_region_search_filter_get(area, region);
3733
3734 LinkNode *panel_types_stack = nullptr;
3735 LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) {
3736 if (panel_add_check(C, workspace, contexts, category_override, pt)) {
3737 BLI_linklist_prepend_alloca(&panel_types_stack, pt);
3738 }
3739 }
3740
3741 const char *category = nullptr;
3742 bool use_category_tabs = (category_override == nullptr) &&
3743 region_uses_category_tabs(area, region);
3744 if (use_category_tabs) {
3745 category = region_panels_collect_categories(region, panel_types_stack, &use_category_tabs);
3746 }
3747
3748 /* Run property search for each panel, stopping if a result is found. */
3749 bool has_result = true;
3750 bool has_instanced_panel = false;
3751 for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) {
3752 PanelType *panel_type = static_cast<PanelType *>(pt_link->link);
3753 /* Note that these checks are duplicated from #ED_region_panels_layout_ex. */
3754 if (panel_type->flag & PANEL_TYPE_INSTANCED) {
3755 has_instanced_panel = true;
3756 continue;
3757 }
3758
3759 if (use_category_tabs) {
3760 if (panel_type->category[0] && !STREQ(category, panel_type->category)) {
3761 continue;
3762 }
3763 }
3764
3765 /* We start property search with an empty panel list, so there's
3766 * no point in trying to find an existing panel with this type. */
3767 has_result = panel_property_search(C, region, style, nullptr, panel_type, search_filter);
3768 if (has_result) {
3769 break;
3770 }
3771 }
3772
3773 /* Run property search for instanced panels (created in the layout calls of previous panels). */
3774 if (!has_result && has_instanced_panel) {
3775 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
3776 /* Note that these checks are duplicated from #ED_region_panels_layout_ex. */
3777 if (panel->type == nullptr || !(panel->type->flag & PANEL_TYPE_INSTANCED)) {
3778 continue;
3779 }
3780 if (use_category_tabs) {
3781 if (panel->type->category[0] && !STREQ(category, panel->type->category)) {
3782 continue;
3783 }
3784 }
3785
3786 has_result = panel_property_search(C, region, style, panel, panel->type, search_filter);
3787 if (has_result) {
3788 break;
3789 }
3790 }
3791 }
3792
3793 /* Free the panels and blocks, as they are only used for search. */
3794 UI_blocklist_free(C, region);
3795 UI_panels_free_instanced(C, region);
3797
3798 return has_result;
3799}
3800
3802{
3803 const uiStyle *style = UI_style_get_dpi();
3804 bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE;
3805 const ScrArea *area = CTX_wm_area(C);
3806 const bool is_global = area && ELEM(area->spacetype, SPACE_TOPBAR, SPACE_STATUSBAR);
3807 const int offset = is_global ? 4.0f * UI_SCALE_FAC : int(UI_HEADER_OFFSET);
3808
3809 /* Height of buttons and scaling needed to achieve it. */
3810 const int buttony = min_ii(UI_UNIT_Y, region->winy - 2 * UI_SCALE_FAC);
3811 const float buttony_scale = buttony / float(UI_UNIT_Y);
3812
3813 /* Vertically center buttons. */
3814 blender::int2 co = {offset, buttony + (region->winy - buttony) / 2};
3815 int maxco = co.x;
3816
3817 /* set view2d view matrix for scrolling (without scrollers) */
3818 UI_view2d_view_ortho(&region->v2d);
3819
3820 /* draw all headers types */
3821 LISTBASE_FOREACH (HeaderType *, ht, &region->runtime->type->headertypes) {
3822 if (ht->poll && !ht->poll(C, ht)) {
3823 continue;
3824 }
3825
3826 uiBlock *block = UI_block_begin(C, region, ht->idname, blender::ui::EmbossType::Emboss);
3827 uiLayout &layout = blender::ui::block_layout(block,
3830 co.x,
3831 co.y,
3832 buttony,
3833 1,
3834 0,
3835 style);
3836
3837 if (buttony_scale != 1.0f) {
3838 layout.scale_y_set(buttony_scale);
3839 }
3840
3841 Header header = {nullptr};
3842 if (ht->draw) {
3843 header.type = ht;
3844 header.layout = &layout;
3845 ht->draw(C, &header);
3846 if (ht->next) {
3847 layout.separator();
3848 }
3849
3850 /* for view2d */
3851 co.x = layout.width();
3852 maxco = std::max(co.x, maxco);
3853 }
3854
3856
3857 /* for view2d */
3858 maxco = std::max(co.x, maxco);
3859
3860 int new_sizex = (maxco + offset) / UI_SCALE_FAC;
3861
3862 if (region_layout_based && (region->sizex != new_sizex)) {
3863 /* region size is layout based and needs to be updated */
3864 ScrArea *area = CTX_wm_area(C);
3865
3866 region->sizex = new_sizex;
3867 ED_area_tag_region_size_update(area, region);
3868 }
3869
3870 UI_block_end(C, block);
3871
3872 /* In most cases there is only ever one header, it never makes sense to draw more than one
3873 * header in the same region, this results in overlapping buttons, see: #60195. */
3874 break;
3875 }
3876
3877 if (!region_layout_based) {
3878 maxco += offset;
3879 }
3880
3881 /* Always as last. */
3882 UI_view2d_totRect_set(&region->v2d, maxco, region->winy);
3883
3884 /* Restore view matrix. */
3886}
3887
3888static void region_draw_blocks_in_view2d(const bContext *C, const ARegion *region)
3889{
3890 UI_view2d_view_ortho(&region->v2d);
3891
3892 /* View2D matrix might have changed due to dynamic sized regions. */
3893 UI_blocklist_update_window_matrix(C, &region->runtime->uiblocks);
3894
3895 /* draw blocks */
3896 UI_blocklist_draw(C, &region->runtime->uiblocks);
3897
3898 /* restore view matrix */
3900}
3901
3903{
3904 /* clear */
3905 ED_region_clear(C, region, region_background_color_id(C, region));
3906
3908 {
3909 /* WORKAROUND: Driver bug. Fixes invalid glyph being rendered (see #147168). */
3911 }
3912
3915}
3916
3918 const ARegion *region,
3919 const uiButtonSectionsAlign align)
3920{
3921 const ThemeColorID bgcolorid = region_background_color_id(C, region);
3922
3923 /* Clear and draw button sections background when using region overlap. Otherwise clear using the
3924 * background color like normal. */
3925 if (region->overlap) {
3927 UI_region_button_sections_draw(region, bgcolorid, align);
3928 }
3929 else {
3930 ED_region_clear(C, region, bgcolorid);
3931 }
3933}
3934
3935void ED_region_header(const bContext *C, ARegion *region)
3936{
3937 /* TODO: remove? */
3938 ED_region_header_layout(C, region);
3939 ED_region_header_draw(C, region);
3940}
3941
3943 ARegion *region,
3944 const uiButtonSectionsAlign align)
3945{
3946 ED_region_header_layout(C, region);
3948}
3949
3951{
3952 UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy);
3954}
3955
3957{
3958 /* Accommodate widget and padding. */
3959 return U.widget_unit + int(UI_SCALE_FAC * HEADER_PADDING_Y);
3960}
3961
3963{
3964 return ED_area_headersize();
3965}
3966
3968{
3971}
3973{
3976}
3978{
3981}
3982
3984{
3985 return area->global != nullptr;
3986}
3987
3988ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int event_xy[2])
3989{
3990 bScreen *screen = CTX_wm_screen(C);
3991 wmWindow *win = CTX_wm_window(C);
3992
3993 ScrArea *area = nullptr;
3994
3995 if (win->parent) {
3996 /* If active window is a child, check itself first. */
3997 area = BKE_screen_find_area_xy(screen, spacetype, event_xy);
3998 }
3999
4000 if (!area) {
4001 /* Check all windows except the active one. */
4002 int event_xy_other[2];
4003 wmWindow *win_other = WM_window_find_under_cursor(win, event_xy, event_xy_other);
4004 if (win_other && win_other != win) {
4005 win = win_other;
4006 screen = WM_window_get_active_screen(win);
4007 area = BKE_screen_find_area_xy(screen, spacetype, event_xy_other);
4008 }
4009 }
4010
4011 if (!area && !win->parent) {
4012 /* If active window is a parent window, check itself last. */
4013 area = BKE_screen_find_area_xy(screen, spacetype, event_xy);
4014 }
4015
4016 return area;
4017}
4018
4020{
4021 ScrArea *global_area = static_cast<ScrArea *>(win->global_areas.areabase.first);
4022
4023 if (!global_area) {
4024 return static_cast<ScrArea *>(screen->areabase.first);
4025 }
4026 if ((global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) {
4027 return global_area;
4028 }
4029 /* Find next visible area. */
4030 return ED_screen_areas_iter_next(screen, global_area);
4031}
4033{
4034 if (area->global == nullptr) {
4035 return area->next;
4036 }
4037
4038 for (ScrArea *area_iter = area->next; area_iter; area_iter = area_iter->next) {
4039 if ((area_iter->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) {
4040 return area_iter;
4041 }
4042 }
4043 /* No visible next global area found, start iterating over layout areas. */
4044 return static_cast<ScrArea *>(screen->areabase.first);
4045}
4046
4048{
4049 return ED_area_headersize(); /* same size as header */
4050}
4051
4053 const char *text_array[],
4054 const float fill_color[4],
4055 const bool full_redraw)
4056{
4057 const int header_height = UI_UNIT_Y;
4058 const uiStyle *style = UI_style_get_dpi();
4059 int fontid = style->widget.uifont_id;
4060 int scissor[4];
4061 int num_lines = 0;
4062
4063 /* background box */
4064 rcti rect = *ED_region_visible_rect(region);
4065
4066 /* Needed in case scripts leave the font size at an unexpected value, see: #102213. */
4067 BLF_size(fontid, style->widget.points * UI_SCALE_FAC);
4068
4069 /* Box fill entire width or just around text. */
4070 if (!full_redraw) {
4071 const char **text = &text_array[0];
4072 while (*text) {
4073 rect.xmax = min_ii(rect.xmax,
4074 rect.xmin + BLF_width(fontid, *text, BLF_DRAW_STR_DUMMY_MAX) +
4075 1.2f * U.widget_unit);
4076 text++;
4077 num_lines++;
4078 }
4079 }
4080 /* Just count the line number. */
4081 else {
4082 const char **text = &text_array[0];
4083 while (*text) {
4084 text++;
4085 num_lines++;
4086 }
4087 }
4088
4089 rect.ymin = rect.ymax - header_height * num_lines;
4090
4091 /* setup scissor */
4092 GPU_scissor_get(scissor);
4093 GPU_scissor(rect.xmin, rect.ymin, BLI_rcti_size_x(&rect) + 1, BLI_rcti_size_y(&rect) + 1);
4094
4097 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
4099 immUniformColor4fv(fill_color);
4100 immRectf(pos, rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1);
4103
4104 /* text */
4106 BLF_clipping(fontid, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
4107 BLF_enable(fontid, BLF_CLIPPING);
4108 int offset = num_lines - 1;
4109 {
4110 const char **text = &text_array[0];
4111 while (*text) {
4112 BLF_position(fontid,
4113 rect.xmin + 0.6f * U.widget_unit,
4114 rect.ymin + 0.3f * U.widget_unit + offset * header_height,
4115 0.0f);
4117 text++;
4118 offset--;
4119 }
4120 }
4121
4122 BLF_disable(fontid, BLF_CLIPPING);
4123
4124 /* restore scissor as it was before */
4125 GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
4126}
4127
4129 const char *text,
4130 const float fill_color[4],
4131 const bool full_redraw)
4132{
4133 const char *text_array[2] = {text, nullptr};
4134 ED_region_info_draw_multiline(region, text_array, fill_color, full_redraw);
4135}
4136
4140
4141static void metadata_panel_draw_field(const char *field, const char *value, void *ctx_v)
4142{
4144 uiLayout *row = &ctx->layout->row(false);
4145 row->label(field, ICON_NONE);
4146 row->label(value, ICON_NONE);
4147}
4148
4150{
4152 ctx.layout = layout;
4154}
4155
4156void ED_region_grid_draw(ARegion *region, float zoomx, float zoomy, float x0, float y0)
4157{
4158 /* the image is located inside (x0, y0), (x0+1, y0+1) as set by view2d */
4159 int x1, y1, x2, y2;
4160 UI_view2d_view_to_region(&region->v2d, x0, y0, &x1, &y1);
4161 UI_view2d_view_to_region(&region->v2d, x0 + 1.0f, y0 + 1.0f, &x2, &y2);
4162
4164 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
4165
4166 float gridcolor[4];
4167 UI_GetThemeColor4fv(TH_GRID, gridcolor);
4168
4170 /* To fake alpha-blending, color shading is reduced when alpha is nearing 0. */
4171 immUniformThemeColorBlendShade(TH_BACK, TH_GRID, gridcolor[3], 20 * gridcolor[3]);
4172 immRectf(pos, x1, y1, x2, y2);
4174
4175 /* gridsize adapted to zoom level */
4176 float gridsize = 0.5f * (zoomx + zoomy);
4177 float gridstep = 1.0f / 32.0f;
4178 if (gridsize <= 0.0f) {
4179 return;
4180 }
4181
4182 if (gridsize < 1.0f) {
4183 while (gridsize < 1.0f) {
4184 gridsize *= 4.0f;
4185 gridstep *= 4.0f;
4186 }
4187 }
4188 else {
4189 while (gridsize >= 4.0f) {
4190 gridsize /= 4.0f;
4191 gridstep /= 4.0f;
4192 }
4193 }
4194
4195 float blendfac = 0.25f * gridsize - floorf(0.25f * gridsize);
4196 CLAMP(blendfac, 0.0f, 1.0f);
4197
4198 int count_fine = 1.0f / gridstep;
4199 int count_large = 1.0f / (4.0f * gridstep);
4200
4201 if (count_fine > 0) {
4203 pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
4205 format, "color", blender::gpu::VertAttrType::SFLOAT_32_32_32);
4206
4208 immBegin(GPU_PRIM_LINES, 4 * count_fine + 4 * count_large);
4209
4210 float theme_color[3];
4211 UI_GetThemeColorShade3fv(TH_GRID, int(20.0f * (1.0f - blendfac)), theme_color);
4212 float fac = 0.0f;
4213
4214 /* the fine resolution level */
4215 for (int i = 0; i < count_fine; i++) {
4216 immAttr3fv(color, theme_color);
4217 immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
4218 immAttr3fv(color, theme_color);
4219 immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
4220 immAttr3fv(color, theme_color);
4221 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
4222 immAttr3fv(color, theme_color);
4223 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
4224 fac += gridstep;
4225 }
4226
4227 if (count_large > 0) {
4228 UI_GetThemeColor3fv(TH_GRID, theme_color);
4229 fac = 0.0f;
4230
4231 /* the large resolution level */
4232 for (int i = 0; i < count_large; i++) {
4233 immAttr3fv(color, theme_color);
4234 immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
4235 immAttr3fv(color, theme_color);
4236 immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
4237 immAttr3fv(color, theme_color);
4238 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
4239 immAttr3fv(color, theme_color);
4240 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
4241 fac += 4.0f * gridstep;
4242 }
4243 }
4244
4245 immEnd();
4247 }
4248}
4249
4250/* If the area has overlapping regions, it returns visible rect for Region *region */
4251/* rect gets returned in local region coordinates */
4252static void region_visible_rect_calc(ARegion *region, rcti *rect)
4253{
4254 ARegion *region_iter = region;
4255
4256 /* allow function to be called without area */
4257 while (region_iter->prev) {
4258 region_iter = region_iter->prev;
4259 }
4260
4261 *rect = region->winrct;
4262
4263 /* check if a region overlaps with the current one */
4264 for (; region_iter; region_iter = region_iter->next) {
4265 if (region != region_iter && region_iter->overlap) {
4266 if (BLI_rcti_isect(rect, &region_iter->winrct, nullptr)) {
4267 int alignment = RGN_ALIGN_ENUM_FROM_MASK(region_iter->alignment);
4268
4269 if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
4270 /* Overlap left, also check 1 pixel offset (2 regions on one side). */
4271 if (abs(rect->xmin - region_iter->winrct.xmin) < 2) {
4272 rect->xmin = region_iter->winrct.xmax;
4273 }
4274
4275 /* Overlap right. */
4276 if (abs(rect->xmax - region_iter->winrct.xmax) < 2) {
4277 rect->xmax = region_iter->winrct.xmin;
4278 }
4279 }
4280 else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
4281 /* Same logic as above for vertical regions. */
4282 if (abs(rect->ymin - region_iter->winrct.ymin) < 2) {
4283 rect->ymin = region_iter->winrct.ymax;
4284 }
4285 if (abs(rect->ymax - region_iter->winrct.ymax) < 2) {
4286 rect->ymax = region_iter->winrct.ymin;
4287 }
4288 }
4289 else if (alignment == RGN_ALIGN_FLOAT) {
4290 /* Skip floating. */
4291 }
4292 else {
4293 BLI_assert_msg(0, "Region overlap with unknown alignment");
4294 }
4295 }
4296 }
4297 }
4298 BLI_rcti_translate(rect, -region->winrct.xmin, -region->winrct.ymin);
4299}
4300
4302{
4303 rcti *rect = &region->runtime->visible_rect;
4304 if (rect->xmin == 0 && rect->ymin == 0 && rect->xmax == 0 && rect->ymax == 0) {
4305 region_visible_rect_calc(region, rect);
4306 }
4307 return rect;
4308}
4309
4310/* Cache display helpers */
4311
4313{
4314 /* Local coordinate visible rect inside region, to accommodate overlapping ui. */
4315 const rcti *rect_visible = ED_region_visible_rect(region);
4316 const int region_bottom = rect_visible->ymin;
4317
4319 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
4321 immUniformColor4ub(128, 128, 255, 64);
4322 immRectf(pos, 0, region_bottom, region->winx, region_bottom + 8 * UI_SCALE_FAC);
4324}
4325
4326void ED_region_cache_draw_curfra_label(const int framenr, const float x, const float y)
4327{
4328 using namespace blender;
4329 const uiStyle *style = UI_style_get();
4330 int fontid = style->widget.uifont_id;
4331
4332 /* Format frame number. */
4333 char numstr[32];
4334 BLF_size(fontid, 11.0f * UI_SCALE_FAC);
4335 SNPRINTF_UTF8(numstr, "%d", framenr);
4336
4337 float2 text_dims = {0.0f, 0.0f};
4338 BLF_width_and_height(fontid, numstr, sizeof(numstr), &text_dims.x, &text_dims.y);
4339 float padding = 3.0f * UI_SCALE_FAC;
4340
4341 /* Rounded corner background box. */
4342 float4 bg_color;
4343 UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color);
4344 float4 outline_color;
4345 UI_GetThemeColorShade4fv(TH_CFRAME, 5, outline_color);
4346
4347 rctf rect{};
4348 rect.xmin = x - text_dims.x / 2 - padding;
4349 rect.xmax = x + text_dims.x / 2 + padding;
4350 rect.ymin = y;
4351 rect.ymax = y + text_dims.y + padding * 2;
4354 &rect, bg_color, nullptr, 1.0f, outline_color, U.pixelsize, 3 * UI_SCALE_FAC);
4355
4356 /* Text label. */
4358 BLF_position(fontid, x - text_dims.x * 0.5f, y + padding, 0.0f);
4359 BLF_draw(fontid, numstr, sizeof(numstr));
4360}
4361
4363 ARegion *region, const int num_segments, const int *points, const int sfra, const int efra)
4364{
4365 if (num_segments) {
4366 /* Local coordinate visible rect inside region, to accommodate overlapping ui. */
4367 const rcti *rect_visible = ED_region_visible_rect(region);
4368 const int region_bottom = rect_visible->ymin;
4369
4371 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
4373 immUniformColor4ub(128, 128, 255, 128);
4374
4375 for (int a = 0; a < num_segments; a++) {
4376 float x1 = float(points[a * 2] - sfra) / (efra - sfra + 1) * region->winx;
4377 float x2 = float(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * region->winx;
4378
4379 immRectf(pos, x1, region_bottom, x2, region_bottom + 8 * UI_SCALE_FAC);
4380 /* TODO(merwin): use primitive restart to draw multiple rects more efficiently */
4381 }
4382
4384 }
4385}
4386
4388{
4389 ARegion *region = params->region;
4390 const bContext *C = params->context;
4391 wmMsgBus *mbus = params->message_bus;
4392
4393 if (region->runtime->gizmo_map != nullptr) {
4394 WM_gizmomap_message_subscribe(C, region->runtime->gizmo_map, region, mbus);
4395 }
4396
4397 if (!BLI_listbase_is_empty(&region->runtime->uiblocks)) {
4398 UI_region_message_subscribe(region, mbus);
4399 }
4400
4401 if (region->runtime->type->message_subscribe != nullptr) {
4402 region->runtime->type->message_subscribe(params);
4403 }
4404}
4405
4407{
4408 /* Use a larger value because toggling scrollbars can jump in size. */
4409 const int snap_match_threshold = 16;
4410 if (region->runtime->type->snap_size != nullptr) {
4411 const int snap_size_x = region->runtime->type->snap_size(region, region->sizex, 0);
4412 const int snap_size_y = region->runtime->type->snap_size(region, region->sizey, 1);
4413 return (((abs(region->sizex - snap_size_x) <= snap_match_threshold) << 0) |
4414 ((abs(region->sizey - snap_size_y) <= snap_match_threshold) << 1));
4415 }
4416 return 0;
4417}
4418
4419bool ED_region_snap_size_apply(ARegion *region, int snap_flag)
4420{
4421 bool changed = false;
4422 if (region->runtime->type->snap_size != nullptr) {
4423 if (snap_flag & (1 << 0)) {
4424 short snap_size = region->runtime->type->snap_size(region, region->sizex, 0);
4425 if (snap_size != region->sizex) {
4426 region->sizex = snap_size;
4427 changed = true;
4428 }
4429 }
4430 if (snap_flag & (1 << 1)) {
4431 short snap_size = region->runtime->type->snap_size(region, region->sizey, 1);
4432 if (snap_size != region->sizey) {
4433 region->sizey = snap_size;
4434 changed = true;
4435 }
4436 }
4437 }
4438 return changed;
4439}
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:406
#define BKE_ST_MAXNAME
Definition BKE_screen.hh:72
void BKE_spacedata_freelist(ListBase *lb)
Definition screen.cc:305
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:638
SpaceType * BKE_spacetype_from_id(int spaceid)
Definition screen.cc:257
ARegionType * BKE_regiontype_from_id(const SpaceType *st, int regionid)
Definition screen.cc:267
void BKE_area_region_free(SpaceType *st, ARegion *region)
Definition screen.cc:597
@ PANEL_TYPE_NO_HEADER
@ PANEL_TYPE_INSTANCED
@ PANEL_TYPE_LAYOUT_VERT_BAR
@ PANEL_TYPE_NO_SEARCH
@ PANEL_TYPE_HEADER_EXPAND
ARegion * BKE_area_region_copy(const SpaceType *st, const ARegion *region)
Definition screen.cc:350
void BKE_area_region_panels_free(ListBase *panels)
Definition screen.cc:585
void BKE_workspace_status_clear(WorkSpace *workspace)
Definition workspace.cc:650
bool BKE_workspace_owner_id_check(const WorkSpace *workspace, const char *owner_id) ATTR_NONNULL()
Definition workspace.cc:537
bScreen * BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:614
int BLF_set_default()
void BLF_size(int fontid, float size)
Definition blf.cc:443
void BLF_enable(int fontid, FontFlags flag)
Definition blf.cc:320
void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax)
Definition blf.cc:912
void BLF_batch_discard()
Definition blf_font.cc:346
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:789
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:440
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:585
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:802
void BLF_disable(int fontid, FontFlags flag)
Definition blf.cc:329
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:388
@ BLF_CLIPPING
Definition BLF_enums.hh:34
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define BLI_INLINE
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
void void BLI_INLINE bool BLI_listbase_is_single(const ListBase *lb)
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)
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void BLI_rcti_union(struct rcti *rct_a, const struct rcti *rct_b)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
bool BLI_rcti_is_valid(const struct rcti *rect)
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.cc:566
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
void BLI_rcti_resize(struct rcti *rect, int x, int y)
Definition rct.cc:621
void BLI_rcti_sanitize(struct rcti *rect)
Definition rct.cc:446
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:194
BLI_INLINE int BLI_rcti_cent_y(const struct rcti *rct)
Definition BLI_rect.h:181
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
BLI_INLINE int BLI_rcti_cent_x(const struct rcti *rct)
Definition BLI_rect.h:177
void BLI_str_rstrip(char *str) ATTR_NONNULL(1)
Definition string.cc:980
#define SNPRINTF_UTF8(dst, format,...)
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
#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
@ AREA_FLAG_ACTIVE_TOOL_UPDATE
@ AREA_FLAG_OFFSCREEN
@ AREA_FLAG_REGION_SIZE_UPDATE
@ AREA_FLAG_STACKED_FULLSCREEN
@ AREA_FLAG_ACTIONZONES_UPDATE
@ HEADER_NO_PULLDOWN
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ PNL_PIN
@ RGN_DRAW_NO_REBUILD
@ RGN_DRAW_PARTIAL
@ RGN_DRAWING
@ RGN_DRAW
@ RGN_REFRESH_UI
@ RGN_DRAW_EDITOR_OVERLAYS
#define AREAMINX
@ GLOBAL_AREA_IS_HIDDEN
@ 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
@ SCREENFULL
@ SCREENMAXIMIZED
@ SCREENNORMAL
@ 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_NAV_BAR
@ RGN_TYPE_FOOTER
@ RGN_TYPE_HEADER
@ RGN_TYPE_TOOLS
@ RGN_TYPE_TOOL_PROPS
#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_INDICATE_OVERFLOW
@ 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)
#define RGN_ALIGN_FLAG_FROM_MASK(align)
@ SPACE_FLAG_TYPE_WAS_ACTIVE
@ SPACE_FLAG_TYPE_TEMPORARY
eSpace_Type
@ SPACE_CLIP
@ SPACE_OUTLINER
@ SPACE_STATUSBAR
@ SPACE_TOPBAR
@ SPACE_NODE
@ SPACE_PROPERTIES
@ SPACE_EMPTY
@ SPACE_IMAGE
@ SPACE_VIEW3D
#define UI_SCALE_FAC
#define UI_INV_SCALE_FAC
@ USER_AREA_CORNER_HANDLE
@ USER_REDUCE_MOTION
@ USER_REGION_OVERLAP
@ 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_LEFT
@ V2D_SCROLL_HORIZONTAL
@ V2D_SCROLL_RIGHT
@ V2D_SCROLL_BOTTOM
@ V2D_SCROLL_VERTICAL
const char * ED_buttons_search_string_get(SpaceProperties *sbuts)
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:693
int ED_area_headersize()
Definition area.cc:3956
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:767
@ ED_KEYMAP_ASSET_SHELF
Definition ED_screen.hh:768
@ ED_KEYMAP_UI
Definition ED_screen.hh:758
@ ED_KEYMAP_ANIMATION
Definition ED_screen.hh:762
@ ED_KEYMAP_HEADER
Definition ED_screen.hh:764
@ ED_KEYMAP_TOOL
Definition ED_screen.hh:760
@ ED_KEYMAP_GPENCIL
Definition ED_screen.hh:766
@ ED_KEYMAP_GIZMO
Definition ED_screen.hh:759
@ ED_KEYMAP_VIEW2D
Definition ED_screen.hh:761
@ ED_KEYMAP_FRAMES
Definition ED_screen.hh:763
@ ED_KEYMAP_FOOTER
Definition ED_screen.hh:765
void ED_region_do_msg_notify_tag_redraw(bContext *C, wmMsgSubscribeKey *msg_key, wmMsgSubscribeValue *msg_val)
Definition area.cc:361
AZScrollDirection
@ AZ_SCROLL_HOR
@ AZ_SCROLL_VERT
@ AZONE_REGION
@ AZONE_FULLSCREEN
@ AZONE_REGION_SCROLL
@ AZONE_AREA
@ 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
void GPU_clear_color(float red, float green, float blue, float alpha)
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
blender::gpu::FrameBuffer * GPU_framebuffer_active_get()
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immUniformColor4f(float r, float g, float b, float a)
void immVertex2f(uint attr_id, float x, float y)
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 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_DEVICE_ATI
@ GPU_DRIVER_OPENSOURCE
@ GPU_OS_UNIX
bool GPU_type_matches_ex(GPUDeviceType device, GPUOSType os, GPUDriverType driver, GPUBackendType backend)
@ GPU_PRIM_TRI_FAN
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_SHADER_3D_FLAT_COLOR
void GPU_line_width(float width)
Definition gpu_state.cc:166
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_scissor(int x, int y, int width, int height)
Definition gpu_state.cc:193
void GPU_scissor_get(int coords[4])
Definition gpu_state.cc:268
void GPU_vertformat_clear(GPUVertFormat *)
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
void IMB_metadata_foreach(const ImBuf *ibuf, IMBMetadataForeachCb callback, void *userdata)
Definition metadata.cc:87
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
uiBut * uiDefButR(uiBlock *block, ButType type, int retval, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, blender::StringRefNull propname, int index, float min, float max, std::optional< blender::StringRef > tip)
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)
PanelCategoryDyn * UI_panel_category_find(const ARegion *region, const char *idname)
#define INSTANCED_PANEL_UNIQUE_STR_SIZE
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
void UI_blocklist_free(const bContext *C, ARegion *region)
#define UI_PANEL_CATEGORY_MIN_WIDTH
Panel * UI_panel_find_by_type(ListBase *lb, const PanelType *pt)
void UI_draw_roundbox_4fv_ex(const rctf *rect, const float inner1[4], const float inner2[4], float shade_dir, const float outline[4], float outline_width, float rad)
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)
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:402
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)
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_AZONESPOTW_RIGHT
void UI_draw_roundbox_3fv_alpha(const rctf *rect, bool filled, float rad, const float col[3], float alpha)
void UI_panel_category_clear_all(ARegion *region)
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
#define UI_PANEL_CATEGORY_MARGIN_WIDTH
void UI_blocklist_free_inactive(const bContext *C, ARegion *region)
void UI_blocklist_draw(const bContext *C, const ListBase *lb)
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)
uiButtonSectionsAlign
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
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)
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)
#define UI_AZONESPOTW_LEFT
void UI_block_set_search_only(uiBlock *block, bool search_only)
const char * UI_panel_category_active_get(ARegion *region, bool set_fallback)
#define UI_AZONESPOTH
void UI_region_free_active_but_all(bContext *C, ARegion *region)
void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha)
#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)
bool UI_block_apply_search_filter(uiBlock *block, const char *search_filter)
bool uiLayoutEndsWithPanelHeader(const uiLayout &layout)
#define UI_MAX_DRAW_STR
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_BLACK
@ TH_HEADER
@ TH_GRID
@ TH_BACK
@ TH_EDITOR_BORDER
@ TH_PREVIEW_BACK
@ TH_CFRAME
@ TH_HEADER_TEXT_HI
@ 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])
void UI_GetThemeColorShade4fv(int colorid, int offset, 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])
void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom)
Definition view2d.cc:1504
void UI_view2d_view_restore(const bContext *C)
Definition view2d.cc:1162
void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
Definition view2d.cc:221
void UI_view2d_totRect_set(View2D *v2d, int width, int height)
Definition view2d.cc:1036
void UI_view2d_view_ortho(const View2D *v2d)
Definition view2d.cc:1095
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:1723
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1921
@ WM_CAPABILITY_WINDOW_DECORATION_STYLES
Definition WM_api.hh:203
#define WM_TOOLSYSTEM_SPACE_MASK
#define NC_WINDOW
Definition WM_types.hh:375
#define ND_FILEREAD
Definition WM_types.hh:412
#define ND_SPACE_CHANGED
Definition WM_types.hh:538
#define NC_WM
Definition WM_types.hh:374
@ KM_NOTHING
Definition WM_types.hh:310
@ KM_ANY
Definition WM_types.hh:309
#define NC_SPACE
Definition WM_types.hh:392
#define KM_MOD_HELD
Definition WM_types.hh:326
ScrArea * ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area)
Definition area.cc:4032
void ED_area_do_mgs_subscribe_for_tool_header(const wmRegionMessageSubscribeParams *params)
Definition area.cc:394
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, blender::wm::OpCallContext op_context)
Definition area.cc:2975
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:693
int ED_region_snap_size_test(const ARegion *region)
Definition area.cc:4406
static constexpr float STATUS_MOUSE_ICON_PAD
Definition area.cc:928
static void area_draw_azone(ScrArea *area, ARegion *region, AZone *az)
Corner widgets use for dragging and splitting the view.
Definition area.cc:161
bool ED_region_property_search(const bContext *C, ARegion *region, ListBase *paneltypes, const char *contexts[], const char *category_override)
Definition area.cc:3723
void ED_region_tag_refresh_ui(ARegion *region)
Definition area.cc:647
void ED_area_init(bContext *C, const wmWindow *win, ScrArea *area)
Definition area.cc:2140
void ED_area_do_mgs_subscribe_for_tool_ui(const wmRegionMessageSubscribeParams *params)
Definition area.cc:409
void ED_region_update_rect(ARegion *region)
Definition area.cc:2296
ScrArea * ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen)
Definition area.cc:4019
BLI_INLINE bool streq_array_any(const char *s, const char *arr[])
Definition area.cc:2956
static bool panel_add_check(const bContext *C, const WorkSpace *workspace, const char *contexts[], const char *category_override, PanelType *panel_type)
Definition area.cc:3151
void ED_region_cursor_set(wmWindow *win, ScrArea *area, ARegion *region)
Definition area.cc:2311
const char * ED_area_region_search_filter_get(const ScrArea *area, const ARegion *region)
Definition area.cc:825
void ED_region_clear(const bContext *C, const ARegion *region, const int colorid)
Definition area.cc:2933
void ED_region_draw_overflow_indication(const ScrArea *area, ARegion *region, rcti *mask)
Definition area.cc:3417
void ED_region_do_listen(wmRegionListenerParams *params)
Definition area.cc:93
int ED_area_max_regionsize(const ScrArea *area, const ARegion *scale_region, const AZEdge edge)
Definition area.cc:762
static void region_evaulate_visibility(ARegion *region)
Definition area.cc:1924
int ED_area_global_size_y(const ScrArea *area)
Definition area.cc:3967
void ED_region_search_filter_update(const ScrArea *area, ARegion *region)
Definition area.cc:838
void ED_region_toggle_hidden(bContext *C, ARegion *region)
Definition area.cc:2373
static void area_calc_totrct(const bScreen *screen, ScrArea *area, const rcti *window_rect)
Definition area.cc:1873
static int panel_draw_width_from_max_width_get(const ARegion *region, const PanelType *panel_type, const int max_width)
Definition area.cc:3223
static void region_draw_blocks_in_view2d(const bContext *C, const ARegion *region)
Definition area.cc:3888
static bool region_uses_category_tabs(const ScrArea *area, const ARegion *region)
Definition area.cc:3191
static SpaceLink * area_get_prevspace(ScrArea *area)
Definition area.cc:2844
static void area_azone_tag_update(ScrArea *area)
Definition area.cc:270
int ED_area_global_min_size_y(const ScrArea *area)
Definition area.cc:3972
ScrArea * ED_area_find_under_cursor(const bContext *C, int spacetype, const int event_xy[2])
Definition area.cc:3988
static void area_azone_init(const wmWindow *win, const bScreen *screen, ScrArea *area)
Definition area.cc:1034
static void region_draw_gradient(const ARegion *region)
Definition area.cc:66
void ED_area_and_region_types_init(ScrArea *area)
Definition area.cc:2123
void ED_region_floating_init(ARegion *region)
Definition area.cc:2301
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:851
static void region_draw_azones(ScrArea *area, ARegion *region)
Definition area.cc:275
void ED_area_tag_redraw_no_rebuild(ScrArea *area)
Definition area.cc:702
int ED_area_global_max_size_y(const ScrArea *area)
Definition area.cc:3977
void ED_region_panels(const bContext *C, ARegion *region)
Definition area.cc:3609
void ED_region_do_msg_notify_tag_redraw(bContext *, wmMsgSubscribeKey *, wmMsgSubscribeValue *msg_val)
Definition area.cc:361
void region_toggle_hidden(bContext *C, ARegion *region, const bool do_fade)
Definition area.cc:2358
static void ed_workspace_status_icon_item(WorkSpace *workspace, const int icon, const bool inverted=false)
Definition area.cc:939
void ED_region_header(const bContext *C, ARegion *region)
Definition area.cc:3935
static void region_overlap_fix(ScrArea *area, ARegion *region)
Definition area.cc:1403
const rcti * ED_region_visible_rect(ARegion *region)
Definition area.cc:4301
static void area_offscreen_exit(wmWindowManager *wm, wmWindow *win, ScrArea *area)
Definition area.cc:2249
static void ed_workspace_status_space(WorkSpace *workspace, const float space_factor)
Definition area.cc:907
void ED_area_do_listen(wmSpaceTypeListenerParams *params)
Definition area.cc:125
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:3641
static const char * region_panels_collect_categories(ARegion *region, LinkNode *panel_types_stack, bool *use_category_tabs)
Definition area.cc:3199
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:4362
static void region_align_info_from_area(ScrArea *area, RegionTypeAlignInfo *r_align_info)
Definition area.cc:2445
void ED_region_header_init(ARegion *region)
Definition area.cc:3950
static constexpr float STATUS_AFTER_TEXT
Definition area.cc:927
void ED_region_header_with_button_sections(const bContext *C, ARegion *region, const uiButtonSectionsAlign align)
Definition area.cc:3942
bool ED_area_is_global(const ScrArea *area)
Definition area.cc:3983
void ED_region_cache_draw_background(ARegion *region)
Definition area.cc:4312
void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2)
Definition area.cc:2663
void ED_region_header_draw_with_button_sections(const bContext *C, const ARegion *region, const uiButtonSectionsAlign align)
Definition area.cc:3917
static void ed_workspace_status_text_item(WorkSpace *workspace, std::string text)
Definition area.cc:930
void ED_region_pixelspace(const ARegion *region)
Definition area.cc:87
void ED_area_offscreen_free(wmWindowManager *wm, wmWindow *win, ScrArea *area)
Definition area.cc:2279
void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
Definition area.cc:2378
void ED_region_header_draw(const bContext *C, ARegion *region)
Definition area.cc:3902
static void region_azone_edge_init(ScrArea *area, ARegion *region, AZEdge edge, const bool is_fullscreen)
Definition area.cc:1282
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:638
int ED_area_headersize()
Definition area.cc:3956
ScrArea * ED_area_offscreen_create(wmWindow *win, eSpace_Type space_type)
Definition area.cc:2238
void ED_area_tag_redraw_regiontype(ScrArea *area, int regiontype)
Definition area.cc:711
bool ED_area_has_shared_border(ScrArea *a, ScrArea *b)
Definition area.cc:2085
static void region_azone_edge(const ScrArea *area, AZone *az, const ARegion *region)
Definition area.cc:1155
bool ED_region_snap_size_apply(ARegion *region, int snap_flag)
Definition area.cc:4419
bool ED_region_is_overlap(int spacetype, int regiontype)
Definition area.cc:1478
static void metadata_panel_draw_field(const char *field, const char *value, void *ctx_v)
Definition area.cc:4141
void ED_region_tag_redraw_partial(ARegion *region, const rcti *rct, bool rebuild)
Definition area.cc:666
static void region_azone_scrollbar_init(ScrArea *area, ARegion *region, AZScrollDirection direction)
Definition area.cc:1307
void ED_region_panels_ex(const bContext *C, ARegion *region, blender::wm::OpCallContext op_context, const char *contexts[])
Definition area.cc:3598
void ED_area_tag_region_size_update(ScrArea *area, ARegion *changed_region)
Definition area.cc:729
static void region_visible_rect_calc(ARegion *region, rcti *rect)
Definition area.cc:4252
static void region_clear_fully_transparent(const bContext *C)
Definition area.cc:2948
static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *region)
Definition area.cc:1363
void ED_region_visibility_change_update(bContext *C, ScrArea *area, ARegion *region)
Definition area.cc:2351
void ED_region_do_layout(bContext *C, ARegion *region)
Definition area.cc:456
void ED_region_tag_redraw_cursor(ARegion *region)
Definition area.cc:631
void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_region_exit)
Definition area.cc:2699
void ED_region_cache_draw_curfra_label(const int framenr, const float x, const float y)
Definition area.cc:4326
void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src)
Definition area.cc:2411
static void region_draw_status_text(ScrArea *, ARegion *region)
Definition area.cc:320
static bool region_background_is_transparent(const ScrArea *area, const ARegion *region)
Definition area.cc:1140
void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *area)
Definition area.cc:2046
void ED_area_tag_refresh(ScrArea *area)
Definition area.cc:722
static bool region_azone_edge_poll(const ScrArea *area, const ARegion *region, const bool is_fullscreen)
Definition area.cc:1239
void ED_region_info_draw_multiline(ARegion *region, const char *text_array[], const float fill_color[4], const bool full_redraw)
Definition area.cc:4052
int ED_region_global_size_y()
Definition area.cc:4047
static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region)
Definition area.cc:1201
static void ed_default_handlers(wmWindowManager *wm, ScrArea *area, ARegion *region, ListBase *handlers, int flag)
Definition area.cc:1939
static short region_alignment_from_header_and_tool_header_state(const RegionTypeAlignInfo *region_align_info, const short fallback)
Definition area.cc:2473
void ED_area_do_refresh(bContext *C, ScrArea *area)
Definition area.cc:133
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:2520
void ED_region_panels_layout(const bContext *C, ARegion *region)
Definition area.cc:3519
void ED_region_panels_init(wmWindowManager *wm, ARegion *region)
Definition area.cc:3616
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
static void area_init_type_fallback(ScrArea *area, eSpace_Type space_type)
Definition area.cc:2093
void ED_region_image_metadata_panel_draw(ImBuf *ibuf, uiLayout *layout)
Definition area.cc:4149
void ED_region_tag_redraw_editor_overlays(ARegion *region)
Definition area.cc:654
static void region_draw_azone_tab_arrow(ScrArea *area, ARegion *region, AZone *az)
Definition area.cc:236
static void region_update_rect(ARegion *region)
Definition area.cc:2287
int ED_area_header_switchbutton(const bContext *C, uiBlock *block, int yco)
Definition area.cc:2892
void ED_region_info_draw(ARegion *region, const char *text, const float fill_color[4], const bool full_redraw)
Definition area.cc:4128
void ED_region_visibility_change_update_ex(bContext *C, ScrArea *area, ARegion *region, bool is_hidden, bool do_init)
Definition area.cc:2332
static void area_offscreen_init(ScrArea *area)
Definition area.cc:2225
static constexpr float STATUS_BEFORE_TEXT
Definition area.cc:926
void ED_area_do_msg_notify_tag_refresh(bContext *, wmMsgSubscribeKey *, wmMsgSubscribeValue *msg_val)
Definition area.cc:384
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:888
void ED_region_do_draw(bContext *C, ARegion *region)
Definition area.cc:479
void ED_region_panels_layout_ex(const bContext *C, ARegion *region, ListBase *paneltypes, blender::wm::OpCallContext op_context, const char *contexts[], const char *category_override)
Definition area.cc:3233
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
static void region_align_info_to_area(ScrArea *area, const RegionTypeAlignInfo region_align_info_src[RGN_TYPE_NUM])
Definition area.cc:2635
void ED_region_message_subscribe(wmRegionMessageSubscribeParams *params)
Definition area.cc:4387
int ED_area_footersize()
Definition area.cc:3962
void ED_region_grid_draw(ARegion *region, float zoomx, float zoomy, float x0, float y0)
Definition area.cc:4156
static ThemeColorID region_background_color_id(const bContext *, const ARegion *region)
Definition area.cc:2920
static void fullscreen_azone_init(ScrArea *area, ARegion *region)
Definition area.cc:1108
void ED_area_prevspace(bContext *C, ScrArea *area)
Definition area.cc:2866
static void area_draw_azone_fullscreen(short, short, short x2, short y2, float alpha)
Corner widget use for quitting full-screen.
Definition area.cc:145
static bool area_is_pseudo_minimized(const ScrArea *area)
Definition area.cc:451
static void region_rect_recursive(ScrArea *area, ARegion *region, rcti *remainder, rcti *overlap_remainder, int quad)
Definition area.cc:1530
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:180
static void region_azones_scrollbars_init(ScrArea *area, ARegion *region)
Definition area.cc:1329
static void region_azones_add_edge(ScrArea *area, ARegion *region, const int alignment, const bool is_fullscreen)
Definition area.cc:1342
void ED_region_panels_draw(const bContext *C, ARegion *region)
Definition area.cc:3529
static int rct_fits(const rcti *rect, const eScreenAxis dir_axis, int size)
Definition area.cc:1390
void ED_region_header_layout(const bContext *C, ARegion *region)
Definition area.cc:3801
int pad[32 - sizeof(int)]
#define U
ATTR_WARN_UNUSED_RESULT const BMLoop * l
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static RandomNumberGenerator from_random_seed()
Definition rand.cc:288
void range(std::string text, int icon1, int icon2)
Definition area.cc:970
void separator(float factor=1.0f)
Definition area.cc:965
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:979
WorkspaceStatus(bContext *C)
Definition area.cc:912
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:958
void opmodal(std::string text, const wmOperatorType *ot, int propvalue, bool inverted=false)
Definition area.cc:989
nullptr float
#define str(s)
uint pos
blender::gpu::Batch * quad
#define printf(...)
constexpr T sign(T) RET
#define abs
uint padding(uint offset, uint alignment)
BLI_INLINE float fb(float length, float L)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
format
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define G(x, y, z)
static void add(blender::Map< std::string, std::string > &messages, Message &msg)
Definition msgfmt.cc:222
int2 block_layout_resolve(uiBlock *block)
void block_layout_free(uiBlock *block)
uiLayout & block_layout(uiBlock *block, LayoutDirection direction, LayoutType type, int x, int y, int size, int em, int padding, const uiStyle *style)
VecBase< int32_t, 2 > int2
const int status
#define floorf
#define fabsf
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
bool area_regions_poll(bContext *C, const bScreen *screen, ScrArea *area)
eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b)
void screen_area_spacelink_add(const Scene *scene, ScrArea *area, eSpace_Type space_type)
eScreenAxis
@ SCREEN_AXIS_V
@ SCREEN_AXIS_H
#define AZONEFADEOUT
void(* draw)(const bContext *C, ARegion *region)
void(* layout)(const bContext *C, ARegion *region)
struct ARegion * prev
ListBase panels_category_active
ARegionRuntimeHandle * runtime
struct ARegion * next
ListBase panels
ListBase ui_lists
float alpha
AZEdge edge
ARegion * region
AZScrollDirection direction
uiLayout * layout
HeaderType * type
struct LinkNode * next
void * last
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::@241167160041050015211272272215127257032251326366 by_type[RGN_TYPE_NUM]
ListBase areabase
struct bToolRef * tool
ListBase handlers
ScrVert * v2
ListBase actionzones
ListBase spacedata
short butspacetype_subtype
struct SpaceType * type
ScrArea_Runtime runtime
ScrVert * v1
struct ScrArea * next
ListBase regionbase
ScrGlobalAreaData * global
ScrVert * v4
void(* free)(SpaceLink *sl)
Definition BKE_screen.hh:95
void(* refresh)(const bContext *C, ScrArea *area)
int(* space_subtype_get)(ScrArea *area)
SpaceLink *(* create)(const ScrArea *area, const Scene *scene)
Definition BKE_screen.hh:93
void(* exit)(wmWindowManager *wm, ScrArea *area)
void(* init)(wmWindowManager *wm, ScrArea *area)
Definition BKE_screen.hh:98
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
ListBase areabase
ThemeSpace space_view3d
float x
float y
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
void scale_y_set(float scale)
void operator_context_set(blender::wm::OpCallContext opcontext)
void label(blender::StringRef name, int icon)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
int width() const
uiLayout & row(bool align)
short panelspace
uiFontStyle widget
short y
short x
int xy[2]
Definition WM_types.hh:761
unsigned int data
Definition WM_types.hh:358
unsigned int category
Definition WM_types.hh:358
struct wmWindow * parent
WindowRuntimeHandle * runtime
struct wmEvent * eventstate
ScrAreaMap global_areas
struct WorkSpaceInstanceHook * workspace_hook
i
Definition text_draw.cc:230
bool ED_time_scrub_event_in_region_poll(const wmWindow *, const ScrArea *, const ARegion *region, const wmEvent *event)
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:582
void WM_draw_region_free(ARegion *region)
Definition wm_draw.cc:1677
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)
bool WM_event_handler_region_marker_poll(const wmWindow *win, const ScrArea *area, const ARegion *region, const wmEvent *event)
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:4238
wmOperatorType * ot
Definition wm_files.cc:4237
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:895
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])
eWM_CapabilitiesFlag WM_capabilities_flag()
void WM_window_screen_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:541
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:145