Blender  V3.3
interface_panel.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 /* a full doc with API notes can be found in
9  * bf-blender/trunk/blender/doc/guides/interface_API.txt */
10 
11 #include <ctype.h>
12 #include <math.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include "MEM_guardedalloc.h"
17 
18 #include "PIL_time.h"
19 
20 #include "BLI_blenlib.h"
21 #include "BLI_math.h"
22 #include "BLI_utildefines.h"
23 
24 #include "BLT_translation.h"
25 
26 #include "DNA_screen_types.h"
27 #include "DNA_userdef_types.h"
28 
29 #include "BKE_context.h"
30 #include "BKE_screen.h"
31 
32 #include "RNA_access.h"
33 
34 #include "BLF_api.h"
35 
36 #include "WM_api.h"
37 #include "WM_types.h"
38 
39 #include "ED_screen.h"
40 
41 #include "UI_interface.h"
42 #include "UI_interface_icons.h"
43 #include "UI_resources.h"
44 #include "UI_view2d.h"
45 
46 #include "GPU_batch_presets.h"
47 #include "GPU_immediate.h"
48 #include "GPU_matrix.h"
49 #include "GPU_state.h"
50 
51 #include "interface_intern.h"
52 
53 /* -------------------------------------------------------------------- */
57 #define ANIMATION_TIME 0.30
58 #define ANIMATION_INTERVAL 0.02
59 
60 typedef enum uiPanelRuntimeFlag {
61  PANEL_LAST_ADDED = (1 << 0),
62  PANEL_ACTIVE = (1 << 2),
63  PANEL_WAS_ACTIVE = (1 << 3),
64  PANEL_ANIM_ALIGN = (1 << 4),
65  PANEL_NEW_ADDED = (1 << 5),
73  PANEL_WAS_CLOSED = (1 << 9),
78  PANEL_IS_DRAG_DROP = (1 << 10),
80  PANEL_ACTIVE_BORDER = (1 << 11),
82 
83 /* The state of the mouse position relative to the panel. */
84 typedef enum uiPanelMouseState {
89 
90 typedef enum uiHandlePanelState {
95 
96 typedef struct uiHandlePanelData {
98 
99  /* Animation. */
101  double starttime;
102 
103  /* Dragging. */
108 
109 typedef struct PanelSort {
114 
115 static void panel_set_expansion_from_list_data(const bContext *C, Panel *panel);
116 static int get_panel_real_size_y(const Panel *panel);
117 static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelState state);
118 static int compare_panel(const void *a, const void *b);
119 static bool panel_type_context_poll(ARegion *region,
120  const PanelType *panel_type,
121  const char *context);
122 
125 /* -------------------------------------------------------------------- */
130  Panel **r_panel_animation,
131  bool *r_no_animation)
132 {
133  LISTBASE_FOREACH (Panel *, panel, lb) {
134  /* Detect panel active flag changes. */
135  if (!(panel->type && panel->type->parent)) {
136  if ((panel->runtime_flag & PANEL_WAS_ACTIVE) && !(panel->runtime_flag & PANEL_ACTIVE)) {
137  return true;
138  }
139  if (!(panel->runtime_flag & PANEL_WAS_ACTIVE) && (panel->runtime_flag & PANEL_ACTIVE)) {
140  return true;
141  }
142  }
143 
144  /* Detect changes in panel expansions. */
145  if ((bool)(panel->runtime_flag & PANEL_WAS_CLOSED) != UI_panel_is_closed(panel)) {
146  *r_panel_animation = panel;
147  return false;
148  }
149 
150  if ((panel->runtime_flag & PANEL_ACTIVE) && !UI_panel_is_closed(panel)) {
151  if (panel_active_animation_changed(&panel->children, r_panel_animation, r_no_animation)) {
152  return true;
153  }
154  }
155 
156  /* Detect animation. */
157  if (panel->activedata) {
158  uiHandlePanelData *data = panel->activedata;
159  if (data->state == PANEL_STATE_ANIMATION) {
160  *r_panel_animation = panel;
161  }
162  else {
163  /* Don't animate while handling other interaction. */
164  *r_no_animation = true;
165  }
166  }
167  if ((panel->runtime_flag & PANEL_ANIM_ALIGN) && !(*r_panel_animation)) {
168  *r_panel_animation = panel;
169  }
170  }
171 
172  return false;
173 }
174 
178 static bool properties_space_needs_realign(const ScrArea *area, const ARegion *region)
179 {
180  if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) {
181  SpaceProperties *sbuts = area->spacedata.first;
182 
183  if (sbuts->mainbo != sbuts->mainb) {
184  return true;
185  }
186  }
187 
188  return false;
189 }
190 
191 static bool panels_need_realign(const ScrArea *area, ARegion *region, Panel **r_panel_animation)
192 {
193  *r_panel_animation = NULL;
194 
195  if (properties_space_needs_realign(area, region)) {
196  return true;
197  }
198 
199  /* Detect if a panel was added or removed. */
200  Panel *panel_animation = NULL;
201  bool no_animation = false;
202  if (panel_active_animation_changed(&region->panels, &panel_animation, &no_animation)) {
203  return true;
204  }
205 
206  /* Detect panel marked for animation, if we're not already animating. */
207  if (panel_animation) {
208  if (!no_animation) {
209  *r_panel_animation = panel_animation;
210  }
211  return true;
212  }
213 
214  return false;
215 }
216 
219 /* -------------------------------------------------------------------- */
224  ListBase *panels,
225  PanelType *panel_type,
226  PointerRNA *custom_data)
227 {
228  Panel *panel = MEM_callocN(sizeof(Panel), __func__);
229  panel->type = panel_type;
230  BLI_strncpy(panel->panelname, panel_type->idname, sizeof(panel->panelname));
231 
232  panel->runtime.custom_data_ptr = custom_data;
233  panel->runtime_flag |= PANEL_NEW_ADDED;
234 
235  /* Add the panel's children too. Although they aren't instanced panels, we can still use this
236  * function to create them, as UI_panel_begin does other things we don't need to do. */
237  LISTBASE_FOREACH (LinkData *, child, &panel_type->children) {
238  PanelType *child_type = child->data;
239  panel_add_instanced(region, &panel->children, child_type, custom_data);
240  }
241 
242  /* Make sure the panel is added to the end of the display-order as well. This is needed for
243  * loading existing files.
244  *
245  * NOTE: We could use special behavior to place it after the panel that starts the list of
246  * instanced panels, but that would add complexity that isn't needed for now. */
247  int max_sortorder = 0;
248  LISTBASE_FOREACH (Panel *, existing_panel, panels) {
249  if (existing_panel->sortorder > max_sortorder) {
250  max_sortorder = existing_panel->sortorder;
251  }
252  }
253  panel->sortorder = max_sortorder + 1;
254 
255  BLI_addtail(panels, panel);
256 
257  return panel;
258 }
259 
261  ARegion *region,
262  ListBase *panels,
263  const char *panel_idname,
264  PointerRNA *custom_data)
265 {
266  ARegionType *region_type = region->type;
267 
268  PanelType *panel_type = BLI_findstring(
269  &region_type->paneltypes, panel_idname, offsetof(PanelType, idname));
270 
271  if (panel_type == NULL) {
272  printf("Panel type '%s' not found.\n", panel_idname);
273  return NULL;
274  }
275 
276  Panel *new_panel = panel_add_instanced(region, panels, panel_type, custom_data);
277 
278  /* Do this after #panel_add_instatnced so all sub-panels are added. */
280 
281  return new_panel;
282 }
283 
284 void UI_list_panel_unique_str(Panel *panel, char *r_name)
285 {
286  /* The panel sort-order will be unique for a specific panel type because the instanced
287  * panel list is regenerated for every change in the data order / length. */
288  snprintf(r_name, INSTANCED_PANEL_UNIQUE_STR_LEN, "%d", panel->sortorder);
289 }
290 
298 static void panel_delete(const bContext *C, ARegion *region, ListBase *panels, Panel *panel)
299 {
300  /* Recursively delete children. */
301  LISTBASE_FOREACH_MUTABLE (Panel *, child, &panel->children) {
302  panel_delete(C, region, &panel->children, child);
303  }
304  BLI_freelistN(&panel->children);
305 
306  BLI_remlink(panels, panel);
307  if (panel->activedata) {
308  MEM_freeN(panel->activedata);
309  }
310  MEM_freeN(panel);
311 }
312 
314 {
315  /* Delete panels with the instanced flag. */
316  LISTBASE_FOREACH_MUTABLE (Panel *, panel, &region->panels) {
317  if ((panel->type != NULL) && (panel->type->flag & PANEL_TYPE_INSTANCED)) {
318  /* Make sure the panel's handler is removed before deleting it. */
319  if (C != NULL && panel->activedata != NULL) {
321  }
322 
323  /* Free panel's custom data. */
324  if (panel->runtime.custom_data_ptr != NULL) {
325  MEM_freeN(panel->runtime.custom_data_ptr);
326  }
327 
328  /* Free the panel and its sub-panels. */
329  panel_delete(C, region, &region->panels, panel);
330  }
331  }
332 }
333 
335  ListBase *data,
336  uiListPanelIDFromDataFunc panel_idname_func)
337 {
338  /* Check for NULL data. */
339  int data_len = 0;
340  Link *data_link = NULL;
341  if (data == NULL) {
342  data_len = 0;
343  data_link = NULL;
344  }
345  else {
346  data_len = BLI_listbase_count(data);
347  data_link = data->first;
348  }
349 
350  int i = 0;
351  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
352  if (panel->type != NULL && panel->type->flag & PANEL_TYPE_INSTANCED) {
353  /* The panels were reordered by drag and drop. */
354  if (panel->flag & PNL_INSTANCED_LIST_ORDER_CHANGED) {
355  return false;
356  }
357 
358  /* We reached the last data item before the last instanced panel. */
359  if (data_link == NULL) {
360  return false;
361  }
362 
363  /* Check if the panel type matches the panel type from the data item. */
364  char panel_idname[MAX_NAME];
365  panel_idname_func(data_link, panel_idname);
366  if (!STREQ(panel_idname, panel->type->idname)) {
367  return false;
368  }
369 
370  data_link = data_link->next;
371  i++;
372  }
373  }
374 
375  /* If we didn't make it to the last list item, the panel list isn't complete. */
376  if (i != data_len) {
377  return false;
378  }
379 
380  return true;
381 }
382 
383 static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *drag_panel)
384 {
385  /* Without a type we cannot access the reorder callback. */
386  if (drag_panel->type == NULL) {
387  return;
388  }
389  /* Don't reorder if this instanced panel doesn't support drag and drop reordering. */
390  if (drag_panel->type->reorder == NULL) {
391  return;
392  }
393 
394  char *context = NULL;
395  if (!UI_panel_category_is_visible(region)) {
396  context = drag_panel->type->context;
397  }
398 
399  /* Find how many instanced panels with this context string. */
400  int list_panels_len = 0;
401  int start_index = -1;
402  LISTBASE_FOREACH (const Panel *, panel, &region->panels) {
403  if (panel->type) {
404  if (panel->type->flag & PANEL_TYPE_INSTANCED) {
405  if (panel_type_context_poll(region, panel->type, context)) {
406  if (panel == drag_panel) {
407  BLI_assert(start_index == -1); /* This panel should only appear once. */
408  start_index = list_panels_len;
409  }
410  list_panels_len++;
411  }
412  }
413  }
414  }
415  BLI_assert(start_index != -1); /* The drag panel should definitely be in the list. */
416 
417  /* Sort the matching instanced panels by their display order. */
418  PanelSort *panel_sort = MEM_callocN(list_panels_len * sizeof(*panel_sort), __func__);
419  PanelSort *sort_index = panel_sort;
420  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
421  if (panel->type) {
422  if (panel->type->flag & PANEL_TYPE_INSTANCED) {
423  if (panel_type_context_poll(region, panel->type, context)) {
424  sort_index->panel = panel;
425  sort_index++;
426  }
427  }
428  }
429  }
430  qsort(panel_sort, list_panels_len, sizeof(*panel_sort), compare_panel);
431 
432  /* Find how many of those panels are above this panel. */
433  int move_to_index = 0;
434  for (; move_to_index < list_panels_len; move_to_index++) {
435  if (panel_sort[move_to_index].panel == drag_panel) {
436  break;
437  }
438  }
439 
440  MEM_freeN(panel_sort);
441 
442  if (move_to_index == start_index) {
443  /* In this case, the reorder was not changed, so don't do any updates or call the callback. */
444  return;
445  }
446 
447  /* Set the bit to tell the interface to instanced the list. */
448  drag_panel->flag |= PNL_INSTANCED_LIST_ORDER_CHANGED;
449 
450  CTX_store_set(C, drag_panel->runtime.context);
451 
452  /* Finally, move this panel's list item to the new index in its list. */
453  drag_panel->type->reorder(C, drag_panel, move_to_index);
454 
455  CTX_store_set(C, NULL);
456 }
457 
463 static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, short *flag_index)
464 {
465  const bool open = (flag & (1 << *flag_index));
466  bool changed = (open == UI_panel_is_closed(panel));
467 
468  SET_FLAG_FROM_TEST(panel->flag, !open, PNL_CLOSED);
469 
470  LISTBASE_FOREACH (Panel *, child, &panel->children) {
471  *flag_index = *flag_index + 1;
472  changed |= panel_set_expand_from_list_data_recursive(child, flag, flag_index);
473  }
474  return changed;
475 }
476 
482 {
483  BLI_assert(panel->type != NULL);
485  if (panel->type->get_list_data_expand_flag == NULL) {
486  /* Instanced panel doesn't support loading expansion. */
487  return;
488  }
489 
490  const short expand_flag = panel->type->get_list_data_expand_flag(C, panel);
491  short flag_index = 0;
492 
493  /* Start panel animation if the open state was changed. */
494  if (panel_set_expand_from_list_data_recursive(panel, expand_flag, &flag_index)) {
496  }
497 }
498 
503 {
504  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
505  if (panel->runtime_flag & PANEL_ACTIVE) {
506  PanelType *panel_type = panel->type;
507  if (panel_type != NULL && panel->type->flag & PANEL_TYPE_INSTANCED) {
509  }
510  }
511  }
512 }
513 
517 static void get_panel_expand_flag(const Panel *panel, short *flag, short *flag_index)
518 {
519  const bool open = !(panel->flag & PNL_CLOSED);
520  SET_FLAG_FROM_TEST(*flag, open, (1 << *flag_index));
521 
522  LISTBASE_FOREACH (const Panel *, child, &panel->children) {
523  *flag_index = *flag_index + 1;
524  get_panel_expand_flag(child, flag, flag_index);
525  }
526 }
527 
536 static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *region)
537 {
538  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
539  PanelType *panel_type = panel->type;
540  if (panel_type == NULL) {
541  continue;
542  }
543 
544  /* Check for #PANEL_ACTIVE so we only set the expand flag for active panels. */
545  if (panel_type->flag & PANEL_TYPE_INSTANCED && panel->runtime_flag & PANEL_ACTIVE) {
546  short expand_flag;
547  short flag_index = 0;
548  get_panel_expand_flag(panel, &expand_flag, &flag_index);
549  if (panel->type->set_list_data_expand_flag) {
550  panel->type->set_list_data_expand_flag(C, panel, expand_flag);
551  }
552  }
553  }
554 }
555 
558 /* -------------------------------------------------------------------- */
562 static bool panel_custom_data_active_get(const Panel *panel)
563 {
564  /* The caller should make sure the panel is active and has a type. */
566  BLI_assert(panel->type != NULL);
567 
568  if (panel->type->active_property[0] != '\0') {
570  if (ptr != NULL && !RNA_pointer_is_null(ptr)) {
571  return RNA_boolean_get(ptr, panel->type->active_property);
572  }
573  }
574 
575  return false;
576 }
577 
579 {
580  /* Since the panel is interacted with, it should be active and have a type. */
582  BLI_assert(panel->type != NULL);
583 
584  if (panel->type->active_property[0] != '\0') {
587  if (ptr != NULL && !RNA_pointer_is_null(ptr)) {
588  RNA_boolean_set(ptr, panel->type->active_property, true);
589  }
590  }
591 }
592 
596 static void panel_set_flag_recursive(Panel *panel, short flag, bool value)
597 {
598  SET_FLAG_FROM_TEST(panel->flag, value, flag);
599 
600  LISTBASE_FOREACH (Panel *, child, &panel->children) {
601  panel_set_flag_recursive(child, flag, value);
602  }
603 }
604 
608 static void panel_set_runtime_flag_recursive(Panel *panel, short flag, bool value)
609 {
610  SET_FLAG_FROM_TEST(panel->runtime_flag, value, flag);
611 
612  LISTBASE_FOREACH (Panel *, sub_panel, &panel->children) {
613  panel_set_runtime_flag_recursive(sub_panel, flag, value);
614  }
615 }
616 
617 static void panels_collapse_all(ARegion *region, const Panel *from_panel)
618 {
619  const bool has_category_tabs = UI_panel_category_is_visible(region);
620  const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : NULL;
621  const PanelType *from_pt = from_panel->type;
622 
623  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
624  PanelType *pt = panel->type;
625 
626  /* Close panels with headers in the same context. */
627  if (pt && from_pt && !(pt->flag & PANEL_TYPE_NO_HEADER)) {
628  if (!pt->context[0] || !from_pt->context[0] || STREQ(pt->context, from_pt->context)) {
629  if ((panel->flag & PNL_PIN) || !category || !pt->category[0] ||
630  STREQ(pt->category, category)) {
631  panel->flag |= PNL_CLOSED;
632  }
633  }
634  }
635  }
636 }
637 
638 static bool panel_type_context_poll(ARegion *region,
639  const PanelType *panel_type,
640  const char *context)
641 {
642  if (!BLI_listbase_is_empty(&region->panels_category)) {
643  return STREQ(panel_type->category, UI_panel_category_active_get(region, false));
644  }
645 
646  if (panel_type->context[0] && STREQ(panel_type->context, context)) {
647  return true;
648  }
649 
650  return false;
651 }
652 
654 {
655  const char *idname = pt->idname;
656 
657  LISTBASE_FOREACH (Panel *, panel, lb) {
658  if (STREQLEN(panel->panelname, idname, sizeof(panel->panelname))) {
659  return panel;
660  }
661  }
662  return NULL;
663 }
664 
666  ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open)
667 {
668  Panel *panel_last;
669  const char *drawname = CTX_IFACE_(pt->translation_context, pt->label);
670  const char *idname = pt->idname;
671  const bool newpanel = (panel == NULL);
672 
673  if (newpanel) {
674  panel = MEM_callocN(sizeof(Panel), __func__);
675  panel->type = pt;
676  BLI_strncpy(panel->panelname, idname, sizeof(panel->panelname));
677 
678  if (pt->flag & PANEL_TYPE_DEFAULT_CLOSED) {
679  panel->flag |= PNL_CLOSED;
680  panel->runtime_flag |= PANEL_WAS_CLOSED;
681  }
682 
683  panel->ofsx = 0;
684  panel->ofsy = 0;
685  panel->sizex = 0;
686  panel->sizey = 0;
687  panel->blocksizex = 0;
688  panel->blocksizey = 0;
689  panel->runtime_flag |= PANEL_NEW_ADDED;
690 
691  BLI_addtail(lb, panel);
692  }
693  else {
694  /* Panel already exists. */
695  panel->type = pt;
696  }
697 
698  panel->runtime.block = block;
699 
700  BLI_strncpy(panel->drawname, drawname, sizeof(panel->drawname));
701 
702  /* If a new panel is added, we insert it right after the panel that was last added.
703  * This way new panels are inserted in the right place between versions. */
704  for (panel_last = lb->first; panel_last; panel_last = panel_last->next) {
705  if (panel_last->runtime_flag & PANEL_LAST_ADDED) {
706  BLI_remlink(lb, panel);
707  BLI_insertlinkafter(lb, panel_last, panel);
708  break;
709  }
710  }
711 
712  if (newpanel) {
713  panel->sortorder = (panel_last) ? panel_last->sortorder + 1 : 0;
714 
715  LISTBASE_FOREACH (Panel *, panel_next, lb) {
716  if (panel_next != panel && panel_next->sortorder >= panel->sortorder) {
717  panel_next->sortorder++;
718  }
719  }
720  }
721 
722  if (panel_last) {
723  panel_last->runtime_flag &= ~PANEL_LAST_ADDED;
724  }
725 
726  /* Assign the new panel to the block. */
727  block->panel = panel;
729  if (region->alignment == RGN_ALIGN_FLOAT) {
731  }
732 
733  *r_open = false;
734 
735  if (UI_panel_is_closed(panel)) {
736  return panel;
737  }
738 
739  *r_open = true;
740 
741  return panel;
742 }
743 
745 {
746  uiBlock *block = panel->runtime.block;
747 
749 }
750 
752 {
753  uiBlock *block = panel->runtime.block;
754 
755  /* A button group should always be created in #UI_panel_header_buttons_begin. */
757 
758  uiButtonGroup *button_group = block->button_groups.last;
759 
760  button_group->flag &= ~UI_BUTTON_GROUP_LOCK;
761 
762  /* Repurpose the first header button group if it is empty, in case the first button added to
763  * the panel doesn't add a new group (if the button is created directly rather than through an
764  * interface layout call). */
765  if (BLI_listbase_is_single(&block->button_groups) &&
766  BLI_listbase_is_empty(&button_group->buttons)) {
767  button_group->flag &= ~UI_BUTTON_GROUP_PANEL_HEADER;
768  }
769  else {
770  /* Always add a new button group. Although this may result in many empty groups, without it,
771  * new buttons in the panel body not protected with a #ui_block_new_button_group call would
772  * end up in the panel header group. */
773  ui_block_new_button_group(block, 0);
774  }
775 }
776 
777 static float panel_region_offset_x_get(const ARegion *region)
778 {
779  if (UI_panel_category_is_visible(region)) {
782  }
783  }
784 
785  return 0.0f;
786 }
787 
792 static void panel_calculate_size_recursive(ARegion *region, Panel *panel)
793 {
794  int width = panel->blocksizex;
795  int height = panel->blocksizey;
796 
797  LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
798  if (child_panel->runtime_flag & PANEL_ACTIVE) {
799  panel_calculate_size_recursive(region, child_panel);
800  width = max_ii(width, child_panel->sizex);
801  height += get_panel_real_size_y(child_panel);
802  }
803  }
804 
805  /* Update total panel size. */
806  if (panel->runtime_flag & PANEL_NEW_ADDED) {
807  panel->runtime_flag &= ~PANEL_NEW_ADDED;
808  panel->sizex = width;
809  panel->sizey = height;
810  }
811  else {
812  const int old_sizex = panel->sizex, old_sizey = panel->sizey;
813  const int old_region_ofsx = panel->runtime.region_ofsx;
814 
815  /* Update width/height if non-zero. */
816  if (width != 0) {
817  panel->sizex = width;
818  }
819  if (height != 0 || !UI_panel_is_closed(panel)) {
820  panel->sizey = height;
821  }
822 
823  /* Check if we need to do an animation. */
824  if (panel->sizex != old_sizex || panel->sizey != old_sizey) {
825  panel->runtime_flag |= PANEL_ANIM_ALIGN;
826  panel->ofsy += old_sizey - panel->sizey;
827  }
828 
830  if (old_region_ofsx != panel->runtime.region_ofsx) {
831  panel->runtime_flag |= PANEL_ANIM_ALIGN;
832  }
833  }
834 }
835 
836 void UI_panel_end(Panel *panel, int width, int height)
837 {
838  /* Store the size of the buttons layout in the panel. The actual panel size
839  * (including sub-panels) is calculated in #UI_panels_end. */
840  panel->blocksizex = width;
841  panel->blocksizey = height;
842 }
843 
844 static void ui_offset_panel_block(uiBlock *block)
845 {
846  const uiStyle *style = UI_style_get_dpi();
847 
848  /* Compute bounds and offset. */
849  ui_block_bounds_calc(block);
850 
851  const int ofsy = block->panel->sizey - style->panelspace;
852 
853  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
854  but->rect.ymin += ofsy;
855  but->rect.ymax += ofsy;
856  }
857 
858  block->rect.xmax = block->panel->sizex;
859  block->rect.ymax = block->panel->sizey;
860  block->rect.xmin = block->rect.ymin = 0.0;
861 }
862 
864 {
866 }
867 
868 static void panel_matches_search_filter_recursive(const Panel *panel, bool *filter_matches)
869 {
870  *filter_matches |= panel->runtime_flag & PANEL_SEARCH_FILTER_MATCH;
871 
872  /* If the panel has no match we need to make sure that its children are too. */
873  if (!*filter_matches) {
874  LISTBASE_FOREACH (const Panel *, child_panel, &panel->children) {
875  panel_matches_search_filter_recursive(child_panel, filter_matches);
876  }
877  }
878 }
879 
881 {
882  bool search_filter_matches = false;
883  panel_matches_search_filter_recursive(panel, &search_filter_matches);
884  return search_filter_matches;
885 }
886 
891  Panel *panel,
892  const bool use_search_closed)
893 {
894  /* This has to run on inactive panels that may not have a type,
895  * but we can prevent running on header-less panels in some cases. */
896  if (panel->type == NULL || !(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
898  }
899 
900  LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
901  /* Don't check if the sub-panel is active, otherwise the
902  * expansion won't be reset when the parent is closed. */
903  panel_set_expansion_from_search_filter_recursive(C, child_panel, use_search_closed);
904  }
905 }
906 
911  ARegion *region,
912  const bool use_search_closed)
913 {
914  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
915  /* Don't check if the panel is active, otherwise the expansion won't
916  * be correct when switching back to tab after exiting search. */
917  panel_set_expansion_from_search_filter_recursive(C, panel, use_search_closed);
918  }
920 }
921 
926 static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *parent_panel)
927 {
928  uiBlock *block = panel->runtime.block;
929  BLI_assert(block != NULL);
930  BLI_assert(block->active);
931  if (parent_panel != NULL && UI_panel_is_closed(parent_panel)) {
932  /* The parent panel is closed, so this panel can be completely removed. */
933  UI_block_set_search_only(block, true);
934  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
935  but->flag |= UI_HIDDEN;
936  }
937  }
938  else if (UI_panel_is_closed(panel)) {
939  /* If sub-panels have no search results but the parent panel does, then the parent panel open
940  * and the sub-panels will close. In that case there must be a way to hide the buttons in the
941  * panel but keep the header buttons. */
942  LISTBASE_FOREACH (uiButtonGroup *, button_group, &block->button_groups) {
943  if (button_group->flag & UI_BUTTON_GROUP_PANEL_HEADER) {
944  continue;
945  }
946  LISTBASE_FOREACH (LinkData *, link, &button_group->buttons) {
947  uiBut *but = link->data;
948  but->flag |= UI_HIDDEN;
949  }
950  }
951  }
952 
953  LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
954  if (child_panel->runtime_flag & PANEL_ACTIVE) {
955  BLI_assert(child_panel->runtime.block != NULL);
956  panel_remove_invisible_layouts_recursive(child_panel, panel);
957  }
958  }
959 }
960 
962 {
963  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
964  if (panel->runtime_flag & PANEL_ACTIVE) {
965  BLI_assert(panel->runtime.block != NULL);
967  }
968  }
969 }
970 
971 bool UI_panel_is_closed(const Panel *panel)
972 {
973  /* Header-less panels can never be closed, otherwise they could disappear. */
974  if (panel->type && panel->type->flag & PANEL_TYPE_NO_HEADER) {
975  return false;
976  }
977 
979  return !UI_panel_matches_search_filter(panel);
980  }
981 
982  return panel->flag & PNL_CLOSED;
983 }
984 
985 bool UI_panel_is_active(const Panel *panel)
986 {
987  return panel->runtime_flag & PANEL_ACTIVE;
988 }
989 
992 /* -------------------------------------------------------------------- */
996 void UI_panels_draw(const bContext *C, ARegion *region)
997 {
998  /* Draw in reverse order, because #uiBlocks are added in reverse order
999  * and we need child panels to draw on top. */
1000  LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->uiblocks) {
1001  if (block->active && block->panel && !UI_panel_is_dragging(block->panel) &&
1002  !UI_block_is_search_only(block)) {
1003  UI_block_draw(C, block);
1004  }
1005  }
1006 
1007  LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->uiblocks) {
1008  if (block->active && block->panel && UI_panel_is_dragging(block->panel) &&
1009  !UI_block_is_search_only(block)) {
1010  UI_block_draw(C, block);
1011  }
1012  }
1013 }
1014 
1015 #define PNL_ICON UI_UNIT_X /* Could be UI_UNIT_Y too. */
1016 
1017 void UI_panel_label_offset(const uiBlock *block, int *r_x, int *r_y)
1018 {
1019  Panel *panel = block->panel;
1020  const bool is_subpanel = (panel->type && panel->type->parent);
1021 
1022  *r_x = UI_UNIT_X * 1.0f;
1023  *r_y = UI_UNIT_Y * 1.5f;
1024 
1025  if (is_subpanel) {
1026  *r_x += (0.7f * UI_UNIT_X);
1027  }
1028 }
1029 
1030 static void panel_title_color_get(const Panel *panel,
1031  const bool show_background,
1032  const bool region_search_filter_active,
1033  uchar r_color[4])
1034 {
1035  if (!show_background) {
1036  /* Use menu colors for floating panels. */
1037  bTheme *btheme = UI_GetTheme();
1038  const uiWidgetColors *wcol = &btheme->tui.wcol_menu_back;
1039  copy_v4_v4_uchar(r_color, (const uchar *)wcol->text);
1040  return;
1041  }
1042 
1043  const bool search_match = UI_panel_matches_search_filter(panel);
1044 
1045  UI_GetThemeColor4ubv(TH_TITLE, r_color);
1046  if (region_search_filter_active && !search_match) {
1047  r_color[0] *= 0.5;
1048  r_color[1] *= 0.5;
1049  r_color[2] *= 0.5;
1050  }
1051 }
1052 
1053 static void panel_draw_highlight_border(const Panel *panel,
1054  const rcti *rect,
1055  const rcti *header_rect)
1056 {
1057  const bool is_subpanel = panel->type->parent != NULL;
1058  if (is_subpanel) {
1059  return;
1060  }
1061 
1062  const bTheme *btheme = UI_GetTheme();
1063  const float aspect = panel->runtime.block->aspect;
1064  const float radius = (btheme->tui.panel_roundness * U.widget_unit * 0.5f) / aspect;
1066 
1067  float color[4];
1070  &(const rctf){
1071  .xmin = rect->xmin,
1072  .xmax = rect->xmax,
1073  .ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin,
1074  .ymax = header_rect->ymax,
1075  },
1076  false,
1077  radius,
1078  color);
1079 }
1080 
1081 static void panel_draw_aligned_widgets(const uiStyle *style,
1082  const Panel *panel,
1083  const rcti *header_rect,
1084  const float aspect,
1085  const bool show_pin,
1086  const bool show_background,
1087  const bool region_search_filter_active)
1088 {
1089  const bool is_subpanel = panel->type->parent != NULL;
1090  const uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle;
1091 
1092  const int header_height = BLI_rcti_size_y(header_rect);
1093  const int scaled_unit = round_fl_to_int(UI_UNIT_X / aspect);
1094 
1095  /* Offset triangle and text to the right for sub-panels. */
1096  const rcti widget_rect = {
1097  .xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0),
1098  .xmax = header_rect->xmax,
1099  .ymin = header_rect->ymin,
1100  .ymax = header_rect->ymax,
1101  };
1102 
1103  uchar title_color[4];
1104  panel_title_color_get(panel, show_background, region_search_filter_active, title_color);
1105  title_color[3] = 255;
1106 
1107  /* Draw collapse icon. */
1108  {
1109  const float size_y = BLI_rcti_size_y(&widget_rect);
1111  UI_icon_draw_ex(widget_rect.xmin + size_y * 0.2f,
1112  widget_rect.ymin + size_y * 0.2f,
1113  UI_panel_is_closed(panel) ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT,
1114  aspect * U.inv_dpi_fac,
1115  0.7f,
1116  0.0f,
1117  title_color,
1118  false);
1120  }
1121 
1122  /* Draw text label. */
1123  if (panel->drawname[0] != '\0') {
1124  const rcti title_rect = {
1125  .xmin = widget_rect.xmin + (panel->labelofs / aspect) + scaled_unit * 1.1f,
1126  .xmax = widget_rect.xmax,
1127  .ymin = widget_rect.ymin - 2.0f / aspect,
1128  .ymax = widget_rect.ymax,
1129  };
1130  UI_fontstyle_draw(fontstyle,
1131  &title_rect,
1132  panel->drawname,
1133  sizeof(panel->drawname),
1134  title_color,
1135  &(struct uiFontStyleDraw_Params){
1136  .align = UI_STYLE_TEXT_LEFT,
1137  });
1138  }
1139 
1140  /* Draw the pin icon. */
1141  if (show_pin && (panel->flag & PNL_PIN)) {
1143  UI_icon_draw_ex(widget_rect.xmax - scaled_unit * 2.2f,
1144  widget_rect.ymin + 5.0f / aspect,
1145  ICON_PINNED,
1146  aspect * U.inv_dpi_fac,
1147  1.0f,
1148  0.0f,
1149  title_color,
1150  false);
1152  }
1153 
1154  /* Draw drag widget. */
1155  if (!is_subpanel && show_background) {
1156  const int drag_widget_size = header_height * 0.7f;
1157  GPU_matrix_push();
1158  /* The magic numbers here center the widget vertically and offset it to the left.
1159  * Currently this depends on the height of the header, although it could be independent. */
1160  GPU_matrix_translate_2f(widget_rect.xmax - scaled_unit * 1.15,
1161  widget_rect.ymin + (header_height - drag_widget_size) * 0.5f);
1162 
1163  const int col_tint = 84;
1164  float color_high[4], color_dark[4];
1165  UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, color_high);
1166  UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, color_dark);
1167 
1169  U.pixelsize, color_high, color_dark, drag_widget_size);
1172  GPU_matrix_pop();
1173  }
1174 }
1175 
1176 static void panel_draw_aligned_backdrop(const Panel *panel,
1177  const rcti *rect,
1178  const rcti *header_rect)
1179 {
1180  const bool is_subpanel = panel->type->parent != NULL;
1181  const bool is_open = !UI_panel_is_closed(panel);
1182 
1183  if (is_subpanel && !is_open) {
1184  return;
1185  }
1186 
1187  const bTheme *btheme = UI_GetTheme();
1188  const float aspect = panel->runtime.block->aspect;
1189  const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f / aspect;
1190 
1193 
1194  /* Panel backdrop. */
1195  if (is_open || panel->type->flag & PANEL_TYPE_NO_HEADER) {
1196  float panel_backcolor[4];
1198  UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor);
1199 
1201  &(const rctf){
1202  .xmin = rect->xmin,
1203  .xmax = rect->xmax,
1204  .ymin = rect->ymin,
1205  .ymax = rect->ymax,
1206  },
1207  true,
1208  radius,
1209  panel_backcolor);
1210  }
1211 
1212  /* Panel header backdrops for non sub-panels. */
1213  if (!is_subpanel) {
1214  float panel_headercolor[4];
1216  panel_headercolor);
1218 
1219  /* Change the width a little bit to line up with the sides. */
1221  &(const rctf){
1222  .xmin = rect->xmin,
1223  .xmax = rect->xmax,
1224  .ymin = header_rect->ymin,
1225  .ymax = header_rect->ymax,
1226  },
1227  true,
1228  radius,
1229  panel_headercolor);
1230  }
1231 
1233  immUnbindProgram();
1234 }
1235 
1236 void ui_draw_aligned_panel(const uiStyle *style,
1237  const uiBlock *block,
1238  const rcti *rect,
1239  const bool show_pin,
1240  const bool show_background,
1241  const bool region_search_filter_active)
1242 {
1243  const Panel *panel = block->panel;
1244 
1245  /* Add 0.001f to prevent flicker from float inaccuracy. */
1246  const rcti header_rect = {
1247  rect->xmin,
1248  rect->xmax,
1249  rect->ymax,
1250  rect->ymax + floor(PNL_HEADER / block->aspect + 0.001f),
1251  };
1252 
1253  if (show_background) {
1254  panel_draw_aligned_backdrop(panel, rect, &header_rect);
1255  }
1256 
1257  /* Draw the widgets and text in the panel header. */
1258  if (!(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1260  panel,
1261  &header_rect,
1262  block->aspect,
1263  show_pin,
1264  show_background,
1265  region_search_filter_active);
1266  }
1267 
1268  if (panel_custom_data_active_get(panel)) {
1269  panel_draw_highlight_border(panel, rect, &header_rect);
1270  }
1271 }
1272 
1273 bool UI_panel_should_show_background(const ARegion *region, const PanelType *panel_type)
1274 {
1275  if (region->alignment == RGN_ALIGN_FLOAT) {
1276  return false;
1277  }
1278 
1279  if (panel_type && panel_type->flag & PANEL_TYPE_NO_HEADER) {
1280  if (region->regiontype == RGN_TYPE_TOOLS) {
1281  /* We never want a background around active tools. */
1282  return false;
1283  }
1284  /* Without a header there is no background except for region overlap. */
1285  return region->overlap != 0;
1286  }
1287 
1288  return true;
1289 }
1290 
1293 /* -------------------------------------------------------------------- */
1297 #define TABS_PADDING_BETWEEN_FACTOR 4.0f
1298 #define TABS_PADDING_TEXT_FACTOR 6.0f
1299 
1300 void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
1301 {
1302  // #define USE_FLAT_INACTIVE
1303  const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT);
1304  View2D *v2d = &region->v2d;
1305  const uiStyle *style = UI_style_get();
1306  const uiFontStyle *fstyle = &style->widget;
1307  const int fontid = fstyle->uifont_id;
1308  float fstyle_points = fstyle->points;
1309  const float aspect = ((uiBlock *)region->uiblocks.first)->aspect;
1310  const float zoom = 1.0f / aspect;
1311  const int px = U.pixelsize;
1312  const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom);
1313  const float dpi_fac = UI_DPI_FAC;
1314  /* Padding of tabs around text. */
1315  const int tab_v_pad_text = round_fl_to_int(TABS_PADDING_TEXT_FACTOR * dpi_fac * zoom) + 2 * px;
1316  /* Padding between tabs. */
1317  const int tab_v_pad = round_fl_to_int(TABS_PADDING_BETWEEN_FACTOR * dpi_fac * zoom);
1318  bTheme *btheme = UI_GetTheme();
1319  const float tab_curve_radius = btheme->tui.wcol_tab.roundness * U.widget_unit * zoom;
1322  bool is_alpha;
1323  bool do_scaletabs = false;
1324 #ifdef USE_FLAT_INACTIVE
1325  bool is_active_prev = false;
1326 #endif
1327  float scaletabs = 1.0f;
1328  /* Same for all tabs. */
1329  /* Intentionally don't scale by 'px'. */
1330  const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width);
1331  const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3);
1332  const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f;
1333 
1334  int y_ofs = tab_v_pad;
1335 
1336  /* Primary theme colors. */
1337  uchar theme_col_back[4];
1338  uchar theme_col_text[3];
1339  uchar theme_col_text_hi[3];
1340 
1341  /* Tab colors. */
1342  uchar theme_col_tab_bg[4];
1343  float theme_col_tab_active[4];
1344  float theme_col_tab_inactive[4];
1345  float theme_col_tab_outline[4];
1346 
1347  UI_GetThemeColor4ubv(TH_BACK, theme_col_back);
1348  UI_GetThemeColor3ubv(TH_TEXT, theme_col_text);
1349  UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi);
1350 
1351  UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg);
1352  UI_GetThemeColor4fv(TH_TAB_ACTIVE, theme_col_tab_active);
1353  UI_GetThemeColor4fv(TH_TAB_INACTIVE, theme_col_tab_inactive);
1354  UI_GetThemeColor4fv(TH_TAB_OUTLINE, theme_col_tab_outline);
1355 
1356  is_alpha = (region->overlap && (theme_col_back[3] != 255));
1357 
1358  BLF_enable(fontid, BLF_ROTATION);
1359  BLF_rotation(fontid, M_PI_2);
1360  ui_fontscale(&fstyle_points, aspect);
1361  BLF_size(fontid, fstyle_points * U.pixelsize, U.dpi);
1362 
1363  /* Check the region type supports categories to avoid an assert
1364  * for showing 3D view panels in the properties space. */
1365  if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) {
1367  }
1368 
1369  /* Calculate tab rectangle and check if we need to scale down. */
1370  LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1371  rcti *rct = &pc_dyn->rect;
1372  const char *category_id = pc_dyn->idname;
1373  const char *category_id_draw = IFACE_(category_id);
1374  const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
1375 
1376  rct->xmin = rct_xmin;
1377  rct->xmax = rct_xmax;
1378 
1379  rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2));
1380  rct->ymax = v2d->mask.ymax - (y_ofs);
1381 
1382  y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2);
1383  }
1384 
1385  if (y_ofs > BLI_rcti_size_y(&v2d->mask)) {
1386  scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs;
1387 
1388  LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1389  rcti *rct = &pc_dyn->rect;
1390  rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
1391  rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
1392  }
1393 
1394  do_scaletabs = true;
1395  }
1396 
1397  /* Begin drawing. */
1398  GPU_line_smooth(true);
1399 
1403 
1404  /* Draw the background. */
1405  if (is_alpha) {
1407  immUniformColor4ubv(theme_col_tab_bg);
1408  }
1409  else {
1410  immUniformColor3ubv(theme_col_tab_bg);
1411  }
1412 
1413  if (is_left) {
1414  immRecti(
1415  pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax);
1416  }
1417  else {
1418  immRecti(
1419  pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax);
1420  }
1421 
1422  if (is_alpha) {
1424  }
1425 
1426  immUnbindProgram();
1427 
1428  LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1429  const rcti *rct = &pc_dyn->rect;
1430  const char *category_id = pc_dyn->idname;
1431  const char *category_id_draw = IFACE_(category_id);
1432  const int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2);
1433  size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
1434 #if 0
1435  int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
1436 #endif
1437 
1438  const bool is_active = STREQ(category_id, category_id_active);
1439 
1441 
1442 #ifdef USE_FLAT_INACTIVE
1443  /* Draw line between inactive tabs. */
1444  if (is_active == false && is_active_prev == false && pc_dyn->prev) {
1448  immUniformColor3fvAlpha(theme_col_tab_outline, 0.3f);
1449  immRecti(pos,
1450  is_left ? v2d->mask.xmin + (category_tabs_width / 5) :
1451  v2d->mask.xmax - (category_tabs_width / 5),
1452  rct->ymax + px,
1453  is_left ? (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5) :
1454  (v2d->mask.xmax - category_tabs_width) + (category_tabs_width / 5),
1455  rct->ymax + (px * 3));
1456  immUnbindProgram();
1457  }
1458 
1459  is_active_prev = is_active;
1460 
1461  if (is_active)
1462 #endif
1463  {
1464  /* Draw filled rectangle and outline for tab. */
1467  &(const rctf){
1468  .xmin = rct->xmin,
1469  .xmax = rct->xmax,
1470  .ymin = rct->ymin,
1471  .ymax = rct->ymax,
1472  },
1473  true,
1474  tab_curve_radius,
1475  is_active ? theme_col_tab_active : theme_col_tab_inactive);
1477  &(const rctf){
1478  .xmin = rct->xmin,
1479  .xmax = rct->xmax,
1480  .ymin = rct->ymin,
1481  .ymax = rct->ymax,
1482  },
1483  false,
1484  tab_curve_radius,
1485  theme_col_tab_outline);
1486 
1487  /* Disguise the outline on one side to join the tab to the panel. */
1491 
1492  immUniformColor4fv(is_active ? theme_col_tab_active : theme_col_tab_inactive);
1493  immRecti(pos,
1494  is_left ? rct->xmax - px : rct->xmin,
1495  rct->ymin + px,
1496  is_left ? rct->xmax : rct->xmin + px,
1497  rct->ymax - px);
1498  immUnbindProgram();
1499  }
1500 
1501  /* Tab titles. */
1502 
1503  if (do_scaletabs) {
1504  category_draw_len = BLF_width_to_strlen(
1505  fontid, category_id_draw, category_draw_len, category_width, NULL);
1506  }
1507 
1508  BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f);
1509  BLF_color3ubv(fontid, is_active ? theme_col_text_hi : theme_col_text);
1510  BLF_draw(fontid, category_id_draw, category_draw_len);
1511 
1513 
1514  /* Not essential, but allows events to be handled right up to the region edge (T38171). */
1515  if (is_left) {
1516  pc_dyn->rect.xmin = v2d->mask.xmin;
1517  }
1518  else {
1519  pc_dyn->rect.xmax = v2d->mask.xmax;
1520  }
1521  }
1522 
1523  GPU_line_smooth(false);
1524 
1525  BLF_disable(fontid, BLF_ROTATION);
1526 }
1527 
1528 #undef TABS_PADDING_BETWEEN_FACTOR
1529 #undef TABS_PADDING_TEXT_FACTOR
1530 
1533 /* -------------------------------------------------------------------- */
1537 static int get_panel_size_y(const Panel *panel)
1538 {
1539  if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1540  return panel->sizey;
1541  }
1542 
1543  return PNL_HEADER + panel->sizey;
1544 }
1545 
1546 static int get_panel_real_size_y(const Panel *panel)
1547 {
1548  const int sizey = UI_panel_is_closed(panel) ? 0 : panel->sizey;
1549 
1550  if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1551  return sizey;
1552  }
1553 
1554  return PNL_HEADER + sizey;
1555 }
1556 
1557 int UI_panel_size_y(const Panel *panel)
1558 {
1559  return get_panel_real_size_y(panel);
1560 }
1561 
1566 static int get_panel_real_ofsy(Panel *panel)
1567 {
1568  if (UI_panel_is_closed(panel)) {
1569  return panel->ofsy + panel->sizey;
1570  }
1571  return panel->ofsy;
1572 }
1573 
1574 bool UI_panel_is_dragging(const Panel *panel)
1575 {
1576  return panel->runtime_flag & PANEL_IS_DRAG_DROP;
1577 }
1578 
1587 static int find_highest_panel(const void *a, const void *b)
1588 {
1589  const Panel *panel_a = ((PanelSort *)a)->panel;
1590  const Panel *panel_b = ((PanelSort *)b)->panel;
1591 
1592  /* Stick uppermost header-less panels to the top of the region -
1593  * prevent them from being sorted (multiple header-less panels have to be sorted though). */
1594  if (panel_a->type->flag & PANEL_TYPE_NO_HEADER && panel_b->type->flag & PANEL_TYPE_NO_HEADER) {
1595  /* Pass the no-header checks and check for `ofsy` and #Panel.sortorder below. */
1596  }
1597  else if (panel_a->type->flag & PANEL_TYPE_NO_HEADER) {
1598  return -1;
1599  }
1600  else if (panel_b->type->flag & PANEL_TYPE_NO_HEADER) {
1601  return 1;
1602  }
1603 
1604  if (panel_a->ofsy + panel_a->sizey < panel_b->ofsy + panel_b->sizey) {
1605  return 1;
1606  }
1607  if (panel_a->ofsy + panel_a->sizey > panel_b->ofsy + panel_b->sizey) {
1608  return -1;
1609  }
1610  if (panel_a->sortorder > panel_b->sortorder) {
1611  return 1;
1612  }
1613  if (panel_a->sortorder < panel_b->sortorder) {
1614  return -1;
1615  }
1616 
1617  return 0;
1618 }
1619 
1620 static int compare_panel(const void *a, const void *b)
1621 {
1622  const Panel *panel_a = ((PanelSort *)a)->panel;
1623  const Panel *panel_b = ((PanelSort *)b)->panel;
1624 
1625  if (panel_a->sortorder > panel_b->sortorder) {
1626  return 1;
1627  }
1628  if (panel_a->sortorder < panel_b->sortorder) {
1629  return -1;
1630  }
1631 
1632  return 0;
1633 }
1634 
1635 static void align_sub_panels(Panel *panel)
1636 {
1637  /* Position sub panels. */
1638  int ofsy = panel->ofsy + panel->sizey - panel->blocksizey;
1639 
1640  LISTBASE_FOREACH (Panel *, pachild, &panel->children) {
1641  if (pachild->runtime_flag & PANEL_ACTIVE) {
1642  pachild->ofsx = panel->ofsx;
1643  pachild->ofsy = ofsy - get_panel_size_y(pachild);
1644  ofsy -= get_panel_real_size_y(pachild);
1645 
1646  if (pachild->children.first) {
1647  align_sub_panels(pachild);
1648  }
1649  }
1650  }
1651 }
1652 
1656 static bool uiAlignPanelStep(ARegion *region, const float factor, const bool drag)
1657 {
1658  /* Count active panels. */
1659  int active_panels_len = 0;
1660  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1661  if (panel->runtime_flag & PANEL_ACTIVE) {
1662  /* These panels should have types since they are currently displayed to the user. */
1663  BLI_assert(panel->type != NULL);
1664  active_panels_len++;
1665  }
1666  }
1667  if (active_panels_len == 0) {
1668  return false;
1669  }
1670 
1671  /* Sort panels. */
1672  PanelSort *panel_sort = MEM_mallocN(sizeof(PanelSort) * active_panels_len, __func__);
1673  {
1674  PanelSort *ps = panel_sort;
1675  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1676  if (panel->runtime_flag & PANEL_ACTIVE) {
1677  ps->panel = panel;
1678  ps++;
1679  }
1680  }
1681  }
1682 
1683  if (drag) {
1684  /* While dragging, sort based on location and update #Panel.sortorder. */
1685  qsort(panel_sort, active_panels_len, sizeof(PanelSort), find_highest_panel);
1686  for (int i = 0; i < active_panels_len; i++) {
1687  panel_sort[i].panel->sortorder = i;
1688  }
1689  }
1690  else {
1691  /* Otherwise use #Panel.sortorder. */
1692  qsort(panel_sort, active_panels_len, sizeof(PanelSort), compare_panel);
1693  }
1694 
1695  /* X offset. */
1696  const int region_offset_x = panel_region_offset_x_get(region);
1697  for (int i = 0; i < active_panels_len; i++) {
1698  PanelSort *ps = &panel_sort[i];
1699  const bool show_background = UI_panel_should_show_background(region, ps->panel->type);
1700  ps->panel->runtime.region_ofsx = region_offset_x;
1701  ps->new_offset_x = region_offset_x + (show_background ? UI_PANEL_MARGIN_X : 0);
1702  }
1703 
1704  /* Y offset. */
1705  for (int i = 0, y = 0; i < active_panels_len; i++) {
1706  PanelSort *ps = &panel_sort[i];
1707  const bool show_background = UI_panel_should_show_background(region, ps->panel->type);
1708 
1709  y -= get_panel_real_size_y(ps->panel);
1710 
1711  /* Separate panel boxes a bit further (if they are drawn). */
1712  if (show_background) {
1713  y -= UI_PANEL_MARGIN_Y;
1714  }
1715  ps->new_offset_y = y;
1716  /* The header still draws offset by the size of closed panels, so apply the offset here. */
1717  if (UI_panel_is_closed(ps->panel)) {
1718  panel_sort[i].new_offset_y -= ps->panel->sizey;
1719  }
1720  }
1721 
1722  /* Interpolate based on the input factor. */
1723  bool changed = false;
1724  for (int i = 0; i < active_panels_len; i++) {
1725  PanelSort *ps = &panel_sort[i];
1726  if (ps->panel->flag & PNL_SELECT) {
1727  continue;
1728  }
1729 
1730  if (ps->new_offset_x != ps->panel->ofsx) {
1731  const float x = interpf((float)ps->new_offset_x, (float)ps->panel->ofsx, factor);
1732  ps->panel->ofsx = round_fl_to_int(x);
1733  changed = true;
1734  }
1735  if (ps->new_offset_y != ps->panel->ofsy) {
1736  const float y = interpf((float)ps->new_offset_y, (float)ps->panel->ofsy, factor);
1737  ps->panel->ofsy = round_fl_to_int(y);
1738  changed = true;
1739  }
1740  }
1741 
1742  /* Set locations for tabbed and sub panels. */
1743  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1744  if (panel->runtime_flag & PANEL_ACTIVE) {
1745  if (panel->children.first) {
1746  align_sub_panels(panel);
1747  }
1748  }
1749  }
1750 
1751  MEM_freeN(panel_sort);
1752 
1753  return changed;
1754 }
1755 
1756 static void ui_panels_size(ARegion *region, int *r_x, int *r_y)
1757 {
1758  int sizex = 0;
1759  int sizey = 0;
1760  bool has_panel_with_background = false;
1761 
1762  /* Compute size taken up by panels, for setting in view2d. */
1763  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1764  if (panel->runtime_flag & PANEL_ACTIVE) {
1765  const int pa_sizex = panel->ofsx + panel->sizex;
1766  const int pa_sizey = get_panel_real_ofsy(panel);
1767 
1768  sizex = max_ii(sizex, pa_sizex);
1769  sizey = min_ii(sizey, pa_sizey);
1770  if (UI_panel_should_show_background(region, panel->type)) {
1771  has_panel_with_background = true;
1772  }
1773  }
1774  }
1775 
1776  if (sizex == 0) {
1777  sizex = UI_PANEL_WIDTH;
1778  }
1779  if (sizey == 0) {
1780  sizey = -UI_PANEL_WIDTH;
1781  }
1782  /* Extra margin after the list so the view scrolls a few pixels further than the panel border.
1783  * Also makes the bottom match the top margin. */
1784  if (has_panel_with_background) {
1785  sizey -= UI_PANEL_MARGIN_Y;
1786  }
1787 
1788  *r_x = sizex;
1789  *r_y = sizey;
1790 }
1791 
1792 static void ui_do_animate(bContext *C, Panel *panel)
1793 {
1794  uiHandlePanelData *data = panel->activedata;
1795  ARegion *region = CTX_wm_region(C);
1796 
1797  float fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME;
1798  fac = min_ff(sqrtf(fac), 1.0f);
1799 
1800  if (uiAlignPanelStep(region, fac, false)) {
1801  ED_region_tag_redraw(region);
1802  }
1803  else {
1804  if (UI_panel_is_dragging(panel)) {
1805  /* NOTE: doing this in #panel_activate_state would require
1806  * removing `const` for context in many other places. */
1807  reorder_instanced_panel_list(C, region, panel);
1808  }
1809 
1811  }
1812 }
1813 
1815 {
1816  LISTBASE_FOREACH (Panel *, panel, lb) {
1817  /* Flags to copy over to the next layout pass. */
1818  const short flag_copy = PANEL_USE_CLOSED_FROM_SEARCH | PANEL_IS_DRAG_DROP;
1819 
1820  const bool was_active = panel->runtime_flag & PANEL_ACTIVE;
1821  const bool was_closed = UI_panel_is_closed(panel);
1822  panel->runtime_flag &= flag_copy;
1823  SET_FLAG_FROM_TEST(panel->runtime_flag, was_active, PANEL_WAS_ACTIVE);
1824  SET_FLAG_FROM_TEST(panel->runtime_flag, was_closed, PANEL_WAS_CLOSED);
1825 
1826  panels_layout_begin_clear_flags(&panel->children);
1827  }
1828 }
1829 
1830 void UI_panels_begin(const bContext *UNUSED(C), ARegion *region)
1831 {
1832  /* Set all panels as inactive, so that at the end we know which ones were used. Also
1833  * clear other flags so we know later that their values were set for the current redraw. */
1835 }
1836 
1837 void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
1838 {
1839  ScrArea *area = CTX_wm_area(C);
1840 
1842 
1843  const bool region_search_filter_active = region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE;
1844 
1845  if (properties_space_needs_realign(area, region)) {
1846  region_panels_set_expansion_from_search_filter(C, region, region_search_filter_active);
1847  }
1848  else if (region->flag & RGN_FLAG_SEARCH_FILTER_UPDATE) {
1849  region_panels_set_expansion_from_search_filter(C, region, region_search_filter_active);
1850  }
1851 
1852  if (region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE) {
1853  /* Clean up the extra panels and buttons created for searching. */
1855  }
1856 
1857  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1858  if (panel->runtime_flag & PANEL_ACTIVE) {
1859  BLI_assert(panel->runtime.block != NULL);
1860  panel_calculate_size_recursive(region, panel);
1861  }
1862  }
1863 
1864  /* Offset contents. */
1865  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
1866  if (block->active && block->panel) {
1867  ui_offset_panel_block(block);
1868  }
1869  }
1870 
1871  /* Re-align, possibly with animation. */
1872  Panel *panel;
1873  if (panels_need_realign(area, region, &panel)) {
1874  if (panel) {
1876  }
1877  else {
1878  uiAlignPanelStep(region, 1.0, false);
1879  }
1880  }
1881 
1882  /* Compute size taken up by panels. */
1883  ui_panels_size(region, r_x, r_y);
1884 }
1885 
1888 /* -------------------------------------------------------------------- */
1892 #define DRAG_REGION_PAD (PNL_HEADER * 0.5)
1893 static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
1894 {
1895  uiHandlePanelData *data = panel->activedata;
1896  ARegion *region = CTX_wm_region(C);
1897 
1898  /* Keep the drag position in the region with a small pad to keep the panel visible. */
1899  const int y = clamp_i(event->xy[1], region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD);
1900 
1901  float dy = (float)(y - data->starty);
1902 
1903  /* Adjust for region zoom. */
1904  dy *= BLI_rctf_size_y(&region->v2d.cur) / (float)BLI_rcti_size_y(&region->winrct);
1905 
1906  /* Add the movement of the view due to edge scrolling while dragging. */
1907  dy += ((float)region->v2d.cur.ymin - data->start_cur_ymin);
1908 
1909  panel->ofsy = data->startofsy + round_fl_to_int(dy);
1910 
1911  uiAlignPanelStep(region, 0.2f, true);
1912 
1913  ED_region_tag_redraw(region);
1914 }
1915 #undef DRAG_REGION_PAD
1916 
1919 /* -------------------------------------------------------------------- */
1924  const Panel *panel,
1925  const int mx,
1926  const int my)
1927 {
1928  if (!IN_RANGE((float)mx, block->rect.xmin, block->rect.xmax)) {
1929  return PANEL_MOUSE_OUTSIDE;
1930  }
1931 
1932  if (IN_RANGE((float)my, block->rect.ymax, block->rect.ymax + PNL_HEADER)) {
1934  }
1935 
1936  if (!UI_panel_is_closed(panel)) {
1937  if (IN_RANGE((float)my, block->rect.ymin, block->rect.ymax + PNL_HEADER)) {
1939  }
1940  }
1941 
1942  return PANEL_MOUSE_OUTSIDE;
1943 }
1944 
1947  int xy_init[2];
1949 
1951 {
1952  uiPanelDragCollapseHandle *dragcol_data = userdata;
1953  MEM_freeN(dragcol_data);
1954 }
1955 
1956 static void ui_panel_drag_collapse(const bContext *C,
1957  const uiPanelDragCollapseHandle *dragcol_data,
1958  const int xy_dst[2])
1959 {
1960  ARegion *region = CTX_wm_region(C);
1961 
1962  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
1963  float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)};
1964  float xy_b_block[2] = {UNPACK2(xy_dst)};
1965  Panel *panel = block->panel;
1966 
1967  if (panel == NULL || (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER))) {
1968  continue;
1969  }
1970  const int oldflag = panel->flag;
1971 
1972  /* Lock axis. */
1973  xy_b_block[0] = dragcol_data->xy_init[0];
1974 
1975  /* Use cursor coords in block space. */
1976  ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]);
1977  ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]);
1978 
1979  /* Set up `rect` to match header size. */
1980  rctf rect = block->rect;
1981  rect.ymin = rect.ymax;
1982  rect.ymax = rect.ymin + PNL_HEADER;
1983 
1984  /* Touch all panels between last mouse coordinate and the current one. */
1985  if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) {
1986  /* Force panel to open or close. */
1988  SET_FLAG_FROM_TEST(panel->flag, dragcol_data->was_first_open, PNL_CLOSED);
1989 
1990  /* If panel->flag has changed this means a panel was opened/closed here. */
1991  if (panel->flag != oldflag) {
1993  }
1994  }
1995  }
1996  /* Update the instanced panel data expand flags with the changes made here. */
1998 }
1999 
2006 static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, void *userdata)
2007 {
2008  wmWindow *win = CTX_wm_window(C);
2009  uiPanelDragCollapseHandle *dragcol_data = userdata;
2010  short retval = WM_UI_HANDLER_CONTINUE;
2011 
2012  switch (event->type) {
2013  case MOUSEMOVE:
2014  ui_panel_drag_collapse(C, dragcol_data, event->xy);
2015 
2016  retval = WM_UI_HANDLER_BREAK;
2017  break;
2018  case LEFTMOUSE:
2019  if (event->val == KM_RELEASE) {
2020  /* Done! */
2024  dragcol_data,
2025  true);
2027  }
2028  /* Don't let any left-mouse event fall through! */
2029  retval = WM_UI_HANDLER_BREAK;
2030  break;
2031  }
2032 
2033  return retval;
2034 }
2035 
2036 static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was_open)
2037 {
2038  wmWindow *win = CTX_wm_window(C);
2039  const wmEvent *event = win->eventstate;
2040  uiPanelDragCollapseHandle *dragcol_data = MEM_mallocN(sizeof(*dragcol_data), __func__);
2041 
2042  dragcol_data->was_first_open = was_open;
2043  copy_v2_v2_int(dragcol_data->xy_init, event->xy);
2044 
2046  &win->modalhandlers,
2049  dragcol_data,
2050  0);
2051 }
2052 
2059 static void ui_handle_panel_header(const bContext *C,
2060  const uiBlock *block,
2061  const int mx,
2062  const int event_type,
2063  const bool ctrl,
2064  const bool shift)
2065 {
2066  Panel *panel = block->panel;
2067  ARegion *region = CTX_wm_region(C);
2068 
2069  BLI_assert(panel->type != NULL);
2070  BLI_assert(!(panel->type->flag & PANEL_TYPE_NO_HEADER));
2071 
2072  const bool is_subpanel = (panel->type->parent != NULL);
2073  const bool use_pin = UI_panel_category_is_visible(region) && UI_panel_can_be_pinned(panel);
2074  const bool show_pin = use_pin && (panel->flag & PNL_PIN);
2075  const bool show_drag = !is_subpanel;
2076 
2077  /* Handle panel pinning. */
2078  if (use_pin && ELEM(event_type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE) && shift) {
2079  panel->flag ^= PNL_PIN;
2080  ED_region_tag_redraw(region);
2081  return;
2082  }
2083 
2084  float expansion_area_xmax = block->rect.xmax;
2085  if (show_drag) {
2086  expansion_area_xmax -= (PNL_ICON * 1.5f);
2087  }
2088  if (show_pin) {
2089  expansion_area_xmax -= PNL_ICON;
2090  }
2091 
2092  /* Collapse and expand panels. */
2093  if (ELEM(event_type, EVT_RETKEY, EVT_PADENTER, EVT_AKEY) || mx < expansion_area_xmax) {
2094  if (ctrl && !is_subpanel) {
2095  /* For parent panels, collapse all other panels or toggle children. */
2096  if (UI_panel_is_closed(panel) || BLI_listbase_is_empty(&panel->children)) {
2097  panels_collapse_all(region, panel);
2098 
2099  /* Reset the view - we don't want to display a view without content. */
2100  UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
2101  }
2102  else {
2103  /* If a panel has sub-panels and it's open, toggle the expansion
2104  * of the sub-panels (based on the expansion of the first sub-panel). */
2105  Panel *first_child = panel->children.first;
2106  BLI_assert(first_child != NULL);
2108  panel->flag |= PNL_CLOSED;
2109  }
2110  }
2111 
2113 
2114  if (event_type == LEFTMOUSE) {
2116  }
2117 
2118  /* Set panel custom data (modifier) active when expanding sub-panels, but not top-level
2119  * panels to allow collapsing and expanding without setting the active element. */
2120  if (is_subpanel) {
2122  }
2123 
2126  return;
2127  }
2128 
2129  /* Handle panel dragging. For now don't allow dragging in floating regions. */
2130  if (show_drag && !(region->alignment == RGN_ALIGN_FLOAT)) {
2131  const float drag_area_xmin = block->rect.xmax - (PNL_ICON * 1.5f);
2132  const float drag_area_xmax = block->rect.xmax;
2133  if (IN_RANGE(mx, drag_area_xmin, drag_area_xmax)) {
2135  return;
2136  }
2137  }
2138 
2139  /* Handle panel unpinning. */
2140  if (show_pin) {
2141  const float pin_area_xmin = expansion_area_xmax;
2142  const float pin_area_xmax = pin_area_xmin + PNL_ICON;
2143  if (IN_RANGE(mx, pin_area_xmin, pin_area_xmax)) {
2144  panel->flag ^= PNL_PIN;
2145  ED_region_tag_redraw(region);
2146  return;
2147  }
2148  }
2149 }
2150 
2152 {
2153  /* Check for more than one category. */
2154  return region->panels_category.first &&
2155  region->panels_category.first != region->panels_category.last;
2156 }
2157 
2158 PanelCategoryDyn *UI_panel_category_find(const ARegion *region, const char *idname)
2159 {
2160  return BLI_findstring(&region->panels_category, idname, offsetof(PanelCategoryDyn, idname));
2161 }
2162 
2164 {
2165  return BLI_findstring(
2166  &region->panels_category_active, idname, offsetof(PanelCategoryStack, idname));
2167 }
2168 
2169 static void ui_panel_category_active_set(ARegion *region, const char *idname, bool fallback)
2170 {
2171  ListBase *lb = &region->panels_category_active;
2172  PanelCategoryStack *pc_act = UI_panel_category_active_find(region, idname);
2173 
2174  if (pc_act) {
2175  BLI_remlink(lb, pc_act);
2176  }
2177  else {
2178  pc_act = MEM_callocN(sizeof(PanelCategoryStack), __func__);
2179  BLI_strncpy(pc_act->idname, idname, sizeof(pc_act->idname));
2180  }
2181 
2182  if (fallback) {
2183  /* For fall-backs, add at the end so explicitly chosen categories have priority. */
2184  BLI_addtail(lb, pc_act);
2185  }
2186  else {
2187  BLI_addhead(lb, pc_act);
2188  }
2189 
2190  /* Validate all active panels. We could do this on load, they are harmless -
2191  * but we should remove them somewhere.
2192  * (Add-ons could define panels and gather cruft over time). */
2193  {
2194  PanelCategoryStack *pc_act_next;
2195  /* intentionally skip first */
2196  pc_act_next = pc_act->next;
2197  while ((pc_act = pc_act_next)) {
2198  pc_act_next = pc_act->next;
2199  if (!BLI_findstring(
2200  &region->type->paneltypes, pc_act->idname, offsetof(PanelType, category))) {
2201  BLI_remlink(lb, pc_act);
2202  MEM_freeN(pc_act);
2203  }
2204  }
2205  }
2206 }
2207 
2208 void UI_panel_category_active_set(ARegion *region, const char *idname)
2209 {
2210  ui_panel_category_active_set(region, idname, false);
2211 }
2212 
2213 void UI_panel_category_active_set_default(ARegion *region, const char *idname)
2214 {
2215  if (!UI_panel_category_active_find(region, idname)) {
2216  ui_panel_category_active_set(region, idname, true);
2217  }
2218 }
2219 
2220 const char *UI_panel_category_active_get(ARegion *region, bool set_fallback)
2221 {
2223  if (UI_panel_category_find(region, pc_act->idname)) {
2224  return pc_act->idname;
2225  }
2226  }
2227 
2228  if (set_fallback) {
2229  PanelCategoryDyn *pc_dyn = region->panels_category.first;
2230  if (pc_dyn) {
2231  ui_panel_category_active_set(region, pc_dyn->idname, true);
2232  return pc_dyn->idname;
2233  }
2234  }
2235 
2236  return NULL;
2237 }
2238 
2240 {
2242  if (BLI_rcti_isect_pt(&ptd->rect, event->mval[0], event->mval[1])) {
2243  return ptd;
2244  }
2245  }
2246 
2247  return NULL;
2248 }
2249 
2250 void UI_panel_category_add(ARegion *region, const char *name)
2251 {
2252  PanelCategoryDyn *pc_dyn = MEM_callocN(sizeof(*pc_dyn), __func__);
2253  BLI_addtail(&region->panels_category, pc_dyn);
2254 
2255  BLI_strncpy(pc_dyn->idname, name, sizeof(pc_dyn->idname));
2256 
2257  /* 'pc_dyn->rect' must be set on draw. */
2258 }
2259 
2261 {
2262  BLI_freelistN(&region->panels_category);
2263 }
2264 
2266  ARegion *region,
2267  const uiBut *active_but)
2268 {
2269  const bool is_mousewheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE);
2270  const bool inside_tabregion =
2272  (event->mval[0] < ((PanelCategoryDyn *)region->panels_category.first)->rect.xmax) :
2273  (event->mval[0] > ((PanelCategoryDyn *)region->panels_category.first)->rect.xmin));
2274 
2275  /* If mouse is inside non-tab region, ctrl key is required. */
2276  if (is_mousewheel && (event->modifier & KM_CTRL) == 0 && !inside_tabregion) {
2277  return WM_UI_HANDLER_CONTINUE;
2278  }
2279 
2280  if (active_but && ui_but_supports_cycling(active_but)) {
2281  /* Skip - exception to make cycling buttons using ctrl+mousewheel work in tabbed regions. */
2282  }
2283  else {
2284  const char *category = UI_panel_category_active_get(region, false);
2285  if (LIKELY(category)) {
2286  PanelCategoryDyn *pc_dyn = UI_panel_category_find(region, category);
2287  if (LIKELY(pc_dyn)) {
2288  if (is_mousewheel) {
2289  /* We can probably get rid of this and only allow Ctrl-Tabbing. */
2290  pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev;
2291  }
2292  else {
2293  const bool backwards = event->modifier & KM_SHIFT;
2294  pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next;
2295  if (!pc_dyn) {
2296  /* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */
2297  pc_dyn = backwards ? region->panels_category.last : region->panels_category.first;
2298  }
2299  }
2300 
2301  if (pc_dyn) {
2302  /* Intentionally don't reset scroll in this case,
2303  * allowing for quick browsing between tabs. */
2304  UI_panel_category_active_set(region, pc_dyn->idname);
2305  ED_region_tag_redraw(region);
2306  }
2307  }
2308  }
2309  return WM_UI_HANDLER_BREAK;
2310  }
2311 
2312  return WM_UI_HANDLER_CONTINUE;
2313 }
2314 
2316  const wmEvent *event,
2317  ARegion *region,
2318  const uiBut *active_but)
2319 {
2320  /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
2321  if (ISMOUSE_MOTION(event->type)) {
2322  return WM_UI_HANDLER_CONTINUE;
2323  }
2324 
2325  /* We only use KM_PRESS events in this function, so it's simpler to return early. */
2326  if (event->val != KM_PRESS) {
2327  return WM_UI_HANDLER_CONTINUE;
2328  }
2329 
2330  /* Scroll-bars can overlap panels now, they have handling priority. */
2331  if (UI_view2d_mouse_in_scrollers(region, &region->v2d, event->xy)) {
2332  return WM_UI_HANDLER_CONTINUE;
2333  }
2334 
2335  int retval = WM_UI_HANDLER_CONTINUE;
2336 
2337  /* Handle category tabs. */
2338  if (UI_panel_category_is_visible(region)) {
2339  if (event->type == LEFTMOUSE) {
2340  PanelCategoryDyn *pc_dyn = panel_categories_find_mouse_over(region, event);
2341  if (pc_dyn) {
2342  UI_panel_category_active_set(region, pc_dyn->idname);
2343  ED_region_tag_redraw(region);
2344 
2345  /* Reset scroll to the top (T38348). */
2346  UI_view2d_offset(&region->v2d, -1.0f, 1.0f);
2347 
2348  retval = WM_UI_HANDLER_BREAK;
2349  }
2350  }
2351  else if (((event->type == EVT_TABKEY) && (event->modifier & KM_CTRL)) ||
2352  ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
2353  /* Cycle tabs. */
2354  retval = ui_handle_panel_category_cycling(event, region, active_but);
2355  }
2356  }
2357 
2358  if (retval == WM_UI_HANDLER_BREAK) {
2359  return retval;
2360  }
2361 
2362  const bool region_has_active_button = (ui_region_find_active_but(region) != NULL);
2363 
2364  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
2365  Panel *panel = block->panel;
2366  if (panel == NULL || panel->type == NULL) {
2367  continue;
2368  }
2369  /* We can't expand or collapse panels without headers, they would disappear. */
2370  if (panel->type->flag & PANEL_TYPE_NO_HEADER) {
2371  continue;
2372  }
2373 
2374  int mx = event->xy[0];
2375  int my = event->xy[1];
2376  ui_window_to_block(region, block, &mx, &my);
2377 
2378  const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
2379 
2380  if (mouse_state != PANEL_MOUSE_OUTSIDE) {
2381  /* Mark panels that have been interacted with so their expansion
2382  * doesn't reset when property search finishes. */
2385 
2386  /* The panel collapse / expand key "A" is special as it takes priority over
2387  * active button handling. */
2388  if (event->type == EVT_AKEY &&
2389  ((event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0)) {
2390  retval = WM_UI_HANDLER_BREAK;
2392  C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT);
2393  break;
2394  }
2395  }
2396 
2397  /* Don't do any other panel handling with an active button. */
2398  if (region_has_active_button) {
2399  continue;
2400  }
2401 
2402  if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) {
2403  /* All mouse clicks inside panel headers should return in break. */
2404  if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) {
2405  retval = WM_UI_HANDLER_BREAK;
2407  C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT);
2408  }
2409  else if (event->type == RIGHTMOUSE) {
2410  retval = WM_UI_HANDLER_BREAK;
2411  ui_popup_context_menu_for_panel(C, region, block->panel);
2412  }
2413  break;
2414  }
2415  }
2416 
2417  return retval;
2418 }
2419 
2420 static void ui_panel_custom_data_set_recursive(Panel *panel, PointerRNA *custom_data)
2421 {
2422  panel->runtime.custom_data_ptr = custom_data;
2423 
2424  LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
2425  ui_panel_custom_data_set_recursive(child_panel, custom_data);
2426  }
2427 }
2428 
2429 void UI_panel_context_pointer_set(Panel *panel, const char *name, PointerRNA *ptr)
2430 {
2431  uiLayoutSetContextPointer(panel->layout, name, ptr);
2432  panel->runtime.context = uiLayoutGetContextStore(panel->layout);
2433 }
2434 
2435 void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
2436 {
2437  BLI_assert(panel->type != NULL);
2438 
2439  /* Free the old custom data, which should be shared among all of the panel's sub-panels. */
2440  if (panel->runtime.custom_data_ptr != NULL) {
2442  }
2443 
2444  ui_panel_custom_data_set_recursive(panel, custom_data);
2445 }
2446 
2448 {
2449  return panel->runtime.custom_data_ptr;
2450 }
2451 
2453 {
2454  ARegion *region = CTX_wm_region(C);
2455 
2456  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
2457  Panel *panel = block->panel;
2458  if (panel == NULL) {
2459  continue;
2460  }
2461 
2462  int mx = event->xy[0];
2463  int my = event->xy[1];
2464  ui_window_to_block(region, block, &mx, &my);
2465  const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
2467  return UI_panel_custom_data_get(panel);
2468  }
2469  }
2470 
2471  return NULL;
2472 }
2473 
2474 bool UI_panel_can_be_pinned(const Panel *panel)
2475 {
2476  return (panel->type->parent == NULL) && !(panel->type->flag & PANEL_TYPE_INSTANCED);
2477 }
2478 
2481 /* -------------------------------------------------------------------- */
2485 /* NOTE: this is modal handler and should not swallow events for animation. */
2486 static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
2487 {
2488  Panel *panel = userdata;
2489  uiHandlePanelData *data = panel->activedata;
2490 
2491  /* Verify if we can stop. */
2492  if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
2494  }
2495  else if (event->type == MOUSEMOVE) {
2496  if (data->state == PANEL_STATE_DRAG) {
2497  ui_do_drag(C, event, panel);
2498  }
2499  }
2500  else if (event->type == TIMER && event->customdata == data->animtimer) {
2501  if (data->state == PANEL_STATE_ANIMATION) {
2502  ui_do_animate(C, panel);
2503  }
2504  else if (data->state == PANEL_STATE_DRAG) {
2505  ui_do_drag(C, event, panel);
2506  }
2507  }
2508 
2509  data = panel->activedata;
2510 
2511  if (data && data->state == PANEL_STATE_ANIMATION) {
2512  return WM_UI_HANDLER_CONTINUE;
2513  }
2514  return WM_UI_HANDLER_BREAK;
2515 }
2516 
2517 static void ui_handler_remove_panel(bContext *C, void *userdata)
2518 {
2519  Panel *panel = userdata;
2520 
2522 }
2523 
2525  wmWindow *win,
2526  const ARegion *region,
2527  Panel *panel,
2528  const uiHandlePanelState state)
2529 {
2530  if (panel->activedata == NULL) {
2531  panel->activedata = MEM_callocN(sizeof(uiHandlePanelData), __func__);
2534  }
2535 
2536  uiHandlePanelData *data = panel->activedata;
2537 
2539 
2540  data->state = state;
2541  data->startx = win->eventstate->xy[0];
2542  data->starty = win->eventstate->xy[1];
2543  data->startofsx = panel->ofsx;
2544  data->startofsy = panel->ofsy;
2545  data->start_cur_xmin = region->v2d.cur.xmin;
2546  data->start_cur_ymin = region->v2d.cur.ymin;
2547  data->starttime = PIL_check_seconds_timer();
2548 }
2549 
2555 static void panel_activate_state(const bContext *C, Panel *panel, const uiHandlePanelState state)
2556 {
2557  uiHandlePanelData *data = panel->activedata;
2558  wmWindow *win = CTX_wm_window(C);
2559  ARegion *region = CTX_wm_region(C);
2560 
2561  if (data != NULL && data->state == state) {
2562  return;
2563  }
2564 
2565  if (state == PANEL_STATE_DRAG) {
2567 
2568  panel_set_flag_recursive(panel, PNL_SELECT, true);
2570 
2571  panel_handle_data_ensure(C, win, region, panel, state);
2572 
2573  /* Initiate edge panning during drags for scrolling beyond the initial region view. */
2574  wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
2576  }
2577  else if (state == PANEL_STATE_ANIMATION) {
2578  panel_set_flag_recursive(panel, PNL_SELECT, false);
2579 
2580  panel_handle_data_ensure(C, win, region, panel, state);
2581  }
2582  else if (state == PANEL_STATE_EXIT) {
2584 
2585  BLI_assert(data != NULL);
2586 
2587  if (data->animtimer) {
2588  WM_event_remove_timer(CTX_wm_manager(C), win, data->animtimer);
2589  data->animtimer = NULL;
2590  }
2591 
2592  MEM_freeN(data);
2593  panel->activedata = NULL;
2594 
2597  }
2598 
2599  ED_region_tag_redraw(region);
2600 }
2601 
typedef float(TangentPoint)[2]
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
void CTX_store_set(bContext *C, bContextStore *store)
Definition: context.c:188
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
@ PANEL_TYPE_NO_HEADER
Definition: BKE_screen.h:280
@ PANEL_TYPE_INSTANCED
Definition: BKE_screen.h:285
@ PANEL_TYPE_DEFAULT_CLOSED
Definition: BKE_screen.h:279
@ BLF_ROTATION
Definition: BLF_api.h:334
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition: blf.c:407
size_t BLF_width_to_strlen(int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2)
Definition: blf.c:596
#define BLF_DRAW_STR_DUMMY_MAX
Definition: BLF_api.h:356
void BLF_disable(int fontid, int option)
Definition: blf.c:279
void BLF_rotation(int fontid, float angle)
Definition: blf.c:766
float BLF_width(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: blf.c:688
void BLF_draw(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2)
Definition: blf.c:538
void BLF_enable(int fontid, int option)
Definition: blf.c:270
void BLF_size(int fontid, float size, int dpi)
Definition: blf.c:363
void BLF_position(int fontid, float x, float y, float z)
Definition: blf.c:308
#define BLI_assert(a)
Definition: BLI_assert.h:46
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:60
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
Definition: BLI_listbase.h:348
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:301
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:466
void void BLI_INLINE bool BLI_listbase_is_single(const struct ListBase *lb)
Definition: BLI_listbase.h:265
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int round_fl_to_int(float a)
MINLINE int min_ii(int a, int b)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
#define M_PI_2
Definition: BLI_math_base.h:23
MINLINE float interpf(float a, float b, float t)
MINLINE int clamp_i(int value, int min, int max)
MINLINE void copy_v4_v4_uchar(unsigned char r[4], const unsigned char a[4])
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition: BLI_rect.h:190
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 float BLI_rctf_size_y(const struct rctf *rct)
Definition: BLI_rect.h:198
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
unsigned char uchar
Definition: BLI_sys_types.h:70
unsigned int uint
Definition: BLI_sys_types.h:67
#define UNPACK2(a)
#define STREQLEN(a, b, n)
#define IN_RANGE(a, b, c)
#define UNUSED(x)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
#define snprintf
Definition: BLI_winstuff.h:53
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define MAX_NAME
Definition: DNA_defs.h:48
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ RGN_FLAG_SEARCH_FILTER_UPDATE
@ RGN_FLAG_SEARCH_FILTER_ACTIVE
@ PNL_SELECT
@ PNL_PIN
@ PNL_CLOSED
@ PNL_INSTANCED_LIST_ORDER_CHANGED
@ RGN_TYPE_WINDOW
@ RGN_TYPE_TOOLS
@ RGN_ALIGN_RIGHT
@ RGN_ALIGN_FLOAT
#define RGN_TYPE_HAS_CATEGORY_MASK
@ SPACE_PROPERTIES
void(* uiListPanelIDFromDataFunc)(void *data_link, char *r_idname)
Definition: ED_anim_api.h:804
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
GPUBatch
Definition: GPU_batch.h:78
void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id)
Definition: gpu_batch.cc:287
void GPU_batch_draw(GPUBatch *batch)
Definition: gpu_batch.cc:223
struct GPUBatch * GPU_batch_preset_panel_drag_widget(float pixelsize, const float col_high[4], const float col_dark[4], float width) ATTR_WARN_UNUSED_RESULT
void immUniformColor4ubv(const unsigned char rgba[4])
void immUnbindProgram(void)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniformColor3ubv(const unsigned char rgb[3])
void immUniformColor4fv(const float rgba[4])
GPUVertFormat * immVertexFormat(void)
void immUniformColor3fvAlpha(const float rgb[3], float a)
void immRecti(uint pos, int x1, int y1, int x2, int y2)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
void GPU_matrix_pop(void)
Definition: gpu_matrix.cc:126
void GPU_matrix_push(void)
Definition: gpu_matrix.cc:119
void GPU_matrix_translate_2f(float x, float y)
Definition: gpu_matrix.cc:174
@ GPU_SHADER_2D_UNIFORM_COLOR
Definition: GPU_shader.h:201
@ GPU_SHADER_2D_FLAT_COLOR
Definition: GPU_shader.h:208
@ GPU_BLEND_NONE
Definition: GPU_state.h:60
@ GPU_BLEND_ALPHA
Definition: GPU_state.h:62
void GPU_blend(eGPUBlend blend)
Definition: gpu_state.cc:39
void GPU_line_smooth(bool enable)
Definition: gpu_state.cc:75
@ GPU_FETCH_INT_TO_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_I32
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
Platform independent time functions.
#define C
Definition: RandGen.cpp:25
#define UI_UNIT_Y
struct bContextStore * uiLayoutGetContextStore(uiLayout *layout)
const struct uiStyle * UI_style_get_dpi(void)
void UI_block_theme_style_set(uiBlock *block, char theme_style)
Definition: interface.cc:3634
void UI_draw_roundbox_4fv(const struct rctf *rect, bool filled, float rad, const float col[4])
#define UI_PANEL_WIDTH
Definition: UI_interface.h:240
const struct uiStyle * UI_style_get(void)
@ UI_BLOCK_THEME_STYLE_POPUP
Definition: UI_interface.h:770
void UI_draw_roundbox_corner_set(int type)
#define UI_PANEL_MARGIN_X
Definition: UI_interface.h:250
bool UI_block_is_search_only(const uiBlock *block)
Definition: interface.cc:3639
void UI_block_draw(const struct bContext *C, struct uiBlock *block)
#define UI_DPI_FAC
Definition: UI_interface.h:305
void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str, size_t str_len, const uchar col[4], const struct uiFontStyleDraw_Params *fs_params)
#define UI_PANEL_CATEGORY_MARGIN_WIDTH
Definition: UI_interface.h:246
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, struct PointerRNA *ptr)
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
#define UI_PANEL_MARGIN_Y
Definition: UI_interface.h:251
#define UI_UNIT_X
#define INSTANCED_PANEL_UNIQUE_STR_LEN
void UI_block_set_search_only(uiBlock *block, bool search_only)
Definition: interface.cc:3644
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)
@ TH_SELECT_ACTIVE
Definition: UI_resources.h:258
@ TH_TAB_OUTLINE
Definition: UI_resources.h:48
@ TH_PANEL_HEADER
Definition: UI_resources.h:56
@ TH_TAB_ACTIVE
Definition: UI_resources.h:45
@ TH_BACK
Definition: UI_resources.h:39
@ TH_PANEL_SUB_BACK
Definition: UI_resources.h:58
@ TH_TITLE
Definition: UI_resources.h:44
@ TH_TAB_BACK
Definition: UI_resources.h:47
@ TH_TAB_INACTIVE
Definition: UI_resources.h:46
@ TH_MATCH
Definition: UI_resources.h:256
@ TH_PANEL_BACK
Definition: UI_resources.h:57
@ TH_TEXT
Definition: UI_resources.h:42
@ TH_TEXT_HI
Definition: UI_resources.h:43
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
Definition: resources.c:1323
struct bTheme * UI_GetTheme(void)
Definition: resources.c:1067
void UI_GetThemeColor4fv(int colorid, float col[4])
Definition: resources.c:1173
void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
Definition: resources.c:1331
void UI_GetThemeColor4ubv(int colorid, unsigned char col[4])
Definition: resources.c:1352
char char UI_view2d_mouse_in_scrollers(const struct ARegion *region, const struct View2D *v2d, const int xy[2]) ATTR_NONNULL(1
void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac)
Definition: view2d.cc:1954
@ KM_PRESS
Definition: WM_types.h:267
@ KM_RELEASE
Definition: WM_types.h:268
#define WM_UI_HANDLER_CONTINUE
Definition: WM_types.h:298
@ WM_OP_INVOKE_DEFAULT
Definition: WM_types.h:201
@ KM_CTRL
Definition: WM_types.h:239
@ KM_ALT
Definition: WM_types.h:240
@ KM_OSKEY
Definition: WM_types.h:242
@ KM_SHIFT
Definition: WM_types.h:238
#define WM_UI_HANDLER_BREAK
Definition: WM_types.h:299
unsigned int U
Definition: btGjkEpa3.h:78
static float is_left(const float p0[2], const float p1[2], const float p2[2])
Definition: convexhull_2d.c:37
uint pos
struct @653::@655 batch
void ui_block_bounds_calc(uiBlock *block)
Definition: interface.cc:444
void ui_window_to_block_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y)
Definition: interface.cc:191
void ui_fontscale(float *points, float aspect)
Definition: interface.cc:2014
void ui_window_to_block(const ARegion *region, uiBlock *block, int *r_x, int *r_y)
Definition: interface.cc:228
bool ui_but_supports_cycling(const uiBut *but)
Definition: interface.cc:2484
void ui_block_new_button_group(uiBlock *block, uiButtonGroupFlag flag)
void ui_popup_context_menu_for_panel(bContext *C, ARegion *region, Panel *panel)
static int roundboxtype
void ui_handle_afterfunc_add_operator(wmOperatorType *ot, wmOperatorCallContext opcontext)
uiBut * ui_region_find_active_but(struct ARegion *region) ATTR_WARN_UNUSED_RESULT
@ UI_BUTTON_GROUP_LOCK
@ UI_BUTTON_GROUP_PANEL_HEADER
#define PNL_HEADER
@ UI_HIDDEN
static Panel * panel_add_instanced(ARegion *region, ListBase *panels, PanelType *panel_type, PointerRNA *custom_data)
bool UI_panel_is_closed(const Panel *panel)
static int get_panel_real_ofsy(Panel *panel)
static void ui_panel_drag_collapse_handler_remove(bContext *UNUSED(C), void *userdata)
struct uiHandlePanelData uiHandlePanelData
static void panel_title_color_get(const Panel *panel, const bool show_background, const bool region_search_filter_active, uchar r_color[4])
static void panel_set_expansion_from_search_filter_recursive(const bContext *C, Panel *panel, const bool use_search_closed)
static void panel_set_runtime_flag_recursive(Panel *panel, short flag, bool value)
static void region_panels_set_expansion_from_search_filter(const bContext *C, ARegion *region, const bool use_search_closed)
void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
static void ui_panels_size(ARegion *region, int *r_x, int *r_y)
bool UI_panel_can_be_pinned(const Panel *panel)
PointerRNA * UI_region_panel_custom_data_under_cursor(const bContext *C, const wmEvent *event)
static void panel_delete(const bContext *C, ARegion *region, ListBase *panels, Panel *panel)
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)
PanelCategoryDyn * UI_panel_category_find(const ARegion *region, const char *idname)
struct uiPanelDragCollapseHandle uiPanelDragCollapseHandle
PanelCategoryStack * UI_panel_category_active_find(ARegion *region, const char *idname)
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)
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
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)
static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelState state)
static bool panel_custom_data_active_get(const Panel *panel)
int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *region, const uiBut *active_but)
int UI_panel_size_y(const Panel *panel)
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)
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)
void UI_panel_category_clear_all(ARegion *region)
static PanelCategoryDyn * panel_categories_find_mouse_over(ARegion *region, const wmEvent *event)
static bool panels_need_realign(const ScrArea *area, ARegion *region, Panel **r_panel_animation)
void UI_panels_begin(const bContext *UNUSED(C), ARegion *region)
struct PanelSort PanelSort
#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_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
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)
Panel * UI_panel_add_instanced(const bContext *C, ARegion *region, ListBase *panels, const char *panel_idname, PointerRNA *custom_data)
PointerRNA * UI_panel_custom_data_get(const Panel *panel)
#define ANIMATION_INTERVAL
void ui_draw_aligned_panel(const uiStyle *style, const uiBlock *block, const rcti *rect, const bool show_pin, const bool show_background, const bool region_search_filter_active)
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)
bool UI_panel_list_matches_data(ARegion *region, ListBase *data, uiListPanelIDFromDataFunc panel_idname_func)
static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was_open)
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)
const char * UI_panel_category_active_get(ARegion *region, bool set_fallback)
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_category_add(ARegion *region, const char *name)
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)
Panel * UI_panel_find_by_type(ListBase *lb, const PanelType *pt)
static void panel_calculate_size_recursive(ARegion *region, Panel *panel)
static void ui_panel_category_active_set(ARegion *region, const char *idname, bool fallback)
static void panel_draw_highlight_border(const Panel *panel, const rcti *rect, const rcti *header_rect)
static void panel_draw_aligned_backdrop(const Panel *panel, const rcti *rect, const rcti *header_rect)
static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block, const Panel *panel, const int mx, const int my)
Panel * UI_panel_begin(ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open)
static bool properties_space_needs_realign(const ScrArea *area, const ARegion *region)
uiHandlePanelState
@ PANEL_STATE_DRAG
@ PANEL_STATE_ANIMATION
@ PANEL_STATE_EXIT
const int state
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define sqrtf(x)
Definition: metal/compat.h:243
static unsigned a[3]
Definition: RandGen.cpp:78
static void area(int d1, int d2, int e1, int e2, float weights[2])
T floor(const T &a)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:4874
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:717
bool RNA_pointer_is_null(const PointerRNA *ptr)
Definition: rna_access.c:164
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
ListBase paneltypes
Definition: BKE_screen.h:198
ListBase panels_category_active
ListBase panels_category
ListBase panels
short alignment
short regiontype
struct ARegionType * type
ListBase uiblocks
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
struct PanelCategoryDyn * next
struct PanelCategoryDyn * prev
struct PanelCategoryStack * next
Panel * panel
short(* get_list_data_expand_flag)(const struct bContext *C, struct Panel *pa)
Definition: BKE_screen.h:260
char idname[BKE_ST_MAXNAME]
Definition: BKE_screen.h:223
char context[BKE_ST_MAXNAME]
Definition: BKE_screen.h:227
void(* reorder)(struct bContext *C, struct Panel *pa, int new_index)
Definition: BKE_screen.h:253
char translation_context[BKE_ST_MAXNAME]
Definition: BKE_screen.h:226
char active_property[BKE_ST_MAXNAME]
Definition: BKE_screen.h:232
ListBase children
Definition: BKE_screen.h:271
char category[BKE_ST_MAXNAME]
Definition: BKE_screen.h:228
struct PanelType * parent
Definition: BKE_screen.h:270
char label[BKE_ST_MAXNAME]
Definition: BKE_screen.h:224
struct bContextStore * context
struct uiBlock * block
struct PointerRNA * custom_data_ptr
struct PanelType * type
short labelofs
struct uiLayout * layout
Panel_Runtime runtime
int blocksizey
void * activedata
int blocksizex
short runtime_flag
char drawname[64]
short flag
char panelname[64]
struct Panel * next
ListBase children
uiWidgetColors wcol_menu_back
float panel_roundness
uiWidgetColors wcol_tab
ThemeUI tui
float xmax
Definition: DNA_vec_types.h:69
float xmin
Definition: DNA_vec_types.h:69
float ymax
Definition: DNA_vec_types.h:70
float ymin
Definition: DNA_vec_types.h:70
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63
ListBase button_groups
struct Panel * panel
ListBase buttons
uiHandlePanelState state
uiFontStyle paneltitle
short panelspace
uiFontStyle widget
uiFontStyle widgetlabel
unsigned char text[4]
short val
Definition: WM_types.h:680
int xy[2]
Definition: WM_types.h:682
int mval[2]
Definition: WM_types.h:684
uint8_t modifier
Definition: WM_types.h:693
short type
Definition: WM_types.h:678
void * customdata
Definition: WM_types.h:715
struct wmEvent * eventstate
double PIL_check_seconds_timer(void)
Definition: time.c:64
wmEventHandler_UI * WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn, void *user_data, const char flag)
void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn, void *user_data, const bool postpone)
#define ISMOUSE_MOTION(event_type)
@ RIGHTMOUSE
@ TIMER
@ EVT_TABKEY
@ EVT_AKEY
@ WHEELUPMOUSE
@ EVT_PADENTER
@ WHEELDOWNMOUSE
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_RETKEY
PointerRNA * ptr
Definition: wm_files.c:3480
wmOperatorType * ot
Definition: wm_files.c:3479
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1682
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1630