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