Blender V4.3
interface_panel.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9/* a full doc with API notes can be found in
10 * bf-blender/trunk/blender/doc/guides/interface_API.txt */
11
12#include <cctype>
13#include <cmath>
14#include <cstdlib>
15#include <cstring>
16
17#include "MEM_guardedalloc.h"
18
19#include "BLI_blenlib.h"
20#include "BLI_time.h"
21#include "BLI_utildefines.h"
22
23#include "BLT_translation.hh"
24
25#include "DNA_screen_types.h"
26#include "DNA_userdef_types.h"
27
28#include "BKE_context.hh"
29#include "BKE_screen.hh"
30
31#include "RNA_access.hh"
32
33#include "BLF_api.hh"
34
35#include "WM_api.hh"
36#include "WM_types.hh"
37
38#include "ED_screen.hh"
39
40#include "UI_interface.hh"
41#include "UI_interface_c.hh"
42#include "UI_interface_icons.hh"
43#include "UI_resources.hh"
44#include "UI_view2d.hh"
45
46#include "GPU_batch_presets.hh"
47#include "GPU_immediate.hh"
48#include "GPU_matrix.hh"
49#include "GPU_state.hh"
50
51#include "interface_intern.hh"
52
53/* -------------------------------------------------------------------- */
56
57#define ANIMATION_TIME 0.30
58#define ANIMATION_INTERVAL 0.02
59
82
83/* The state of the mouse position relative to the panel. */
90
96
99
100 /* Animation. */
102 double starttime;
103
104 /* Dragging. */
108};
109
115
116static void panel_set_expansion_from_list_data(const bContext *C, Panel *panel);
117static int get_panel_real_size_y(const Panel *panel);
118static void panel_activate_state(const bContext *C, Panel *panel, const uiHandlePanelState state);
119static int compare_panel(const void *a, const void *b);
120static bool panel_type_context_poll(ARegion *region,
121 const PanelType *panel_type,
122 const char *context);
123
125
126/* -------------------------------------------------------------------- */
129
131 Panel **r_panel_animation,
132 bool *r_no_animation)
133{
134 LISTBASE_FOREACH (Panel *, panel, lb) {
135 /* Detect panel active flag changes. */
136 if (!(panel->type && panel->type->parent)) {
137 if ((panel->runtime_flag & PANEL_WAS_ACTIVE) && !(panel->runtime_flag & PANEL_ACTIVE)) {
138 return true;
139 }
140 if (!(panel->runtime_flag & PANEL_WAS_ACTIVE) && (panel->runtime_flag & PANEL_ACTIVE)) {
141 return true;
142 }
143 }
144
145 /* Detect changes in panel expansions. */
146 if (bool(panel->runtime_flag & PANEL_WAS_CLOSED) != UI_panel_is_closed(panel)) {
147 *r_panel_animation = panel;
148 return false;
149 }
150
151 if ((panel->runtime_flag & PANEL_ACTIVE) && !UI_panel_is_closed(panel)) {
152 if (panel_active_animation_changed(&panel->children, r_panel_animation, r_no_animation)) {
153 return true;
154 }
155 }
156
157 /* Detect animation. */
158 if (panel->activedata) {
159 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
160 if (data->state == PANEL_STATE_ANIMATION) {
161 *r_panel_animation = panel;
162 }
163 else {
164 /* Don't animate while handling other interaction. */
165 *r_no_animation = true;
166 }
167 }
168 if ((panel->runtime_flag & PANEL_ANIM_ALIGN) && !(*r_panel_animation)) {
169 *r_panel_animation = panel;
170 }
171 }
172
173 return false;
174}
175
179static bool properties_space_needs_realign(const ScrArea *area, const ARegion *region)
180{
181 if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) {
182 const SpaceProperties *sbuts = static_cast<SpaceProperties *>(area->spacedata.first);
183
184 if (sbuts->mainbo != sbuts->mainb) {
185 return true;
186 }
187 }
188
189 return false;
190}
191
192static bool panels_need_realign(const ScrArea *area, ARegion *region, Panel **r_panel_animation)
193{
194 *r_panel_animation = nullptr;
195
196 if (properties_space_needs_realign(area, region)) {
197 return true;
198 }
199
200 /* Detect if a panel was added or removed. */
201 Panel *panel_animation = nullptr;
202 bool no_animation = false;
203 if (panel_active_animation_changed(&region->panels, &panel_animation, &no_animation)) {
204 return true;
205 }
206
207 /* Detect panel marked for animation, if we're not already animating. */
208 if (panel_animation) {
209 if (!no_animation) {
210 *r_panel_animation = panel_animation;
211 }
212 return true;
213 }
214
215 return false;
216}
217
219
220/* -------------------------------------------------------------------- */
223
224static Panel *panel_add_instanced(ListBase *panels, PanelType *panel_type, PointerRNA *custom_data)
225{
226 Panel *panel = BKE_panel_new(panel_type);
227
228 panel->runtime->custom_data_ptr = custom_data;
230
231 /* Add the panel's children too. Although they aren't instanced panels, we can still use this
232 * function to create them, as UI_panel_begin does other things we don't need to do. */
233 LISTBASE_FOREACH (LinkData *, child, &panel_type->children) {
234 PanelType *child_type = static_cast<PanelType *>(child->data);
235 panel_add_instanced(&panel->children, child_type, custom_data);
236 }
237
238 /* Make sure the panel is added to the end of the display-order as well. This is needed for
239 * loading existing files.
240 *
241 * NOTE: We could use special behavior to place it after the panel that starts the list of
242 * instanced panels, but that would add complexity that isn't needed for now. */
243 int max_sortorder = 0;
244 LISTBASE_FOREACH (Panel *, existing_panel, panels) {
245 if (existing_panel->sortorder > max_sortorder) {
246 max_sortorder = existing_panel->sortorder;
247 }
248 }
249 panel->sortorder = max_sortorder + 1;
250
251 BLI_addtail(panels, panel);
252
253 return panel;
254}
255
257 ARegion *region,
258 ListBase *panels,
259 const char *panel_idname,
260 PointerRNA *custom_data)
261{
262 ARegionType *region_type = region->type;
263
264 PanelType *panel_type = static_cast<PanelType *>(
265 BLI_findstring(&region_type->paneltypes, panel_idname, offsetof(PanelType, idname)));
266
267 if (panel_type == nullptr) {
268 printf("Panel type '%s' not found.\n", panel_idname);
269 return nullptr;
270 }
271
272 Panel *new_panel = panel_add_instanced(panels, panel_type, custom_data);
273
274 /* Do this after #panel_add_instatnced so all sub-panels are added. */
276
277 return new_panel;
278}
279
280void UI_list_panel_unique_str(Panel *panel, char *r_name)
281{
282 /* The panel sort-order will be unique for a specific panel type because the instanced
283 * panel list is regenerated for every change in the data order / length. */
285}
286
294static void panel_delete(ARegion *region, ListBase *panels, Panel *panel)
295{
296 /* Recursively delete children. */
297 LISTBASE_FOREACH_MUTABLE (Panel *, child, &panel->children) {
298 panel_delete(region, &panel->children, child);
299 }
300 BLI_freelistN(&panel->children);
301
302 BLI_remlink(panels, panel);
303 BKE_panel_free(panel);
304}
305
307{
308 /* Delete panels with the instanced flag. */
309 LISTBASE_FOREACH_MUTABLE (Panel *, panel, &region->panels) {
310 if ((panel->type != nullptr) && (panel->type->flag & PANEL_TYPE_INSTANCED)) {
311 /* Make sure the panel's handler is removed before deleting it. */
312 if (C != nullptr && panel->activedata != nullptr) {
314 }
315
316 /* Free panel's custom data. */
317 if (panel->runtime->custom_data_ptr != nullptr) {
318 MEM_delete(panel->runtime->custom_data_ptr);
319 }
320
321 /* Free the panel and its sub-panels. */
322 panel_delete(region, &region->panels, panel);
323 }
324 }
325}
326
328 ListBase *data,
329 uiListPanelIDFromDataFunc panel_idname_func)
330{
331 /* Check for nullptr data. */
332 int data_len = 0;
333 Link *data_link = nullptr;
334 if (data == nullptr) {
335 data_len = 0;
336 data_link = nullptr;
337 }
338 else {
339 data_len = BLI_listbase_count(data);
340 data_link = static_cast<Link *>(data->first);
341 }
342
343 int i = 0;
344 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
345 if (panel->type != nullptr && panel->type->flag & PANEL_TYPE_INSTANCED) {
346 /* The panels were reordered by drag and drop. */
347 if (panel->flag & PNL_INSTANCED_LIST_ORDER_CHANGED) {
348 return false;
349 }
350
351 /* We reached the last data item before the last instanced panel. */
352 if (data_link == nullptr) {
353 return false;
354 }
355
356 /* Check if the panel type matches the panel type from the data item. */
357 char panel_idname[MAX_NAME];
358 panel_idname_func(data_link, panel_idname);
359 if (!STREQ(panel_idname, panel->type->idname)) {
360 return false;
361 }
362
363 data_link = data_link->next;
364 i++;
365 }
366 }
367
368 /* If we didn't make it to the last list item, the panel list isn't complete. */
369 if (i != data_len) {
370 return false;
371 }
372
373 return true;
374}
375
376static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *drag_panel)
377{
378 /* Without a type we cannot access the reorder callback. */
379 if (drag_panel->type == nullptr) {
380 return;
381 }
382 /* Don't reorder if this instanced panel doesn't support drag and drop reordering. */
383 if (drag_panel->type->reorder == nullptr) {
384 return;
385 }
386
387 char *context = nullptr;
388 if (!UI_panel_category_is_visible(region)) {
389 context = drag_panel->type->context;
390 }
391
392 /* Find how many instanced panels with this context string. */
393 int list_panels_len = 0;
394 int start_index = -1;
395 LISTBASE_FOREACH (const Panel *, panel, &region->panels) {
396 if (panel->type) {
397 if (panel->type->flag & PANEL_TYPE_INSTANCED) {
398 if (panel_type_context_poll(region, panel->type, context)) {
399 if (panel == drag_panel) {
400 BLI_assert(start_index == -1); /* This panel should only appear once. */
401 start_index = list_panels_len;
402 }
403 list_panels_len++;
404 }
405 }
406 }
407 }
408 BLI_assert(start_index != -1); /* The drag panel should definitely be in the list. */
409
410 /* Sort the matching instanced panels by their display order. */
411 PanelSort *panel_sort = static_cast<PanelSort *>(
412 MEM_callocN(list_panels_len * sizeof(*panel_sort), __func__));
413 PanelSort *sort_index = panel_sort;
414 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
415 if (panel->type) {
416 if (panel->type->flag & PANEL_TYPE_INSTANCED) {
417 if (panel_type_context_poll(region, panel->type, context)) {
418 sort_index->panel = panel;
419 sort_index++;
420 }
421 }
422 }
423 }
424 qsort(panel_sort, list_panels_len, sizeof(*panel_sort), compare_panel);
425
426 /* Find how many of those panels are above this panel. */
427 int move_to_index = 0;
428 for (; move_to_index < list_panels_len; move_to_index++) {
429 if (panel_sort[move_to_index].panel == drag_panel) {
430 break;
431 }
432 }
433
434 MEM_freeN(panel_sort);
435
436 if (move_to_index == start_index) {
437 /* In this case, the reorder was not changed, so don't do any updates or call the callback. */
438 return;
439 }
440
441 /* Set the bit to tell the interface to instanced the list. */
443
444 CTX_store_set(C, drag_panel->runtime->context);
445
446 /* Finally, move this panel's list item to the new index in its list. */
447 drag_panel->type->reorder(C, drag_panel, move_to_index);
448
449 CTX_store_set(C, nullptr);
450}
451
457static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, short *flag_index)
458{
459 const bool open = (flag & (1 << *flag_index));
460 bool changed = (open == UI_panel_is_closed(panel));
461
462 SET_FLAG_FROM_TEST(panel->flag, !open, PNL_CLOSED);
463
464 LISTBASE_FOREACH (Panel *, child, &panel->children) {
465 *flag_index = *flag_index + 1;
466 changed |= panel_set_expand_from_list_data_recursive(child, flag, flag_index);
467 }
468 return changed;
469}
470
476{
477 BLI_assert(panel->type != nullptr);
479 if (panel->type->get_list_data_expand_flag == nullptr) {
480 /* Instanced panel doesn't support loading expansion. */
481 return;
482 }
483
484 const short expand_flag = panel->type->get_list_data_expand_flag(C, panel);
485 short flag_index = 0;
486
487 /* Start panel animation if the open state was changed. */
488 if (panel_set_expand_from_list_data_recursive(panel, expand_flag, &flag_index)) {
490 }
491}
492
497{
498 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
499 if (panel->runtime_flag & PANEL_ACTIVE) {
500 PanelType *panel_type = panel->type;
501 if (panel_type != nullptr && panel->type->flag & PANEL_TYPE_INSTANCED) {
503 }
504 }
505 }
506}
507
511static void get_panel_expand_flag(const Panel *panel, short *flag, short *flag_index)
512{
513 const bool open = !(panel->flag & PNL_CLOSED);
514 SET_FLAG_FROM_TEST(*flag, open, (1 << *flag_index));
515
516 LISTBASE_FOREACH (const Panel *, child, &panel->children) {
517 *flag_index = *flag_index + 1;
518 get_panel_expand_flag(child, flag, flag_index);
519 }
520}
521
530static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *region)
531{
532 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
533 PanelType *panel_type = panel->type;
534 if (panel_type == nullptr) {
535 continue;
536 }
537
538 /* Check for #PANEL_ACTIVE so we only set the expand flag for active panels. */
539 if (panel_type->flag & PANEL_TYPE_INSTANCED && panel->runtime_flag & PANEL_ACTIVE) {
540 short expand_flag;
541 short flag_index = 0;
542 get_panel_expand_flag(panel, &expand_flag, &flag_index);
543 if (panel->type->set_list_data_expand_flag) {
544 panel->type->set_list_data_expand_flag(C, panel, expand_flag);
545 }
546 }
547 }
548}
549
551
552/* -------------------------------------------------------------------- */
555
556static bool panel_custom_pin_to_last_get(const Panel *panel)
557{
558 if (panel->type->pin_to_last_property[0] != '\0') {
560 if (ptr != nullptr && !RNA_pointer_is_null(ptr)) {
562 }
563 }
564
565 return false;
566}
567
568static void panel_custom_pin_to_last_set(const bContext *C, const Panel *panel, const bool value)
569{
570 if (panel->type->pin_to_last_property[0] != '\0') {
572 if (ptr != nullptr && !RNA_pointer_is_null(ptr)) {
575 RNA_property_update(const_cast<bContext *>(C), ptr, prop);
576 }
577 }
578}
579
580static bool panel_custom_data_active_get(const Panel *panel)
581{
582 /* The caller should make sure the panel is active and has a type. */
584 BLI_assert(panel->type != nullptr);
585
586 if (panel->type->active_property[0] != '\0') {
588 if (ptr != nullptr && !RNA_pointer_is_null(ptr)) {
589 return RNA_boolean_get(ptr, panel->type->active_property);
590 }
591 }
592
593 return false;
594}
595
597{
598 /* Since the panel is interacted with, it should be active and have a type. */
600 BLI_assert(panel->type != nullptr);
601
602 if (panel->type->active_property[0] != '\0') {
605 if (ptr != nullptr && !RNA_pointer_is_null(ptr)) {
606 RNA_boolean_set(ptr, panel->type->active_property, true);
607 }
608 }
609}
610
614static void panel_set_flag_recursive(Panel *panel, short flag, bool value)
615{
616 SET_FLAG_FROM_TEST(panel->flag, value, flag);
617
618 LISTBASE_FOREACH (Panel *, child, &panel->children) {
619 panel_set_flag_recursive(child, flag, value);
620 }
621}
622
626static void panel_set_runtime_flag_recursive(Panel *panel, short flag, bool value)
627{
628 SET_FLAG_FROM_TEST(panel->runtime_flag, value, flag);
629
630 LISTBASE_FOREACH (Panel *, sub_panel, &panel->children) {
631 panel_set_runtime_flag_recursive(sub_panel, flag, value);
632 }
633}
634
635static void panels_collapse_all(ARegion *region, const Panel *from_panel)
636{
637 const bool has_category_tabs = UI_panel_category_is_visible(region);
638 const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : nullptr;
639 const PanelType *from_pt = from_panel->type;
640
641 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
642 PanelType *pt = panel->type;
643
644 /* Close panels with headers in the same context. */
645 if (pt && from_pt && !(pt->flag & PANEL_TYPE_NO_HEADER)) {
646 if (!pt->context[0] || !from_pt->context[0] || STREQ(pt->context, from_pt->context)) {
647 if ((panel->flag & PNL_PIN) || !category || !pt->category[0] ||
648 STREQ(pt->category, category))
649 {
650 panel->flag |= PNL_CLOSED;
651 }
652 }
653 }
654 }
655}
656
658 const PanelType *panel_type,
659 const char *context)
660{
661 if (!BLI_listbase_is_empty(&region->panels_category)) {
662 return STREQ(panel_type->category, UI_panel_category_active_get(region, false));
663 }
664
665 if (panel_type->context[0] && STREQ(panel_type->context, context)) {
666 return true;
667 }
668
669 return false;
670}
671
673{
674 const char *idname = pt->idname;
675
676 LISTBASE_FOREACH (Panel *, panel, lb) {
677 if (STREQLEN(panel->panelname, idname, sizeof(panel->panelname))) {
678 return panel;
679 }
680 }
681 return nullptr;
682}
683
685 ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open)
686{
687 Panel *panel_last;
688 const char *drawname = CTX_IFACE_(pt->translation_context, pt->label);
689 const bool newpanel = (panel == nullptr);
690
691 if (newpanel) {
692 panel = BKE_panel_new(pt);
693
695 panel->flag |= PNL_CLOSED;
697 }
698
699 panel->ofsx = 0;
700 panel->ofsy = 0;
701 panel->sizex = 0;
702 panel->sizey = 0;
703 panel->blocksizex = 0;
704 panel->blocksizey = 0;
706
707 BLI_addtail(lb, panel);
708 }
709 else {
710 /* Panel already exists. */
711 panel->type = pt;
712 }
713
714 panel->runtime->block = block;
715
716 UI_panel_drawname_set(panel, drawname);
717
718 /* If a new panel is added, we insert it right after the panel that was last added.
719 * This way new panels are inserted in the right place between versions. */
720 for (panel_last = static_cast<Panel *>(lb->first); panel_last; panel_last = panel_last->next) {
721 if (panel_last->runtime_flag & PANEL_LAST_ADDED) {
722 BLI_remlink(lb, panel);
723 BLI_insertlinkafter(lb, panel_last, panel);
724 break;
725 }
726 }
727
728 if (newpanel) {
729 panel->sortorder = (panel_last) ? panel_last->sortorder + 1 : 0;
730
731 LISTBASE_FOREACH (Panel *, panel_next, lb) {
732 if (panel_next != panel && panel_next->sortorder >= panel->sortorder) {
733 panel_next->sortorder++;
734 }
735 }
736 }
737
738 if (panel_last) {
739 panel_last->runtime_flag &= ~PANEL_LAST_ADDED;
740 }
741
742 /* Assign the new panel to the block. */
743 block->panel = panel;
745 if (region->alignment == RGN_ALIGN_FLOAT) {
747 }
748
749 *r_open = !UI_panel_is_closed(panel);
750
751 return panel;
752}
753
760
762{
763 uiBlock *block = panel->runtime->block;
764
765 /* A button group should always be created in #UI_panel_header_buttons_begin. */
767
768 uiButtonGroup &button_group = block->button_groups.last();
769 button_group.flag &= ~UI_BUTTON_GROUP_LOCK;
770
771 /* Repurpose the first header button group if it is empty, in case the first button added to
772 * the panel doesn't add a new group (if the button is created directly rather than through an
773 * interface layout call). */
774 if (block->button_groups.size() == 1 && button_group.buttons.is_empty()) {
775 button_group.flag &= ~UI_BUTTON_GROUP_PANEL_HEADER;
776 }
777 else {
778 /* Always add a new button group. Although this may result in many empty groups, without it,
779 * new buttons in the panel body not protected with a #ui_block_new_button_group call would
780 * end up in the panel header group. */
782 }
783}
784
785static float panel_region_offset_x_get(const ARegion *region)
786{
787 if (UI_panel_category_is_visible(region)) {
790 }
791 }
792
793 return 0.0f;
794}
795
800static void panel_calculate_size_recursive(ARegion *region, Panel *panel)
801{
802 int width = panel->blocksizex;
803 int height = panel->blocksizey;
804
805 LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
806 if (child_panel->runtime_flag & PANEL_ACTIVE) {
807 panel_calculate_size_recursive(region, child_panel);
808 width = max_ii(width, child_panel->sizex);
809 height += get_panel_real_size_y(child_panel);
810 }
811 }
812
813 /* Update total panel size. */
814 if (panel->runtime_flag & PANEL_NEW_ADDED) {
816 panel->sizex = width;
817 panel->sizey = height;
818 }
819 else {
820 const int old_sizex = panel->sizex, old_sizey = panel->sizey;
821 const int old_region_ofsx = panel->runtime->region_ofsx;
822
823 /* Update width/height if non-zero. */
824 if (width != 0) {
825 panel->sizex = width;
826 }
827 if (height != 0 || !UI_panel_is_closed(panel)) {
828 panel->sizey = height;
829 }
830
831 /* Check if we need to do an animation. */
832 if (panel->sizex != old_sizex || panel->sizey != old_sizey) {
834 panel->ofsy += old_sizey - panel->sizey;
835 }
836
838 if (old_region_ofsx != panel->runtime->region_ofsx) {
840 }
841 }
842}
843
844void UI_panel_end(Panel *panel, int width, int height)
845{
846 /* Store the size of the buttons layout in the panel. The actual panel size
847 * (including sub-panels) is calculated in #UI_panels_end. */
848 panel->blocksizex = width;
849 panel->blocksizey = height;
850}
851
853{
854 MEM_SAFE_FREE(panel->drawname);
855 panel->drawname = BLI_strdupn(name.data(), name.size());
856}
857
859{
860 const uiStyle *style = UI_style_get_dpi();
861
862 /* Compute bounds and offset. */
864
865 const int ofsy = block->panel->sizey - style->panelspace;
866
867 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
868 but->rect.ymin += ofsy;
869 but->rect.ymax += ofsy;
870 }
871
872 block->rect.xmax = block->panel->sizex;
873 block->rect.ymax = block->panel->sizey;
874 block->rect.xmin = block->rect.ymin = 0.0;
875}
876
881
882static void panel_matches_search_filter_recursive(const Panel *panel, bool *filter_matches)
883{
884 *filter_matches |= bool(panel->runtime_flag & PANEL_SEARCH_FILTER_MATCH);
885
886 /* If the panel has no match we need to make sure that its children are too. */
887 if (!*filter_matches) {
888 LISTBASE_FOREACH (const Panel *, child_panel, &panel->children) {
889 panel_matches_search_filter_recursive(child_panel, filter_matches);
890 }
891 }
892}
893
895{
896 bool search_filter_matches = false;
897 panel_matches_search_filter_recursive(panel, &search_filter_matches);
898 return search_filter_matches;
899}
900
905 Panel *panel,
906 const bool use_search_closed)
907{
908 /* This has to run on inactive panels that may not have a type,
909 * but we can prevent running on header-less panels in some cases. */
910 if (panel->type == nullptr || !(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
912 }
913
914 LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
915 /* Don't check if the sub-panel is active, otherwise the
916 * expansion won't be reset when the parent is closed. */
917 panel_set_expansion_from_search_filter_recursive(C, child_panel, use_search_closed);
918 }
919}
920
925 ARegion *region,
926 const bool use_search_closed)
927{
928 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
929 /* Don't check if the panel is active, otherwise the expansion won't
930 * be correct when switching back to tab after exiting search. */
931 panel_set_expansion_from_search_filter_recursive(C, panel, use_search_closed);
932 }
934}
935
940static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *parent_panel)
941{
942 uiBlock *block = panel->runtime->block;
943 BLI_assert(block != nullptr);
944 BLI_assert(block->active);
945 if (parent_panel != nullptr && UI_panel_is_closed(parent_panel)) {
946 /* The parent panel is closed, so this panel can be completely removed. */
947 UI_block_set_search_only(block, true);
948 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
949 but->flag |= UI_HIDDEN;
950 }
951 }
952 else if (UI_panel_is_closed(panel)) {
953 /* If sub-panels have no search results but the parent panel does, then the parent panel open
954 * and the sub-panels will close. In that case there must be a way to hide the buttons in the
955 * panel but keep the header buttons. */
956 for (const uiButtonGroup &button_group : block->button_groups) {
957 if (button_group.flag & UI_BUTTON_GROUP_PANEL_HEADER) {
958 continue;
959 }
960 for (uiBut *but : button_group.buttons) {
961 but->flag |= UI_HIDDEN;
962 }
963 }
964 }
965
966 LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
967 if (child_panel->runtime_flag & PANEL_ACTIVE) {
968 BLI_assert(child_panel->runtime->block != nullptr);
970 }
971 }
972}
973
975{
976 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
977 if (panel->runtime_flag & PANEL_ACTIVE) {
978 BLI_assert(panel->runtime->block != nullptr);
980 }
981 }
982}
983
984bool UI_panel_is_closed(const Panel *panel)
985{
986 /* Header-less panels can never be closed, otherwise they could disappear. */
987 if (panel->type && panel->type->flag & PANEL_TYPE_NO_HEADER) {
988 return false;
989 }
990
992 return !UI_panel_matches_search_filter(panel);
993 }
994
995 return panel->flag & PNL_CLOSED;
996}
997
998bool UI_panel_is_active(const Panel *panel)
999{
1000 return panel->runtime_flag & PANEL_ACTIVE;
1001}
1002
1004
1005/* -------------------------------------------------------------------- */
1008
1009void UI_panels_draw(const bContext *C, ARegion *region)
1010{
1011 /* Draw in reverse order, because #uiBlocks are added in reverse order
1012 * and we need child panels to draw on top. */
1013 LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->uiblocks) {
1014 if (block->active && block->panel && !UI_panel_is_dragging(block->panel) &&
1016 {
1017 UI_block_draw(C, block);
1018 }
1019 }
1020
1021 LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->uiblocks) {
1022 if (block->active && block->panel && UI_panel_is_dragging(block->panel) &&
1024 {
1025 UI_block_draw(C, block);
1026 }
1027 }
1028}
1029
1030#define PNL_ICON UI_UNIT_X /* Could be UI_UNIT_Y too. */
1031
1032void UI_panel_label_offset(const uiBlock *block, int *r_x, int *r_y)
1033{
1034 Panel *panel = block->panel;
1035 const bool is_subpanel = (panel->type && panel->type->parent);
1036
1037 *r_x = UI_UNIT_X * 1.0f;
1038 *r_y = UI_UNIT_Y * 1.5f;
1039
1040 if (is_subpanel) {
1041 *r_x += (0.7f * UI_UNIT_X);
1042 }
1043}
1044
1045static void panel_title_color_get(const Panel *panel,
1046 const bool show_background,
1047 const bool region_search_filter_active,
1048 uchar r_color[4])
1049{
1050 if (!show_background) {
1051 /* Use menu colors for floating panels. */
1052 bTheme *btheme = UI_GetTheme();
1053 const uiWidgetColors *wcol = &btheme->tui.wcol_menu_back;
1054 copy_v4_v4_uchar(r_color, (const uchar *)wcol->text);
1055 return;
1056 }
1057
1058 const bool search_match = UI_panel_matches_search_filter(panel);
1059
1061 if (region_search_filter_active && !search_match) {
1062 r_color[0] *= 0.5;
1063 r_color[1] *= 0.5;
1064 r_color[2] *= 0.5;
1065 }
1066}
1067
1068static void panel_draw_highlight_border(const Panel *panel,
1069 const rcti *rect,
1070 const rcti *header_rect)
1071{
1072 const bool is_subpanel = panel->type->parent != nullptr;
1073 if (is_subpanel) {
1074 return;
1075 }
1076
1077 const bTheme *btheme = UI_GetTheme();
1078 const float aspect = panel->runtime->block->aspect;
1079 const float radius = (btheme->tui.panel_roundness * U.widget_unit * 0.5f) / aspect;
1081
1082 rctf box_rect;
1083 box_rect.xmin = rect->xmin;
1084 box_rect.xmax = rect->xmax;
1085 box_rect.ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin;
1086 box_rect.ymax = header_rect->ymax;
1087
1088 float color[4];
1090 UI_draw_roundbox_4fv(&box_rect, false, radius, color);
1091}
1092
1093static void panel_draw_aligned_widgets(const uiStyle *style,
1094 const Panel *panel,
1095 const rcti *header_rect,
1096 const float aspect,
1097 const bool show_pin,
1098 const bool show_background,
1099 const bool region_search_filter_active)
1100{
1101 const bool is_subpanel = panel->type->parent != nullptr;
1102 const uiFontStyle *fontstyle = (is_subpanel) ? &style->widget : &style->paneltitle;
1103
1104 const int header_height = BLI_rcti_size_y(header_rect);
1105 const int scaled_unit = round_fl_to_int(UI_UNIT_X / aspect);
1106
1107 /* Offset triangle and text to the right for sub-panels. */
1108 rcti widget_rect;
1109 widget_rect.xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0);
1110 widget_rect.xmax = header_rect->xmax;
1111 widget_rect.ymin = header_rect->ymin;
1112 widget_rect.ymax = header_rect->ymax;
1113
1114 uchar title_color[4];
1115 panel_title_color_get(panel, show_background, region_search_filter_active, title_color);
1116 title_color[3] = 255;
1117
1118 /* Draw collapse icon. */
1119 {
1120 const float size_y = BLI_rcti_size_y(&widget_rect);
1122 UI_icon_draw_ex(widget_rect.xmin + size_y * 0.2f,
1123 widget_rect.ymin + size_y * (UI_panel_is_closed(panel) ? 0.17f : 0.14f),
1124 UI_panel_is_closed(panel) ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT,
1125 aspect * UI_INV_SCALE_FAC,
1126 0.8f,
1127 0.0f,
1128 title_color,
1129 false,
1132 }
1133
1134 /* Draw text label. */
1135 if (panel->drawname && panel->drawname[0] != '\0') {
1136 rcti title_rect;
1137 title_rect.xmin = widget_rect.xmin + (panel->labelofs / aspect) + scaled_unit * 1.1f;
1138 title_rect.xmax = widget_rect.xmax;
1139 title_rect.ymin = widget_rect.ymin - 2.0f / aspect;
1140 title_rect.ymax = widget_rect.ymax;
1141
1143 params.align = UI_STYLE_TEXT_LEFT;
1145 fontstyle, &title_rect, panel->drawname, strlen(panel->drawname), title_color, &params);
1146 }
1147
1148 /* Draw the pin icon. */
1149 if (show_pin && (panel->flag & PNL_PIN)) {
1151 UI_icon_draw_ex(widget_rect.xmax - scaled_unit * 2.2f,
1152 widget_rect.ymin + 5.0f / aspect,
1153 ICON_PINNED,
1154 aspect * UI_INV_SCALE_FAC,
1155 1.0f,
1156 0.0f,
1157 title_color,
1158 false,
1161 }
1162
1163 /* Draw drag widget. */
1164 if (!is_subpanel && show_background) {
1165 const int drag_widget_size = header_height * 0.7f;
1166 const int col_tint = 84;
1167 float color_high[4], color_dark[4];
1168 UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, color_high);
1169 UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, color_dark);
1170 if (panel_custom_pin_to_last_get(panel)) {
1172 UI_icon_draw_ex(widget_rect.xmax - scaled_unit * 1.15,
1173 widget_rect.ymin + (header_height - drag_widget_size) * 0.5f,
1174 ICON_PINNED,
1175 aspect * UI_INV_SCALE_FAC,
1176 1.0f,
1177 0.0f,
1178 title_color,
1179 false,
1182 }
1183 else {
1185 /* The magic numbers here center the widget vertically and offset it to the left.
1186 * Currently this depends on the height of the header, although it could be independent. */
1187 GPU_matrix_translate_2f(widget_rect.xmax - scaled_unit * 1.15,
1188 widget_rect.ymin + (header_height - drag_widget_size) * 0.5f);
1189 blender::gpu::Batch *batch = GPU_batch_preset_panel_drag_widget(
1190 U.pixelsize, color_high, color_dark, drag_widget_size);
1194 }
1195 }
1196}
1197
1199 const Panel *panel,
1200 const float radius,
1201 float subpanel_backcolor[4])
1202{
1203 /* Draw backdrops for layout panels. */
1204 const float aspect = ui_block_is_popup_any(panel->runtime->block) ?
1205 panel->runtime->block->aspect :
1206 1.0f;
1207
1208 for (const LayoutPanelBody &body : panel->runtime->layout_panels.bodies) {
1209
1210 rctf panel_blockspace = panel->runtime->block->rect;
1211 panel_blockspace.ymax = panel->runtime->block->rect.ymax + body.end_y;
1212 panel_blockspace.ymin = panel->runtime->block->rect.ymax + body.start_y;
1213
1214 if (panel_blockspace.ymax <= panel->runtime->block->rect.ymin) {
1215 /* Layout panels no longer fits in block rectangle, stop drawing backdrops. */
1216 break;
1217 }
1218 if (panel_blockspace.ymin >= panel->runtime->block->rect.ymax) {
1219 /* Skip layout panels that scrolled to the top of the block rectangle. */
1220 continue;
1221 }
1222 /* If the layout panel is at the end of the root panel, it's bottom corners are rounded. */
1223 const bool is_main_panel_end = panel_blockspace.ymin - panel->runtime->block->rect.ymin <
1224 (10.0f / aspect);
1225 if (is_main_panel_end) {
1226 panel_blockspace.ymin = panel->runtime->block->rect.ymin;
1228 }
1229 else {
1231 }
1232 panel_blockspace.ymax = std::min(panel_blockspace.ymax, panel->runtime->block->rect.ymax);
1233
1234 rcti panel_pixelspace = ui_to_pixelrect(region, panel->runtime->block, &panel_blockspace);
1235 rctf panel_pixelspacef;
1236 BLI_rctf_rcti_copy(&panel_pixelspacef, &panel_pixelspace);
1237 UI_draw_roundbox_4fv(&panel_pixelspacef, true, radius, subpanel_backcolor);
1238 }
1239}
1240
1241static void panel_draw_aligned_backdrop(const ARegion *region,
1242 const Panel *panel,
1243 const rcti *rect,
1244 const rcti *header_rect)
1245{
1246 const bool is_open = !UI_panel_is_closed(panel);
1247 const bool is_subpanel = panel->type->parent != nullptr;
1248 const bool has_header = (panel->type->flag & PANEL_TYPE_NO_HEADER) == 0;
1249
1250 if (is_subpanel && !is_open) {
1251 return;
1252 }
1253
1254 const bTheme *btheme = UI_GetTheme();
1255 const float aspect = panel->runtime->block->aspect;
1256 const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f / aspect;
1257
1260
1261 /* Panel backdrop. */
1262 if (is_open || !has_header) {
1263 float panel_backcolor[4];
1265 if (!has_header) {
1266 UI_GetThemeColor4fv(TH_BACK, panel_backcolor);
1267 }
1268 else {
1269 UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor);
1270 }
1271
1272 rctf box_rect;
1273 box_rect.xmin = rect->xmin;
1274 box_rect.xmax = rect->xmax;
1275 box_rect.ymin = rect->ymin;
1276 box_rect.ymax = rect->ymax;
1277 UI_draw_roundbox_4fv(&box_rect, true, radius, panel_backcolor);
1278
1279 float subpanel_backcolor[4];
1280 UI_GetThemeColor4fv(TH_PANEL_SUB_BACK, subpanel_backcolor);
1281 ui_draw_layout_panels_backdrop(region, panel, radius, subpanel_backcolor);
1282 }
1283
1284 /* Panel header backdrops for non sub-panels. */
1285 if (!is_subpanel && has_header) {
1286 float panel_headercolor[4];
1288 panel_headercolor);
1290
1291 /* Change the width a little bit to line up with the sides. */
1292 rctf box_rect;
1293 box_rect.xmin = rect->xmin;
1294 box_rect.xmax = rect->xmax;
1295 box_rect.ymin = header_rect->ymin;
1296 box_rect.ymax = header_rect->ymax;
1297 UI_draw_roundbox_4fv(&box_rect, true, radius, panel_headercolor);
1298 }
1299
1302}
1303
1305 const uiStyle *style,
1306 const uiBlock *block,
1307 const rcti *rect,
1308 const bool show_pin,
1309 const bool show_background,
1310 const bool region_search_filter_active)
1311{
1312 const Panel *panel = block->panel;
1313
1314 /* Add 0.001f to prevent flicker from float inaccuracy. */
1315 const rcti header_rect = {
1316 rect->xmin,
1317 rect->xmax,
1318 rect->ymax,
1319 rect->ymax + int(floor(PNL_HEADER / block->aspect + 0.001f)),
1320 };
1321
1322 if (show_background || (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1323 panel_draw_aligned_backdrop(region, panel, rect, &header_rect);
1324 }
1325
1326 /* Draw the widgets and text in the panel header. */
1327 if (!(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1329 panel,
1330 &header_rect,
1331 block->aspect,
1332 show_pin,
1333 show_background,
1334 region_search_filter_active);
1335 }
1336
1337 if (panel_custom_data_active_get(panel)) {
1338 panel_draw_highlight_border(panel, rect, &header_rect);
1339 }
1340}
1341
1342bool UI_panel_should_show_background(const ARegion *region, const PanelType *panel_type)
1343{
1344 if (region->alignment == RGN_ALIGN_FLOAT) {
1345 return false;
1346 }
1347
1348 if (panel_type && panel_type->flag & PANEL_TYPE_NO_HEADER) {
1349 if (region->regiontype == RGN_TYPE_TOOLS) {
1350 /* We never want a background around active tools. */
1351 return false;
1352 }
1353 /* Without a header there is no background except for region overlap. */
1354 return region->overlap != 0;
1355 }
1356
1357 return true;
1358}
1359
1361
1362/* -------------------------------------------------------------------- */
1365
1366#define TABS_PADDING_BETWEEN_FACTOR 4.0f
1367#define TABS_PADDING_TEXT_FACTOR 6.0f
1368
1369void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
1370{
1371 // #define USE_FLAT_INACTIVE
1373 View2D *v2d = &region->v2d;
1374 const uiStyle *style = UI_style_get();
1375 const uiFontStyle *fstyle = &style->widget;
1376 const int fontid = fstyle->uifont_id;
1377 float fstyle_points = fstyle->points;
1378 const float aspect = BLI_listbase_is_empty(&region->uiblocks) ?
1379 1.0f :
1380 ((uiBlock *)region->uiblocks.first)->aspect;
1381 const float zoom = 1.0f / aspect;
1382 const int px = U.pixelsize;
1383 const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom);
1384 const float dpi_fac = UI_SCALE_FAC;
1385 /* Padding of tabs around text. */
1386 const int tab_v_pad_text = round_fl_to_int(TABS_PADDING_TEXT_FACTOR * dpi_fac * zoom) + 2 * px;
1387 /* Padding between tabs. */
1388 const int tab_v_pad = round_fl_to_int(TABS_PADDING_BETWEEN_FACTOR * dpi_fac * zoom);
1389 bTheme *btheme = UI_GetTheme();
1390 const float tab_curve_radius = btheme->tui.wcol_tab.roundness * U.widget_unit * zoom;
1393 bool is_alpha;
1394#ifdef USE_FLAT_INACTIVE
1395 bool is_active_prev = false;
1396#endif
1397 /* Same for all tabs. */
1398 /* Intentionally don't scale by 'px'. */
1399 const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width);
1400 const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3);
1401 int y_ofs = tab_v_pad;
1402
1403 /* Primary theme colors. */
1404 uchar theme_col_back[4];
1405 uchar theme_col_text[3];
1406 uchar theme_col_text_hi[3];
1407
1408 /* Tab colors. */
1409 uchar theme_col_tab_bg[4];
1410 float theme_col_tab_active[4];
1411 float theme_col_tab_inactive[4];
1412 float theme_col_tab_outline[4];
1413
1414 UI_GetThemeColor4ubv(TH_BACK, theme_col_back);
1415 UI_GetThemeColor3ubv(TH_TEXT, theme_col_text);
1416 UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi);
1417
1418 UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg);
1419 UI_GetThemeColor4fv(TH_TAB_ACTIVE, theme_col_tab_active);
1420 UI_GetThemeColor4fv(TH_TAB_INACTIVE, theme_col_tab_inactive);
1421 UI_GetThemeColor4fv(TH_TAB_OUTLINE, theme_col_tab_outline);
1422
1423 is_alpha = (region->overlap && (theme_col_back[3] != 255));
1424
1425 BLF_enable(fontid, BLF_ROTATION);
1426 BLF_rotation(fontid, is_left ? M_PI_2 : -M_PI_2);
1427 ui_fontscale(&fstyle_points, aspect);
1428 BLF_size(fontid, fstyle_points * UI_SCALE_FAC);
1429
1430 /* Check the region type supports categories to avoid an assert
1431 * for showing 3D view panels in the properties space. */
1432 if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) {
1434 }
1435
1436 /* Calculate tab rectangle for each panel. */
1437 LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1438 rcti *rct = &pc_dyn->rect;
1439 const char *category_id = pc_dyn->idname;
1440 const char *category_id_draw = IFACE_(category_id);
1441 const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
1442
1443 rct->xmin = rct_xmin;
1444 rct->xmax = rct_xmax;
1445
1446 rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2));
1447 rct->ymax = v2d->mask.ymax - (y_ofs);
1448
1449 y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2);
1450 }
1451
1452 const int max_scroll = max_ii(y_ofs - BLI_rcti_size_y(&v2d->mask), 0);
1453 const int scroll = clamp_i(region->category_scroll, 0, max_scroll);
1454 region->category_scroll = scroll;
1455 LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1456 rcti *rct = &pc_dyn->rect;
1457 rct->ymin += scroll;
1458 rct->ymax += scroll;
1459 }
1460
1461 /* Begin drawing. */
1462 GPU_line_smooth(true);
1463
1467
1468 /* Draw the background. */
1469 if (is_alpha) {
1471 immUniformColor4ubv(theme_col_tab_bg);
1472 }
1473 else {
1474 immUniformColor3ubv(theme_col_tab_bg);
1475 }
1476
1477 if (is_left) {
1478 immRecti(
1479 pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax);
1480 }
1481 else {
1482 immRecti(
1483 pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax);
1484 }
1485
1486 if (is_alpha) {
1488 }
1489
1491
1492 LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1493 const rcti *rct = &pc_dyn->rect;
1494 if (rct->ymin > v2d->mask.ymax) {
1495 /* Scrolled outside the top of the view, check the next tab. */
1496 continue;
1497 }
1498 if (rct->ymax < v2d->mask.ymin) {
1499 /* Scrolled past visible bounds, no need to draw other tabs. */
1500 break;
1501 }
1502 const char *category_id = pc_dyn->idname;
1503 const char *category_id_draw = IFACE_(category_id);
1504 size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
1505 const bool is_active = STREQ(category_id, category_id_active);
1506
1508
1509#ifdef USE_FLAT_INACTIVE
1510 /* Draw line between inactive tabs. */
1511 if (is_active == false && is_active_prev == false && pc_dyn->prev) {
1515 immUniformColor3fvAlpha(theme_col_tab_outline, 0.3f);
1516 immRecti(pos,
1517 is_left ? v2d->mask.xmin + (category_tabs_width / 5) :
1518 v2d->mask.xmax - (category_tabs_width / 5),
1519 rct->ymax + px,
1520 is_left ? (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5) :
1521 (v2d->mask.xmax - category_tabs_width) + (category_tabs_width / 5),
1522 rct->ymax + (px * 3));
1524 }
1525
1526 is_active_prev = is_active;
1527
1528 if (is_active)
1529#endif
1530 {
1531 /* Draw filled rectangle and outline for tab. */
1533 rctf box_rect;
1534 box_rect.xmin = rct->xmin;
1535 box_rect.xmax = rct->xmax;
1536 box_rect.ymin = rct->ymin;
1537 box_rect.ymax = rct->ymax;
1538
1539 UI_draw_roundbox_4fv(&box_rect,
1540 true,
1541 tab_curve_radius,
1542 is_active ? theme_col_tab_active : theme_col_tab_inactive);
1543 UI_draw_roundbox_4fv(&box_rect, false, tab_curve_radius, theme_col_tab_outline);
1544
1545 /* Disguise the outline on one side to join the tab to the panel. */
1549
1550 immUniformColor4fv(is_active ? theme_col_tab_active : theme_col_tab_inactive);
1551 immRecti(pos,
1552 is_left ? rct->xmax - px : rct->xmin,
1553 rct->ymin + px,
1554 is_left ? rct->xmax : rct->xmin + px,
1555 rct->ymax - px);
1557 }
1558
1559 /* Tab titles. */
1560
1561 /* Offset toward the middle of the rect. */
1562 const int text_v_ofs = (rct_xmax - rct_xmin) * 0.5f;
1563 /* Offset down as the font size increases. */
1564 const int text_size_offset = int(fstyle_points * UI_SCALE_FAC * 0.35f);
1565
1566 BLF_position(fontid,
1567 is_left ? rct->xmax - text_v_ofs + text_size_offset :
1568 rct->xmin + text_v_ofs - text_size_offset,
1569 is_left ? rct->ymin + tab_v_pad_text : rct->ymax - tab_v_pad_text,
1570 0.0f);
1571 BLF_color3ubv(fontid, is_active ? theme_col_text_hi : theme_col_text);
1572 BLF_draw(fontid, category_id_draw, category_draw_len);
1573
1575
1576 /* Not essential, but allows events to be handled right up to the region edge (#38171). */
1577 if (is_left) {
1578 pc_dyn->rect.xmin = v2d->mask.xmin;
1579 }
1580 else {
1581 pc_dyn->rect.xmax = v2d->mask.xmax;
1582 }
1583 }
1584
1585 GPU_line_smooth(false);
1586
1587 BLF_disable(fontid, BLF_ROTATION);
1588}
1589
1590#undef TABS_PADDING_BETWEEN_FACTOR
1591#undef TABS_PADDING_TEXT_FACTOR
1592
1594
1595/* -------------------------------------------------------------------- */
1598
1599static int get_panel_size_y(const Panel *panel)
1600{
1601 if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1602 return panel->sizey;
1603 }
1604
1605 return PNL_HEADER + panel->sizey;
1606}
1607
1608static int get_panel_real_size_y(const Panel *panel)
1609{
1610 const int sizey = UI_panel_is_closed(panel) ? 0 : panel->sizey;
1611
1612 if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1613 return sizey;
1614 }
1615
1616 return PNL_HEADER + sizey;
1617}
1618
1619int UI_panel_size_y(const Panel *panel)
1620{
1621 return get_panel_real_size_y(panel);
1622}
1623
1628static int get_panel_real_ofsy(Panel *panel)
1629{
1630 if (UI_panel_is_closed(panel)) {
1631 return panel->ofsy + panel->sizey;
1632 }
1633 return panel->ofsy;
1634}
1635
1636bool UI_panel_is_dragging(const Panel *panel)
1637{
1638 return panel->runtime_flag & PANEL_IS_DRAG_DROP;
1639}
1640
1648
1649static int find_highest_panel(const void *a, const void *b)
1650{
1651 const Panel *panel_a = ((PanelSort *)a)->panel;
1652 const Panel *panel_b = ((PanelSort *)b)->panel;
1653
1654 /* Stick uppermost header-less panels to the top of the region -
1655 * prevent them from being sorted (multiple header-less panels have to be sorted though). */
1656 if (panel_a->type->flag & PANEL_TYPE_NO_HEADER && panel_b->type->flag & PANEL_TYPE_NO_HEADER) {
1657 /* Pass the no-header checks and check for `ofsy` and #Panel.sortorder below. */
1658 }
1659 else if (panel_a->type->flag & PANEL_TYPE_NO_HEADER) {
1660 return -1;
1661 }
1662 else if (panel_b->type->flag & PANEL_TYPE_NO_HEADER) {
1663 return 1;
1664 }
1665
1666 const bool pin_last_a = panel_custom_pin_to_last_get(panel_a);
1667 const bool pin_last_b = panel_custom_pin_to_last_get(panel_b);
1668 if (pin_last_a && !pin_last_b) {
1669 return 1;
1670 }
1671 if (!pin_last_a && pin_last_b) {
1672 return -1;
1673 }
1674
1675 if (panel_a->ofsy + panel_a->sizey < panel_b->ofsy + panel_b->sizey) {
1676 return 1;
1677 }
1678 if (panel_a->ofsy + panel_a->sizey > panel_b->ofsy + panel_b->sizey) {
1679 return -1;
1680 }
1681 if (panel_a->sortorder > panel_b->sortorder) {
1682 return 1;
1683 }
1684 if (panel_a->sortorder < panel_b->sortorder) {
1685 return -1;
1686 }
1687
1688 return 0;
1689}
1690
1691static int compare_panel(const void *a, const void *b)
1692{
1693 const Panel *panel_a = ((PanelSort *)a)->panel;
1694 const Panel *panel_b = ((PanelSort *)b)->panel;
1695
1696 if (panel_a->sortorder > panel_b->sortorder) {
1697 return 1;
1698 }
1699 if (panel_a->sortorder < panel_b->sortorder) {
1700 return -1;
1701 }
1702
1703 return 0;
1704}
1705
1706static void align_sub_panels(Panel *panel)
1707{
1708 /* Position sub panels. */
1709 int ofsy = panel->ofsy + panel->sizey - panel->blocksizey;
1710
1711 LISTBASE_FOREACH (Panel *, pachild, &panel->children) {
1712 if (pachild->runtime_flag & PANEL_ACTIVE) {
1713 pachild->ofsx = panel->ofsx;
1714 pachild->ofsy = ofsy - get_panel_size_y(pachild);
1715 ofsy -= get_panel_real_size_y(pachild);
1716
1717 if (pachild->children.first) {
1718 align_sub_panels(pachild);
1719 }
1720 }
1721 }
1722}
1723
1727static bool uiAlignPanelStep(ARegion *region, const float factor, const bool drag)
1728{
1729 /* Count active panels. */
1730 int active_panels_len = 0;
1731 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1732 if (panel->runtime_flag & PANEL_ACTIVE) {
1733 /* These panels should have types since they are currently displayed to the user. */
1734 BLI_assert(panel->type != nullptr);
1735 active_panels_len++;
1736 }
1737 }
1738 if (active_panels_len == 0) {
1739 return false;
1740 }
1741
1742 /* Sort panels. */
1743 PanelSort *panel_sort = static_cast<PanelSort *>(
1744 MEM_mallocN(sizeof(PanelSort) * active_panels_len, __func__));
1745 {
1746 PanelSort *ps = panel_sort;
1747 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1748 if (panel->runtime_flag & PANEL_ACTIVE) {
1749 ps->panel = panel;
1750 ps++;
1751 }
1752 }
1753 }
1754
1755 if (drag) {
1756 /* While dragging, sort based on location and update #Panel.sortorder. */
1757 qsort(panel_sort, active_panels_len, sizeof(PanelSort), find_highest_panel);
1758 for (int i = 0; i < active_panels_len; i++) {
1759 panel_sort[i].panel->sortorder = i;
1760 }
1761 }
1762 else {
1763 /* Otherwise use #Panel.sortorder. */
1764 qsort(panel_sort, active_panels_len, sizeof(PanelSort), compare_panel);
1765 }
1766
1767 /* X offset. */
1768 const int region_offset_x = panel_region_offset_x_get(region);
1769 for (int i = 0; i < active_panels_len; i++) {
1770 PanelSort *ps = &panel_sort[i];
1771 const bool show_background = UI_panel_should_show_background(region, ps->panel->type);
1772 ps->panel->runtime->region_ofsx = region_offset_x;
1773 ps->new_offset_x = region_offset_x + (show_background ? UI_PANEL_MARGIN_X : 0);
1774 }
1775
1776 /* Y offset. */
1777 for (int i = 0, y = 0; i < active_panels_len; i++) {
1778 PanelSort *ps = &panel_sort[i];
1779 const bool show_background = UI_panel_should_show_background(region, ps->panel->type);
1780
1782
1783 /* Separate panel boxes a bit further (if they are drawn). */
1784 if (show_background) {
1786 }
1787 ps->new_offset_y = y;
1788 /* The header still draws offset by the size of closed panels, so apply the offset here. */
1789 if (UI_panel_is_closed(ps->panel)) {
1790 panel_sort[i].new_offset_y -= ps->panel->sizey;
1791 }
1792 }
1793
1794 /* Interpolate based on the input factor. */
1795 bool changed = false;
1796 for (int i = 0; i < active_panels_len; i++) {
1797 PanelSort *ps = &panel_sort[i];
1798 if (ps->panel->flag & PNL_SELECT) {
1799 continue;
1800 }
1801
1802 if (ps->new_offset_x != ps->panel->ofsx) {
1803 const float x = interpf(float(ps->new_offset_x), float(ps->panel->ofsx), factor);
1804 ps->panel->ofsx = round_fl_to_int(x);
1805 changed = true;
1806 }
1807 if (ps->new_offset_y != ps->panel->ofsy) {
1808 const float y = interpf(float(ps->new_offset_y), float(ps->panel->ofsy), factor);
1809 ps->panel->ofsy = round_fl_to_int(y);
1810 changed = true;
1811 }
1812 }
1813
1814 /* Set locations for tabbed and sub panels. */
1815 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1816 if (panel->runtime_flag & PANEL_ACTIVE) {
1817 if (panel->children.first) {
1818 align_sub_panels(panel);
1819 }
1820 }
1821 }
1822
1823 MEM_freeN(panel_sort);
1824
1825 return changed;
1826}
1827
1828static void ui_panels_size(ARegion *region, int *r_x, int *r_y)
1829{
1830 int sizex = 0;
1831 int sizey = 0;
1832 bool has_panel_with_background = false;
1833
1834 /* Compute size taken up by panels, for setting in view2d. */
1835 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1836 if (panel->runtime_flag & PANEL_ACTIVE) {
1837 const int pa_sizex = panel->ofsx + panel->sizex;
1838 const int pa_sizey = get_panel_real_ofsy(panel);
1839
1840 sizex = max_ii(sizex, pa_sizex);
1841 sizey = min_ii(sizey, pa_sizey);
1842 if (UI_panel_should_show_background(region, panel->type)) {
1843 has_panel_with_background = true;
1844 }
1845 }
1846 }
1847
1848 if (sizex == 0) {
1849 sizex = UI_PANEL_WIDTH;
1850 }
1851 if (sizey == 0) {
1852 sizey = -UI_PANEL_WIDTH;
1853 }
1854 /* Extra margin after the list so the view scrolls a few pixels further than the panel border.
1855 * Also makes the bottom match the top margin. */
1856 if (has_panel_with_background) {
1857 sizey -= UI_PANEL_MARGIN_Y;
1858 }
1859
1860 *r_x = sizex;
1861 *r_y = sizey;
1862}
1863
1864static void ui_do_animate(bContext *C, Panel *panel)
1865{
1866 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
1867 ARegion *region = CTX_wm_region(C);
1868
1869 float fac = (BLI_time_now_seconds() - data->starttime) / ANIMATION_TIME;
1870 fac = min_ff(sqrtf(fac), 1.0f);
1871
1872 if (uiAlignPanelStep(region, fac, false)) {
1873 ED_region_tag_redraw(region);
1874 }
1875 else {
1876 if (UI_panel_is_dragging(panel)) {
1877 /* NOTE: doing this in #panel_activate_state would require
1878 * removing `const` for context in many other places. */
1879 reorder_instanced_panel_list(C, region, panel);
1880 }
1881
1883 }
1884}
1885
1887{
1888 LISTBASE_FOREACH (Panel *, panel, lb) {
1889 /* Flags to copy over to the next layout pass. */
1890 const short flag_copy = PANEL_USE_CLOSED_FROM_SEARCH | PANEL_IS_DRAG_DROP;
1891
1892 const bool was_active = panel->runtime_flag & PANEL_ACTIVE;
1893 const bool was_closed = UI_panel_is_closed(panel);
1894 panel->runtime_flag &= flag_copy;
1895 SET_FLAG_FROM_TEST(panel->runtime_flag, was_active, PANEL_WAS_ACTIVE);
1896 SET_FLAG_FROM_TEST(panel->runtime_flag, was_closed, PANEL_WAS_CLOSED);
1897
1898 panels_layout_begin_clear_flags(&panel->children);
1899 }
1900}
1901
1902void UI_panels_begin(const bContext * /*C*/, ARegion *region)
1903{
1904 /* Set all panels as inactive, so that at the end we know which ones were used. Also
1905 * clear other flags so we know later that their values were set for the current redraw. */
1907}
1908
1909void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
1910{
1911 ScrArea *area = CTX_wm_area(C);
1912
1914
1915 const bool region_search_filter_active = region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE;
1916
1917 if (properties_space_needs_realign(area, region)) {
1918 region_panels_set_expansion_from_search_filter(C, region, region_search_filter_active);
1919 }
1920 else if (region->flag & RGN_FLAG_SEARCH_FILTER_UPDATE) {
1921 region_panels_set_expansion_from_search_filter(C, region, region_search_filter_active);
1922 }
1923
1924 if (region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE) {
1925 /* Clean up the extra panels and buttons created for searching. */
1927 }
1928
1929 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1930 if (panel->runtime_flag & PANEL_ACTIVE) {
1931 BLI_assert(panel->runtime->block != nullptr);
1932 panel_calculate_size_recursive(region, panel);
1933 }
1934 }
1935
1936 /* Offset contents. */
1937 LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
1938 if (block->active && block->panel) {
1939 ui_offset_panel_block(block);
1940
1941 /* Update bounds for all "views" in this block. Usually this is done in #UI_block_end(), but
1942 * that wouldn't work because of the offset applied above. */
1944 }
1945 }
1946
1947 /* Re-align, possibly with animation. */
1948 Panel *panel;
1949 if (panels_need_realign(area, region, &panel)) {
1950 if (panel) {
1952 }
1953 else {
1954 uiAlignPanelStep(region, 1.0, false);
1955 }
1956 }
1957
1958 /* Compute size taken up by panels. */
1959 ui_panels_size(region, r_x, r_y);
1960}
1961
1963
1964/* -------------------------------------------------------------------- */
1967
1968#define DRAG_REGION_PAD (PNL_HEADER * 0.5)
1969static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
1970{
1971 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
1972 ARegion *region = CTX_wm_region(C);
1973
1974 /* Keep the drag position in the region with a small pad to keep the panel visible. */
1975 const int y = clamp_i(event->xy[1], region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD);
1976
1977 float dy = float(y - data->starty);
1978
1979 /* Adjust for region zoom. */
1980 dy *= BLI_rctf_size_y(&region->v2d.cur) / float(BLI_rcti_size_y(&region->winrct));
1981
1982 /* Add the movement of the view due to edge scrolling while dragging. */
1983 dy += (float(region->v2d.cur.ymin) - data->start_cur_ymin);
1984
1985 panel->ofsy = data->startofsy + round_fl_to_int(dy);
1986
1987 uiAlignPanelStep(region, 0.2f, true);
1988
1989 ED_region_tag_redraw(region);
1990}
1991#undef DRAG_REGION_PAD
1992
1994
1995/* -------------------------------------------------------------------- */
1998
2000{
2001 for (LayoutPanelHeader &header : panel.runtime->layout_panels.headers) {
2002 if (IN_RANGE(float(my - panel.runtime->block->rect.ymax), header.start_y, header.end_y)) {
2003 return &header;
2004 }
2005 }
2006 return nullptr;
2007}
2008
2010 const Panel *panel,
2011 const int mx,
2012 const int my)
2013{
2014 if (!IN_RANGE(float(mx), block->rect.xmin, block->rect.xmax)) {
2015 return PANEL_MOUSE_OUTSIDE;
2016 }
2017
2018 if (IN_RANGE(float(my), block->rect.ymax, block->rect.ymax + PNL_HEADER)) {
2020 }
2021 if (ui_layout_panel_header_under_mouse(*panel, my) != nullptr) {
2023 }
2024
2025 if (!UI_panel_is_closed(panel)) {
2026 if (IN_RANGE(float(my), block->rect.ymin, block->rect.ymax + PNL_HEADER)) {
2028 }
2029 }
2030
2031 return PANEL_MOUSE_OUTSIDE;
2032}
2033
2038
2039static void ui_panel_drag_collapse_handler_remove(bContext * /*C*/, void *userdata)
2040{
2041 uiPanelDragCollapseHandle *dragcol_data = static_cast<uiPanelDragCollapseHandle *>(userdata);
2042 MEM_freeN(dragcol_data);
2043}
2044
2046 const uiPanelDragCollapseHandle *dragcol_data,
2047 const int xy_dst[2])
2048{
2049 ARegion *region = CTX_wm_region_popup(C);
2050 if (!region) {
2051 region = CTX_wm_region(C);
2052 }
2053 LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
2054 float xy_a_block[2] = {float(dragcol_data->xy_init[0]), float(dragcol_data->xy_init[1])};
2055 float xy_b_block[2] = {float(xy_dst[0]), float(xy_dst[1])};
2056 Panel *panel = block->panel;
2057
2058 if (panel == nullptr) {
2059 continue;
2060 }
2061
2062 /* Lock axis. */
2063 xy_b_block[0] = dragcol_data->xy_init[0];
2064
2065 /* Use cursor coords in block space. */
2066 ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]);
2067 ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]);
2068
2069 for (LayoutPanelHeader &header : panel->runtime->layout_panels.headers) {
2070 rctf rect = block->rect;
2071 rect.ymin = block->rect.ymax + header.start_y;
2072 rect.ymax = block->rect.ymax + header.end_y;
2073
2074 if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) {
2076 &header.open_owner_ptr, header.open_prop_name.c_str(), !dragcol_data->was_first_open);
2078 const_cast<bContext *>(C),
2079 &header.open_owner_ptr,
2080 RNA_struct_find_property(&header.open_owner_ptr, header.open_prop_name.c_str()));
2081 ED_region_tag_redraw(region);
2083 }
2084 }
2085
2086 if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
2087 continue;
2088 }
2089 const int oldflag = panel->flag;
2090
2091 /* Set up `rect` to match header size. */
2092 rctf rect = block->rect;
2093 rect.ymin = rect.ymax;
2094 rect.ymax = rect.ymin + PNL_HEADER;
2095
2096 /* Touch all panels between last mouse coordinate and the current one. */
2097 if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) {
2098 /* Force panel to open or close. */
2100 SET_FLAG_FROM_TEST(panel->flag, dragcol_data->was_first_open, PNL_CLOSED);
2101
2102 /* If panel->flag has changed this means a panel was opened/closed here. */
2103 if (panel->flag != oldflag) {
2105 }
2106 }
2107 }
2108 /* Update the instanced panel data expand flags with the changes made here. */
2110}
2111
2118static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, void *userdata)
2119{
2120 wmWindow *win = CTX_wm_window(C);
2121 uiPanelDragCollapseHandle *dragcol_data = static_cast<uiPanelDragCollapseHandle *>(userdata);
2122 short retval = WM_UI_HANDLER_CONTINUE;
2123
2124 switch (event->type) {
2125 case MOUSEMOVE:
2126 ui_panel_drag_collapse(C, dragcol_data, event->xy);
2127
2128 retval = WM_UI_HANDLER_BREAK;
2129 break;
2130 case LEFTMOUSE:
2131 if (event->val == KM_RELEASE) {
2132 /* Done! */
2136 dragcol_data,
2137 true);
2139 }
2140 /* Don't let any left-mouse event fall through! */
2141 retval = WM_UI_HANDLER_BREAK;
2142 break;
2143 }
2144
2145 return retval;
2146}
2147
2148void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was_open)
2149{
2150 wmWindow *win = CTX_wm_window(C);
2151 const wmEvent *event = win->eventstate;
2152 uiPanelDragCollapseHandle *dragcol_data = MEM_cnew<uiPanelDragCollapseHandle>(__func__);
2153
2154 dragcol_data->was_first_open = was_open;
2155 copy_v2_v2_int(dragcol_data->xy_init, event->xy);
2156
2158 &win->modalhandlers,
2161 dragcol_data,
2163}
2164
2166{
2167 const bool is_open = RNA_boolean_get(&header->open_owner_ptr, header->open_prop_name.c_str());
2168 RNA_boolean_set(&header->open_owner_ptr, header->open_prop_name.c_str(), !is_open);
2170 const_cast<bContext *>(C),
2171 &header->open_owner_ptr,
2172 RNA_struct_find_property(&header->open_owner_ptr, header->open_prop_name.c_str()));
2173 return !is_open;
2174}
2175
2177 bContext *C, const uiBlock *block, const int /*mx*/, const int my, const int event_type)
2178{
2179 Panel *panel = block->panel;
2180 BLI_assert(panel->type != nullptr);
2181
2183 if (header == nullptr) {
2184 return;
2185 }
2186 const bool new_state = ui_layout_panel_toggle_open(C, header);
2189
2190 if (event_type == LEFTMOUSE) {
2192 }
2193}
2194
2202 const uiBlock *block,
2203 const int mx,
2204 const int event_type,
2205 const bool ctrl,
2206 const bool shift)
2207{
2208 Panel *panel = block->panel;
2209 ARegion *region = CTX_wm_region(C);
2210
2211 BLI_assert(panel->type != nullptr);
2213
2214 const bool is_subpanel = (panel->type->parent != nullptr);
2215 const bool use_pin = UI_panel_category_is_visible(region) && UI_panel_can_be_pinned(panel);
2216 const bool show_pin = use_pin && (panel->flag & PNL_PIN);
2217 const bool show_drag = !is_subpanel;
2218
2219 /* Handle panel pinning. */
2220 if (use_pin && ELEM(event_type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE) && shift) {
2221 panel->flag ^= PNL_PIN;
2222 ED_region_tag_redraw(region);
2223 return;
2224 }
2225
2226 float expansion_area_xmax = block->rect.xmax;
2227 if (show_drag) {
2228 expansion_area_xmax -= (PNL_ICON * 1.5f);
2229 }
2230 if (show_pin) {
2231 expansion_area_xmax -= PNL_ICON;
2232 }
2233
2234 /* Collapse and expand panels. */
2235 if (ELEM(event_type, EVT_RETKEY, EVT_PADENTER, EVT_AKEY) || mx < expansion_area_xmax) {
2236 if (ctrl && !is_subpanel) {
2237 /* For parent panels, collapse all other panels or toggle children. */
2238 if (UI_panel_is_closed(panel) || BLI_listbase_is_empty(&panel->children)) {
2239 panels_collapse_all(region, panel);
2240
2241 /* Reset the view - we don't want to display a view without content. */
2242 UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
2243 }
2244 else {
2245 /* If a panel has sub-panels and it's open, toggle the expansion
2246 * of the sub-panels (based on the expansion of the first sub-panel). */
2247 Panel *first_child = static_cast<Panel *>(panel->children.first);
2248 BLI_assert(first_child != nullptr);
2250 panel->flag |= PNL_CLOSED;
2251 }
2252 }
2253
2255
2256 if (event_type == LEFTMOUSE) {
2258 }
2259
2260 /* Set panel custom data (modifier) active when expanding sub-panels, but not top-level
2261 * panels to allow collapsing and expanding without setting the active element. */
2262 if (is_subpanel) {
2264 }
2265
2268 return;
2269 }
2270
2271 /* Handle panel dragging. For now don't allow dragging in floating regions. */
2272 if (show_drag && !(region->alignment == RGN_ALIGN_FLOAT)) {
2273 const float drag_area_xmin = block->rect.xmax - (PNL_ICON * 1.5f);
2274 const float drag_area_xmax = block->rect.xmax;
2275 if (IN_RANGE(mx, drag_area_xmin, drag_area_xmax)) {
2276 if (panel_custom_pin_to_last_get(panel)) {
2277 panel_custom_pin_to_last_set(C, panel, false);
2278 return;
2279 }
2280 else {
2282 return;
2283 }
2284 }
2285 }
2286
2287 /* Handle panel unpinning. */
2288 if (show_pin) {
2289 const float pin_area_xmin = expansion_area_xmax;
2290 const float pin_area_xmax = pin_area_xmin + PNL_ICON;
2291 if (IN_RANGE(mx, pin_area_xmin, pin_area_xmax)) {
2292 panel->flag ^= PNL_PIN;
2293 ED_region_tag_redraw(region);
2294 return;
2295 }
2296 }
2297}
2298
2300{
2301 /* Check for more than one category. */
2302 return region->panels_category.first &&
2303 region->panels_category.first != region->panels_category.last;
2304}
2305
2306PanelCategoryDyn *UI_panel_category_find(const ARegion *region, const char *idname)
2307{
2308 return static_cast<PanelCategoryDyn *>(
2309 BLI_findstring(&region->panels_category, idname, offsetof(PanelCategoryDyn, idname)));
2310}
2311
2312int UI_panel_category_index_find(ARegion *region, const char *idname)
2313{
2314 return BLI_findstringindex(&region->panels_category, idname, offsetof(PanelCategoryDyn, idname));
2315}
2316
2318{
2319 return static_cast<PanelCategoryStack *>(BLI_findstring(
2320 &region->panels_category_active, idname, offsetof(PanelCategoryStack, idname)));
2321}
2322
2323static void ui_panel_category_active_set(ARegion *region, const char *idname, bool fallback)
2324{
2325 ListBase *lb = &region->panels_category_active;
2326 PanelCategoryStack *pc_act = UI_panel_category_active_find(region, idname);
2327
2328 if (pc_act) {
2329 BLI_remlink(lb, pc_act);
2330 }
2331 else {
2332 pc_act = MEM_cnew<PanelCategoryStack>(__func__);
2333 STRNCPY(pc_act->idname, idname);
2334 }
2335
2336 if (fallback) {
2337 /* For fall-backs, add at the end so explicitly chosen categories have priority. */
2338 BLI_addtail(lb, pc_act);
2339 }
2340 else {
2341 BLI_addhead(lb, pc_act);
2342 }
2343
2344 /* Validate all active panels. We could do this on load, they are harmless -
2345 * but we should remove them somewhere.
2346 * (Add-ons could define panels and gather cruft over time). */
2347 {
2348 PanelCategoryStack *pc_act_next;
2349 /* intentionally skip first */
2350 pc_act_next = pc_act->next;
2351 while ((pc_act = pc_act_next)) {
2352 pc_act_next = pc_act->next;
2353 if (!BLI_findstring(
2354 &region->type->paneltypes, pc_act->idname, offsetof(PanelType, category)))
2355 {
2356 BLI_remlink(lb, pc_act);
2357 MEM_freeN(pc_act);
2358 }
2359 }
2360 }
2361}
2362
2363void UI_panel_category_active_set(ARegion *region, const char *idname)
2364{
2365 ui_panel_category_active_set(region, idname, false);
2366}
2367
2368void UI_panel_category_index_active_set(ARegion *region, const int index)
2369{
2370 PanelCategoryDyn *pc_dyn = static_cast<PanelCategoryDyn *>(
2371 BLI_findlink(&region->panels_category, index));
2372 if (!pc_dyn) {
2373 return;
2374 }
2375
2376 ui_panel_category_active_set(region, pc_dyn->idname, false);
2377}
2378
2379void UI_panel_category_active_set_default(ARegion *region, const char *idname)
2380{
2381 if (!UI_panel_category_active_find(region, idname)) {
2382 ui_panel_category_active_set(region, idname, true);
2383 }
2384}
2385
2386const char *UI_panel_category_active_get(ARegion *region, bool set_fallback)
2387{
2389 if (UI_panel_category_find(region, pc_act->idname)) {
2390 return pc_act->idname;
2391 }
2392 }
2393
2394 if (set_fallback) {
2395 PanelCategoryDyn *pc_dyn = static_cast<PanelCategoryDyn *>(region->panels_category.first);
2396 if (pc_dyn) {
2397 ui_panel_category_active_set(region, pc_dyn->idname, true);
2398 return pc_dyn->idname;
2399 }
2400 }
2401
2402 return nullptr;
2403}
2404
2406{
2408 if (BLI_rcti_isect_pt(&ptd->rect, event->mval[0], event->mval[1])) {
2409 return ptd;
2410 }
2411 }
2412
2413 return nullptr;
2414}
2415
2416void UI_panel_category_add(ARegion *region, const char *name)
2417{
2418 PanelCategoryDyn *pc_dyn = MEM_cnew<PanelCategoryDyn>(__func__);
2419 BLI_addtail(&region->panels_category, pc_dyn);
2420
2421 STRNCPY(pc_dyn->idname, name);
2422
2423 /* 'pc_dyn->rect' must be set on draw. */
2424}
2425
2427{
2429}
2430
2432 ARegion *region,
2433 const uiBut *active_but)
2434{
2435 const bool is_mousewheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE);
2436 const bool inside_tabregion =
2438 (event->mval[0] < ((PanelCategoryDyn *)region->panels_category.first)->rect.xmax) :
2439 (event->mval[0] > ((PanelCategoryDyn *)region->panels_category.first)->rect.xmin));
2440
2441 /* If mouse is inside non-tab region, ctrl key is required. */
2442 if (is_mousewheel && (event->modifier & KM_CTRL) == 0 && !inside_tabregion) {
2444 }
2445
2446 if (active_but && ui_but_supports_cycling(active_but)) {
2447 /* Skip - exception to make cycling buttons using ctrl+mousewheel work in tabbed regions. */
2448 }
2449 else {
2450 const char *category = UI_panel_category_active_get(region, false);
2451 if (LIKELY(category)) {
2452 PanelCategoryDyn *pc_dyn = UI_panel_category_find(region, category);
2453 /* Cyclic behavior between categories
2454 * using Ctrl+Tab (+Shift for backwards) or Ctrl+Wheel Up/Down. */
2455 if (LIKELY(pc_dyn) && (event->modifier & KM_CTRL)) {
2456 if (is_mousewheel) {
2457 /* We can probably get rid of this and only allow Ctrl-Tabbing. */
2458 pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev;
2459 }
2460 else {
2461 const bool backwards = event->modifier & KM_SHIFT;
2462 pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next;
2463 if (!pc_dyn) {
2464 /* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */
2465 pc_dyn = backwards ? static_cast<PanelCategoryDyn *>(region->panels_category.last) :
2466 static_cast<PanelCategoryDyn *>(region->panels_category.first);
2467 }
2468 }
2469
2470 if (pc_dyn) {
2471 /* Intentionally don't reset scroll in this case,
2472 * allowing for quick browsing between tabs. */
2473 UI_panel_category_active_set(region, pc_dyn->idname);
2474 ED_region_tag_redraw(region);
2475 }
2476 return WM_UI_HANDLER_BREAK;
2477 }
2478 }
2479 }
2480
2482}
2483
2485 const wmEvent *event,
2486 ARegion *region,
2487 const uiBut *active_but)
2488{
2489 /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
2490 if (ISMOUSE_MOTION(event->type)) {
2492 }
2493
2494 /* We only use KM_PRESS events in this function, so it's simpler to return early. */
2495 if (event->val != KM_PRESS) {
2497 }
2498
2499 /* Scroll-bars can overlap panels now, they have handling priority. */
2500 if (UI_view2d_mouse_in_scrollers(region, &region->v2d, event->xy)) {
2502 }
2503
2504 int retval = WM_UI_HANDLER_CONTINUE;
2505
2506 /* Handle category tabs. */
2507 if (UI_panel_category_is_visible(region)) {
2508 if (event->type == LEFTMOUSE) {
2509 PanelCategoryDyn *pc_dyn = panel_categories_find_mouse_over(region, event);
2510 if (pc_dyn) {
2511 UI_panel_category_active_set(region, pc_dyn->idname);
2512 ED_region_tag_redraw(region);
2513
2514 /* Reset scroll to the top (#38348). */
2515 UI_view2d_offset(&region->v2d, -1.0f, 1.0f);
2516
2517 retval = WM_UI_HANDLER_BREAK;
2518 }
2519 }
2520 else if (((event->type == EVT_TABKEY) && (event->modifier & KM_CTRL)) ||
2522 {
2523 /* Cycle tabs. */
2524 retval = ui_handle_panel_category_cycling(event, region, active_but);
2525 }
2526 }
2527
2528 if (retval == WM_UI_HANDLER_BREAK) {
2529 return retval;
2530 }
2531
2532 const uiBut *region_active_but = ui_region_find_active_but(region);
2533 const bool region_has_active_button = region_active_but &&
2534 region_active_but->type != UI_BTYPE_LABEL;
2535
2536 LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
2537 Panel *panel = block->panel;
2538 if (panel == nullptr || panel->type == nullptr) {
2539 continue;
2540 }
2541 /* We can't expand or collapse panels without headers, they would disappear. Layout panels can
2542 * be expanded and collapsed though. */
2543 const bool has_panel_header = !(panel->type->flag & PANEL_TYPE_NO_HEADER);
2544
2545 int mx = event->xy[0];
2546 int my = event->xy[1];
2547 ui_window_to_block(region, block, &mx, &my);
2548
2549 const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
2550
2551 if (has_panel_header && mouse_state != PANEL_MOUSE_OUTSIDE) {
2552 /* Mark panels that have been interacted with so their expansion
2553 * doesn't reset when property search finishes. */
2556
2557 /* The panel collapse / expand key "A" is special as it takes priority over
2558 * active button handling. */
2559 if (event->type == EVT_AKEY &&
2560 ((event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0))
2561 {
2562 retval = WM_UI_HANDLER_BREAK;
2564 C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT);
2565 break;
2566 }
2567 }
2568
2569 /* Don't do any other panel handling with an active button. */
2570 if (region_has_active_button) {
2571 continue;
2572 }
2573
2574 if (has_panel_header && mouse_state == PANEL_MOUSE_INSIDE_HEADER) {
2575 /* All mouse clicks inside panel headers should return in break. */
2576 if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) {
2577 retval = WM_UI_HANDLER_BREAK;
2579 C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT);
2580 }
2581 else if (event->type == RIGHTMOUSE) {
2582 retval = WM_UI_HANDLER_BREAK;
2583 ui_popup_context_menu_for_panel(C, region, block->panel);
2584 }
2585 break;
2586 }
2587 if (mouse_state == PANEL_MOUSE_INSIDE_LAYOUT_PANEL_HEADER) {
2588 if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) {
2589 retval = WM_UI_HANDLER_BREAK;
2590 ui_handle_layout_panel_header(C, block, mx, my, event->type);
2591 }
2592 }
2593 }
2594
2595 return retval;
2596}
2597
2598static void ui_panel_custom_data_set_recursive(Panel *panel, PointerRNA *custom_data)
2599{
2600 panel->runtime->custom_data_ptr = custom_data;
2601
2602 LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
2603 ui_panel_custom_data_set_recursive(child_panel, custom_data);
2604 }
2605}
2606
2607void UI_panel_context_pointer_set(Panel *panel, const char *name, PointerRNA *ptr)
2608{
2609 uiLayoutSetContextPointer(panel->layout, name, ptr);
2611}
2612
2614{
2615 BLI_assert(panel->type != nullptr);
2616
2617 /* Free the old custom data, which should be shared among all of the panel's sub-panels. */
2618 if (panel->runtime->custom_data_ptr != nullptr) {
2619 MEM_delete(panel->runtime->custom_data_ptr);
2620 }
2621
2622 ui_panel_custom_data_set_recursive(panel, custom_data);
2623}
2624
2626{
2627 return panel->runtime->custom_data_ptr;
2628}
2629
2631{
2632 ARegion *region = CTX_wm_region(C);
2633 if (region) {
2634 LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
2635 Panel *panel = block->panel;
2636 if (panel == nullptr) {
2637 continue;
2638 }
2639
2640 int mx = event->xy[0];
2641 int my = event->xy[1];
2642 ui_window_to_block(region, block, &mx, &my);
2643 const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
2645 return UI_panel_custom_data_get(panel);
2646 }
2647 }
2648 }
2649
2650 return nullptr;
2651}
2652
2654{
2655 return (panel->type->parent == nullptr) && !(panel->type->flag & PANEL_TYPE_INSTANCED);
2656}
2657
2659
2660/* -------------------------------------------------------------------- */
2663
2664/* NOTE: this is modal handler and should not swallow events for animation. */
2665static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
2666{
2667 Panel *panel = static_cast<Panel *>(userdata);
2668 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
2669
2670 /* Verify if we can stop. */
2671 if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
2673 }
2674 else if (event->type == MOUSEMOVE) {
2675 if (data->state == PANEL_STATE_DRAG) {
2676 ui_do_drag(C, event, panel);
2677 }
2678 }
2679 else if (event->type == TIMER && event->customdata == data->animtimer) {
2680 if (data->state == PANEL_STATE_ANIMATION) {
2681 ui_do_animate(C, panel);
2682 }
2683 else if (data->state == PANEL_STATE_DRAG) {
2684 ui_do_drag(C, event, panel);
2685 }
2686 }
2687
2688 data = static_cast<uiHandlePanelData *>(panel->activedata);
2689
2690 if (data && data->state == PANEL_STATE_ANIMATION) {
2692 }
2693 return WM_UI_HANDLER_BREAK;
2694}
2695
2696static void ui_handler_remove_panel(bContext *C, void *userdata)
2697{
2698 Panel *panel = static_cast<Panel *>(userdata);
2699
2701}
2702
2704 wmWindow *win,
2705 const ARegion *region,
2706 Panel *panel,
2708{
2710
2711 if (panel->activedata == nullptr) {
2712 panel->activedata = MEM_callocN(sizeof(uiHandlePanelData), __func__);
2714 &win->modalhandlers,
2717 panel,
2719 }
2720
2721 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
2722
2723 /* Only create a new timer if necessary. Reuse can occur when PANEL_STATE_ANIMATION follows
2724 * PANEL_STATE_DRAG for example (i.e. panel->activedata was present already). */
2725 if (!data->animtimer) {
2727 }
2728
2729 data->state = state;
2730 data->startx = win->eventstate->xy[0];
2731 data->starty = win->eventstate->xy[1];
2732 data->startofsx = panel->ofsx;
2733 data->startofsy = panel->ofsy;
2734 data->start_cur_xmin = region->v2d.cur.xmin;
2735 data->start_cur_ymin = region->v2d.cur.ymin;
2736 data->starttime = BLI_time_now_seconds();
2737}
2738
2745{
2746 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
2747 wmWindow *win = CTX_wm_window(C);
2748 ARegion *region = CTX_wm_region(C);
2749
2750 if (data != nullptr && data->state == state) {
2751 return;
2752 }
2753
2754 if (state == PANEL_STATE_DRAG) {
2756
2759
2760 panel_handle_data_ensure(C, win, region, panel, state);
2761
2762 /* Initiate edge panning during drags for scrolling beyond the initial region view. */
2763 wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
2765 }
2766 else if (state == PANEL_STATE_ANIMATION) {
2767 panel_set_flag_recursive(panel, PNL_SELECT, false);
2768
2769 panel_handle_data_ensure(C, win, region, panel, state);
2770 }
2771 else if (state == PANEL_STATE_EXIT) {
2773
2774 BLI_assert(data != nullptr);
2775
2776 if (data->animtimer) {
2777 WM_event_timer_remove(CTX_wm_manager(C), win, data->animtimer);
2778 data->animtimer = nullptr;
2779 }
2780
2781 MEM_freeN(data);
2782 panel->activedata = nullptr;
2783
2786 }
2787
2788 ED_region_tag_redraw(region);
2789}
2790
ARegion * CTX_wm_region_popup(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
void CTX_store_set(bContext *C, const bContextStore *store)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
@ PANEL_TYPE_NO_HEADER
@ PANEL_TYPE_INSTANCED
@ PANEL_TYPE_DEFAULT_CLOSED
void BKE_panel_free(Panel *panel)
Definition screen.cc:529
Panel * BKE_panel_new(PanelType *panel_type)
Definition screen.cc:518
@ BLF_ROTATION
Definition BLF_api.hh:361
void BLF_size(int fontid, float size)
Definition blf.cc:426
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition blf.cc:459
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:393
void BLF_disable(int fontid, int option)
Definition blf.cc:321
void BLF_rotation(int fontid, float angle)
Definition blf.cc:872
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:568
void BLF_enable(int fontid, int option)
Definition blf.cc:312
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:791
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:371
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:331
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_findstringindex(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
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)
#define M_PI_2
MINLINE float interpf(float target, float origin, float t)
MINLINE int clamp_i(int value, int min, int max)
MINLINE void copy_v4_v4_uchar(unsigned char r[4], const unsigned char a[4])
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
void BLI_rctf_rcti_copy(struct rctf *dst, const struct rcti *src)
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
#define STRNCPY(dst, src)
Definition BLI_string.h:593
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:29
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned char uchar
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define STREQLEN(a, b, n)
#define IN_RANGE(a, b, c)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define MAX_NAME
Definition DNA_defs.h:50
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ RGN_ALIGN_RIGHT
@ RGN_ALIGN_FLOAT
@ PNL_SELECT
@ PNL_PIN
@ PNL_CLOSED
@ PNL_INSTANCED_LIST_ORDER_CHANGED
@ RGN_FLAG_SEARCH_FILTER_UPDATE
@ RGN_FLAG_SEARCH_FILTER_ACTIVE
@ RGN_TYPE_WINDOW
@ RGN_TYPE_TOOLS
#define RGN_TYPE_HAS_CATEGORY_MASK
@ SPACE_PROPERTIES
#define UI_SCALE_FAC
#define UI_INV_SCALE_FAC
void(*)(void *data_link, char *r_idname) uiListPanelIDFromDataFunc
void ED_region_tag_refresh_ui(ARegion *region)
Definition area.cc:662
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, eGPUBuiltinShader shader_id)
void GPU_batch_draw(blender::gpu::Batch *batch)
blender::gpu::Batch * GPU_batch_preset_panel_drag_widget(float pixelsize, const float col_high[4], const float col_dark[4], float width) ATTR_WARN_UNUSED_RESULT
void immUniformColor4ubv(const unsigned char rgba[4])
void immUnbindProgram()
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniformColor3ubv(const unsigned char rgb[3])
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void immUniformColor3fvAlpha(const float rgb[3], float a)
void immRecti(uint pos, int x1, int y1, int x2, int y2)
void GPU_matrix_push()
void GPU_matrix_pop()
void GPU_matrix_translate_2f(float x, float y)
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_SHADER_3D_FLAT_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_FETCH_INT_TO_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_I32
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
#define C
Definition RandGen.cpp:29
@ UI_BLOCK_THEME_STYLE_POPUP
bool UI_panel_is_closed(const Panel *panel)
#define UI_UNIT_Y
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
void UI_block_theme_style_set(uiBlock *block, char theme_style)
#define INSTANCED_PANEL_UNIQUE_STR_SIZE
#define UI_PANEL_WIDTH
const uiStyle * UI_style_get_dpi()
void UI_draw_roundbox_corner_set(int type)
#define UI_PANEL_MARGIN_X
@ UI_STYLE_TEXT_LEFT
const uiStyle * UI_style_get()
bool UI_block_is_search_only(const uiBlock *block)
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
@ UI_CNR_NONE
void UI_block_draw(const bContext *C, uiBlock *block)
#define UI_PANEL_CATEGORY_MARGIN_WIDTH
void UI_fontstyle_draw(const uiFontStyle *fs, const rcti *rect, const char *str, size_t str_len, const uchar col[4], const uiFontStyleDraw_Params *fs_params)
#define UI_PANEL_MARGIN_Y
bool UI_panel_is_dragging(const Panel *panel)
#define UI_UNIT_X
@ UI_BTYPE_LABEL
bContextStore * uiLayoutGetContextStore(uiLayout *layout)
void UI_block_set_search_only(uiBlock *block, bool search_only)
const char * UI_panel_category_active_get(ARegion *region, bool set_fallback)
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *ptr)
#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)
@ TH_SELECT_ACTIVE
@ TH_TAB_OUTLINE
@ TH_PANEL_HEADER
@ TH_TAB_ACTIVE
@ TH_BACK
@ TH_PANEL_SUB_BACK
@ TH_TITLE
@ TH_TAB_BACK
@ TH_TAB_INACTIVE
@ TH_MATCH
@ TH_PANEL_BACK
@ TH_TEXT
@ TH_TEXT_HI
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
void UI_GetThemeColor4fv(int colorid, float col[4])
void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
bTheme * UI_GetTheme()
void UI_GetThemeColor4ubv(int colorid, unsigned char col[4])
char char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, const int xy[2]) ATTR_NONNULL(1
void UI_view2d_offset(View2D *v2d, float xfac, float yfac)
Definition view2d.cc:1952
eWM_EventHandlerFlag
Definition WM_api.hh:462
#define WM_UI_HANDLER_CONTINUE
Definition WM_types.hh:315
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
#define WM_UI_HANDLER_BREAK
Definition WM_types.hh:316
@ KM_CTRL
Definition WM_types.hh:256
@ KM_ALT
Definition WM_types.hh:257
@ KM_OSKEY
Definition WM_types.hh:259
@ KM_SHIFT
Definition WM_types.hh:255
#define U
int64_t size() const
const T & last(const int64_t n=0) const
bool is_empty() const
constexpr int64_t size() const
constexpr const char * data() const
local_group_size(16, 16) .push_constant(Type b
static float is_left(const float p0[2], const float p1[2], const float p2[2])
#define printf
#define offsetof(t, d)
#define sqrtf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
struct @157336070235062372277311340362362342103123126032::@262166344314164341202215145112231240022370055142 batch
void ui_window_to_block(const ARegion *region, const uiBlock *block, int *x, int *y)
Definition interface.cc:221
void ui_block_bounds_calc(uiBlock *block)
Definition interface.cc:436
rcti ui_to_pixelrect(const ARegion *region, const uiBlock *block, const rctf *src_rect)
void ui_window_to_block_fl(const ARegion *region, const uiBlock *block, float *x, float *y)
Definition interface.cc:184
void ui_fontscale(float *points, float aspect)
bool ui_but_supports_cycling(const uiBut *but)
void ui_block_new_button_group(uiBlock *block, uiButtonGroupFlag flag)
void ui_popup_context_menu_for_panel(bContext *C, ARegion *region, Panel *panel)
static int roundboxtype
void ui_handle_afterfunc_add_operator(wmOperatorType *ot, wmOperatorCallContext opcontext)
@ UI_HIDDEN
uiButtonGroupFlag
@ UI_BUTTON_GROUP_LOCK
@ UI_BUTTON_GROUP_PANEL_HEADER
bool ui_block_is_popup_any(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
#define PNL_HEADER
uiBut * ui_region_find_active_but(ARegion *region) ATTR_WARN_UNUSED_RESULT
void ui_block_views_bounds_calc(const uiBlock *block)
bool UI_panel_is_closed(const Panel *panel)
static void panel_activate_state(const bContext *C, Panel *panel, const uiHandlePanelState state)
static int get_panel_real_ofsy(Panel *panel)
static void panel_title_color_get(const Panel *panel, const bool show_background, const bool region_search_filter_active, uchar r_color[4])
static void panel_set_expansion_from_search_filter_recursive(const bContext *C, Panel *panel, const bool use_search_closed)
static void panel_set_runtime_flag_recursive(Panel *panel, short flag, bool value)
static void region_panels_set_expansion_from_search_filter(const bContext *C, ARegion *region, const bool use_search_closed)
void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
static void ui_panels_size(ARegion *region, int *r_x, int *r_y)
PanelCategoryDyn * UI_panel_category_find(const ARegion *region, const char *idname)
bool UI_panel_can_be_pinned(const Panel *panel)
static void ui_panel_drag_collapse_handler_remove(bContext *, void *userdata)
static void ui_do_animate(bContext *C, Panel *panel)
static int get_panel_real_size_y(const Panel *panel)
static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *region)
void UI_panel_drawname_set(Panel *panel, blender::StringRef name)
Panel * UI_panel_find_by_type(ListBase *lb, const PanelType *pt)
uiPanelRuntimeFlag
@ PANEL_NEW_ADDED
@ PANEL_WAS_CLOSED
@ PANEL_ANIM_ALIGN
@ PANEL_ACTIVE_BORDER
@ PANEL_WAS_ACTIVE
@ PANEL_SEARCH_FILTER_MATCH
@ PANEL_USE_CLOSED_FROM_SEARCH
@ PANEL_ACTIVE
@ PANEL_LAST_ADDED
@ PANEL_IS_DRAG_DROP
static void region_panels_set_expansion_from_list_data(const bContext *C, ARegion *region)
static void ui_panel_drag_collapse(const bContext *C, const uiPanelDragCollapseHandle *dragcol_data, const int xy_dst[2])
bool UI_panel_matches_search_filter(const Panel *panel)
static void panel_set_flag_recursive(Panel *panel, short flag, bool value)
static void region_panels_remove_invisible_layouts(ARegion *region)
void UI_panel_header_buttons_begin(Panel *panel)
void UI_panels_free_instanced(const bContext *C, ARegion *region)
static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
static void panel_delete(ARegion *region, ListBase *panels, Panel *panel)
void ui_draw_layout_panels_backdrop(const ARegion *region, const Panel *panel, const float radius, float subpanel_backcolor[4])
bool UI_panel_category_is_visible(const ARegion *region)
static void ui_panel_custom_data_set_recursive(Panel *panel, PointerRNA *custom_data)
#define ANIMATION_TIME
bool ui_layout_panel_toggle_open(const bContext *C, LayoutPanelHeader *header)
static void get_panel_expand_flag(const Panel *panel, short *flag, short *flag_index)
static bool panel_type_context_poll(ARegion *region, const PanelType *panel_type, const char *context)
Panel * UI_panel_begin(ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open)
static bool panel_custom_data_active_get(const Panel *panel)
int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *region, const uiBut *active_but)
int UI_panel_size_y(const Panel *panel)
void UI_panels_begin(const bContext *, ARegion *region)
static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, short *flag_index)
void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
void UI_panel_category_active_set(ARegion *region, const char *idname)
void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
static void ui_handler_remove_panel(bContext *C, void *userdata)
static bool panel_custom_pin_to_last_get(const Panel *panel)
void ui_panel_tag_search_filter_match(Panel *panel)
#define DRAG_REGION_PAD
bool UI_panel_is_active(const Panel *panel)
static int get_panel_size_y(const Panel *panel)
bool UI_panel_should_show_background(const ARegion *region, const PanelType *panel_type)
static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *parent_panel)
static void panel_draw_aligned_widgets(const uiStyle *style, const Panel *panel, const rcti *header_rect, const float aspect, const bool show_pin, const bool show_background, const bool region_search_filter_active)
static PanelCategoryDyn * panel_categories_find_mouse_over(ARegion *region, const wmEvent *event)
void UI_panel_category_clear_all(ARegion *region)
PanelCategoryStack * UI_panel_category_active_find(ARegion *region, const char *idname)
static bool panels_need_realign(const ScrArea *area, ARegion *region, Panel **r_panel_animation)
LayoutPanelHeader * ui_layout_panel_header_under_mouse(const Panel &panel, const int my)
#define PNL_ICON
static float panel_region_offset_x_get(const ARegion *region)
static void align_sub_panels(Panel *panel)
uiPanelMouseState
@ PANEL_MOUSE_INSIDE_HEADER
@ PANEL_MOUSE_INSIDE_LAYOUT_PANEL_HEADER
@ PANEL_MOUSE_INSIDE_CONTENT
@ PANEL_MOUSE_OUTSIDE
static void panel_matches_search_filter_recursive(const Panel *panel, bool *filter_matches)
static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *drag_panel)
static void panel_set_expansion_from_list_data(const bContext *C, Panel *panel)
#define TABS_PADDING_BETWEEN_FACTOR
int UI_panel_category_index_find(ARegion *region, const char *idname)
static Panel * panel_add_instanced(ListBase *panels, PanelType *panel_type, PointerRNA *custom_data)
static void panel_handle_data_ensure(const bContext *C, wmWindow *win, const ARegion *region, Panel *panel, const uiHandlePanelState state)
void UI_panel_category_active_set_default(ARegion *region, const char *idname)
#define ANIMATION_INTERVAL
void UI_panel_label_offset(const uiBlock *block, int *r_x, int *r_y)
#define TABS_PADDING_TEXT_FACTOR
static int compare_panel(const void *a, const void *b)
static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, void *userdata)
static void panels_collapse_all(ARegion *region, const Panel *from_panel)
static void panels_layout_begin_clear_flags(ListBase *lb)
static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
void UI_panel_end(Panel *panel, int width, int height)
void UI_panel_category_index_active_set(ARegion *region, const int index)
bool UI_panel_list_matches_data(ARegion *region, ListBase *data, uiListPanelIDFromDataFunc panel_idname_func)
void UI_panel_header_buttons_end(Panel *panel)
bool UI_panel_is_dragging(const Panel *panel)
static void ui_offset_panel_block(uiBlock *block)
void UI_panels_draw(const bContext *C, ARegion *region)
static void panel_custom_data_active_set(Panel *panel)
static int ui_handle_panel_category_cycling(const wmEvent *event, ARegion *region, const uiBut *active_but)
PointerRNA * UI_panel_custom_data_get(const Panel *panel)
static bool panel_active_animation_changed(ListBase *lb, Panel **r_panel_animation, bool *r_no_animation)
static bool uiAlignPanelStep(ARegion *region, const float factor, const bool drag)
void UI_list_panel_unique_str(Panel *panel, char *r_name)
void UI_panel_context_pointer_set(Panel *panel, const char *name, PointerRNA *ptr)
void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was_open)
Panel * UI_panel_add_instanced(const bContext *C, ARegion *region, ListBase *panels, const char *panel_idname, PointerRNA *custom_data)
void UI_panel_category_add(ARegion *region, const char *name)
static void panel_draw_aligned_backdrop(const ARegion *region, const Panel *panel, const rcti *rect, const rcti *header_rect)
static int find_highest_panel(const void *a, const void *b)
static void ui_handle_panel_header(const bContext *C, const uiBlock *block, const int mx, const int event_type, const bool ctrl, const bool shift)
static void ui_handle_layout_panel_header(bContext *C, const uiBlock *block, const int, const int my, const int event_type)
static void panel_calculate_size_recursive(ARegion *region, Panel *panel)
PointerRNA * UI_region_panel_custom_data_under_cursor(const bContext *C, const wmEvent *event)
const char * UI_panel_category_active_get(ARegion *region, bool set_fallback)
static void ui_panel_category_active_set(ARegion *region, const char *idname, bool fallback)
void ui_draw_aligned_panel(const ARegion *region, const uiStyle *style, const uiBlock *block, const rcti *rect, const bool show_pin, const bool show_background, const bool region_search_filter_active)
static void panel_draw_highlight_border(const Panel *panel, const rcti *rect, const rcti *header_rect)
static void panel_custom_pin_to_last_set(const bContext *C, const Panel *panel, const bool value)
static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block, const Panel *panel, const int mx, const int my)
static bool properties_space_needs_realign(const ScrArea *area, const ARegion *region)
uiHandlePanelState
@ PANEL_STATE_DRAG
@ PANEL_STATE_ANIMATION
@ PANEL_STATE_EXIT
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float2 floor(const float2 a)
static ulong state[N]
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
bool RNA_pointer_is_null(const PointerRNA *ptr)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
ListBase paneltypes
ListBase panels_category_active
ListBase panels_category
ListBase panels
struct ARegionType * type
ListBase uiblocks
PointerRNA open_owner_ptr
std::string open_prop_name
blender::Vector< LayoutPanelBody > bodies
blender::Vector< LayoutPanelHeader > headers
void * last
void * first
struct PanelCategoryDyn * next
struct PanelCategoryDyn * prev
struct PanelCategoryStack * next
void(* reorder)(bContext *C, Panel *pa, int new_index)
char idname[BKE_ST_MAXNAME]
char pin_to_last_property[BKE_ST_MAXNAME]
char context[BKE_ST_MAXNAME]
char translation_context[BKE_ST_MAXNAME]
char active_property[BKE_ST_MAXNAME]
ListBase children
char category[BKE_ST_MAXNAME]
char label[BKE_ST_MAXNAME]
short(* get_list_data_expand_flag)(const bContext *C, Panel *pa)
PanelType * parent
bContextStore * context
uiBlock * block
PointerRNA * custom_data_ptr
LayoutPanels layout_panels
struct PanelType * type
short labelofs
struct Panel_Runtime * runtime
struct uiLayout * layout
void * activedata
short runtime_flag
char * drawname
struct Panel * next
ListBase children
ListBase spacedata
uiWidgetColors wcol_menu_back
float panel_roundness
uiWidgetColors wcol_tab
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
ListBase buttons
blender::Vector< uiButtonGroup > button_groups
eButType type
uiButtonGroupFlag flag
blender::Vector< uiBut * > buttons
uiHandlePanelState state
uiFontStyle paneltitle
uiFontStyle widget
unsigned char text[4]
short val
Definition WM_types.hh:724
int xy[2]
Definition WM_types.hh:726
int mval[2]
Definition WM_types.hh:728
uint8_t modifier
Definition WM_types.hh:739
short type
Definition WM_types.hh:722
void * customdata
Definition WM_types.hh:772
struct wmEvent * eventstate
wmEventHandler_UI * WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn, void *user_data, const eWM_EventHandlerFlag flag)
void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn, void *user_data, const bool postpone)
#define ISMOUSE_MOTION(event_type)
@ RIGHTMOUSE
@ TIMER
@ EVT_TABKEY
@ EVT_AKEY
@ WHEELUPMOUSE
@ EVT_PADENTER
@ WHEELDOWNMOUSE
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_RETKEY
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_tooltip_clear(bContext *C, wmWindow *win)
Definition wm_tooltip.cc:81
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)
uint8_t flag
Definition wm_window.cc:138