Blender  V2.93
interface_panel.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
24 /* a full doc with API notes can be found in
25  * bf-blender/trunk/blender/doc/guides/interface_API.txt */
26 
27 #include <ctype.h>
28 #include <math.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include "MEM_guardedalloc.h"
33 
34 #include "PIL_time.h"
35 
36 #include "BLI_blenlib.h"
37 #include "BLI_math.h"
38 #include "BLI_utildefines.h"
39 
40 #include "BLT_translation.h"
41 
42 #include "DNA_screen_types.h"
43 #include "DNA_userdef_types.h"
44 
45 #include "BKE_context.h"
46 #include "BKE_screen.h"
47 
48 #include "RNA_access.h"
49 
50 #include "BLF_api.h"
51 
52 #include "WM_api.h"
53 #include "WM_types.h"
54 
55 #include "ED_screen.h"
56 
57 #include "UI_interface.h"
58 #include "UI_interface_icons.h"
59 #include "UI_resources.h"
60 #include "UI_view2d.h"
61 
62 #include "GPU_batch_presets.h"
63 #include "GPU_immediate.h"
64 #include "GPU_matrix.h"
65 #include "GPU_state.h"
66 
67 #include "interface_intern.h"
68 
69 /* -------------------------------------------------------------------- */
73 #define ANIMATION_TIME 0.30
74 #define ANIMATION_INTERVAL 0.02
75 
76 typedef enum uiPanelRuntimeFlag {
77  PANEL_LAST_ADDED = (1 << 0),
78  PANEL_ACTIVE = (1 << 2),
79  PANEL_WAS_ACTIVE = (1 << 3),
80  PANEL_ANIM_ALIGN = (1 << 4),
81  PANEL_NEW_ADDED = (1 << 5),
89  PANEL_WAS_CLOSED = (1 << 9),
94  PANEL_IS_DRAG_DROP = (1 << 10),
96  PANEL_ACTIVE_BORDER = (1 << 11),
98 
99 /* The state of the mouse position relative to the panel. */
100 typedef enum uiPanelMouseState {
105 
106 typedef enum uiHandlePanelState {
111 
112 typedef struct uiHandlePanelData {
114 
115  /* Animation. */
117  double starttime;
118 
119  /* Dragging. */
124 
125 typedef struct PanelSort {
130 
131 static void panel_set_expansion_from_list_data(const bContext *C, Panel *panel);
132 static int get_panel_real_size_y(const Panel *panel);
133 static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelState state);
134 static int compare_panel(const void *a, const void *b);
135 static bool panel_type_context_poll(ARegion *region,
136  const PanelType *panel_type,
137  const char *context);
138 
141 /* -------------------------------------------------------------------- */
146  Panel **r_panel_animation,
147  bool *r_no_animation)
148 {
149  LISTBASE_FOREACH (Panel *, panel, lb) {
150  /* Detect panel active flag changes. */
151  if (!(panel->type && panel->type->parent)) {
152  if ((panel->runtime_flag & PANEL_WAS_ACTIVE) && !(panel->runtime_flag & PANEL_ACTIVE)) {
153  return true;
154  }
155  if (!(panel->runtime_flag & PANEL_WAS_ACTIVE) && (panel->runtime_flag & PANEL_ACTIVE)) {
156  return true;
157  }
158  }
159 
160  /* Detect changes in panel expansions. */
161  if ((bool)(panel->runtime_flag & PANEL_WAS_CLOSED) != UI_panel_is_closed(panel)) {
162  *r_panel_animation = panel;
163  return false;
164  }
165 
166  if ((panel->runtime_flag & PANEL_ACTIVE) && !UI_panel_is_closed(panel)) {
167  if (panel_active_animation_changed(&panel->children, r_panel_animation, r_no_animation)) {
168  return true;
169  }
170  }
171 
172  /* Detect animation. */
173  if (panel->activedata) {
174  uiHandlePanelData *data = panel->activedata;
175  if (data->state == PANEL_STATE_ANIMATION) {
176  *r_panel_animation = panel;
177  }
178  else {
179  /* Don't animate while handling other interaction. */
180  *r_no_animation = true;
181  }
182  }
183  if ((panel->runtime_flag & PANEL_ANIM_ALIGN) && !(*r_panel_animation)) {
184  *r_panel_animation = panel;
185  }
186  }
187 
188  return false;
189 }
190 
194 static bool properties_space_needs_realign(const ScrArea *area, const ARegion *region)
195 {
196  if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) {
197  SpaceProperties *sbuts = area->spacedata.first;
198 
199  if (sbuts->mainbo != sbuts->mainb) {
200  return true;
201  }
202  }
203 
204  return false;
205 }
206 
207 static bool panels_need_realign(const ScrArea *area, ARegion *region, Panel **r_panel_animation)
208 {
209  *r_panel_animation = NULL;
210 
211  if (properties_space_needs_realign(area, region)) {
212  return true;
213  }
214 
215  /* Detect if a panel was added or removed. */
216  Panel *panel_animation = NULL;
217  bool no_animation = false;
218  if (panel_active_animation_changed(&region->panels, &panel_animation, &no_animation)) {
219  return true;
220  }
221 
222  /* Detect panel marked for animation, if we're not already animating. */
223  if (panel_animation) {
224  if (!no_animation) {
225  *r_panel_animation = panel_animation;
226  }
227  return true;
228  }
229 
230  return false;
231 }
232 
235 /* -------------------------------------------------------------------- */
240  ListBase *panels,
241  PanelType *panel_type,
242  PointerRNA *custom_data)
243 {
244  Panel *panel = MEM_callocN(sizeof(Panel), __func__);
245  panel->type = panel_type;
246  BLI_strncpy(panel->panelname, panel_type->idname, sizeof(panel->panelname));
247 
248  panel->runtime.custom_data_ptr = custom_data;
249  panel->runtime_flag |= PANEL_NEW_ADDED;
250 
251  /* Add the panel's children too. Although they aren't instanced panels, we can still use this
252  * function to create them, as UI_panel_begin does other things we don't need to do. */
253  LISTBASE_FOREACH (LinkData *, child, &panel_type->children) {
254  PanelType *child_type = child->data;
255  panel_add_instanced(region, &panel->children, child_type, custom_data);
256  }
257 
258  /* Make sure the panel is added to the end of the display-order as well. This is needed for
259  * loading existing files.
260  *
261  * Note: We could use special behavior to place it after the panel that starts the list of
262  * instanced panels, but that would add complexity that isn't needed for now. */
263  int max_sortorder = 0;
264  LISTBASE_FOREACH (Panel *, existing_panel, panels) {
265  if (existing_panel->sortorder > max_sortorder) {
266  max_sortorder = existing_panel->sortorder;
267  }
268  }
269  panel->sortorder = max_sortorder + 1;
270 
271  BLI_addtail(panels, panel);
272 
273  return panel;
274 }
275 
281  ARegion *region,
282  ListBase *panels,
283  const char *panel_idname,
284  PointerRNA *custom_data)
285 {
286  ARegionType *region_type = region->type;
287 
288  PanelType *panel_type = BLI_findstring(
289  &region_type->paneltypes, panel_idname, offsetof(PanelType, idname));
290 
291  if (panel_type == NULL) {
292  printf("Panel type '%s' not found.\n", panel_idname);
293  return NULL;
294  }
295 
296  Panel *new_panel = panel_add_instanced(region, panels, panel_type, custom_data);
297 
298  /* Do this after #panel_add_instatnced so all sub-panels are added. */
300 
301  return new_panel;
302 }
303 
308 void UI_list_panel_unique_str(Panel *panel, char *r_name)
309 {
310  /* The panel sort-order will be unique for a specific panel type because the instanced
311  * panel list is regenerated for every change in the data order / length. */
312  snprintf(r_name, INSTANCED_PANEL_UNIQUE_STR_LEN, "%d", panel->sortorder);
313 }
314 
322 static void panel_delete(const bContext *C, ARegion *region, ListBase *panels, Panel *panel)
323 {
324  /* Recursively delete children. */
325  LISTBASE_FOREACH_MUTABLE (Panel *, child, &panel->children) {
326  panel_delete(C, region, &panel->children, child);
327  }
328  BLI_freelistN(&panel->children);
329 
330  BLI_remlink(panels, panel);
331  if (panel->activedata) {
332  MEM_freeN(panel->activedata);
333  }
334  MEM_freeN(panel);
335 }
336 
344 {
345  /* Delete panels with the instanced flag. */
346  LISTBASE_FOREACH_MUTABLE (Panel *, panel, &region->panels) {
347  if ((panel->type != NULL) && (panel->type->flag & PANEL_TYPE_INSTANCED)) {
348  /* Make sure the panel's handler is removed before deleting it. */
349  if (C != NULL && panel->activedata != NULL) {
351  }
352 
353  /* Free panel's custom data. */
354  if (panel->runtime.custom_data_ptr != NULL) {
355  MEM_freeN(panel->runtime.custom_data_ptr);
356  }
357 
358  /* Free the panel and its sub-panels. */
359  panel_delete(C, region, &region->panels, panel);
360  }
361  }
362 }
363 
374  ListBase *data,
375  uiListPanelIDFromDataFunc panel_idname_func)
376 {
377  /* Check for NULL data. */
378  int data_len = 0;
379  Link *data_link = NULL;
380  if (data == NULL) {
381  data_len = 0;
382  data_link = NULL;
383  }
384  else {
385  data_len = BLI_listbase_count(data);
386  data_link = data->first;
387  }
388 
389  int i = 0;
390  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
391  if (panel->type != NULL && panel->type->flag & PANEL_TYPE_INSTANCED) {
392  /* The panels were reordered by drag and drop. */
393  if (panel->flag & PNL_INSTANCED_LIST_ORDER_CHANGED) {
394  return false;
395  }
396 
397  /* We reached the last data item before the last instanced panel. */
398  if (data_link == NULL) {
399  return false;
400  }
401 
402  /* Check if the panel type matches the panel type from the data item. */
403  char panel_idname[MAX_NAME];
404  panel_idname_func(data_link, panel_idname);
405  if (!STREQ(panel_idname, panel->type->idname)) {
406  return false;
407  }
408 
409  data_link = data_link->next;
410  i++;
411  }
412  }
413 
414  /* If we didn't make it to the last list item, the panel list isn't complete. */
415  if (i != data_len) {
416  return false;
417  }
418 
419  return true;
420 }
421 
422 static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *drag_panel)
423 {
424  /* Without a type we cannot access the reorder callback. */
425  if (drag_panel->type == NULL) {
426  return;
427  }
428  /* Don't reorder if this instanced panel doesn't support drag and drop reordering. */
429  if (drag_panel->type->reorder == NULL) {
430  return;
431  }
432 
433  char *context = NULL;
434  if (!UI_panel_category_is_visible(region)) {
435  context = drag_panel->type->context;
436  }
437 
438  /* Find how many instanced panels with this context string. */
439  int list_panels_len = 0;
440  int start_index = -1;
441  LISTBASE_FOREACH (const Panel *, panel, &region->panels) {
442  if (panel->type) {
443  if (panel->type->flag & PANEL_TYPE_INSTANCED) {
444  if (panel_type_context_poll(region, panel->type, context)) {
445  if (panel == drag_panel) {
446  BLI_assert(start_index == -1); /* This panel should only appear once. */
447  start_index = list_panels_len;
448  }
449  list_panels_len++;
450  }
451  }
452  }
453  }
454  BLI_assert(start_index != -1); /* The drag panel should definitely be in the list. */
455 
456  /* Sort the matching instanced panels by their display order. */
457  PanelSort *panel_sort = MEM_callocN(list_panels_len * sizeof(*panel_sort), __func__);
458  PanelSort *sort_index = panel_sort;
459  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
460  if (panel->type) {
461  if (panel->type->flag & PANEL_TYPE_INSTANCED) {
462  if (panel_type_context_poll(region, panel->type, context)) {
463  sort_index->panel = panel;
464  sort_index++;
465  }
466  }
467  }
468  }
469  qsort(panel_sort, list_panels_len, sizeof(*panel_sort), compare_panel);
470 
471  /* Find how many of those panels are above this panel. */
472  int move_to_index = 0;
473  for (; move_to_index < list_panels_len; move_to_index++) {
474  if (panel_sort[move_to_index].panel == drag_panel) {
475  break;
476  }
477  }
478 
479  MEM_freeN(panel_sort);
480 
481  if (move_to_index == start_index) {
482  /* In this case, the reorder was not changed, so don't do any updates or call the callback. */
483  return;
484  }
485 
486  /* Set the bit to tell the interface to instanced the list. */
487  drag_panel->flag |= PNL_INSTANCED_LIST_ORDER_CHANGED;
488 
489  /* Finally, move this panel's list item to the new index in its list. */
490  drag_panel->type->reorder(C, drag_panel, move_to_index);
491 }
492 
498 static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, short *flag_index)
499 {
500  const bool open = (flag & (1 << *flag_index));
501  bool changed = (open == UI_panel_is_closed(panel));
502 
503  SET_FLAG_FROM_TEST(panel->flag, !open, PNL_CLOSED);
504 
505  LISTBASE_FOREACH (Panel *, child, &panel->children) {
506  *flag_index = *flag_index + 1;
507  changed |= panel_set_expand_from_list_data_recursive(child, flag, flag_index);
508  }
509  return changed;
510 }
511 
517 {
518  BLI_assert(panel->type != NULL);
520  if (panel->type->get_list_data_expand_flag == NULL) {
521  /* Instanced panel doesn't support loading expansion. */
522  return;
523  }
524 
525  const short expand_flag = panel->type->get_list_data_expand_flag(C, panel);
526  short flag_index = 0;
527 
528  /* Start panel animation if the open state was changed. */
529  if (panel_set_expand_from_list_data_recursive(panel, expand_flag, &flag_index)) {
531  }
532 }
533 
538 {
539  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
540  if (panel->runtime_flag & PANEL_ACTIVE) {
541  PanelType *panel_type = panel->type;
542  if (panel_type != NULL && panel->type->flag & PANEL_TYPE_INSTANCED) {
544  }
545  }
546  }
547 }
548 
552 static void get_panel_expand_flag(const Panel *panel, short *flag, short *flag_index)
553 {
554  const bool open = !(panel->flag & PNL_CLOSED);
555  SET_FLAG_FROM_TEST(*flag, open, (1 << *flag_index));
556 
557  LISTBASE_FOREACH (const Panel *, child, &panel->children) {
558  *flag_index = *flag_index + 1;
559  get_panel_expand_flag(child, flag, flag_index);
560  }
561 }
562 
571 static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *region)
572 {
573  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
574  PanelType *panel_type = panel->type;
575  if (panel_type == NULL) {
576  continue;
577  }
578 
579  /* Check for #PANEL_ACTIVE so we only set the expand flag for active panels. */
580  if (panel_type->flag & PANEL_TYPE_INSTANCED && panel->runtime_flag & PANEL_ACTIVE) {
581  short expand_flag;
582  short flag_index = 0;
583  get_panel_expand_flag(panel, &expand_flag, &flag_index);
584  if (panel->type->set_list_data_expand_flag) {
585  panel->type->set_list_data_expand_flag(C, panel, expand_flag);
586  }
587  }
588  }
589 }
590 
593 /* -------------------------------------------------------------------- */
597 static bool panel_custom_data_active_get(const Panel *panel)
598 {
599  /* The caller should make sure the panel is active and has a type. */
601  BLI_assert(panel->type != NULL);
602 
603  if (panel->type->active_property[0] != '\0') {
605  if (ptr != NULL && !RNA_pointer_is_null(ptr)) {
606  return RNA_boolean_get(ptr, panel->type->active_property);
607  }
608  }
609 
610  return false;
611 }
612 
614 {
615  /* Since the panel is interacted with, it should be active and have a type. */
617  BLI_assert(panel->type != NULL);
618 
619  if (panel->type->active_property[0] != '\0') {
622  if (ptr != NULL && !RNA_pointer_is_null(ptr)) {
623  RNA_boolean_set(ptr, panel->type->active_property, true);
624  }
625  }
626 }
627 
631 static void panel_set_flag_recursive(Panel *panel, short flag, bool value)
632 {
633  SET_FLAG_FROM_TEST(panel->flag, value, flag);
634 
635  LISTBASE_FOREACH (Panel *, child, &panel->children) {
636  panel_set_flag_recursive(child, flag, value);
637  }
638 }
639 
643 static void panel_set_runtime_flag_recursive(Panel *panel, short flag, bool value)
644 {
645  SET_FLAG_FROM_TEST(panel->runtime_flag, value, flag);
646 
647  LISTBASE_FOREACH (Panel *, sub_panel, &panel->children) {
648  panel_set_runtime_flag_recursive(sub_panel, flag, value);
649  }
650 }
651 
652 static void panels_collapse_all(ARegion *region, const Panel *from_panel)
653 {
654  const bool has_category_tabs = UI_panel_category_is_visible(region);
655  const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : NULL;
656  const PanelType *from_pt = from_panel->type;
657 
658  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
659  PanelType *pt = panel->type;
660 
661  /* Close panels with headers in the same context. */
662  if (pt && from_pt && !(pt->flag & PANEL_TYPE_NO_HEADER)) {
663  if (!pt->context[0] || !from_pt->context[0] || STREQ(pt->context, from_pt->context)) {
664  if ((panel->flag & PNL_PIN) || !category || !pt->category[0] ||
665  STREQ(pt->category, category)) {
666  panel->flag |= PNL_CLOSED;
667  }
668  }
669  }
670  }
671 }
672 
673 static bool panel_type_context_poll(ARegion *region,
674  const PanelType *panel_type,
675  const char *context)
676 {
677  if (UI_panel_category_is_visible(region)) {
678  return STREQ(panel_type->category, UI_panel_category_active_get(region, false));
679  }
680 
681  if (panel_type->context[0] && STREQ(panel_type->context, context)) {
682  return true;
683  }
684 
685  return false;
686 }
687 
689 {
690  const char *idname = pt->idname;
691 
692  LISTBASE_FOREACH (Panel *, panel, lb) {
693  if (STREQLEN(panel->panelname, idname, sizeof(panel->panelname))) {
694  return panel;
695  }
696  }
697  return NULL;
698 }
699 
704  ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open)
705 {
706  Panel *panel_last;
707  const char *drawname = CTX_IFACE_(pt->translation_context, pt->label);
708  const char *idname = pt->idname;
709  const bool newpanel = (panel == NULL);
710 
711  if (newpanel) {
712  panel = MEM_callocN(sizeof(Panel), __func__);
713  panel->type = pt;
714  BLI_strncpy(panel->panelname, idname, sizeof(panel->panelname));
715 
716  if (pt->flag & PANEL_TYPE_DEFAULT_CLOSED) {
717  panel->flag |= PNL_CLOSED;
718  panel->runtime_flag |= PANEL_WAS_CLOSED;
719  }
720 
721  panel->ofsx = 0;
722  panel->ofsy = 0;
723  panel->sizex = 0;
724  panel->sizey = 0;
725  panel->blocksizex = 0;
726  panel->blocksizey = 0;
727  panel->runtime_flag |= PANEL_NEW_ADDED;
728 
729  BLI_addtail(lb, panel);
730  }
731  else {
732  /* Panel already exists. */
733  panel->type = pt;
734  }
735 
736  panel->runtime.block = block;
737 
738  BLI_strncpy(panel->drawname, drawname, sizeof(panel->drawname));
739 
740  /* If a new panel is added, we insert it right after the panel that was last added.
741  * This way new panels are inserted in the right place between versions. */
742  for (panel_last = lb->first; panel_last; panel_last = panel_last->next) {
743  if (panel_last->runtime_flag & PANEL_LAST_ADDED) {
744  BLI_remlink(lb, panel);
745  BLI_insertlinkafter(lb, panel_last, panel);
746  break;
747  }
748  }
749 
750  if (newpanel) {
751  panel->sortorder = (panel_last) ? panel_last->sortorder + 1 : 0;
752 
753  LISTBASE_FOREACH (Panel *, panel_next, lb) {
754  if (panel_next != panel && panel_next->sortorder >= panel->sortorder) {
755  panel_next->sortorder++;
756  }
757  }
758  }
759 
760  if (panel_last) {
761  panel_last->runtime_flag &= ~PANEL_LAST_ADDED;
762  }
763 
764  /* Assign the new panel to the block. */
765  block->panel = panel;
767  if (region->alignment == RGN_ALIGN_FLOAT) {
769  }
770 
771  *r_open = false;
772 
773  if (UI_panel_is_closed(panel)) {
774  return panel;
775  }
776 
777  *r_open = true;
778 
779  return panel;
780 }
781 
788 {
789  uiBlock *block = panel->runtime.block;
790 
792 }
793 
798 {
799  uiBlock *block = panel->runtime.block;
800 
801  /* A button group should always be created in #UI_panel_header_buttons_begin. */
803 
804  uiButtonGroup *button_group = block->button_groups.last;
805 
806  button_group->flag &= ~UI_BUTTON_GROUP_LOCK;
807 
808  /* Repurpose the first header button group if it is empty, in case the first button added to
809  * the panel doesn't add a new group (if the button is created directly rather than through an
810  * interface layout call). */
811  if (BLI_listbase_is_single(&block->button_groups) &&
812  BLI_listbase_is_empty(&button_group->buttons)) {
813  button_group->flag &= ~UI_BUTTON_GROUP_PANEL_HEADER;
814  }
815  else {
816  /* Always add a new button group. Although this may result in many empty groups, without it,
817  * new buttons in the panel body not protected with a #ui_block_new_button_group call would
818  * end up in the panel header group. */
819  ui_block_new_button_group(block, 0);
820  }
821 }
822 
823 static float panel_region_offset_x_get(const ARegion *region)
824 {
825  if (UI_panel_category_is_visible(region)) {
828  }
829  }
830 
831  return 0.0f;
832 }
833 
838 static void panel_calculate_size_recursive(ARegion *region, Panel *panel)
839 {
840  int width = panel->blocksizex;
841  int height = panel->blocksizey;
842 
843  LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
844  if (child_panel->runtime_flag & PANEL_ACTIVE) {
845  panel_calculate_size_recursive(region, child_panel);
846  width = max_ii(width, child_panel->sizex);
847  height += get_panel_real_size_y(child_panel);
848  }
849  }
850 
851  /* Update total panel size. */
852  if (panel->runtime_flag & PANEL_NEW_ADDED) {
853  panel->runtime_flag &= ~PANEL_NEW_ADDED;
854  panel->sizex = width;
855  panel->sizey = height;
856  }
857  else {
858  const int old_sizex = panel->sizex, old_sizey = panel->sizey;
859  const int old_region_ofsx = panel->runtime.region_ofsx;
860 
861  /* Update width/height if non-zero. */
862  if (width != 0) {
863  panel->sizex = width;
864  }
865  if (height != 0 || !UI_panel_is_closed(panel)) {
866  panel->sizey = height;
867  }
868 
869  /* Check if we need to do an animation. */
870  if (panel->sizex != old_sizex || panel->sizey != old_sizey) {
871  panel->runtime_flag |= PANEL_ANIM_ALIGN;
872  panel->ofsy += old_sizey - panel->sizey;
873  }
874 
876  if (old_region_ofsx != panel->runtime.region_ofsx) {
877  panel->runtime_flag |= PANEL_ANIM_ALIGN;
878  }
879  }
880 }
881 
882 void UI_panel_end(Panel *panel, int width, int height)
883 {
884  /* Store the size of the buttons layout in the panel. The actual panel size
885  * (including sub-panels) is calculated in #UI_panels_end. */
886  panel->blocksizex = width;
887  panel->blocksizey = height;
888 }
889 
890 static void ui_offset_panel_block(uiBlock *block)
891 {
892  const uiStyle *style = UI_style_get_dpi();
893 
894  /* Compute bounds and offset. */
895  ui_block_bounds_calc(block);
896 
897  const int ofsy = block->panel->sizey - style->panelspace;
898 
899  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
900  but->rect.ymin += ofsy;
901  but->rect.ymax += ofsy;
902  }
903 
904  block->rect.xmax = block->panel->sizex;
905  block->rect.ymax = block->panel->sizey;
906  block->rect.xmin = block->rect.ymin = 0.0;
907 }
908 
910 {
912 }
913 
914 static void panel_matches_search_filter_recursive(const Panel *panel, bool *filter_matches)
915 {
916  *filter_matches |= panel->runtime_flag & PANEL_SEARCH_FILTER_MATCH;
917 
918  /* If the panel has no match we need to make sure that its children are too. */
919  if (!*filter_matches) {
920  LISTBASE_FOREACH (const Panel *, child_panel, &panel->children) {
921  panel_matches_search_filter_recursive(child_panel, filter_matches);
922  }
923  }
924 }
925 
931 {
932  bool search_filter_matches = false;
933  panel_matches_search_filter_recursive(panel, &search_filter_matches);
934  return search_filter_matches;
935 }
936 
941  Panel *panel,
942  const bool use_search_closed)
943 {
944  /* This has to run on inactive panels that may not have a type,
945  * but we can prevent running on header-less panels in some cases. */
946  if (panel->type == NULL || !(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
948  }
949 
950  LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
951  /* Don't check if the sub-panel is active, otherwise the
952  * expansion won't be reset when the parent is closed. */
953  panel_set_expansion_from_search_filter_recursive(C, child_panel, use_search_closed);
954  }
955 }
956 
961  ARegion *region,
962  const bool use_search_closed)
963 {
964  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
965  /* Don't check if the panel is active, otherwise the expansion won't
966  * be correct when switching back to tab after exiting search. */
967  panel_set_expansion_from_search_filter_recursive(C, panel, use_search_closed);
968  }
970 }
971 
976 static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *parent_panel)
977 {
978  uiBlock *block = panel->runtime.block;
979  BLI_assert(block != NULL);
980  BLI_assert(block->active);
981  if (parent_panel != NULL && UI_panel_is_closed(parent_panel)) {
982  /* The parent panel is closed, so this panel can be completely removed. */
983  UI_block_set_search_only(block, true);
984  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
985  but->flag |= UI_HIDDEN;
986  }
987  }
988  else if (UI_panel_is_closed(panel)) {
989  /* If sub-panels have no search results but the parent panel does, then the parent panel open
990  * and the sub-panels will close. In that case there must be a way to hide the buttons in the
991  * panel but keep the header buttons. */
992  LISTBASE_FOREACH (uiButtonGroup *, button_group, &block->button_groups) {
993  if (button_group->flag & UI_BUTTON_GROUP_PANEL_HEADER) {
994  continue;
995  }
996  LISTBASE_FOREACH (LinkData *, link, &button_group->buttons) {
997  uiBut *but = link->data;
998  but->flag |= UI_HIDDEN;
999  }
1000  }
1001  }
1002 
1003  LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
1004  if (child_panel->runtime_flag & PANEL_ACTIVE) {
1005  BLI_assert(child_panel->runtime.block != NULL);
1006  panel_remove_invisible_layouts_recursive(child_panel, panel);
1007  }
1008  }
1009 }
1010 
1012 {
1013  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1014  if (panel->runtime_flag & PANEL_ACTIVE) {
1015  BLI_assert(panel->runtime.block != NULL);
1017  }
1018  }
1019 }
1020 
1025 bool UI_panel_is_closed(const Panel *panel)
1026 {
1027  /* Header-less panels can never be closed, otherwise they could disappear. */
1028  if (panel->type && panel->type->flag & PANEL_TYPE_NO_HEADER) {
1029  return false;
1030  }
1031 
1033  return !UI_panel_matches_search_filter(panel);
1034  }
1035 
1036  return panel->flag & PNL_CLOSED;
1037 }
1038 
1039 bool UI_panel_is_active(const Panel *panel)
1040 {
1041  return panel->runtime_flag & PANEL_ACTIVE;
1042 }
1043 
1046 /* -------------------------------------------------------------------- */
1053 void UI_panels_draw(const bContext *C, ARegion *region)
1054 {
1055  /* Draw in reverse order, because #uiBlocks are added in reverse order
1056  * and we need child panels to draw on top. */
1057  LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->uiblocks) {
1058  if (block->active && block->panel && !UI_panel_is_dragging(block->panel) &&
1059  !UI_block_is_search_only(block)) {
1060  UI_block_draw(C, block);
1061  }
1062  }
1063 
1064  LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->uiblocks) {
1065  if (block->active && block->panel && UI_panel_is_dragging(block->panel) &&
1066  !UI_block_is_search_only(block)) {
1067  UI_block_draw(C, block);
1068  }
1069  }
1070 }
1071 
1072 #define PNL_ICON UI_UNIT_X /* Could be UI_UNIT_Y too. */
1073 
1074 /* For button layout next to label. */
1075 void UI_panel_label_offset(const uiBlock *block, int *r_x, int *r_y)
1076 {
1077  Panel *panel = block->panel;
1078  const bool is_subpanel = (panel->type && panel->type->parent);
1079 
1080  *r_x = UI_UNIT_X * 1.0f;
1081  *r_y = UI_UNIT_Y * 1.5f;
1082 
1083  if (is_subpanel) {
1084  *r_x += (0.7f * UI_UNIT_X);
1085  }
1086 }
1087 
1088 static void panel_title_color_get(const Panel *panel,
1089  const bool show_background,
1090  const bool region_search_filter_active,
1091  uchar r_color[4])
1092 {
1093  if (!show_background) {
1094  /* Use menu colors for floating panels. */
1095  bTheme *btheme = UI_GetTheme();
1096  const uiWidgetColors *wcol = &btheme->tui.wcol_menu_back;
1097  copy_v4_v4_uchar(r_color, (const uchar *)wcol->text);
1098  return;
1099  }
1100 
1101  const bool search_match = UI_panel_matches_search_filter(panel);
1102 
1103  UI_GetThemeColor4ubv(TH_TITLE, r_color);
1104  if (region_search_filter_active && !search_match) {
1105  r_color[0] *= 0.5;
1106  r_color[1] *= 0.5;
1107  r_color[2] *= 0.5;
1108  }
1109 }
1110 
1111 static void panel_draw_highlight_border(const Panel *panel,
1112  const rcti *rect,
1113  const rcti *header_rect)
1114 {
1115  const bool draw_box_style = panel->type->flag & PANEL_TYPE_DRAW_BOX;
1116  const bool is_subpanel = panel->type->parent != NULL;
1117  if (is_subpanel) {
1118  return;
1119  }
1120 
1121  float radius;
1122  if (draw_box_style) {
1123  /* Use the theme for box widgets. */
1124  const uiWidgetColors *box_wcol = &UI_GetTheme()->tui.wcol_box;
1126  radius = box_wcol->roundness * U.widget_unit;
1127  }
1128  else {
1130  radius = 0.0f;
1131  }
1132 
1133  float color[4];
1136  &(const rctf){
1137  .xmin = rect->xmin,
1138  .xmax = rect->xmax,
1139  .ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin,
1140  .ymax = header_rect->ymax,
1141  },
1142  false,
1143  radius,
1144  color);
1145 }
1146 
1147 static void panel_draw_aligned_widgets(const uiStyle *style,
1148  const Panel *panel,
1149  const rcti *header_rect,
1150  const float aspect,
1151  const bool show_pin,
1152  const bool show_background,
1153  const bool region_search_filter_active)
1154 {
1155  const bool is_subpanel = panel->type->parent != NULL;
1156  const uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle;
1157 
1158  const int header_height = BLI_rcti_size_y(header_rect);
1159  const int scaled_unit = round_fl_to_int(UI_UNIT_X / aspect);
1160 
1161  /* Offset triangle and text to the right for subpanels. */
1162  const rcti widget_rect = {
1163  .xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0),
1164  .xmax = header_rect->xmax,
1165  .ymin = header_rect->ymin,
1166  .ymax = header_rect->ymax,
1167  };
1168 
1169  uchar title_color[4];
1170  panel_title_color_get(panel, show_background, region_search_filter_active, title_color);
1171  title_color[3] = 255;
1172 
1173  /* Draw collapse icon. */
1174  {
1175  rctf collapse_rect = {
1176  .xmin = widget_rect.xmin,
1177  .xmax = widget_rect.xmin + header_height,
1178  .ymin = widget_rect.ymin,
1179  .ymax = widget_rect.ymax,
1180  };
1181  BLI_rctf_scale(&collapse_rect, 0.25f);
1182 
1183  float triangle_color[4];
1184  rgba_uchar_to_float(triangle_color, title_color);
1185 
1186  ui_draw_anti_tria_rect(&collapse_rect, UI_panel_is_closed(panel) ? 'h' : 'v', triangle_color);
1187  }
1188 
1189  /* Draw text label. */
1190  if (panel->drawname[0] != '\0') {
1191  /* + 0.001f to avoid flirting with float inaccuracy .*/
1192  const rcti title_rect = {
1193  .xmin = widget_rect.xmin + (panel->labelofs / aspect) + scaled_unit * 1.1f,
1194  .xmax = widget_rect.xmax,
1195  .ymin = widget_rect.ymin - 2.0f / aspect,
1196  .ymax = widget_rect.ymax,
1197  };
1198  UI_fontstyle_draw(fontstyle,
1199  &title_rect,
1200  panel->drawname,
1201  title_color,
1202  &(struct uiFontStyleDraw_Params){
1203  .align = UI_STYLE_TEXT_LEFT,
1204  });
1205  }
1206 
1207  /* Draw the pin icon. */
1208  if (show_pin && (panel->flag & PNL_PIN)) {
1210  UI_icon_draw_ex(widget_rect.xmax - scaled_unit * 2.2f,
1211  widget_rect.ymin + 5.0f / aspect,
1212  ICON_PINNED,
1213  aspect * U.inv_dpi_fac,
1214  1.0f,
1215  0.0f,
1216  title_color,
1217  false);
1219  }
1220 
1221  /* Draw drag widget. */
1222  if (!is_subpanel && show_background) {
1223  const int drag_widget_size = header_height * 0.7f;
1224  GPU_matrix_push();
1225  /* The magic numbers here center the widget vertically and offset it to the left.
1226  * Currently this depends on the height of the header, although it could be independent. */
1227  GPU_matrix_translate_2f(widget_rect.xmax - scaled_unit * 1.15,
1228  widget_rect.ymin + (header_height - drag_widget_size) * 0.5f);
1229 
1230  const int col_tint = 84;
1231  float color_high[4], color_dark[4];
1232  UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, color_high);
1233  UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, color_dark);
1234 
1236  U.pixelsize, color_high, color_dark, drag_widget_size);
1239  GPU_matrix_pop();
1240  }
1241 }
1242 
1243 static void panel_draw_aligned_backdrop(const Panel *panel,
1244  const rcti *rect,
1245  const rcti *header_rect)
1246 {
1247  const bool draw_box_style = panel->type->flag & PANEL_TYPE_DRAW_BOX;
1248  const bool is_subpanel = panel->type->parent != NULL;
1249  const bool is_open = !UI_panel_is_closed(panel);
1250 
1251  if (is_subpanel && !is_open) {
1252  return;
1253  }
1254 
1257 
1258  /* Draw with an opaque box backdrop for box style panels. */
1259  if (draw_box_style) {
1260  /* Use the theme for box widgets. */
1261  const uiWidgetColors *box_wcol = &UI_GetTheme()->tui.wcol_box;
1262 
1263  if (is_subpanel) {
1264  /* Use rounded bottom corners for the last subpanel. */
1265  if (panel->next == NULL) {
1267  float color[4];
1269  /* Change the width a little bit to line up with sides. */
1271  &(const rctf){
1272  .xmin = rect->xmin + U.pixelsize,
1273  .xmax = rect->xmax - U.pixelsize,
1274  .ymin = rect->ymin + U.pixelsize,
1275  .ymax = rect->ymax,
1276  },
1277  true,
1278  box_wcol->roundness * U.widget_unit,
1279  color);
1280  }
1281  else {
1284  immRectf(pos, rect->xmin + U.pixelsize, rect->ymin, rect->xmax - U.pixelsize, rect->ymax);
1285  immUnbindProgram();
1286  }
1287  }
1288  else {
1289  /* Expand the top a tiny bit to give header buttons equal size above and below. */
1290  rcti box_rect = {
1291  .xmin = rect->xmin,
1292  .xmax = rect->xmax,
1293  .ymin = is_open ? rect->ymin : header_rect->ymin,
1294  .ymax = header_rect->ymax + U.pixelsize,
1295  };
1296  ui_draw_box_opaque(&box_rect, UI_CNR_ALL);
1297 
1298  /* Mimic the border between aligned box widgets for the bottom of the header. */
1299  if (is_open) {
1302 
1303  /* Top line. */
1304  immUniformColor4ubv(box_wcol->outline);
1305  immRectf(pos, rect->xmin, header_rect->ymin - U.pixelsize, rect->xmax, header_rect->ymin);
1306 
1307  /* Bottom "shadow" line. */
1309  immRectf(pos,
1310  rect->xmin,
1311  header_rect->ymin - U.pixelsize,
1312  rect->xmax,
1313  header_rect->ymin - U.pixelsize - 1);
1314 
1316  immUnbindProgram();
1317  }
1318  }
1319  }
1320  else {
1323 
1324  /* Panel backdrop. */
1325  if (is_open || panel->type->flag & PANEL_TYPE_NO_HEADER) {
1327  immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
1328  }
1329 
1330  /* Panel header backdrops for non sub-panels. */
1331  if (!is_subpanel) {
1333  immRectf(pos, rect->xmin, header_rect->ymin, rect->xmax, header_rect->ymax);
1334  }
1335 
1337  immUnbindProgram();
1338  }
1339 }
1340 
1344 void ui_draw_aligned_panel(const uiStyle *style,
1345  const uiBlock *block,
1346  const rcti *rect,
1347  const bool show_pin,
1348  const bool show_background,
1349  const bool region_search_filter_active)
1350 {
1351  const Panel *panel = block->panel;
1352 
1353  /* Add 0.001f to prevent flicker from float inaccuracy. */
1354  const rcti header_rect = {
1355  rect->xmin,
1356  rect->xmax,
1357  rect->ymax,
1358  rect->ymax + floor(PNL_HEADER / block->aspect + 0.001f),
1359  };
1360 
1361  if (show_background) {
1362  panel_draw_aligned_backdrop(panel, rect, &header_rect);
1363  }
1364 
1365  /* Draw the widgets and text in the panel header. */
1366  if (!(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1368  panel,
1369  &header_rect,
1370  block->aspect,
1371  show_pin,
1372  show_background,
1373  region_search_filter_active);
1374  }
1375 
1376  if (panel_custom_data_active_get(panel)) {
1377  panel_draw_highlight_border(panel, rect, &header_rect);
1378  }
1379 }
1380 
1383 /* -------------------------------------------------------------------- */
1387 #define TABS_PADDING_BETWEEN_FACTOR 4.0f
1388 #define TABS_PADDING_TEXT_FACTOR 6.0f
1389 
1393 void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
1394 {
1395  // #define USE_FLAT_INACTIVE
1396  const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT);
1397  View2D *v2d = &region->v2d;
1398  const uiStyle *style = UI_style_get();
1399  const uiFontStyle *fstyle = &style->widget;
1400  const int fontid = fstyle->uifont_id;
1401  short fstyle_points = fstyle->points;
1402  const float aspect = ((uiBlock *)region->uiblocks.first)->aspect;
1403  const float zoom = 1.0f / aspect;
1404  const int px = U.pixelsize;
1405  const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom);
1406  const float dpi_fac = UI_DPI_FAC;
1407  /* Padding of tabs around text. */
1408  const int tab_v_pad_text = round_fl_to_int(TABS_PADDING_TEXT_FACTOR * dpi_fac * zoom) + 2 * px;
1409  /* Padding between tabs. */
1410  const int tab_v_pad = round_fl_to_int(TABS_PADDING_BETWEEN_FACTOR * dpi_fac * zoom);
1411  bTheme *btheme = UI_GetTheme();
1412  const float tab_curve_radius = btheme->tui.wcol_tab.roundness * U.widget_unit * zoom;
1415  bool is_alpha;
1416  bool do_scaletabs = false;
1417 #ifdef USE_FLAT_INACTIVE
1418  bool is_active_prev = false;
1419 #endif
1420  float scaletabs = 1.0f;
1421  /* Same for all tabs. */
1422  /* Intentionally don't scale by 'px'. */
1423  const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width);
1424  const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3);
1425  const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f;
1426 
1427  int y_ofs = tab_v_pad;
1428 
1429  /* Primary theme colors. */
1430  uchar theme_col_back[4];
1431  uchar theme_col_text[3];
1432  uchar theme_col_text_hi[3];
1433 
1434  /* Tab colors. */
1435  uchar theme_col_tab_bg[4];
1436  float theme_col_tab_active[4];
1437  float theme_col_tab_inactive[4];
1438  float theme_col_tab_outline[4];
1439 
1440  UI_GetThemeColor4ubv(TH_BACK, theme_col_back);
1441  UI_GetThemeColor3ubv(TH_TEXT, theme_col_text);
1442  UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi);
1443 
1444  UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg);
1445  UI_GetThemeColor4fv(TH_TAB_ACTIVE, theme_col_tab_active);
1446  UI_GetThemeColor4fv(TH_TAB_INACTIVE, theme_col_tab_inactive);
1447  UI_GetThemeColor4fv(TH_TAB_OUTLINE, theme_col_tab_outline);
1448 
1449  is_alpha = (region->overlap && (theme_col_back[3] != 255));
1450 
1451  if (fstyle->kerning == 1) {
1453  }
1454 
1455  BLF_enable(fontid, BLF_ROTATION);
1456  BLF_rotation(fontid, M_PI_2);
1457  // UI_fontstyle_set(&style->widget);
1458  ui_fontscale(&fstyle_points, aspect / (U.pixelsize * 1.1f));
1459  BLF_size(fontid, fstyle_points, U.dpi);
1460 
1461  /* Check the region type supports categories to avoid an assert
1462  * for showing 3D view panels in the properties space. */
1463  if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) {
1465  }
1466 
1467  /* Calculate tab rectangle and check if we need to scale down. */
1468  LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1469  rcti *rct = &pc_dyn->rect;
1470  const char *category_id = pc_dyn->idname;
1471  const char *category_id_draw = IFACE_(category_id);
1472  const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
1473 
1474  rct->xmin = rct_xmin;
1475  rct->xmax = rct_xmax;
1476 
1477  rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2));
1478  rct->ymax = v2d->mask.ymax - (y_ofs);
1479 
1480  y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2);
1481  }
1482 
1483  if (y_ofs > BLI_rcti_size_y(&v2d->mask)) {
1484  scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs;
1485 
1486  LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1487  rcti *rct = &pc_dyn->rect;
1488  rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
1489  rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
1490  }
1491 
1492  do_scaletabs = true;
1493  }
1494 
1495  /* Begin drawing. */
1496  GPU_line_smooth(true);
1497 
1501 
1502  /* Draw the background. */
1503  if (is_alpha) {
1505  immUniformColor4ubv(theme_col_tab_bg);
1506  }
1507  else {
1508  immUniformColor3ubv(theme_col_tab_bg);
1509  }
1510 
1511  if (is_left) {
1512  immRecti(
1513  pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax);
1514  }
1515  else {
1516  immRecti(
1517  pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax);
1518  }
1519 
1520  if (is_alpha) {
1522  }
1523 
1524  immUnbindProgram();
1525 
1526  LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
1527  const rcti *rct = &pc_dyn->rect;
1528  const char *category_id = pc_dyn->idname;
1529  const char *category_id_draw = IFACE_(category_id);
1530  const int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2);
1531  size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
1532 #if 0
1533  int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
1534 #endif
1535 
1536  const bool is_active = STREQ(category_id, category_id_active);
1537 
1539 
1540 #ifdef USE_FLAT_INACTIVE
1541  /* Draw line between inactive tabs. */
1542  if (is_active == false && is_active_prev == false && pc_dyn->prev) {
1546  immUniformColor3fvAlpha(theme_col_tab_outline, 0.3f);
1547  immRecti(pos,
1548  is_left ? v2d->mask.xmin + (category_tabs_width / 5) :
1549  v2d->mask.xmax - (category_tabs_width / 5),
1550  rct->ymax + px,
1551  is_left ? (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5) :
1552  (v2d->mask.xmax - category_tabs_width) + (category_tabs_width / 5),
1553  rct->ymax + (px * 3));
1554  immUnbindProgram();
1555  }
1556 
1557  is_active_prev = is_active;
1558 
1559  if (is_active)
1560 #endif
1561  {
1562  /* Draw filled rectangle and outline for tab. */
1565  &(const rctf){
1566  .xmin = rct->xmin,
1567  .xmax = rct->xmax,
1568  .ymin = rct->ymin,
1569  .ymax = rct->ymax,
1570  },
1571  true,
1572  tab_curve_radius,
1573  is_active ? theme_col_tab_active : theme_col_tab_inactive);
1575  &(const rctf){
1576  .xmin = rct->xmin,
1577  .xmax = rct->xmax,
1578  .ymin = rct->ymin,
1579  .ymax = rct->ymax,
1580  },
1581  false,
1582  tab_curve_radius,
1583  theme_col_tab_outline);
1584 
1585  /* Disguise the outline on one side to join the tab to the panel. */
1589 
1590  immUniformColor4fv(is_active ? theme_col_tab_active : theme_col_tab_inactive);
1591  immRecti(pos,
1592  is_left ? rct->xmax - px : rct->xmin,
1593  rct->ymin + px,
1594  is_left ? rct->xmax : rct->xmin + px,
1595  rct->ymax - px);
1596  immUnbindProgram();
1597  }
1598 
1599  /* Tab titles. */
1600 
1601  if (do_scaletabs) {
1602  category_draw_len = BLF_width_to_strlen(
1603  fontid, category_id_draw, category_draw_len, category_width, NULL);
1604  }
1605 
1606  BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f);
1607  BLF_color3ubv(fontid, theme_col_text);
1608  BLF_draw(fontid, category_id_draw, category_draw_len);
1609 
1611 
1612  /* Not essential, but allows events to be handled right up to the region edge (T38171). */
1613  if (is_left) {
1614  pc_dyn->rect.xmin = v2d->mask.xmin;
1615  }
1616  else {
1617  pc_dyn->rect.xmax = v2d->mask.xmax;
1618  }
1619  }
1620 
1621  GPU_line_smooth(false);
1622 
1623  BLF_disable(fontid, BLF_ROTATION);
1624 
1625  if (fstyle->kerning == 1) {
1627  }
1628 }
1629 
1630 #undef TABS_PADDING_BETWEEN_FACTOR
1631 #undef TABS_PADDING_TEXT_FACTOR
1632 
1635 /* -------------------------------------------------------------------- */
1639 static int get_panel_size_y(const Panel *panel)
1640 {
1641  if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1642  return panel->sizey;
1643  }
1644 
1645  return PNL_HEADER + panel->sizey;
1646 }
1647 
1648 static int get_panel_real_size_y(const Panel *panel)
1649 {
1650  const int sizey = UI_panel_is_closed(panel) ? 0 : panel->sizey;
1651 
1652  if (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER)) {
1653  return sizey;
1654  }
1655 
1656  return PNL_HEADER + sizey;
1657 }
1658 
1659 int UI_panel_size_y(const Panel *panel)
1660 {
1661  return get_panel_real_size_y(panel);
1662 }
1663 
1668 static int get_panel_real_ofsy(Panel *panel)
1669 {
1670  if (UI_panel_is_closed(panel)) {
1671  return panel->ofsy + panel->sizey;
1672  }
1673  return panel->ofsy;
1674 }
1675 
1676 bool UI_panel_is_dragging(const Panel *panel)
1677 {
1678  return panel->runtime_flag & PANEL_IS_DRAG_DROP;
1679 }
1680 
1689 static int find_highest_panel(const void *a, const void *b)
1690 {
1691  const Panel *panel_a = ((PanelSort *)a)->panel;
1692  const Panel *panel_b = ((PanelSort *)b)->panel;
1693 
1694  /* Stick uppermost header-less panels to the top of the region -
1695  * prevent them from being sorted (multiple header-less panels have to be sorted though). */
1696  if (panel_a->type->flag & PANEL_TYPE_NO_HEADER && panel_b->type->flag & PANEL_TYPE_NO_HEADER) {
1697  /* Pass the no-header checks and check for `ofsy` and #Panel.sortorder below. */
1698  }
1699  else if (panel_a->type->flag & PANEL_TYPE_NO_HEADER) {
1700  return -1;
1701  }
1702  else if (panel_b->type->flag & PANEL_TYPE_NO_HEADER) {
1703  return 1;
1704  }
1705 
1706  if (panel_a->ofsy + panel_a->sizey < panel_b->ofsy + panel_b->sizey) {
1707  return 1;
1708  }
1709  if (panel_a->ofsy + panel_a->sizey > panel_b->ofsy + panel_b->sizey) {
1710  return -1;
1711  }
1712  if (panel_a->sortorder > panel_b->sortorder) {
1713  return 1;
1714  }
1715  if (panel_a->sortorder < panel_b->sortorder) {
1716  return -1;
1717  }
1718 
1719  return 0;
1720 }
1721 
1722 static int compare_panel(const void *a, const void *b)
1723 {
1724  const Panel *panel_a = ((PanelSort *)a)->panel;
1725  const Panel *panel_b = ((PanelSort *)b)->panel;
1726 
1727  if (panel_a->sortorder > panel_b->sortorder) {
1728  return 1;
1729  }
1730  if (panel_a->sortorder < panel_b->sortorder) {
1731  return -1;
1732  }
1733 
1734  return 0;
1735 }
1736 
1737 static void align_sub_panels(Panel *panel)
1738 {
1739  /* Position sub panels. */
1740  int ofsy = panel->ofsy + panel->sizey - panel->blocksizey;
1741 
1742  LISTBASE_FOREACH (Panel *, pachild, &panel->children) {
1743  if (pachild->runtime_flag & PANEL_ACTIVE) {
1744  pachild->ofsx = panel->ofsx;
1745  pachild->ofsy = ofsy - get_panel_size_y(pachild);
1746  ofsy -= get_panel_real_size_y(pachild);
1747 
1748  if (pachild->children.first) {
1749  align_sub_panels(pachild);
1750  }
1751  }
1752  }
1753 }
1754 
1758 static bool uiAlignPanelStep(ARegion *region, const float factor, const bool drag)
1759 {
1760  /* Count active panels. */
1761  int active_panels_len = 0;
1762  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1763  if (panel->runtime_flag & PANEL_ACTIVE) {
1764  /* These panels should have types since they are currently displayed to the user. */
1765  BLI_assert(panel->type != NULL);
1766  active_panels_len++;
1767  }
1768  }
1769  if (active_panels_len == 0) {
1770  return false;
1771  }
1772 
1773  /* Sort panels. */
1774  PanelSort *panel_sort = MEM_mallocN(sizeof(PanelSort) * active_panels_len, __func__);
1775  {
1776  PanelSort *ps = panel_sort;
1777  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1778  if (panel->runtime_flag & PANEL_ACTIVE) {
1779  ps->panel = panel;
1780  ps++;
1781  }
1782  }
1783  }
1784 
1785  if (drag) {
1786  /* While dragging, sort based on location and update #Panel.sortorder. */
1787  qsort(panel_sort, active_panels_len, sizeof(PanelSort), find_highest_panel);
1788  for (int i = 0; i < active_panels_len; i++) {
1789  panel_sort[i].panel->sortorder = i;
1790  }
1791  }
1792  else {
1793  /* Otherwise use #Panel.sortorder. */
1794  qsort(panel_sort, active_panels_len, sizeof(PanelSort), compare_panel);
1795  }
1796 
1797  /* X offset. */
1798  const int region_offset_x = panel_region_offset_x_get(region);
1799  for (int i = 0; i < active_panels_len; i++) {
1800  PanelSort *ps = &panel_sort[i];
1801  const bool use_box = ps->panel->type->flag & PANEL_TYPE_DRAW_BOX;
1802  ps->panel->runtime.region_ofsx = region_offset_x;
1803  ps->new_offset_x = region_offset_x + ((use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0);
1804  }
1805 
1806  /* Y offset. */
1807  for (int i = 0, y = 0; i < active_panels_len; i++) {
1808  PanelSort *ps = &panel_sort[i];
1809  y -= get_panel_real_size_y(ps->panel);
1810 
1811  const bool use_box = ps->panel->type->flag & PANEL_TYPE_DRAW_BOX;
1812  if (use_box) {
1814  }
1815  ps->new_offset_y = y;
1816  /* The header still draws offset by the size of closed panels, so apply the offset here. */
1817  if (UI_panel_is_closed(ps->panel)) {
1818  panel_sort[i].new_offset_y -= ps->panel->sizey;
1819  }
1820  }
1821 
1822  /* Interpolate based on the input factor. */
1823  bool changed = false;
1824  for (int i = 0; i < active_panels_len; i++) {
1825  PanelSort *ps = &panel_sort[i];
1826  if (ps->panel->flag & PNL_SELECT) {
1827  continue;
1828  }
1829 
1830  if (ps->new_offset_x != ps->panel->ofsx) {
1831  const float x = interpf((float)ps->new_offset_x, (float)ps->panel->ofsx, factor);
1832  ps->panel->ofsx = round_fl_to_int(x);
1833  changed = true;
1834  }
1835  if (ps->new_offset_y != ps->panel->ofsy) {
1836  const float y = interpf((float)ps->new_offset_y, (float)ps->panel->ofsy, factor);
1837  ps->panel->ofsy = round_fl_to_int(y);
1838  changed = true;
1839  }
1840  }
1841 
1842  /* Set locations for tabbed and sub panels. */
1843  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1844  if (panel->runtime_flag & PANEL_ACTIVE) {
1845  if (panel->children.first) {
1846  align_sub_panels(panel);
1847  }
1848  }
1849  }
1850 
1851  MEM_freeN(panel_sort);
1852 
1853  return changed;
1854 }
1855 
1856 static void ui_panels_size(ARegion *region, int *r_x, int *r_y)
1857 {
1858  int sizex = 0;
1859  int sizey = 0;
1860 
1861  /* Compute size taken up by panels, for setting in view2d. */
1862  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1863  if (panel->runtime_flag & PANEL_ACTIVE) {
1864  const int pa_sizex = panel->ofsx + panel->sizex;
1865  const int pa_sizey = get_panel_real_ofsy(panel);
1866 
1867  sizex = max_ii(sizex, pa_sizex);
1868  sizey = min_ii(sizey, pa_sizey);
1869  }
1870  }
1871 
1872  if (sizex == 0) {
1873  sizex = UI_PANEL_WIDTH;
1874  }
1875  if (sizey == 0) {
1876  sizey = -UI_PANEL_WIDTH;
1877  }
1878 
1879  *r_x = sizex;
1880  *r_y = sizey;
1881 }
1882 
1883 static void ui_do_animate(bContext *C, Panel *panel)
1884 {
1885  uiHandlePanelData *data = panel->activedata;
1886  ARegion *region = CTX_wm_region(C);
1887 
1888  float fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME;
1889  fac = min_ff(sqrtf(fac), 1.0f);
1890 
1891  if (uiAlignPanelStep(region, fac, false)) {
1892  ED_region_tag_redraw(region);
1893  }
1894  else {
1895  if (UI_panel_is_dragging(panel)) {
1896  /* Note: doing this in #panel_activate_state would require
1897  * removing `const` for context in many other places. */
1898  reorder_instanced_panel_list(C, region, panel);
1899  }
1900 
1902  }
1903 }
1904 
1906 {
1907  LISTBASE_FOREACH (Panel *, panel, lb) {
1908  /* Flags to copy over to the next layout pass. */
1909  const short flag_copy = PANEL_USE_CLOSED_FROM_SEARCH | PANEL_IS_DRAG_DROP;
1910 
1911  const bool was_active = panel->runtime_flag & PANEL_ACTIVE;
1912  const bool was_closed = UI_panel_is_closed(panel);
1913  panel->runtime_flag &= flag_copy;
1914  SET_FLAG_FROM_TEST(panel->runtime_flag, was_active, PANEL_WAS_ACTIVE);
1915  SET_FLAG_FROM_TEST(panel->runtime_flag, was_closed, PANEL_WAS_CLOSED);
1916 
1917  panels_layout_begin_clear_flags(&panel->children);
1918  }
1919 }
1920 
1921 void UI_panels_begin(const bContext *UNUSED(C), ARegion *region)
1922 {
1923  /* Set all panels as inactive, so that at the end we know which ones were used. Also
1924  * clear other flags so we know later that their values were set for the current redraw. */
1926 }
1927 
1928 void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
1929 {
1930  ScrArea *area = CTX_wm_area(C);
1931 
1933 
1934  const bool region_search_filter_active = region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE;
1935 
1936  if (properties_space_needs_realign(area, region)) {
1937  region_panels_set_expansion_from_search_filter(C, region, region_search_filter_active);
1938  }
1939  else if (region->flag & RGN_FLAG_SEARCH_FILTER_UPDATE) {
1940  region_panels_set_expansion_from_search_filter(C, region, region_search_filter_active);
1941  }
1942 
1943  if (region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE) {
1944  /* Clean up the extra panels and buttons created for searching. */
1946  }
1947 
1948  LISTBASE_FOREACH (Panel *, panel, &region->panels) {
1949  if (panel->runtime_flag & PANEL_ACTIVE) {
1950  BLI_assert(panel->runtime.block != NULL);
1951  panel_calculate_size_recursive(region, panel);
1952  }
1953  }
1954 
1955  /* Offset contents. */
1956  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
1957  if (block->active && block->panel) {
1958  ui_offset_panel_block(block);
1959  }
1960  }
1961 
1962  /* Re-align, possibly with animation. */
1963  Panel *panel;
1964  if (panels_need_realign(area, region, &panel)) {
1965  if (panel) {
1967  }
1968  else {
1969  uiAlignPanelStep(region, 1.0, false);
1970  }
1971  }
1972 
1973  /* Compute size taken up by panels. */
1974  ui_panels_size(region, r_x, r_y);
1975 }
1976 
1979 /* -------------------------------------------------------------------- */
1983 #define DRAG_REGION_PAD (PNL_HEADER * 0.5)
1984 static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
1985 {
1986  uiHandlePanelData *data = panel->activedata;
1987  ARegion *region = CTX_wm_region(C);
1988 
1989  /* Keep the drag position in the region with a small pad to keep the panel visible. */
1990  const int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD);
1991 
1992  float dy = (float)(y - data->starty);
1993 
1994  /* Adjust for region zoom. */
1995  dy *= BLI_rctf_size_y(&region->v2d.cur) / (float)BLI_rcti_size_y(&region->winrct);
1996 
1997  /* Add the movement of the view due to edge scrolling while dragging. */
1998  dy += ((float)region->v2d.cur.ymin - data->start_cur_ymin);
1999 
2000  panel->ofsy = data->startofsy + round_fl_to_int(dy);
2001 
2002  uiAlignPanelStep(region, 0.2f, true);
2003 
2004  ED_region_tag_redraw(region);
2005 }
2006 #undef DRAG_REGION_PAD
2007 
2010 /* -------------------------------------------------------------------- */
2015  const Panel *panel,
2016  const int mx,
2017  const int my)
2018 {
2019  if (!IN_RANGE((float)mx, block->rect.xmin, block->rect.xmax)) {
2020  return PANEL_MOUSE_OUTSIDE;
2021  }
2022 
2023  if (IN_RANGE((float)my, block->rect.ymax, block->rect.ymax + PNL_HEADER)) {
2025  }
2026 
2027  if (!UI_panel_is_closed(panel)) {
2028  if (IN_RANGE((float)my, block->rect.ymin, block->rect.ymax + PNL_HEADER)) {
2030  }
2031  }
2032 
2033  return PANEL_MOUSE_OUTSIDE;
2034 }
2035 
2038  int xy_init[2];
2040 
2042 {
2043  uiPanelDragCollapseHandle *dragcol_data = userdata;
2044  MEM_freeN(dragcol_data);
2045 }
2046 
2047 static void ui_panel_drag_collapse(const bContext *C,
2048  const uiPanelDragCollapseHandle *dragcol_data,
2049  const int xy_dst[2])
2050 {
2051  ARegion *region = CTX_wm_region(C);
2052 
2053  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
2054  float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)};
2055  float xy_b_block[2] = {UNPACK2(xy_dst)};
2056  Panel *panel = block->panel;
2057 
2058  if (panel == NULL || (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER))) {
2059  continue;
2060  }
2061  const int oldflag = panel->flag;
2062 
2063  /* Lock axis. */
2064  xy_b_block[0] = dragcol_data->xy_init[0];
2065 
2066  /* Use cursor coords in block space. */
2067  ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]);
2068  ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]);
2069 
2070  /* Set up `rect` to match header size. */
2071  rctf rect = block->rect;
2072  rect.ymin = rect.ymax;
2073  rect.ymax = rect.ymin + PNL_HEADER;
2074 
2075  /* Touch all panels between last mouse coordinate and the current one. */
2076  if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) {
2077  /* Force panel to open or close. */
2079  SET_FLAG_FROM_TEST(panel->flag, dragcol_data->was_first_open, PNL_CLOSED);
2080 
2081  /* If panel->flag has changed this means a panel was opened/closed here. */
2082  if (panel->flag != oldflag) {
2084  }
2085  }
2086  }
2087  /* Update the instanced panel data expand flags with the changes made here. */
2089 }
2090 
2097 static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, void *userdata)
2098 {
2099  wmWindow *win = CTX_wm_window(C);
2100  uiPanelDragCollapseHandle *dragcol_data = userdata;
2101  short retval = WM_UI_HANDLER_CONTINUE;
2102 
2103  switch (event->type) {
2104  case MOUSEMOVE:
2105  ui_panel_drag_collapse(C, dragcol_data, &event->x);
2106 
2107  retval = WM_UI_HANDLER_BREAK;
2108  break;
2109  case LEFTMOUSE:
2110  if (event->val == KM_RELEASE) {
2111  /* Done! */
2115  dragcol_data,
2116  true);
2118  }
2119  /* Don't let any left-mouse event fall through! */
2120  retval = WM_UI_HANDLER_BREAK;
2121  break;
2122  }
2123 
2124  return retval;
2125 }
2126 
2127 static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was_open)
2128 {
2129  wmWindow *win = CTX_wm_window(C);
2130  const wmEvent *event = win->eventstate;
2131  uiPanelDragCollapseHandle *dragcol_data = MEM_mallocN(sizeof(*dragcol_data), __func__);
2132 
2133  dragcol_data->was_first_open = was_open;
2134  copy_v2_v2_int(dragcol_data->xy_init, &event->x);
2135 
2137  &win->modalhandlers,
2140  dragcol_data,
2141  0);
2142 }
2143 
2150 static void ui_handle_panel_header(const bContext *C,
2151  const uiBlock *block,
2152  const int mx,
2153  const int event_type,
2154  const short ctrl,
2155  const short shift)
2156 {
2157  Panel *panel = block->panel;
2158  ARegion *region = CTX_wm_region(C);
2159 
2160  BLI_assert(panel->type != NULL);
2161  BLI_assert(!(panel->type->flag & PANEL_TYPE_NO_HEADER));
2162 
2163  const bool is_subpanel = (panel->type->parent != NULL);
2164  const bool use_pin = UI_panel_category_is_visible(region) && !is_subpanel;
2165  const bool show_pin = use_pin && (panel->flag & PNL_PIN);
2166  const bool show_drag = !is_subpanel;
2167 
2168  /* Handle panel pinning. */
2169  if (use_pin && ELEM(event_type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE) && shift) {
2170  panel->flag ^= PNL_PIN;
2171  ED_region_tag_redraw(region);
2172  return;
2173  }
2174 
2175  float expansion_area_xmax = block->rect.xmax;
2176  if (show_drag) {
2177  expansion_area_xmax -= (PNL_ICON * 1.5f);
2178  }
2179  if (show_pin) {
2180  expansion_area_xmax -= PNL_ICON;
2181  }
2182 
2183  /* Collapse and expand panels. */
2184  if (ELEM(event_type, EVT_RETKEY, EVT_PADENTER, EVT_AKEY) || mx < expansion_area_xmax) {
2185  if (ctrl && !is_subpanel) {
2186  /* For parent panels, collapse all other panels or toggle children. */
2187  if (UI_panel_is_closed(panel) || BLI_listbase_is_empty(&panel->children)) {
2188  panels_collapse_all(region, panel);
2189 
2190  /* Reset the view - we don't want to display a view without content. */
2191  UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
2192  }
2193  else {
2194  /* If a panel has sub-panels and it's open, toggle the expansion
2195  * of the sub-panels (based on the expansion of the first sub-panel). */
2196  Panel *first_child = panel->children.first;
2197  BLI_assert(first_child != NULL);
2199  panel->flag |= PNL_CLOSED;
2200  }
2201  }
2202 
2204 
2205  if (event_type == LEFTMOUSE) {
2207  }
2208 
2209  /* Set panel custom data (modifier) active when expanding subpanels, but not top-level
2210  * panels to allow collapsing and expanding without setting the active element. */
2211  if (is_subpanel) {
2213  }
2214 
2217  return;
2218  }
2219 
2220  /* Handle panel dragging. For now don't allow dragging in floating regions. */
2221  if (show_drag && !(region->alignment == RGN_ALIGN_FLOAT)) {
2222  const float drag_area_xmin = block->rect.xmax - (PNL_ICON * 1.5f);
2223  const float drag_area_xmax = block->rect.xmax;
2224  if (IN_RANGE(mx, drag_area_xmin, drag_area_xmax)) {
2226  return;
2227  }
2228  }
2229 
2230  /* Handle panel unpinning. */
2231  if (show_pin) {
2232  const float pin_area_xmin = expansion_area_xmax;
2233  const float pin_area_xmax = pin_area_xmin + PNL_ICON;
2234  if (IN_RANGE(mx, pin_area_xmin, pin_area_xmax)) {
2235  panel->flag ^= PNL_PIN;
2236  ED_region_tag_redraw(region);
2237  return;
2238  }
2239  }
2240 }
2241 
2243 {
2244  /* Check for more than one category. */
2245  return region->panels_category.first &&
2246  region->panels_category.first != region->panels_category.last;
2247 }
2248 
2249 PanelCategoryDyn *UI_panel_category_find(const ARegion *region, const char *idname)
2250 {
2251  return BLI_findstring(&region->panels_category, idname, offsetof(PanelCategoryDyn, idname));
2252 }
2253 
2255 {
2256  return BLI_findstring(
2257  &region->panels_category_active, idname, offsetof(PanelCategoryStack, idname));
2258 }
2259 
2260 static void ui_panel_category_active_set(ARegion *region, const char *idname, bool fallback)
2261 {
2262  ListBase *lb = &region->panels_category_active;
2263  PanelCategoryStack *pc_act = UI_panel_category_active_find(region, idname);
2264 
2265  if (pc_act) {
2266  BLI_remlink(lb, pc_act);
2267  }
2268  else {
2269  pc_act = MEM_callocN(sizeof(PanelCategoryStack), __func__);
2270  BLI_strncpy(pc_act->idname, idname, sizeof(pc_act->idname));
2271  }
2272 
2273  if (fallback) {
2274  /* For fall-backs, add at the end so explicitly chosen categories have priority. */
2275  BLI_addtail(lb, pc_act);
2276  }
2277  else {
2278  BLI_addhead(lb, pc_act);
2279  }
2280 
2281  /* Validate all active panels. We could do this on load, they are harmless -
2282  * but we should remove them somewhere.
2283  * (Add-ons could define panels and gather cruft over time). */
2284  {
2285  PanelCategoryStack *pc_act_next;
2286  /* intentionally skip first */
2287  pc_act_next = pc_act->next;
2288  while ((pc_act = pc_act_next)) {
2289  pc_act_next = pc_act->next;
2290  if (!BLI_findstring(
2291  &region->type->paneltypes, pc_act->idname, offsetof(PanelType, category))) {
2292  BLI_remlink(lb, pc_act);
2293  MEM_freeN(pc_act);
2294  }
2295  }
2296  }
2297 }
2298 
2299 void UI_panel_category_active_set(ARegion *region, const char *idname)
2300 {
2301  ui_panel_category_active_set(region, idname, false);
2302 }
2303 
2304 void UI_panel_category_active_set_default(ARegion *region, const char *idname)
2305 {
2306  if (!UI_panel_category_active_find(region, idname)) {
2307  ui_panel_category_active_set(region, idname, true);
2308  }
2309 }
2310 
2311 const char *UI_panel_category_active_get(ARegion *region, bool set_fallback)
2312 {
2314  if (UI_panel_category_find(region, pc_act->idname)) {
2315  return pc_act->idname;
2316  }
2317  }
2318 
2319  if (set_fallback) {
2320  PanelCategoryDyn *pc_dyn = region->panels_category.first;
2321  if (pc_dyn) {
2322  ui_panel_category_active_set(region, pc_dyn->idname, true);
2323  return pc_dyn->idname;
2324  }
2325  }
2326 
2327  return NULL;
2328 }
2329 
2331 {
2333  if (BLI_rcti_isect_pt(&ptd->rect, event->mval[0], event->mval[1])) {
2334  return ptd;
2335  }
2336  }
2337 
2338  return NULL;
2339 }
2340 
2341 void UI_panel_category_add(ARegion *region, const char *name)
2342 {
2343  PanelCategoryDyn *pc_dyn = MEM_callocN(sizeof(*pc_dyn), __func__);
2344  BLI_addtail(&region->panels_category, pc_dyn);
2345 
2346  BLI_strncpy(pc_dyn->idname, name, sizeof(pc_dyn->idname));
2347 
2348  /* 'pc_dyn->rect' must be set on draw. */
2349 }
2350 
2352 {
2353  BLI_freelistN(&region->panels_category);
2354 }
2355 
2357  ARegion *region,
2358  const uiBut *active_but)
2359 {
2360  const bool is_mousewheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE);
2361  const bool inside_tabregion =
2363  (event->mval[0] < ((PanelCategoryDyn *)region->panels_category.first)->rect.xmax) :
2364  (event->mval[0] > ((PanelCategoryDyn *)region->panels_category.first)->rect.xmin));
2365 
2366  /* If mouse is inside non-tab region, ctrl key is required. */
2367  if (is_mousewheel && !event->ctrl && !inside_tabregion) {
2368  return WM_UI_HANDLER_CONTINUE;
2369  }
2370 
2371  if (active_but && ui_but_supports_cycling(active_but)) {
2372  /* Skip - exception to make cycling buttons using ctrl+mousewheel work in tabbed regions. */
2373  }
2374  else {
2375  const char *category = UI_panel_category_active_get(region, false);
2376  if (LIKELY(category)) {
2377  PanelCategoryDyn *pc_dyn = UI_panel_category_find(region, category);
2378  if (LIKELY(pc_dyn)) {
2379  if (is_mousewheel) {
2380  /* We can probably get rid of this and only allow Ctrl-Tabbing. */
2381  pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev;
2382  }
2383  else {
2384  const bool backwards = event->shift;
2385  pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next;
2386  if (!pc_dyn) {
2387  /* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */
2388  pc_dyn = backwards ? region->panels_category.last : region->panels_category.first;
2389  }
2390  }
2391 
2392  if (pc_dyn) {
2393  /* Intentionally don't reset scroll in this case,
2394  * allowing for quick browsing between tabs. */
2395  UI_panel_category_active_set(region, pc_dyn->idname);
2396  ED_region_tag_redraw(region);
2397  }
2398  }
2399  }
2400  return WM_UI_HANDLER_BREAK;
2401  }
2402 
2403  return WM_UI_HANDLER_CONTINUE;
2404 }
2405 
2412  const wmEvent *event,
2413  ARegion *region,
2414  const uiBut *active_but)
2415 {
2416  /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
2417  if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
2418  return WM_UI_HANDLER_CONTINUE;
2419  }
2420 
2421  /* We only use KM_PRESS events in this function, so it's simpler to return early. */
2422  if (event->val != KM_PRESS) {
2423  return WM_UI_HANDLER_CONTINUE;
2424  }
2425 
2426  /* Scroll-bars can overlap panels now, they have handling priority. */
2427  if (UI_view2d_mouse_in_scrollers(region, &region->v2d, event->x, event->y)) {
2428  return WM_UI_HANDLER_CONTINUE;
2429  }
2430 
2431  int retval = WM_UI_HANDLER_CONTINUE;
2432 
2433  /* Handle category tabs. */
2434  if (UI_panel_category_is_visible(region)) {
2435  if (event->type == LEFTMOUSE) {
2436  PanelCategoryDyn *pc_dyn = panel_categories_find_mouse_over(region, event);
2437  if (pc_dyn) {
2438  UI_panel_category_active_set(region, pc_dyn->idname);
2439  ED_region_tag_redraw(region);
2440 
2441  /* Reset scroll to the top (T38348). */
2442  UI_view2d_offset(&region->v2d, -1.0f, 1.0f);
2443 
2444  retval = WM_UI_HANDLER_BREAK;
2445  }
2446  }
2447  else if ((event->type == EVT_TABKEY && event->ctrl) ||
2448  ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
2449  /* Cycle tabs. */
2450  retval = ui_handle_panel_category_cycling(event, region, active_but);
2451  }
2452  }
2453 
2454  if (retval == WM_UI_HANDLER_BREAK) {
2455  return retval;
2456  }
2457 
2458  const bool region_has_active_button = (ui_region_find_active_but(region) != NULL);
2459 
2460  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
2461  Panel *panel = block->panel;
2462  if (panel == NULL || panel->type == NULL) {
2463  continue;
2464  }
2465  /* We can't expand or collapse panels without headers, they would disappear. */
2466  if (panel->type->flag & PANEL_TYPE_NO_HEADER) {
2467  continue;
2468  }
2469 
2470  int mx = event->x;
2471  int my = event->y;
2472  ui_window_to_block(region, block, &mx, &my);
2473 
2474  const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
2475 
2476  if (mouse_state != PANEL_MOUSE_OUTSIDE) {
2477  /* Mark panels that have been interacted with so their expansion
2478  * doesn't reset when property search finishes. */
2481 
2482  /* The panel collapse / expand key "A" is special as it takes priority over
2483  * active button handling. */
2484  if (event->type == EVT_AKEY && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
2485  retval = WM_UI_HANDLER_BREAK;
2486  ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift);
2487  break;
2488  }
2489  }
2490 
2491  /* Don't do any other panel handling with an active button. */
2492  if (region_has_active_button) {
2493  continue;
2494  }
2495 
2496  if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) {
2497  /* All mouse clicks inside panel headers should return in break. */
2498  if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) {
2499  retval = WM_UI_HANDLER_BREAK;
2500  ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift);
2501  }
2502  else if (event->type == RIGHTMOUSE) {
2503  retval = WM_UI_HANDLER_BREAK;
2504  ui_popup_context_menu_for_panel(C, region, block->panel);
2505  }
2506  break;
2507  }
2508  }
2509 
2510  return retval;
2511 }
2512 
2513 static void ui_panel_custom_data_set_recursive(Panel *panel, PointerRNA *custom_data)
2514 {
2515  panel->runtime.custom_data_ptr = custom_data;
2516 
2517  LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
2518  ui_panel_custom_data_set_recursive(child_panel, custom_data);
2519  }
2520 }
2521 
2522 void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
2523 {
2524  BLI_assert(panel->type != NULL);
2525 
2526  /* Free the old custom data, which should be shared among all of the panel's sub-panels. */
2527  if (panel->runtime.custom_data_ptr != NULL) {
2529  }
2530 
2531  ui_panel_custom_data_set_recursive(panel, custom_data);
2532 }
2533 
2535 {
2536  return panel->runtime.custom_data_ptr;
2537 }
2538 
2540 {
2541  ARegion *region = CTX_wm_region(C);
2542 
2543  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
2544  Panel *panel = block->panel;
2545  if (panel == NULL) {
2546  continue;
2547  }
2548 
2549  int mx = event->x;
2550  int my = event->y;
2551  ui_window_to_block(region, block, &mx, &my);
2552  const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
2554  return UI_panel_custom_data_get(panel);
2555  }
2556  }
2557 
2558  return NULL;
2559 }
2560 
2563 /* -------------------------------------------------------------------- */
2567 /* Note, this is modal handler and should not swallow events for animation. */
2568 static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
2569 {
2570  Panel *panel = userdata;
2571  uiHandlePanelData *data = panel->activedata;
2572 
2573  /* Verify if we can stop. */
2574  if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
2576  }
2577  else if (event->type == MOUSEMOVE) {
2578  if (data->state == PANEL_STATE_DRAG) {
2579  ui_do_drag(C, event, panel);
2580  }
2581  }
2582  else if (event->type == TIMER && event->customdata == data->animtimer) {
2583  if (data->state == PANEL_STATE_ANIMATION) {
2584  ui_do_animate(C, panel);
2585  }
2586  else if (data->state == PANEL_STATE_DRAG) {
2587  ui_do_drag(C, event, panel);
2588  }
2589  }
2590 
2591  data = panel->activedata;
2592 
2593  if (data && data->state == PANEL_STATE_ANIMATION) {
2594  return WM_UI_HANDLER_CONTINUE;
2595  }
2596  return WM_UI_HANDLER_BREAK;
2597 }
2598 
2599 static void ui_handler_remove_panel(bContext *C, void *userdata)
2600 {
2601  Panel *panel = userdata;
2602 
2604 }
2605 
2607  wmWindow *win,
2608  const ARegion *region,
2609  Panel *panel,
2610  const uiHandlePanelState state)
2611 {
2612  if (panel->activedata == NULL) {
2613  panel->activedata = MEM_callocN(sizeof(uiHandlePanelData), __func__);
2616  }
2617 
2618  uiHandlePanelData *data = panel->activedata;
2619 
2621 
2622  data->state = state;
2623  data->startx = win->eventstate->x;
2624  data->starty = win->eventstate->y;
2625  data->startofsx = panel->ofsx;
2626  data->startofsy = panel->ofsy;
2627  data->start_cur_xmin = region->v2d.cur.xmin;
2628  data->start_cur_ymin = region->v2d.cur.ymin;
2629  data->starttime = PIL_check_seconds_timer();
2630 }
2631 
2637 static void panel_activate_state(const bContext *C, Panel *panel, const uiHandlePanelState state)
2638 {
2639  uiHandlePanelData *data = panel->activedata;
2640  wmWindow *win = CTX_wm_window(C);
2641  ARegion *region = CTX_wm_region(C);
2642 
2643  if (data != NULL && data->state == state) {
2644  return;
2645  }
2646 
2647  if (state == PANEL_STATE_DRAG) {
2649 
2650  panel_set_flag_recursive(panel, PNL_SELECT, true);
2652 
2653  panel_handle_data_ensure(C, win, region, panel, state);
2654 
2655  /* Initiate edge panning during drags for scrolling beyond the initial region view. */
2656  wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
2658  }
2659  else if (state == PANEL_STATE_ANIMATION) {
2660  panel_set_flag_recursive(panel, PNL_SELECT, false);
2661 
2662  panel_handle_data_ensure(C, win, region, panel, state);
2663  }
2664  else if (state == PANEL_STATE_EXIT) {
2666 
2667  BLI_assert(data != NULL);
2668 
2669  if (data->animtimer) {
2670  WM_event_remove_timer(CTX_wm_manager(C), win, data->animtimer);
2671  data->animtimer = NULL;
2672  }
2673 
2674  MEM_freeN(data);
2675  panel->activedata = NULL;
2676 
2679  }
2680 
2681  ED_region_tag_redraw(region);
2682 }
2683 
typedef float(TangentPoint)[2]
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:714
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:689
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:699
@ PANEL_TYPE_DRAW_BOX
Definition: BKE_screen.h:305
@ PANEL_TYPE_NO_HEADER
Definition: BKE_screen.h:298
@ PANEL_TYPE_INSTANCED
Definition: BKE_screen.h:303
@ PANEL_TYPE_DEFAULT_CLOSED
Definition: BKE_screen.h:297
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition: blf.c:411
#define BLF_KERNING_DEFAULT
Definition: BLF_api.h:272
float BLF_width(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: blf.c:723
void BLF_draw(int fontid, const char *str, size_t len) ATTR_NONNULL(2)
Definition: blf.c:542
#define BLF_DRAW_STR_DUMMY_MAX
Definition: BLF_api.h:283
size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width) ATTR_NONNULL(2)
Definition: blf.c:636
void BLF_disable(int fontid, int option)
Definition: blf.c:283
void BLF_rotation(int fontid, float angle)
Definition: blf.c:801
#define BLF_ROTATION
Definition: BLF_api.h:269
void BLF_size(int fontid, int size, int dpi)
Definition: blf.c:367
void BLF_enable(int fontid, int option)
Definition: blf.c:274
void BLF_position(int fontid, float x, float y, float z)
Definition: blf.c:312
#define BLI_assert(a)
Definition: BLI_assert.h:58
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:124
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:87
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:188
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
Definition: BLI_listbase.h:184
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:352
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:547
void void BLI_INLINE bool BLI_listbase_is_single(const struct ListBase *lb)
Definition: BLI_listbase.h:120
void * BLI_findstring(const struct ListBase *listbase, const char *id, const int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
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:41
MINLINE float interpf(float a, float b, float t)
MINLINE int clamp_i(int value, int min, int max)
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
Definition: math_color.c:414
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:157
bool BLI_rcti_isect_pt(const struct rcti *rect, const int x, const int y)
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
void BLI_rctf_scale(rctf *rect, const float scale)
Definition: rct.c:694
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition: BLI_rect.h:165
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
unsigned char uchar
Definition: BLI_sys_types.h:86
unsigned int uint
Definition: BLI_sys_types.h:83
#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:69
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define MAX_NAME
Definition: DNA_defs.h:62
@ PNL_SELECT
@ PNL_PIN
@ PNL_CLOSED
@ PNL_INSTANCED_LIST_ORDER_CHANGED
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ RGN_ALIGN_RIGHT
@ RGN_ALIGN_FLOAT
#define RGN_TYPE_HAS_CATEGORY_MASK
@ RGN_FLAG_SEARCH_FILTER_UPDATE
@ RGN_FLAG_SEARCH_FILTER_ACTIVE
@ RGN_TYPE_WINDOW
@ SPACE_PROPERTIES
void(* uiListPanelIDFromDataFunc)(void *data_link, char *r_idname)
Definition: ED_anim_api.h:687
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:667
GPUBatch
Definition: GPU_batch.h:93
void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id)
Definition: gpu_batch.cc:299
void GPU_batch_draw(GPUBatch *batch)
Definition: gpu_batch.cc:234
struct GPUBatch * GPU_batch_preset_panel_drag_widget(const float pixelsize, const float col_high[4], const float col_dark[4], const float width) ATTR_WARN_UNUSED_RESULT
void immUniformColor4ubv(const unsigned char rgba[4])
void immUnbindProgram(void)
void immUniformThemeColor(int color_id)
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)
void immRectf(uint pos, float x1, float y1, float x2, float y2)
_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
_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
void GPU_matrix_pop(void)
Definition: gpu_matrix.cc:142
void GPU_matrix_push(void)
Definition: gpu_matrix.cc:135
void GPU_matrix_translate_2f(float x, float y)
Definition: gpu_matrix.cc:190
@ GPU_SHADER_2D_UNIFORM_COLOR
Definition: GPU_shader.h:171
@ GPU_SHADER_2D_FLAT_COLOR
Definition: GPU_shader.h:178
@ GPU_BLEND_NONE
Definition: GPU_state.h:55
@ GPU_BLEND_ALPHA
Definition: GPU_state.h:57
void GPU_blend(eGPUBlend blend)
Definition: gpu_state.cc:55
void GPU_line_smooth(bool enable)
Definition: gpu_state.cc:85
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ GPU_COMP_I32
Read Guarded memory(de)allocation.
Platform independent time functions.
#define C
Definition: RandGen.cpp:39
#define UI_UNIT_Y
const struct uiStyle * UI_style_get_dpi(void)
void UI_block_theme_style_set(uiBlock *block, char theme_style)
Definition: interface.c:3547
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
@ UI_CNR_NONE
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:243
const struct uiStyle * UI_style_get(void)
void UI_draw_roundbox_corner_set(int type)
bool UI_block_is_search_only(const uiBlock *block)
Definition: interface.c:3552
#define UI_PANEL_BOX_STYLE_MARGIN
Definition: UI_interface.h:251
void UI_block_draw(const struct bContext *C, struct uiBlock *block)
#define UI_DPI_FAC
Definition: UI_interface.h:309
#define UI_PANEL_CATEGORY_MARGIN_WIDTH
Definition: UI_interface.h:249
void UI_draw_roundbox_aa(const struct rctf *rect, bool filled, float rad, const float color[4])
void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str, const uchar col[4], const struct uiFontStyleDraw_Params *fs_params)
#define UI_UNIT_X
@ UI_BLOCK_THEME_STYLE_POPUP
Definition: UI_interface.h:671
#define INSTANCED_PANEL_UNIQUE_STR_LEN
void UI_block_set_search_only(uiBlock *block, bool search_only)
Definition: interface.c:3561
void UI_icon_draw_ex(float x, float y, int icon_id, float aspect, float alpha, float desaturate, const uchar mono_color[4], const bool mono_border)
@ TH_SELECT_ACTIVE
Definition: UI_resources.h:274
@ TH_TAB_OUTLINE
Definition: UI_resources.h:64
@ TH_PANEL_HEADER
Definition: UI_resources.h:72
@ TH_TAB_ACTIVE
Definition: UI_resources.h:61
@ TH_BACK
Definition: UI_resources.h:55
@ TH_WIDGET_EMBOSS
Definition: UI_resources.h:308
@ TH_PANEL_SUB_BACK
Definition: UI_resources.h:74
@ TH_TITLE
Definition: UI_resources.h:60
@ TH_TAB_BACK
Definition: UI_resources.h:63
@ TH_TAB_INACTIVE
Definition: UI_resources.h:62
@ TH_MATCH
Definition: UI_resources.h:272
@ TH_PANEL_BACK
Definition: UI_resources.h:73
@ TH_TEXT
Definition: UI_resources.h:58
@ TH_TEXT_HI
Definition: UI_resources.h:59
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
Definition: resources.c:1350
struct bTheme * UI_GetTheme(void)
Definition: resources.c:1086
void UI_GetThemeColor4fv(int colorid, float col[4])
Definition: resources.c:1199
void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
Definition: resources.c:1359
void UI_GetThemeColor4ubv(int colorid, unsigned char col[4])
Definition: resources.c:1381
char UI_view2d_mouse_in_scrollers(const struct ARegion *region, const struct View2D *v2d, int x, int y)
void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac)
Definition: view2d.c:1964
#define WM_UI_HANDLER_CONTINUE
Definition: WM_types.h:250
@ WM_OP_INVOKE_DEFAULT
Definition: WM_types.h:197
#define KM_PRESS
Definition: WM_types.h:242
#define WM_UI_HANDLER_BREAK
Definition: WM_types.h:251
#define KM_RELEASE
Definition: WM_types.h:243
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:51
GPUBatch * batch
Definition: drawnode.c:3779
uint pos
void ui_block_bounds_calc(uiBlock *block)
Definition: interface.c:441
void ui_window_to_block_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y)
Definition: interface.c:190
void ui_window_to_block(const ARegion *region, uiBlock *block, int *r_x, int *r_y)
Definition: interface.c:227
bool ui_but_supports_cycling(const uiBut *but)
Definition: interface.c:2423
void ui_fontscale(short *points, float aspect)
Definition: interface.c:1943
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
PointerRNA * ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props)
uiBut * ui_region_find_active_but(struct ARegion *region) ATTR_WARN_UNUSED_RESULT
@ UI_BUTTON_GROUP_LOCK
@ UI_BUTTON_GROUP_PANEL_HEADER
void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4])
#define PNL_HEADER
void ui_draw_box_opaque(rcti *rect, int roundboxalign)
@ 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)
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 void ui_handle_panel_header(const bContext *C, const uiBlock *block, const int mx, const int event_type, const short ctrl, const short shift)
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)
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_category_add(ARegion *region, const char *name)
static int find_highest_panel(const void *a, const void *b)
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
#define sqrtf(x)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static ulong state[N]
static unsigned a[3]
Definition: RandGen.cpp:92
static void area(int d1, int d2, int e1, int e2, float weights[2])
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:6272
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:866
bool RNA_pointer_is_null(const PointerRNA *ptr)
Definition: rna_access.c:174
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6261
struct SELECTID_Context context
Definition: select_engine.c:47
ListBase paneltypes
Definition: BKE_screen.h:216
ListBase panels_category_active
ListBase panels_category
ListBase panels
short alignment
short regiontype
struct ARegionType * type
ListBase uiblocks
void * last
Definition: DNA_listBase.h:47
void * first
Definition: DNA_listBase.h:47
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:278
char idname[BKE_ST_MAXNAME]
Definition: BKE_screen.h:241
char context[BKE_ST_MAXNAME]
Definition: BKE_screen.h:245
void(* reorder)(struct bContext *C, struct Panel *pa, int new_index)
Definition: BKE_screen.h:271
char translation_context[BKE_ST_MAXNAME]
Definition: BKE_screen.h:244
char active_property[BKE_ST_MAXNAME]
Definition: BKE_screen.h:250
ListBase children
Definition: BKE_screen.h:289
char category[BKE_ST_MAXNAME]
Definition: BKE_screen.h:246
struct PanelType * parent
Definition: BKE_screen.h:288
char label[BKE_ST_MAXNAME]
Definition: BKE_screen.h:242
struct uiBlock * block
struct PointerRNA * custom_data_ptr
struct PanelType * type
short labelofs
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
uiWidgetColors wcol_tab
uiWidgetColors wcol_box
ThemeUI tui
float xmax
Definition: DNA_vec_types.h:85
float xmin
Definition: DNA_vec_types.h:85
float ymax
Definition: DNA_vec_types.h:86
float ymin
Definition: DNA_vec_types.h:86
int ymin
Definition: DNA_vec_types.h:80
int ymax
Definition: DNA_vec_types.h:80
int xmin
Definition: DNA_vec_types.h:79
int xmax
Definition: DNA_vec_types.h:79
ListBase button_groups
struct Panel * panel
ListBase buttons
uiHandlePanelState state
uiFontStyle paneltitle
short panelspace
uiFontStyle widget
uiFontStyle widgetlabel
unsigned char outline[4]
unsigned char text[4]
int y
Definition: WM_types.h:581
short shift
Definition: WM_types.h:618
short ctrl
Definition: WM_types.h:618
short val
Definition: WM_types.h:579
int mval[2]
Definition: WM_types.h:583
int x
Definition: WM_types.h:581
short type
Definition: WM_types.h:577
void * customdata
Definition: WM_types.h:631
struct wmEvent * eventstate
double PIL_check_seconds_timer(void)
Definition: time.c:80
ccl_device_inline float2 floor(const float2 &a)
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)
@ RIGHTMOUSE
@ TIMER
@ EVT_TABKEY
@ EVT_AKEY
@ WHEELUPMOUSE
@ EVT_PADENTER
@ WHEELDOWNMOUSE
@ MOUSEMOVE
@ LEFTMOUSE
@ INBETWEEN_MOUSEMOVE
@ EVT_RETKEY
#define IS_EVENT_MOD(...)
PointerRNA * ptr
Definition: wm_files.c:3157
wmOperatorType * ot
Definition: wm_files.c:3156
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:1669
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1632