Blender V5.0
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_string_utf8.h"
24#include "BLI_time.h"
25#include "BLI_utildefines.h"
26
27#include "BLT_translation.hh"
28
29#include "DNA_screen_types.h"
30#include "DNA_userdef_types.h"
31
32#include "BKE_context.hh"
33#include "BKE_screen.hh"
34
35#include "RNA_access.hh"
36
37#include "BLF_api.hh"
38
39#include "WM_api.hh"
40#include "WM_types.hh"
41
42#include "ED_screen.hh"
43
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_border(const Panel *panel,
1074 const rcti *rect,
1075 const rcti *header_rect,
1076 const bool is_active)
1077{
1078 const bool is_subpanel = panel->type->parent != nullptr;
1079 if (is_subpanel) {
1080 return;
1081 }
1082
1083 float color[4];
1085 if (color[3] == 0.0f) {
1086 return; /* No border to draw. */
1087 }
1088
1089 const bTheme *btheme = UI_GetTheme();
1090 const float aspect = panel->runtime->block->aspect;
1091 const float radius = (btheme->tui.panel_roundness * U.widget_unit * 0.5f) / aspect;
1093
1094 rctf box_rect;
1095 box_rect.xmin = rect->xmin;
1096 box_rect.xmax = rect->xmax;
1097 box_rect.ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin;
1098 box_rect.ymax = header_rect->ymax;
1099 UI_draw_roundbox_4fv(&box_rect, false, radius, color);
1100}
1101
1102static void panel_draw_aligned_widgets(const uiStyle *style,
1103 const Panel *panel,
1104 const rcti *header_rect,
1105 const float aspect,
1106 const bool show_pin,
1107 const bool show_background,
1108 const bool region_search_filter_active)
1109{
1110 const bool is_subpanel = panel->type->parent != nullptr;
1111 const uiFontStyle *fontstyle = (is_subpanel) ? &style->widget : &style->paneltitle;
1112
1113 const int header_height = BLI_rcti_size_y(header_rect);
1114 const int header_width = BLI_rcti_size_x(header_rect);
1115 const int scaled_unit = round_fl_to_int(UI_UNIT_X / aspect);
1116
1117 /* Offset triangle and text to the right for sub-panels. */
1118 rcti widget_rect;
1119 widget_rect.xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0);
1120 widget_rect.xmax = header_rect->xmax;
1121 widget_rect.ymin = header_rect->ymin;
1122 widget_rect.ymax = header_rect->ymax;
1123
1124 uchar title_color[4];
1125 panel_title_color_get(panel, show_background, region_search_filter_active, title_color);
1126 title_color[3] = 255;
1127
1128 /* Draw collapse icon. */
1129 {
1130 const float size_y = BLI_rcti_size_y(&widget_rect);
1132 float alpha = 0.8f;
1133 /* Dim as its space is reduced to zero. */
1134 if (header_width < (scaled_unit * 4)) {
1135 alpha *= std::max(float(header_width - scaled_unit) / float(scaled_unit * 3), 0.0f);
1136 }
1137 UI_icon_draw_ex(widget_rect.xmin + size_y * 0.2f,
1138 widget_rect.ymin + size_y * (UI_panel_is_closed(panel) ? 0.17f : 0.14f),
1139 UI_panel_is_closed(panel) ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT,
1140 aspect * UI_INV_SCALE_FAC,
1141 alpha,
1142 0.0f,
1143 title_color,
1144 false,
1147 }
1148
1149 /* Draw text label. */
1150 if (panel->drawname && panel->drawname[0] != '\0') {
1151 rcti title_rect;
1152 title_rect.xmin = widget_rect.xmin + (panel->labelofs / aspect) + scaled_unit * 1.1f;
1153 title_rect.xmax = widget_rect.xmax;
1154 if (!is_subpanel && show_background) {
1155 /* Don't draw over the drag widget. */
1156 title_rect.xmax -= scaled_unit;
1157 }
1158 title_rect.ymin = widget_rect.ymin - 2.0f / aspect;
1159 title_rect.ymax = widget_rect.ymax;
1160
1162 params.align = UI_STYLE_TEXT_LEFT;
1164 fontstyle, &title_rect, panel->drawname, strlen(panel->drawname), title_color, &params);
1165 }
1166
1167 /* Draw the pin icon. */
1168 if (show_pin && (panel->flag & PNL_PIN)) {
1170 UI_icon_draw_ex(widget_rect.xmax - scaled_unit * 2.2f,
1171 widget_rect.ymin + 5.0f / aspect,
1172 ICON_PINNED,
1173 aspect * UI_INV_SCALE_FAC,
1174 1.0f,
1175 0.0f,
1176 title_color,
1177 false,
1180 }
1181
1182 /* Draw drag widget. */
1183 if (!is_subpanel && show_background) {
1184 const float x = widget_rect.xmax - scaled_unit * 1.15;
1185 const float y = widget_rect.ymin + (header_height - (header_height * 0.7f)) * 0.5f;
1186 const bool is_pin = panel_custom_pin_to_last_get(panel);
1187 const int icon = is_pin ? ICON_PINNED : ICON_GRIP;
1188 const float size = aspect * UI_INV_SCALE_FAC;
1189 float alpha = is_pin ? 1.0f : 0.5f;
1190 if (header_width < (scaled_unit * 5)) {
1191 alpha *= std::max((header_width - scaled_unit) / float(scaled_unit * 4), 0.0f);
1192 }
1193 UI_icon_draw_ex(x, y, icon, size, alpha, 0.0f, title_color, false, UI_NO_ICON_OVERLAY_TEXT);
1194 }
1195}
1196
1198 const Panel *panel,
1199 const float radius,
1200 float subpanel_backcolor[4])
1201{
1202 /* Draw backdrops for layout panels. */
1203 const float aspect = ui_block_is_popup_any(panel->runtime->block) ?
1204 panel->runtime->block->aspect :
1205 1.0f;
1206
1207 for (const LayoutPanelBody &body : panel->runtime->layout_panels.bodies) {
1208
1209 rctf panel_blockspace = panel->runtime->block->rect;
1210 panel_blockspace.ymax = panel->runtime->block->rect.ymax + body.end_y;
1211 panel_blockspace.ymin = panel->runtime->block->rect.ymax + body.start_y;
1212
1213 if (panel_blockspace.ymax <= panel->runtime->block->rect.ymin) {
1214 /* Layout panels no longer fits in block rectangle, stop drawing backdrops. */
1215 break;
1216 }
1217 if (panel_blockspace.ymin >= panel->runtime->block->rect.ymax) {
1218 /* Skip layout panels that scrolled to the top of the block rectangle. */
1219 continue;
1220 }
1221 /* If the layout panel is at the end of the root panel, it's bottom corners are rounded. */
1222 const bool is_main_panel_end = panel_blockspace.ymin - panel->runtime->block->rect.ymin <
1223 (10.0f / aspect);
1224 if (is_main_panel_end) {
1225 panel_blockspace.ymin = panel->runtime->block->rect.ymin;
1227 }
1228 else {
1230 }
1231 panel_blockspace.ymax = std::min(panel_blockspace.ymax, panel->runtime->block->rect.ymax);
1232
1233 rcti panel_pixelspace = ui_to_pixelrect(region, panel->runtime->block, &panel_blockspace);
1234 rctf panel_pixelspacef;
1235 BLI_rctf_rcti_copy(&panel_pixelspacef, &panel_pixelspace);
1236 UI_draw_roundbox_4fv(&panel_pixelspacef, true, radius, subpanel_backcolor);
1237 }
1238}
1239
1240static void panel_draw_softshadow(const rctf *box_rect,
1241 const int roundboxalign,
1242 const float radius,
1243 const float shadow_width)
1244{
1245 const float outline = U.pixelsize;
1246
1247 rctf shadow_rect = *box_rect;
1248 BLI_rctf_pad(&shadow_rect, -outline, -outline);
1249 UI_draw_roundbox_corner_set(roundboxalign);
1250
1251 const float shadow_alpha = UI_GetTheme()->tui.menu_shadow_fac;
1252 ui_draw_dropshadow(&shadow_rect, radius, shadow_width, 1.0f, shadow_alpha);
1253}
1254
1255static void panel_draw_aligned_backdrop(const ARegion *region,
1256 const Panel *panel,
1257 const rcti *rect,
1258 const rcti *header_rect)
1259{
1260 const bool is_open = !UI_panel_is_closed(panel);
1261 const bool is_subpanel = panel->type->parent != nullptr;
1262 const bool has_header = (panel->type->flag & PANEL_TYPE_NO_HEADER) == 0;
1263 const bool is_dragging = UI_panel_is_dragging(panel);
1264
1265 if (is_subpanel && !is_open) {
1266 return;
1267 }
1268
1269 const bTheme *btheme = UI_GetTheme();
1270 const float aspect = panel->runtime->block->aspect;
1271 const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f / aspect;
1272
1275
1276 /* Draw shadow on top-level panels with headers during drag or region overlap. */
1277 if (!is_subpanel && has_header && (region->overlap || is_dragging)) {
1278 /* Make shadow wider (at least 16px) while the panel is being dragged. */
1279 const float shadow_width = is_dragging ?
1282 const int roundboxalign = is_open ? UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT : UI_CNR_ALL;
1283
1284 rctf box_rect;
1285 box_rect.xmin = rect->xmin;
1286 box_rect.xmax = rect->xmax;
1287 box_rect.ymin = is_open ? rect->ymin : header_rect->ymin;
1288 box_rect.ymax = header_rect->ymax;
1289 panel_draw_softshadow(&box_rect, roundboxalign, radius, shadow_width);
1290 }
1291
1292 /* Panel backdrop. */
1293 if (is_open || !has_header) {
1294 float panel_backcolor[4];
1296 if (!has_header) {
1297 UI_GetThemeColor4fv(TH_BACK, panel_backcolor);
1298 }
1299 else {
1300 UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor);
1301 }
1302
1303 rctf box_rect;
1304 const float padding = is_subpanel ? U.widget_unit * 0.1f / aspect : 0.0f;
1305 box_rect.xmin = rect->xmin + padding;
1306 box_rect.xmax = rect->xmax - padding;
1307 box_rect.ymin = rect->ymin + padding;
1308 box_rect.ymax = rect->ymax;
1309 UI_draw_roundbox_4fv(&box_rect, true, radius, panel_backcolor);
1310
1311 float subpanel_backcolor[4];
1312 UI_GetThemeColor4fv(TH_PANEL_SUB_BACK, subpanel_backcolor);
1313 ui_draw_layout_panels_backdrop(region, panel, radius, subpanel_backcolor);
1314 }
1315
1316 /* Panel header backdrops for non sub-panels. */
1317 if (!is_subpanel && has_header) {
1318 float panel_headercolor[4];
1320 panel_headercolor);
1322
1323 /* Change the width a little bit to line up with the sides. */
1324 rctf box_rect;
1325 box_rect.xmin = rect->xmin;
1326 box_rect.xmax = rect->xmax;
1327 box_rect.ymin = header_rect->ymin;
1328 box_rect.ymax = header_rect->ymax;
1329 UI_draw_roundbox_4fv(&box_rect, true, radius, panel_headercolor);
1330 }
1331
1334}
1335
1337 const uiStyle *style,
1338 const uiBlock *block,
1339 const rcti *rect,
1340 const bool show_pin,
1341 const bool show_background,
1342 const bool region_search_filter_active)
1343{
1344 const Panel *panel = block->panel;
1345
1346 if (panel->sizex < 0 || panel->sizey < 0) {
1347 /* Nothing to draw. */
1348 return;
1349 }
1350
1351 /* Add 0.001f to prevent flicker from float inaccuracy. */
1352 const rcti header_rect = {
1353 rect->xmin,
1354 rect->xmax,
1355 rect->ymax,
1356 rect->ymax + int(floor(PNL_HEADER / block->aspect + 0.001f)),
1357 };
1358
1359 if (show_background || (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1360 panel_draw_aligned_backdrop(region, panel, rect, &header_rect);
1361 }
1362
1363 /* Draw the widgets and text in the panel header. */
1364 if (!(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1366 panel,
1367 &header_rect,
1368 block->aspect,
1369 show_pin,
1370 show_background,
1371 region_search_filter_active);
1372 }
1373
1374 /* Draw the panel outline on non-transparent panels. */
1375 if (UI_panel_should_show_background(region, panel->type)) {
1376 panel_draw_border(panel, rect, &header_rect, panel_custom_data_active_get(panel));
1377 }
1378}
1379
1380bool UI_panel_should_show_background(const ARegion *region, const PanelType *panel_type)
1381{
1382 if (region->alignment == RGN_ALIGN_FLOAT) {
1383 return false;
1384 }
1385
1386 if (panel_type && panel_type->flag & PANEL_TYPE_NO_HEADER) {
1387 if (region->regiontype == RGN_TYPE_TOOLS) {
1388 /* We never want a background around active tools. */
1389 return false;
1390 }
1391 /* Without a header there is no background except for region overlap. */
1392 return region->overlap != 0;
1393 }
1394
1395 return true;
1396}
1397
1399
1400/* -------------------------------------------------------------------- */
1403
1404#define TABS_PADDING_BETWEEN_FACTOR 4.0f
1405#define TABS_PADDING_TEXT_FACTOR 6.0f
1406
1407void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
1408{
1409 // #define USE_FLAT_INACTIVE
1411 View2D *v2d = &region->v2d;
1412 const uiStyle *style = UI_style_get();
1413 const uiFontStyle *fstyle = &style->widget;
1414 UI_fontstyle_set(fstyle);
1415 const int fontid = fstyle->uifont_id;
1416 float fstyle_points = fstyle->points;
1417 const float aspect = BLI_listbase_is_empty(&region->runtime->uiblocks) ?
1418 1.0f :
1419 ((uiBlock *)region->runtime->uiblocks.first)->aspect;
1420 const float zoom = 1.0f / aspect;
1421 const int px = U.pixelsize;
1422 const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom);
1423 const float dpi_fac = UI_SCALE_FAC;
1424 /* Padding of tabs around text. */
1425 const int tab_v_pad_text = round_fl_to_int(TABS_PADDING_TEXT_FACTOR * dpi_fac * zoom) + 2 * px;
1426 /* Padding between tabs. */
1427 const int tab_v_pad = round_fl_to_int(TABS_PADDING_BETWEEN_FACTOR * dpi_fac * zoom);
1428 bTheme *btheme = UI_GetTheme();
1429 const float tab_curve_radius = btheme->tui.wcol_tab.roundness * U.widget_unit * zoom;
1430 /* Round all corners when region overlap is on. */
1431 const int roundboxtype = region->overlap ? UI_CNR_ALL :
1434 bool is_alpha;
1435#ifdef USE_FLAT_INACTIVE
1436 bool is_active_prev = false;
1437#endif
1438 /* Same for all tabs. */
1439 /* Intentionally don't scale by 'px'. */
1440 const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width);
1441 const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3);
1442 int y_ofs = tab_v_pad;
1443
1444 /* Primary theme colors. */
1445 uchar theme_col_back[4];
1446
1447 /* Tab colors. */
1448 uchar theme_col_tab_bg[4];
1449 uchar theme_col_tab_text[3];
1450 uchar theme_col_tab_text_sel[3];
1451 float theme_col_tab_active[4];
1452 float theme_col_tab_inactive[4];
1453 float theme_col_tab_outline[4];
1454 float theme_col_tab_outline_sel[4];
1455
1456 UI_GetThemeColor4ubv(TH_BACK, theme_col_back);
1457 UI_GetThemeColor3ubv(TH_TAB_TEXT, theme_col_tab_text);
1458 UI_GetThemeColor3ubv(TH_TAB_TEXT_HI, theme_col_tab_text_sel);
1459 UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg);
1460 UI_GetThemeColor4fv(TH_TAB_ACTIVE, theme_col_tab_active);
1461 UI_GetThemeColor4fv(TH_TAB_INACTIVE, theme_col_tab_inactive);
1462 UI_GetThemeColor4fv(TH_TAB_OUTLINE, theme_col_tab_outline);
1463 UI_GetThemeColor4fv(TH_TAB_OUTLINE_ACTIVE, theme_col_tab_outline_sel);
1464
1465 is_alpha = (region->overlap && (theme_col_back[3] != 255));
1466
1467 BLF_enable(fontid, BLF_ROTATION);
1468 BLF_rotation(fontid, is_left ? M_PI_2 : -M_PI_2);
1469 ui_fontscale(&fstyle_points, aspect);
1470 BLF_size(fontid, fstyle_points * UI_SCALE_FAC);
1471
1472 /* Check the region type supports categories to avoid an assert
1473 * for showing 3D view panels in the properties space. */
1474 if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) {
1476 }
1477
1478 /* Calculate tab rectangle for each panel. */
1479 LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->runtime->panels_category) {
1480 rcti *rct = &pc_dyn->rect;
1481 const char *category_id = pc_dyn->idname;
1482 const char *category_id_draw = IFACE_(category_id);
1483 const int category_width = round_fl_to_int(
1484 BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX));
1485
1486 rct->xmin = rct_xmin;
1487 rct->xmax = rct_xmax;
1488
1489 rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2));
1490 rct->ymax = v2d->mask.ymax - (y_ofs);
1491
1492 y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2);
1493 }
1494
1495 const int max_scroll = max_ii(y_ofs - BLI_rcti_size_y(&v2d->mask), 0);
1496 const int scroll = clamp_i(region->category_scroll, 0, max_scroll);
1497 region->category_scroll = scroll;
1498 LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->runtime->panels_category) {
1499 rcti *rct = &pc_dyn->rect;
1500 rct->ymin += scroll;
1501 rct->ymax += scroll;
1502 }
1503
1504 /* Begin drawing. */
1505 GPU_line_smooth(true);
1506
1508 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1510
1511 /* Draw the background. */
1512 if (is_alpha) {
1514 immUniformColor4ubv(theme_col_tab_bg);
1515 }
1516 else {
1517 immUniformColor3ubv(theme_col_tab_bg);
1518 }
1519
1520 if (is_left) {
1521 immRectf(
1522 pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax);
1523 }
1524 else {
1525 immRectf(pos,
1526 v2d->mask.xmax - category_tabs_width,
1527 v2d->mask.ymin,
1528 v2d->mask.xmax + 1,
1529 v2d->mask.ymax);
1530 }
1531
1532 if (is_alpha) {
1534 }
1535
1537
1538 /* If the area is too small to show panels, then don't show any tabs as active. */
1539 const bool too_narrow = BLI_rcti_size_x(&region->winrct) <=
1541
1542 LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->runtime->panels_category) {
1543 const rcti *rct = &pc_dyn->rect;
1544 if (rct->ymin > v2d->mask.ymax) {
1545 /* Scrolled outside the top of the view, check the next tab. */
1546 continue;
1547 }
1548 if (rct->ymax < v2d->mask.ymin) {
1549 /* Scrolled past visible bounds, no need to draw other tabs. */
1550 break;
1551 }
1552 const char *category_id = pc_dyn->idname;
1553 const char *category_id_draw = IFACE_(category_id);
1554 size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
1555 const bool is_active = !too_narrow && STREQ(category_id, category_id_active);
1556
1558
1559#ifdef USE_FLAT_INACTIVE
1560 /* Draw line between inactive tabs. */
1561 if (is_active == false && is_active_prev == false && pc_dyn->prev) {
1563 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1565 immUniformColor3fvAlpha(theme_col_tab_outline, 0.3f);
1566 immRectf(pos,
1567 is_left ? v2d->mask.xmin + (category_tabs_width / 5) :
1568 v2d->mask.xmax - (category_tabs_width / 5),
1569 rct->ymax + px,
1570 is_left ? (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5) :
1571 (v2d->mask.xmax - category_tabs_width) + (category_tabs_width / 5),
1572 rct->ymax + (px * 3));
1574 }
1575
1576 is_active_prev = is_active;
1577
1578 if (is_active)
1579#endif
1580 {
1581 /* Draw filled rectangle and outline for tab. */
1583 rctf box_rect;
1584 box_rect.xmin = rct->xmin;
1585 box_rect.xmax = rct->xmax;
1586 box_rect.ymin = rct->ymin;
1587 box_rect.ymax = rct->ymax;
1588
1589 UI_draw_roundbox_4fv(&box_rect,
1590 true,
1591 tab_curve_radius,
1592 is_active ? theme_col_tab_active : theme_col_tab_inactive);
1593 UI_draw_roundbox_4fv(&box_rect,
1594 false,
1595 tab_curve_radius,
1596 is_active ? theme_col_tab_outline_sel : theme_col_tab_outline);
1597
1598 /* Disguise the outline on one side to join the tab to the panel. */
1599 if (!region->overlap) {
1601 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1603
1604 immUniformColor4fv(is_active ? theme_col_tab_active : theme_col_tab_inactive);
1605 immRectf(pos,
1606 is_left ? rct->xmax - px : rct->xmin,
1607 rct->ymin + px,
1608 is_left ? rct->xmax : rct->xmin + px,
1609 rct->ymax - px);
1611 }
1612 }
1613
1614 /* Tab titles. */
1615
1616 /* Offset toward the middle of the rect. */
1617 const int text_v_ofs = round_fl_to_int(float(rct_xmax - rct_xmin) * 0.5f);
1618 /* Offset down as the font size increases. */
1619 const int text_size_offset = round_fl_to_int(fstyle_points * UI_SCALE_FAC * 0.35f);
1620
1621 BLF_position(fontid,
1622 is_left ? rct->xmax - text_v_ofs + text_size_offset :
1623 rct->xmin + text_v_ofs - text_size_offset,
1624 is_left ? rct->ymin + tab_v_pad_text : rct->ymax - tab_v_pad_text,
1625 0.0f);
1626 BLF_color3ubv(fontid, is_active ? theme_col_tab_text_sel : theme_col_tab_text);
1627
1628 if (fstyle->shadow) {
1629 BLF_enable(fontid, BLF_SHADOW);
1630 const float shadow_color[4] = {
1631 fstyle->shadowcolor, fstyle->shadowcolor, fstyle->shadowcolor, fstyle->shadowalpha};
1632 BLF_shadow(fontid, FontShadowType(fstyle->shadow), shadow_color);
1633 BLF_shadow_offset(fontid, fstyle->shadx, fstyle->shady);
1634 }
1635
1636 BLF_draw(fontid, category_id_draw, category_draw_len);
1637
1638 if (fstyle->shadow) {
1639 BLF_disable(fontid, BLF_SHADOW);
1640 }
1641
1643
1644 /* Not essential, but allows events to be handled right up to the region edge (#38171). */
1645 if (is_left) {
1646 pc_dyn->rect.xmin = v2d->mask.xmin;
1647 }
1648 else {
1649 pc_dyn->rect.xmax = v2d->mask.xmax;
1650 }
1651 }
1652
1653 GPU_line_smooth(false);
1654
1655 BLF_disable(fontid, BLF_ROTATION);
1656}
1657
1658#undef TABS_PADDING_BETWEEN_FACTOR
1659#undef TABS_PADDING_TEXT_FACTOR
1660
1662
1663static int ui_panel_category_show_active_tab(ARegion *region, const int mval[2])
1664{
1665 if (!ED_region_panel_category_gutter_isect_xy(region, mval)) {
1667 }
1668 const View2D *v2d = &region->v2d;
1669 LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->runtime->panels_category) {
1670 const bool is_active = STREQ(pc_dyn->idname, region->runtime->category);
1671 if (!is_active) {
1672 continue;
1673 }
1674 const rcti *rct = &pc_dyn->rect;
1675 region->category_scroll = v2d->mask.ymax - (rct->ymax - region->category_scroll);
1676
1677 if (pc_dyn->next) {
1678 const PanelCategoryDyn *pc_dyn_next = static_cast<PanelCategoryDyn *>(pc_dyn->next);
1679 const int tab_v_pad = rct->ymin - pc_dyn_next->rect.ymax;
1680 region->category_scroll -= tab_v_pad;
1681 }
1682 break;
1683 }
1684 ED_region_tag_redraw(region);
1685 return WM_UI_HANDLER_BREAK;
1686}
1687/* -------------------------------------------------------------------- */
1690
1691static int get_panel_size_y(const Panel *panel)
1692{
1693 if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1694 return panel->sizey;
1695 }
1696
1697 return PNL_HEADER + panel->sizey;
1698}
1699
1700static int get_panel_real_size_y(const Panel *panel)
1701{
1702 const int sizey = UI_panel_is_closed(panel) ? 0 : panel->sizey;
1703
1704 if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1705 return sizey;
1706 }
1707
1708 return PNL_HEADER + sizey;
1709}
1710
1711int UI_panel_size_y(const Panel *panel)
1712{
1713 return get_panel_real_size_y(panel);
1714}
1715
1720static int get_panel_real_ofsy(Panel *panel)
1721{
1722 if (UI_panel_is_closed(panel)) {
1723 return panel->ofsy + panel->sizey;
1724 }
1725 return panel->ofsy;
1726}
1727
1728bool UI_panel_is_dragging(const Panel *panel)
1729{
1730 return panel->runtime_flag & PANEL_IS_DRAG_DROP;
1731}
1732
1740
1741static int find_highest_panel(const void *a, const void *b)
1742{
1743 const Panel *panel_a = ((PanelSort *)a)->panel;
1744 const Panel *panel_b = ((PanelSort *)b)->panel;
1745
1746 /* Stick uppermost header-less panels to the top of the region -
1747 * prevent them from being sorted (multiple header-less panels have to be sorted though). */
1748 if (panel_a->type->flag & PANEL_TYPE_NO_HEADER && panel_b->type->flag & PANEL_TYPE_NO_HEADER) {
1749 /* Pass the no-header checks and check for `ofsy` and #Panel.sortorder below. */
1750 }
1751 else if (panel_a->type->flag & PANEL_TYPE_NO_HEADER) {
1752 return -1;
1753 }
1754 else if (panel_b->type->flag & PANEL_TYPE_NO_HEADER) {
1755 return 1;
1756 }
1757
1758 const bool pin_last_a = panel_custom_pin_to_last_get(panel_a);
1759 const bool pin_last_b = panel_custom_pin_to_last_get(panel_b);
1760 if (pin_last_a && !pin_last_b) {
1761 return 1;
1762 }
1763 if (!pin_last_a && pin_last_b) {
1764 return -1;
1765 }
1766
1767 if (panel_a->ofsy + panel_a->sizey < panel_b->ofsy + panel_b->sizey) {
1768 return 1;
1769 }
1770 if (panel_a->ofsy + panel_a->sizey > panel_b->ofsy + panel_b->sizey) {
1771 return -1;
1772 }
1773 if (panel_a->sortorder > panel_b->sortorder) {
1774 return 1;
1775 }
1776 if (panel_a->sortorder < panel_b->sortorder) {
1777 return -1;
1778 }
1779
1780 return 0;
1781}
1782
1783static int compare_panel(const void *a, const void *b)
1784{
1785 const Panel *panel_a = ((PanelSort *)a)->panel;
1786 const Panel *panel_b = ((PanelSort *)b)->panel;
1787
1788 if (panel_a->sortorder > panel_b->sortorder) {
1789 return 1;
1790 }
1791 if (panel_a->sortorder < panel_b->sortorder) {
1792 return -1;
1793 }
1794
1795 return 0;
1796}
1797
1798static void align_sub_panels(Panel *panel)
1799{
1800 /* Position sub panels. */
1801 int ofsy = panel->ofsy + panel->sizey - panel->blocksizey;
1802
1803 LISTBASE_FOREACH (Panel *, pachild, &panel->children) {
1804 if (pachild->runtime_flag & PANEL_ACTIVE) {
1805 pachild->ofsx = panel->ofsx;
1806 pachild->ofsy = ofsy - get_panel_size_y(pachild);
1807 ofsy -= get_panel_real_size_y(pachild);
1808
1809 if (pachild->children.first) {
1810 align_sub_panels(pachild);
1811 }
1812 }
1813 }
1814}
1815
1819static bool uiAlignPanelStep(ARegion *region, const float factor, const bool drag)
1820{
1821 /* Count active panels. */
1822 int active_panels_len = 0;
1823 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1824 if (panel->runtime_flag & PANEL_ACTIVE) {
1825 /* These panels should have types since they are currently displayed to the user. */
1826 BLI_assert(panel->type != nullptr);
1827 active_panels_len++;
1828 }
1829 }
1830 if (active_panels_len == 0) {
1831 return false;
1832 }
1833
1834 /* Sort panels. */
1835 PanelSort *panel_sort = MEM_malloc_arrayN<PanelSort>(active_panels_len, __func__);
1836 {
1837 PanelSort *ps = panel_sort;
1838 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1839 if (panel->runtime_flag & PANEL_ACTIVE) {
1840 ps->panel = panel;
1841 ps++;
1842 }
1843 }
1844 }
1845
1846 if (drag) {
1847 /* While dragging, sort based on location and update #Panel.sortorder. */
1848 qsort(panel_sort, active_panels_len, sizeof(PanelSort), find_highest_panel);
1849 for (int i = 0; i < active_panels_len; i++) {
1850 panel_sort[i].panel->sortorder = i;
1851 }
1852 }
1853 else {
1854 /* Otherwise use #Panel.sortorder. */
1855 qsort(panel_sort, active_panels_len, sizeof(PanelSort), compare_panel);
1856 }
1857
1858 /* X offset. */
1859 const int region_offset_x = panel_region_offset_x_get(region);
1860 for (int i = 0; i < active_panels_len; i++) {
1861 PanelSort *ps = &panel_sort[i];
1862 const bool show_background = UI_panel_should_show_background(region, ps->panel->type);
1863 ps->panel->runtime->region_ofsx = region_offset_x;
1864 ps->new_offset_x = region_offset_x + (show_background ? UI_PANEL_MARGIN_X : 0);
1865 }
1866
1867 /* Y offset. */
1868 for (int i = 0, y = 0; i < active_panels_len; i++) {
1869 PanelSort *ps = &panel_sort[i];
1870 const bool show_background = UI_panel_should_show_background(region, ps->panel->type);
1871
1873
1874 /* Separate panel boxes a bit further (if they are drawn). */
1875 if (show_background) {
1877 }
1878 ps->new_offset_y = y;
1879 /* The header still draws offset by the size of closed panels, so apply the offset here. */
1880 if (UI_panel_is_closed(ps->panel)) {
1881 panel_sort[i].new_offset_y -= ps->panel->sizey;
1882 }
1883 }
1884
1885 /* Interpolate based on the input factor. */
1886 bool changed = false;
1887 for (int i = 0; i < active_panels_len; i++) {
1888 PanelSort *ps = &panel_sort[i];
1889 if (ps->panel->flag & PNL_SELECT) {
1890 continue;
1891 }
1892
1893 if (ps->new_offset_x != ps->panel->ofsx) {
1894 const float x = interpf(float(ps->new_offset_x), float(ps->panel->ofsx), factor);
1895 ps->panel->ofsx = round_fl_to_int(x);
1896 changed = true;
1897 }
1898 if (ps->new_offset_y != ps->panel->ofsy) {
1899 const float y = interpf(float(ps->new_offset_y), float(ps->panel->ofsy), factor);
1900 ps->panel->ofsy = round_fl_to_int(y);
1901 changed = true;
1902 }
1903 }
1904
1905 /* Set locations for tabbed and sub panels. */
1906 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1907 if (panel->runtime_flag & PANEL_ACTIVE) {
1908 if (panel->children.first) {
1909 align_sub_panels(panel);
1910 }
1911 }
1912 }
1913
1914 MEM_freeN(panel_sort);
1915
1916 return changed;
1917}
1918
1919static void ui_panels_size(ARegion *region, int *r_x, int *r_y)
1920{
1921 int sizex = 0;
1922 int sizey = 0;
1923 bool has_panel_with_background = false;
1924
1925 /* Compute size taken up by panels, for setting in view2d. */
1926 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1927 if (panel->runtime_flag & PANEL_ACTIVE) {
1928 const int pa_sizex = panel->ofsx + panel->sizex;
1929 const int pa_sizey = get_panel_real_ofsy(panel);
1930
1931 sizex = max_ii(sizex, pa_sizex);
1932 sizey = min_ii(sizey, pa_sizey);
1933 if (UI_panel_should_show_background(region, panel->type)) {
1934 has_panel_with_background = true;
1935 }
1936 }
1937 }
1938
1939 if (sizex == 0) {
1940 sizex = UI_PANEL_WIDTH;
1941 }
1942 if (sizey == 0) {
1943 sizey = -UI_PANEL_WIDTH;
1944 }
1945 /* Extra margin after the list so the view scrolls a few pixels further than the panel border.
1946 * Also makes the bottom match the top margin. */
1947 if (has_panel_with_background) {
1948 sizey -= UI_PANEL_MARGIN_Y;
1949 }
1950
1951 *r_x = sizex;
1952 *r_y = sizey;
1953}
1954
1955static void ui_do_animate(bContext *C, Panel *panel)
1956{
1957 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
1958 ARegion *region = CTX_wm_region(C);
1959
1960 float fac = (BLI_time_now_seconds() - data->starttime) / ANIMATION_TIME;
1961 fac = min_ff(sqrtf(fac), 1.0f);
1962
1963 if (uiAlignPanelStep(region, fac, false)) {
1964 ED_region_tag_redraw(region);
1965 }
1966 else {
1967 if (UI_panel_is_dragging(panel)) {
1968 /* NOTE: doing this in #panel_activate_state would require
1969 * removing `const` for context in many other places. */
1970 reorder_instanced_panel_list(C, region, panel);
1971 }
1972
1974 }
1975}
1976
1978{
1979 LISTBASE_FOREACH (Panel *, panel, lb) {
1980 /* Flags to copy over to the next layout pass. */
1981 const short flag_copy = PANEL_USE_CLOSED_FROM_SEARCH | PANEL_IS_DRAG_DROP;
1982
1983 const bool was_active = panel->runtime_flag & PANEL_ACTIVE;
1984 const bool was_closed = UI_panel_is_closed(panel);
1985 panel->runtime_flag &= flag_copy;
1986 SET_FLAG_FROM_TEST(panel->runtime_flag, was_active, PANEL_WAS_ACTIVE);
1987 SET_FLAG_FROM_TEST(panel->runtime_flag, was_closed, PANEL_WAS_CLOSED);
1988
1989 panels_layout_begin_clear_flags(&panel->children);
1990 }
1991}
1992
1993void UI_panels_begin(const bContext * /*C*/, ARegion *region)
1994{
1995 /* Set all panels as inactive, so that at the end we know which ones were used. Also
1996 * clear other flags so we know later that their values were set for the current redraw. */
1998}
1999
2000void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
2001{
2002 ScrArea *area = CTX_wm_area(C);
2003
2005
2006 const bool region_search_filter_active = region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE;
2007
2008 if (properties_space_needs_realign(area, region)) {
2009 region_panels_set_expansion_from_search_filter(C, region, region_search_filter_active);
2010 }
2011 else if (region->flag & RGN_FLAG_SEARCH_FILTER_UPDATE) {
2012 region_panels_set_expansion_from_search_filter(C, region, region_search_filter_active);
2013 }
2014
2015 if (region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE) {
2016 /* Clean up the extra panels and buttons created for searching. */
2018 }
2019
2020 LISTBASE_FOREACH (Panel *, panel, &region->panels) {
2021 if (panel->runtime_flag & PANEL_ACTIVE) {
2022 BLI_assert(panel->runtime->block != nullptr);
2023 panel_calculate_size_recursive(region, panel);
2024 }
2025 }
2026
2027 /* Offset contents. */
2028 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
2029 if (block->active && block->panel) {
2030 ui_offset_panel_block(block);
2031
2032 /* Update bounds for all "views" in this block. Usually this is done in #UI_block_end(), but
2033 * that wouldn't work because of the offset applied above. */
2034 ui_block_views_end(region, block);
2035 }
2036 }
2037
2038 /* Re-align, possibly with animation. */
2039 Panel *panel;
2040 if (panels_need_realign(area, region, &panel)) {
2041 if (panel) {
2043 }
2044 else {
2045 uiAlignPanelStep(region, 1.0, false);
2046 }
2047 }
2048
2049 /* Compute size taken up by panels. */
2050 ui_panels_size(region, r_x, r_y);
2051}
2052
2054
2055/* -------------------------------------------------------------------- */
2058
2059#define DRAG_REGION_PAD (PNL_HEADER * 0.5)
2060static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
2061{
2062 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
2063 ARegion *region = CTX_wm_region(C);
2064
2065 /* Keep the drag position in the region with a small pad to keep the panel visible. */
2066 const int y = clamp_i(event->xy[1], region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD);
2067
2068 float dy = float(y - data->starty);
2069
2070 /* Adjust for region zoom. */
2071 dy *= BLI_rctf_size_y(&region->v2d.cur) / float(BLI_rcti_size_y(&region->winrct));
2072
2073 /* Add the movement of the view due to edge scrolling while dragging. */
2074 dy += (region->v2d.cur.ymin - data->start_cur_ymin);
2075
2076 panel->ofsy = data->startofsy + round_fl_to_int(dy);
2077
2078 uiAlignPanelStep(region, 0.2f, true);
2079
2080 ED_region_tag_redraw(region);
2081}
2082#undef DRAG_REGION_PAD
2083
2085
2086/* -------------------------------------------------------------------- */
2089
2091{
2092 for (LayoutPanelHeader &header : panel.runtime->layout_panels.headers) {
2093 if (IN_RANGE(float(my - panel.runtime->block->rect.ymax), header.start_y, header.end_y)) {
2094 return &header;
2095 }
2096 }
2097 return nullptr;
2098}
2099
2101 const Panel *panel,
2102 const int mx,
2103 const int my)
2104{
2105 if (!IN_RANGE(float(mx), block->rect.xmin, block->rect.xmax)) {
2106 return PANEL_MOUSE_OUTSIDE;
2107 }
2108
2109 if (IN_RANGE(float(my), block->rect.ymax, block->rect.ymax + PNL_HEADER)) {
2111 }
2112 if (ui_layout_panel_header_under_mouse(*panel, my) != nullptr) {
2114 }
2115
2116 if (!UI_panel_is_closed(panel)) {
2117 if (IN_RANGE(float(my), block->rect.ymin, block->rect.ymax + PNL_HEADER)) {
2119 }
2120 }
2121
2122 return PANEL_MOUSE_OUTSIDE;
2123}
2124
2129
2130static void ui_panel_drag_collapse_handler_remove(bContext * /*C*/, void *userdata)
2131{
2132 uiPanelDragCollapseHandle *dragcol_data = static_cast<uiPanelDragCollapseHandle *>(userdata);
2133 MEM_freeN(dragcol_data);
2134}
2135
2137 const uiPanelDragCollapseHandle *dragcol_data,
2138 const int xy_dst[2])
2139{
2140 ARegion *region = CTX_wm_region_popup(C);
2141 if (!region) {
2142 region = CTX_wm_region(C);
2143 }
2144 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
2145 float xy_a_block[2] = {float(dragcol_data->xy_init[0]), float(dragcol_data->xy_init[1])};
2146 float xy_b_block[2] = {float(xy_dst[0]), float(xy_dst[1])};
2147 Panel *panel = block->panel;
2148
2149 if (panel == nullptr) {
2150 continue;
2151 }
2152
2153 /* Lock axis. */
2154 xy_b_block[0] = dragcol_data->xy_init[0];
2155
2156 /* Use cursor coords in block space. */
2157 ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]);
2158 ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]);
2159
2160 for (LayoutPanelHeader &header : panel->runtime->layout_panels.headers) {
2161 rctf rect = block->rect;
2162 rect.ymin = block->rect.ymax + header.start_y;
2163 rect.ymax = block->rect.ymax + header.end_y;
2164
2165 if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) {
2167 &header.open_owner_ptr, header.open_prop_name.c_str(), !dragcol_data->was_first_open);
2169 const_cast<bContext *>(C),
2170 &header.open_owner_ptr,
2171 RNA_struct_find_property(&header.open_owner_ptr, header.open_prop_name.c_str()));
2172 ED_region_tag_redraw(region);
2174 }
2175 }
2176
2177 if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
2178 continue;
2179 }
2180 const int oldflag = panel->flag;
2181
2182 /* Set up `rect` to match header size. */
2183 rctf rect = block->rect;
2184 rect.ymin = rect.ymax;
2185 rect.ymax = rect.ymin + PNL_HEADER;
2186
2187 /* Touch all panels between last mouse coordinate and the current one. */
2188 if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) {
2189 /* Force panel to open or close. */
2191 SET_FLAG_FROM_TEST(panel->flag, dragcol_data->was_first_open, PNL_CLOSED);
2192
2193 /* If panel->flag has changed this means a panel was opened/closed here. */
2194 if (panel->flag != oldflag) {
2196 }
2197 }
2198 }
2199 /* Update the instanced panel data expand flags with the changes made here. */
2201}
2202
2209static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, void *userdata)
2210{
2211 wmWindow *win = CTX_wm_window(C);
2212 uiPanelDragCollapseHandle *dragcol_data = static_cast<uiPanelDragCollapseHandle *>(userdata);
2213 short retval = WM_UI_HANDLER_CONTINUE;
2214
2215 switch (event->type) {
2216 case MOUSEMOVE:
2217 ui_panel_drag_collapse(C, dragcol_data, event->xy);
2218
2219 retval = WM_UI_HANDLER_BREAK;
2220 break;
2221 case LEFTMOUSE:
2222 if (event->val == KM_RELEASE) {
2223 /* Done! */
2227 dragcol_data,
2228 true);
2230 }
2231 /* Don't let any left-mouse event fall through! */
2232 retval = WM_UI_HANDLER_BREAK;
2233 break;
2234 default: {
2235 break;
2236 }
2237 }
2238
2239 return retval;
2240}
2241
2242void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was_open)
2243{
2244 wmWindow *win = CTX_wm_window(C);
2245 const wmEvent *event = win->eventstate;
2247
2248 dragcol_data->was_first_open = was_open;
2249 copy_v2_v2_int(dragcol_data->xy_init, event->xy);
2250
2252 &win->modalhandlers,
2255 dragcol_data,
2257}
2258
2260{
2261 const bool is_open = RNA_boolean_get(&header->open_owner_ptr, header->open_prop_name.c_str());
2262 RNA_boolean_set(&header->open_owner_ptr, header->open_prop_name.c_str(), !is_open);
2264 const_cast<bContext *>(C),
2265 &header->open_owner_ptr,
2266 RNA_struct_find_property(&header->open_owner_ptr, header->open_prop_name.c_str()));
2267 return !is_open;
2268}
2269
2271 bContext *C, const uiBlock *block, const int /*mx*/, const int my, const int event_type)
2272{
2273 Panel *panel = block->panel;
2274 BLI_assert(panel->type != nullptr);
2275
2277 if (header == nullptr) {
2278 return;
2279 }
2280 const bool new_state = ui_layout_panel_toggle_open(C, header);
2283
2284 if (event_type == LEFTMOUSE) {
2286 }
2287}
2288
2296 const uiBlock *block,
2297 const int mx,
2298 const int event_type,
2299 const bool ctrl,
2300 const bool shift)
2301{
2302 Panel *panel = block->panel;
2303 ARegion *region = CTX_wm_region(C);
2304
2305 BLI_assert(panel->type != nullptr);
2307
2308 const bool is_subpanel = (panel->type->parent != nullptr);
2309 const bool use_pin = UI_panel_category_is_visible(region) && UI_panel_can_be_pinned(panel);
2310 const bool show_pin = use_pin && (panel->flag & PNL_PIN);
2311 const bool show_drag = !is_subpanel;
2312
2313 /* Handle panel pinning. */
2314 if (use_pin && ELEM(event_type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE) && shift) {
2315 panel->flag ^= PNL_PIN;
2316 ED_region_tag_redraw(region);
2317 return;
2318 }
2319
2320 float expansion_area_xmax = block->rect.xmax;
2321 if (show_drag) {
2322 expansion_area_xmax -= (PNL_ICON * 1.5f);
2323 }
2324 if (show_pin) {
2325 expansion_area_xmax -= PNL_ICON;
2326 }
2327
2328 /* Collapse and expand panels. */
2329 if (ELEM(event_type, EVT_RETKEY, EVT_PADENTER, EVT_AKEY) || mx < expansion_area_xmax) {
2330 if (ctrl && !is_subpanel) {
2331 /* For parent panels, collapse all other panels or toggle children. */
2332 if (UI_panel_is_closed(panel) || BLI_listbase_is_empty(&panel->children)) {
2333 panels_collapse_all(region, panel);
2334
2335 /* Reset the view - we don't want to display a view without content. */
2336 UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
2337 }
2338 else {
2339 /* If a panel has sub-panels and it's open, toggle the expansion
2340 * of the sub-panels (based on the expansion of the first sub-panel). */
2341 Panel *first_child = static_cast<Panel *>(panel->children.first);
2342 BLI_assert(first_child != nullptr);
2344 panel->flag |= PNL_CLOSED;
2345 }
2346 }
2347
2349
2350 if (event_type == LEFTMOUSE) {
2352 }
2353
2354 /* Set panel custom data (modifier) active when expanding sub-panels, but not top-level
2355 * panels to allow collapsing and expanding without setting the active element. */
2356 if (is_subpanel) {
2358 }
2359
2362 return;
2363 }
2364
2365 /* Handle panel dragging. For now don't allow dragging in floating regions. */
2366 if (show_drag && !(region->alignment == RGN_ALIGN_FLOAT)) {
2367 const float drag_area_xmin = block->rect.xmax - (PNL_ICON * 1.5f);
2368 const float drag_area_xmax = block->rect.xmax;
2369 if (IN_RANGE(mx, drag_area_xmin, drag_area_xmax)) {
2370 if (panel_custom_pin_to_last_get(panel)) {
2371 panel_custom_pin_to_last_set(C, panel, false);
2372 return;
2373 }
2375 return;
2376 }
2377 }
2378
2379 /* Handle panel unpinning. */
2380 if (show_pin) {
2381 const float pin_area_xmin = expansion_area_xmax;
2382 const float pin_area_xmax = pin_area_xmin + PNL_ICON;
2383 if (IN_RANGE(mx, pin_area_xmin, pin_area_xmax)) {
2384 panel->flag ^= PNL_PIN;
2385 ED_region_tag_redraw(region);
2386 return;
2387 }
2388 }
2389}
2390
2392{
2393 /* Check for more than one category. */
2394 return region->runtime->panels_category.first &&
2395 region->runtime->panels_category.first != region->runtime->panels_category.last;
2396}
2397
2398PanelCategoryDyn *UI_panel_category_find(const ARegion *region, const char *idname)
2399{
2400 return static_cast<PanelCategoryDyn *>(BLI_findstring(
2401 &region->runtime->panels_category, idname, offsetof(PanelCategoryDyn, idname)));
2402}
2403
2404int UI_panel_category_index_find(ARegion *region, const char *idname)
2405{
2406 return BLI_findstringindex(
2407 &region->runtime->panels_category, idname, offsetof(PanelCategoryDyn, idname));
2408}
2409
2411{
2412 return static_cast<PanelCategoryStack *>(BLI_findstring(
2413 &region->panels_category_active, idname, offsetof(PanelCategoryStack, idname)));
2414}
2415
2416static void ui_panel_category_active_set(ARegion *region, const char *idname, bool fallback)
2417{
2418 ListBase *lb = &region->panels_category_active;
2419 PanelCategoryStack *pc_act = UI_panel_category_active_find(region, idname);
2420
2421 if (pc_act) {
2422 BLI_remlink(lb, pc_act);
2423 }
2424 else {
2425 pc_act = MEM_callocN<PanelCategoryStack>(__func__);
2426 STRNCPY_UTF8(pc_act->idname, idname);
2427 }
2428
2429 if (fallback) {
2430 /* For fall-backs, add at the end so explicitly chosen categories have priority. */
2431 BLI_addtail(lb, pc_act);
2432 }
2433 else {
2434 BLI_addhead(lb, pc_act);
2435 }
2436
2437 /* Validate all active panels. We could do this on load, they are harmless -
2438 * but we should remove them somewhere.
2439 * (Add-ons could define panels and gather cruft over time). */
2440 {
2441 PanelCategoryStack *pc_act_next;
2442 /* intentionally skip first */
2443 pc_act_next = pc_act->next;
2444 while ((pc_act = pc_act_next)) {
2445 pc_act_next = pc_act->next;
2446 if (!BLI_findstring(
2447 &region->runtime->type->paneltypes, pc_act->idname, offsetof(PanelType, category)))
2448 {
2449 BLI_remlink(lb, pc_act);
2450 MEM_freeN(pc_act);
2451 }
2452 }
2453 }
2454}
2455
2456void UI_panel_category_active_set(ARegion *region, const char *idname)
2457{
2458 ui_panel_category_active_set(region, idname, false);
2459}
2460
2461void UI_panel_category_index_active_set(ARegion *region, const int index)
2462{
2463 PanelCategoryDyn *pc_dyn = static_cast<PanelCategoryDyn *>(
2464 BLI_findlink(&region->runtime->panels_category, index));
2465 if (!pc_dyn) {
2466 return;
2467 }
2468
2469 ui_panel_category_active_set(region, pc_dyn->idname, false);
2470}
2471
2472void UI_panel_category_active_set_default(ARegion *region, const char *idname)
2473{
2474 if (!UI_panel_category_active_find(region, idname)) {
2475 ui_panel_category_active_set(region, idname, true);
2476 }
2477}
2478
2479const char *UI_panel_category_active_get(ARegion *region, bool set_fallback)
2480{
2482 if (UI_panel_category_find(region, pc_act->idname)) {
2483 return pc_act->idname;
2484 }
2485 }
2486
2487 if (set_fallback) {
2488 PanelCategoryDyn *pc_dyn = static_cast<PanelCategoryDyn *>(
2489 region->runtime->panels_category.first);
2490 if (pc_dyn) {
2491 ui_panel_category_active_set(region, pc_dyn->idname, true);
2492 return pc_dyn->idname;
2493 }
2494 }
2495
2496 return nullptr;
2497}
2498
2500{
2501 LISTBASE_FOREACH (PanelCategoryDyn *, ptd, &region->runtime->panels_category) {
2502 if (BLI_rcti_isect_pt(&ptd->rect, event->mval[0], event->mval[1])) {
2503 return ptd;
2504 }
2505 }
2506
2507 return nullptr;
2508}
2509
2510void UI_panel_category_add(ARegion *region, const char *name)
2511{
2513 BLI_addtail(&region->runtime->panels_category, pc_dyn);
2514
2515 STRNCPY_UTF8(pc_dyn->idname, name);
2516
2517 /* 'pc_dyn->rect' must be set on draw. */
2518}
2519
2521{
2522 BLI_freelistN(&region->runtime->panels_category);
2523}
2524
2526 ARegion *region,
2527 const uiBut *active_but)
2528{
2529 const bool is_mousewheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE);
2530 const bool inside_tabregion =
2532 (event->mval[0] <
2533 ((PanelCategoryDyn *)region->runtime->panels_category.first)->rect.xmax) :
2534 (event->mval[0] >
2535 ((PanelCategoryDyn *)region->runtime->panels_category.first)->rect.xmin));
2536
2537 /* If mouse is inside non-tab region, ctrl key is required. */
2538 if (is_mousewheel && (event->modifier & KM_CTRL) == 0 && !inside_tabregion) {
2540 }
2541
2542 if (active_but && ui_but_supports_cycling(active_but)) {
2543 /* Skip - exception to make cycling buttons using ctrl+mousewheel work in tabbed regions. */
2544 }
2545 else {
2546 const char *category = UI_panel_category_active_get(region, false);
2547 if (LIKELY(category)) {
2548 PanelCategoryDyn *pc_dyn = UI_panel_category_find(region, category);
2549 /* Cyclic behavior between categories
2550 * using Ctrl+Tab (+Shift for backwards) or Ctrl+Wheel Up/Down. */
2551 if (LIKELY(pc_dyn) && (event->modifier & KM_CTRL)) {
2552 if (is_mousewheel) {
2553 /* We can probably get rid of this and only allow Ctrl-Tabbing. */
2554 pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev;
2555 }
2556 else {
2557 const bool backwards = event->modifier & KM_SHIFT;
2558 pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next;
2559 if (!pc_dyn) {
2560 /* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */
2561 pc_dyn = backwards ?
2562 static_cast<PanelCategoryDyn *>(region->runtime->panels_category.last) :
2563 static_cast<PanelCategoryDyn *>(region->runtime->panels_category.first);
2564 }
2565 }
2566
2567 if (pc_dyn) {
2568 /* Intentionally don't reset scroll in this case,
2569 * allowing for quick browsing between tabs. */
2570 UI_panel_category_active_set(region, pc_dyn->idname);
2571 ED_region_tag_redraw(region);
2572 }
2573 return WM_UI_HANDLER_BREAK;
2574 }
2575 }
2576 }
2577
2579}
2580
2581static void ui_panel_region_width_set(ARegion *region, const float aspect, int unscaled_size)
2582{
2583 const float size_new = unscaled_size / aspect;
2584 if (region->alignment & RGN_ALIGN_RIGHT) {
2585 region->winrct.xmin = region->winrct.xmax - (size_new * UI_SCALE_FAC);
2586 }
2587 else {
2588 region->winrct.xmax = region->winrct.xmin + (size_new * UI_SCALE_FAC);
2589 }
2590 region->winx = size_new * UI_SCALE_FAC;
2591 region->sizex = size_new;
2592 region->v2d.winx = region->winx;
2593 region->v2d.cur.xmin = 0;
2594 region->v2d.cur.xmax = size_new * UI_SCALE_FAC;
2595 region->v2d.mask.xmin = 0;
2596 region->v2d.mask.xmax = size_new * UI_SCALE_FAC;
2598}
2599
2601 const wmEvent *event,
2602 ARegion *region,
2603 const uiBut *active_but)
2604{
2605 /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
2606 if (ISMOUSE_MOTION(event->type)) {
2608 }
2609
2610 /* We only use KM_PRESS events in this function, so it's simpler to return early. */
2611 if (event->val != KM_PRESS) {
2613 }
2614
2615 /* Scroll-bars can overlap panels now, they have handling priority. */
2616 if (UI_view2d_mouse_in_scrollers(region, &region->v2d, event->xy)) {
2618 }
2619
2620 int retval = WM_UI_HANDLER_CONTINUE;
2621
2622 /* Handle category tabs. */
2623 if (UI_panel_category_is_visible(region)) {
2624 if (event->type == LEFTMOUSE) {
2625 PanelCategoryDyn *pc_dyn = panel_categories_find_mouse_over(region, event);
2626 if (pc_dyn) {
2627 const bool already_active = STREQ(pc_dyn->idname,
2628 UI_panel_category_active_get(region, false));
2629 UI_panel_category_active_set(region, pc_dyn->idname);
2630
2631 const float aspect = BLI_rctf_size_y(&region->v2d.cur) /
2632 (BLI_rcti_size_y(&region->v2d.mask) + 1);
2633 const bool too_narrow = BLI_rcti_size_x(&region->winrct) <=
2635 aspect));
2636 if (too_narrow) {
2637 /* Enlarge region. */
2638 ui_panel_region_width_set(region, aspect, 250.0f);
2640 }
2641 else if (already_active) {
2642 /* Minimize region. */
2645 }
2646
2647 ED_region_tag_redraw(region);
2648
2649 /* Reset scroll to the top (#38348). */
2650 UI_view2d_offset(&region->v2d, -1.0f, 1.0f);
2651
2652 retval = WM_UI_HANDLER_BREAK;
2653 }
2654 }
2655 else if (((event->type == EVT_TABKEY) && (event->modifier & KM_CTRL)) ||
2657 {
2658 /* Cycle tabs. */
2659 retval = ui_handle_panel_category_cycling(event, region, active_but);
2660 }
2661 if (event->type == EVT_PADPERIOD) {
2662 retval = ui_panel_category_show_active_tab(region, event->xy);
2663 }
2664 }
2665
2666 if (retval == WM_UI_HANDLER_BREAK) {
2667 return retval;
2668 }
2669
2670 const uiBut *region_active_but = ui_region_find_active_but(region);
2671 const bool region_has_active_button = region_active_but &&
2672 region_active_but->type != ButType::Label;
2673
2674 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
2675 Panel *panel = block->panel;
2676 if (panel == nullptr || panel->type == nullptr) {
2677 continue;
2678 }
2679 /* We can't expand or collapse panels without headers, they would disappear. Layout panels can
2680 * be expanded and collapsed though. */
2681 const bool has_panel_header = !(panel->type->flag & PANEL_TYPE_NO_HEADER);
2682
2683 int mx = event->xy[0];
2684 int my = event->xy[1];
2685 ui_window_to_block(region, block, &mx, &my);
2686
2687 const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
2688
2689 if (has_panel_header && mouse_state != PANEL_MOUSE_OUTSIDE) {
2690 /* Mark panels that have been interacted with so their expansion
2691 * doesn't reset when property search finishes. */
2694
2695 /* The panel collapse / expand key "A" is special as it takes priority over
2696 * active button handling. */
2697 if ((event->type == EVT_AKEY) && (event->modifier == 0)) {
2698 retval = WM_UI_HANDLER_BREAK;
2700 C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT);
2701 break;
2702 }
2703 }
2704
2705 /* Don't do any other panel handling with an active button. */
2706 if (region_has_active_button) {
2707 continue;
2708 }
2709
2710 if (has_panel_header && mouse_state == PANEL_MOUSE_INSIDE_HEADER) {
2711 /* All mouse clicks inside panel headers should return in break. */
2712 if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) {
2713 retval = WM_UI_HANDLER_BREAK;
2715 C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT);
2716 }
2717 else if (event->type == RIGHTMOUSE) {
2718 retval = WM_UI_HANDLER_BREAK;
2719 ui_popup_context_menu_for_panel(C, region, block->panel);
2720 }
2721 break;
2722 }
2723 if (mouse_state == PANEL_MOUSE_INSIDE_LAYOUT_PANEL_HEADER) {
2724 if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) {
2725 retval = WM_UI_HANDLER_BREAK;
2726 ui_handle_layout_panel_header(C, block, mx, my, event->type);
2727 }
2728 }
2729 }
2730
2731 return retval;
2732}
2733
2734static void ui_panel_custom_data_set_recursive(Panel *panel, PointerRNA *custom_data)
2735{
2736 panel->runtime->custom_data_ptr = custom_data;
2737
2738 LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
2739 ui_panel_custom_data_set_recursive(child_panel, custom_data);
2740 }
2741}
2742
2744{
2745 panel->layout->context_ptr_set(name, ptr);
2746 panel->runtime->context = panel->layout->context_store();
2747}
2748
2750{
2751 BLI_assert(panel->type != nullptr);
2752
2753 /* Free the old custom data, which should be shared among all of the panel's sub-panels. */
2754 if (panel->runtime->custom_data_ptr != nullptr) {
2755 MEM_delete(panel->runtime->custom_data_ptr);
2756 }
2757
2758 ui_panel_custom_data_set_recursive(panel, custom_data);
2759}
2760
2762{
2763 return panel->runtime->custom_data_ptr;
2764}
2765
2767{
2768 ARegion *region = CTX_wm_region(C);
2769 if (region) {
2770 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
2771 Panel *panel = block->panel;
2772 if (panel == nullptr) {
2773 continue;
2774 }
2775
2776 int mx = event->xy[0];
2777 int my = event->xy[1];
2778 ui_window_to_block(region, block, &mx, &my);
2779 const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
2781 return UI_panel_custom_data_get(panel);
2782 }
2783 }
2784 }
2785
2786 return nullptr;
2787}
2788
2790{
2791 return (panel->type->parent == nullptr) && !(panel->type->flag & PANEL_TYPE_INSTANCED);
2792}
2793
2795
2796/* -------------------------------------------------------------------- */
2799
2800/* NOTE: this is modal handler and should not swallow events for animation. */
2801static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
2802{
2803 Panel *panel = static_cast<Panel *>(userdata);
2804 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
2805
2806 /* Verify if we can stop. */
2807 if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
2809 }
2810 else if (event->type == MOUSEMOVE) {
2811 if (data->state == PANEL_STATE_DRAG) {
2812 ui_do_drag(C, event, panel);
2813 }
2814 }
2815 else if (event->type == TIMER && event->customdata == data->animtimer) {
2816 if (data->state == PANEL_STATE_ANIMATION) {
2817 ui_do_animate(C, panel);
2818 }
2819 else if (data->state == PANEL_STATE_DRAG) {
2820 ui_do_drag(C, event, panel);
2821 }
2822 }
2823
2824 data = static_cast<uiHandlePanelData *>(panel->activedata);
2825
2826 if (data && data->state == PANEL_STATE_ANIMATION) {
2828 }
2829 return WM_UI_HANDLER_BREAK;
2830}
2831
2832static void ui_handler_remove_panel(bContext *C, void *userdata)
2833{
2834 Panel *panel = static_cast<Panel *>(userdata);
2835
2837}
2838
2840 wmWindow *win,
2841 const ARegion *region,
2842 Panel *panel,
2844{
2846
2847 if (panel->activedata == nullptr) {
2848 panel->activedata = MEM_callocN(sizeof(uiHandlePanelData), __func__);
2850 &win->modalhandlers,
2853 panel,
2855 }
2856
2857 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
2858
2859 /* Only create a new timer if necessary. Reuse can occur when PANEL_STATE_ANIMATION follows
2860 * PANEL_STATE_DRAG for example (i.e. panel->activedata was present already). */
2861 if (!data->animtimer) {
2863 }
2864
2865 data->state = state;
2866 data->startx = win->eventstate->xy[0];
2867 data->starty = win->eventstate->xy[1];
2868 data->startofsx = panel->ofsx;
2869 data->startofsy = panel->ofsy;
2870 data->start_cur_xmin = region->v2d.cur.xmin;
2871 data->start_cur_ymin = region->v2d.cur.ymin;
2872 data->starttime = BLI_time_now_seconds();
2873}
2874
2881{
2882 uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
2883 wmWindow *win = CTX_wm_window(C);
2884 ARegion *region = CTX_wm_region(C);
2885
2886 if (data != nullptr && data->state == state) {
2887 return;
2888 }
2889
2890 if (state == PANEL_STATE_DRAG) {
2892
2895
2896 panel_handle_data_ensure(C, win, region, panel, state);
2897
2898 /* Initiate edge panning during drags for scrolling beyond the initial region view. */
2899 wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
2901 }
2902 else if (state == PANEL_STATE_ANIMATION) {
2903 panel_set_flag_recursive(panel, PNL_SELECT, false);
2904
2905 panel_handle_data_ensure(C, win, region, panel, state);
2906 }
2907 else if (state == PANEL_STATE_EXIT) {
2909
2910 BLI_assert(data != nullptr);
2911
2912 if (data->animtimer) {
2913 WM_event_timer_remove(CTX_wm_manager(C), win, data->animtimer);
2914 data->animtimer = nullptr;
2915 }
2916
2917 MEM_freeN(data);
2918 panel->activedata = nullptr;
2919
2922 }
2923
2924 ED_region_tag_redraw(region);
2925}
2926
2928{
2929 if (panel->activedata) {
2931 }
2932}
2933
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:563
Panel * BKE_panel_new(PanelType *panel_type)
Definition screen.cc:546
void BLF_size(int fontid, float size)
Definition blf.cc:443
void BLF_enable(int fontid, FontFlags flag)
Definition blf.cc:320
void BLF_shadow(int fontid, FontShadowType type, const float rgba[4]=nullptr)
Definition blf.cc:934
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition blf.cc:476
void BLF_shadow_offset(int fontid, int x, int y)
Definition blf.cc:946
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:440
void BLF_rotation(int fontid, float angle)
Definition blf.cc:903
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:585
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:802
void BLF_disable(int fontid, FontFlags flag)
Definition blf.cc:329
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:388
@ BLF_ROTATION
Definition BLF_enums.hh:33
@ BLF_SHADOW
Definition BLF_enums.hh:35
FontShadowType
Definition BLF_enums.hh:13
#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])
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.cc:637
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
size_t size_t size_t BLI_snprintf_utf8(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
unsigned char uchar
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#define STREQLEN(a, b, n)
#define IN_RANGE(a, b, c)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define MAX_NAME
Definition DNA_defs.h:50
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ PNL_SELECT
@ PNL_PIN
@ PNL_CLOSED
@ PNL_INSTANCED_LIST_ORDER_CHANGED
@ RGN_ALIGN_RIGHT
@ RGN_ALIGN_FLOAT
@ RGN_TYPE_WINDOW
@ RGN_TYPE_TOOLS
#define RGN_TYPE_HAS_CATEGORY_MASK
@ RGN_FLAG_SEARCH_FILTER_UPDATE
@ RGN_FLAG_SEARCH_FILTER_ACTIVE
@ 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:647
bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2])
Definition area_query.cc:93
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void immUniformColor4ubv(const unsigned char rgba[4])
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader 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
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define C
Definition RandGen.cpp:29
bool UI_panel_is_closed(const Panel *panel)
#define UI_UNIT_Y
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
void ui_draw_dropshadow(const rctf *rct, float radius, float width, float aspect, float alpha)
void UI_block_theme_style_set(uiBlock *block, char theme_style)
#define INSTANCED_PANEL_UNIQUE_STR_SIZE
#define UI_PANEL_WIDTH
#define UI_PANEL_CATEGORY_MIN_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)
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
@ UI_CNR_NONE
#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)
@ UI_BLOCK_THEME_STYLE_POPUP
#define UI_PANEL_MARGIN_Y
bool UI_panel_is_dragging(const Panel *panel)
#define UI_UNIT_X
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)
@ TH_TAB_OUTLINE
@ TH_PANEL_HEADER
@ TH_TAB_ACTIVE
@ TH_BACK
@ TH_TAB_TEXT
@ TH_PANEL_OUTLINE
@ TH_PANEL_SUB_BACK
@ TH_PANEL_ACTIVE
@ TH_TAB_TEXT_HI
@ TH_TAB_OUTLINE_ACTIVE
@ TH_TITLE
@ TH_TAB_BACK
@ TH_TAB_INACTIVE
@ TH_MATCH
@ TH_PANEL_BACK
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])
int UI_ThemeMenuShadowWidth()
void UI_view2d_curRect_validate(View2D *v2d)
Definition view2d.cc:828
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:1957
eWM_EventHandlerFlag
Definition WM_api.hh:577
#define WM_UI_HANDLER_CONTINUE
Definition WM_types.hh:348
@ KM_CTRL
Definition WM_types.hh:279
@ KM_SHIFT
Definition WM_types.hh:278
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
#define NC_SCREEN
Definition WM_types.hh:377
#define NA_EDITED
Definition WM_types.hh:584
#define WM_UI_HANDLER_BREAK
Definition WM_types.hh:349
#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
nullptr float
static float is_left(const float2 &p0, const float2 &p1, const float2 &p2)
#define offsetof(t, d)
uint pos
#define printf(...)
#define floor
uint padding(uint offset, uint alignment)
void ui_window_to_block(const ARegion *region, const uiBlock *block, int *x, int *y)
Definition interface.cc:228
void ui_block_bounds_calc(uiBlock *block)
Definition interface.cc:490
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:191
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, blender::wm::OpCallContext opcontext)
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
@ UI_HIDDEN
static void ui_panel_region_width_set(ARegion *region, const float aspect, int unscaled_size)
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)
static void panel_draw_border(const Panel *panel, const rcti *rect, const rcti *header_rect, const bool is_active)
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 void panel_draw_softshadow(const rctf *box_rect, const int roundboxalign, const float radius, const float shadow_width)
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_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]
const char * name
#define sqrtf
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
float menu_shadow_fac
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
ButType type
uiButtonGroupFlag flag
blender::Vector< uiBut * > buttons
uiHandlePanelState state
bContextStore * context_store() const
void context_ptr_set(blender::StringRef name, const PointerRNA *ptr)
uiFontStyle paneltitle
short panelspace
uiFontStyle widget
unsigned char text[4]
wmEventModifierFlag modifier
Definition WM_types.hh:774
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int xy[2]
Definition WM_types.hh:761
int mval[2]
Definition WM_types.hh:763
void * customdata
Definition WM_types.hh:807
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)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#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:4238
wmOperatorType * ot
Definition wm_files.cc:4237
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:145