Blender  V3.3
interface_handlers.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. All rights reserved. */
3 
8 #include <ctype.h>
9 #include <float.h>
10 #include <limits.h>
11 #include <math.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include "MEM_guardedalloc.h"
16 
17 #include "DNA_brush_types.h"
18 #include "DNA_curveprofile_types.h"
19 #include "DNA_scene_types.h"
20 #include "DNA_screen_types.h"
21 
22 #include "BLI_array_utils.h"
23 #include "BLI_linklist.h"
24 #include "BLI_listbase.h"
25 #include "BLI_math.h"
26 #include "BLI_rect.h"
27 #include "BLI_sort_utils.h"
28 #include "BLI_string.h"
29 #include "BLI_string_cursor_utf8.h"
30 #include "BLI_string_utf8.h"
31 #include "BLI_utildefines.h"
32 
33 #include "PIL_time.h"
34 
35 #include "BKE_animsys.h"
36 #include "BKE_blender_undo.h"
37 #include "BKE_brush.h"
38 #include "BKE_colorband.h"
39 #include "BKE_colortools.h"
40 #include "BKE_context.h"
41 #include "BKE_curveprofile.h"
42 #include "BKE_movieclip.h"
43 #include "BKE_paint.h"
44 #include "BKE_report.h"
45 #include "BKE_screen.h"
46 #include "BKE_tracking.h"
47 #include "BKE_unit.h"
48 
49 #include "IMB_colormanagement.h"
50 
51 #include "ED_screen.h"
52 #include "ED_undo.h"
53 
54 #include "UI_interface.h"
55 #include "UI_view2d.h"
56 
57 #include "BLF_api.h"
58 
59 #include "interface_intern.h"
60 
61 #include "RNA_access.h"
62 #include "RNA_prototypes.h"
63 
64 #include "WM_api.h"
65 #include "WM_types.h"
66 #include "wm_event_system.h"
67 
68 #ifdef WITH_INPUT_IME
69 # include "BLT_lang.h"
70 # include "BLT_translation.h"
71 # include "wm_window.h"
72 #endif
73 
74 /* -------------------------------------------------------------------- */
84 #define USE_CONT_MOUSE_CORRECT
86 #define USE_DRAG_TOGGLE
87 
89 #define USE_DRAG_MULTINUM
90 
92 #define USE_ALLSELECT
93 
98 #define USE_KEYNAV_LIMIT
99 
101 #define USE_DRAG_POPUP
102 
105 /* -------------------------------------------------------------------- */
113 #define UI_MAX_PASSWORD_STR 128
114 
125 #define UI_PROP_SCALE_LOG_MIN 0.5e-8f
131 #define UI_PROP_SCALE_LOG_SNAP_OFFSET 0.03f
132 
144 #define UI_DRAG_MAP_SOFT_RANGE_PIXEL_MAX 1000
145 
148 /* -------------------------------------------------------------------- */
152 static int ui_do_but_EXIT(bContext *C,
153  uiBut *but,
154  struct uiHandleButtonData *data,
155  const wmEvent *event);
156 static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b);
157 static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str);
158 static void button_tooltip_timer_reset(bContext *C, uiBut *but);
159 
161  uiBlock *block,
162  struct uiHandleButtonData *data,
163  const bool is_click);
165  uiBlock *block,
166  const bool is_click);
167 static void ui_block_interaction_end(struct bContext *C,
169  struct uiBlockInteraction_Handle *interaction);
170 static void ui_block_interaction_update(struct bContext *C,
172  struct uiBlockInteraction_Handle *interaction);
173 
174 #ifdef USE_KEYNAV_LIMIT
175 static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event);
176 static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event);
177 #endif
178 
181 /* -------------------------------------------------------------------- */
185 #define BUTTON_FLASH_DELAY 0.020
186 #define MENU_SCROLL_INTERVAL 0.1
187 #define PIE_MENU_INTERVAL 0.01
188 #define BUTTON_AUTO_OPEN_THRESH 0.2
189 #define BUTTON_MOUSE_TOWARDS_THRESH 1.0
191 #define BUTTON_KEYNAV_PX_LIMIT 8
192 
194 #define MENU_TOWARDS_MARGIN 20
196 #define MENU_TOWARDS_WIGGLE_ROOM 64
198 #define BUTTON_DRAGLOCK_THRESH 3
199 
200 typedef enum uiButtonActivateType {
207 
208 typedef enum uiHandleButtonState {
221 
222 typedef enum uiMenuScrollType {
228 
231  void *user_data;
241 
242 #ifdef USE_ALLSELECT
243 
244 /* Unfortunately there's no good way handle more generally:
245  * (propagate single clicks on layer buttons to other objects) */
246 # define USE_ALLSELECT_LAYER_HACK
247 
248 typedef struct uiSelectContextElem {
250  union {
251  bool val_b;
252  int val_i;
253  float val_f;
254  };
256 
257 typedef struct uiSelectContextStore {
260  bool do_free;
262  /* When set, simply copy values (don't apply difference).
263  * Rules are:
264  * - dragging numbers uses delta.
265  * - typing in values will assign to all. */
266  bool is_copy;
268 
269 static bool ui_selectcontext_begin(bContext *C,
270  uiBut *but,
271  struct uiSelectContextStore *selctx_data);
272 static void ui_selectcontext_end(uiBut *but, uiSelectContextStore *selctx_data);
273 static void ui_selectcontext_apply(bContext *C,
274  uiBut *but,
275  struct uiSelectContextStore *selctx_data,
276  const double value,
277  const double value_orig);
278 
279 # define IS_ALLSELECT_EVENT(event) (((event)->modifier & KM_ALT) != 0)
280 
282 # define UI_BUT_IS_SELECT_CONTEXT UI_BUT_NODE_ACTIVE
283 
284 #endif /* USE_ALLSELECT */
285 
286 #ifdef USE_DRAG_MULTINUM
287 
291 # define DRAG_MULTINUM_THRESHOLD_DRAG_X (UI_UNIT_Y / 4)
292 
298 # define DRAG_MULTINUM_THRESHOLD_DRAG_Y (UI_UNIT_Y / 4)
299 
308 # define DRAG_MULTINUM_THRESHOLD_VERTICAL (0.75f)
309 
310 /* a simple version of uiHandleButtonData when accessing multiple buttons */
311 typedef struct uiButMultiState {
312  double origvalue;
314 
315 # ifdef USE_ALLSELECT
317 # endif
319 
320 typedef struct uiHandleButtonMulti {
321  enum {
330  } init;
331 
332  bool has_mbuts; /* any buttons flagged UI_BUT_DRAG_MULTI */
335 
337 
338  /* In some cases we directly apply the changes to multiple buttons,
339  * so we don't want to do it twice. */
340  bool skip;
341 
342  /* before activating, we need to check gesture direction accumulate signed cursor movement
343  * here so we can tell if this is a vertical motion or not. */
344  float drag_dir[2];
345 
346  /* values copied direct from event->xy
347  * used to detect buttons between the current and initial mouse position */
348  int drag_start[2];
349 
350  /* store x location once BUTTON_MULTI_INIT_SETUP is set,
351  * moving outside this sets BUTTON_MULTI_INIT_ENABLE */
353 
355 
356 #endif /* USE_DRAG_MULTINUM */
357 
358 typedef struct uiHandleButtonData {
363 
365 
366  /* overall state */
368  int retval;
369  /* booleans (could be made into flags) */
372  /* Button is being applied through an extra icon. */
376 
377  /* edited value */
378  /* use 'ui_textedit_string_set' to assign new strings */
379  char *str;
380  char *origstr;
382  float vec[3], origvec[3];
384 
385  /* True when alt is held and the preference for displaying tooltips should be ignored. */
393 
394  /* auto open */
397 
398  /* auto open (hold) */
400 
401  /* text selection/editing */
402  /* size of 'str' (including terminator) */
403  int maxlen;
404  /* Button text selection:
405  * extension direction, selextend, inside ui_do_but_TEX */
407  /* Allow reallocating str/editstr and using 'maxlen' to track alloc size (maxlen + 1) */
409 
410  /* number editing / dragging */
411  /* coords are Window/uiBlock relative (depends on the button) */
417  int dragsel;
420 
424 
425 #ifdef USE_CONT_MOUSE_CORRECT
426  /* when ungrabbing buttons which are #ui_but_is_cursor_warp(),
427  * we may want to position them.
428  * FLT_MAX signifies do-nothing, use #ui_block_to_window_fl()
429  * to get this into a usable space. */
430  float ungrab_mval[2];
431 #endif
432 
433  /* Menu open, see: #UI_screen_free_active_but_highlight. */
436 
437  /* Search box see: #UI_screen_free_active_but_highlight. */
439 #ifdef USE_KEYNAV_LIMIT
441 #endif
442 
443 #ifdef USE_DRAG_MULTINUM
444  /* Multi-buttons will be updated in unison with the active button. */
446 #endif
447 
448 #ifdef USE_ALLSELECT
450 #endif
451 
453 
454  /* Text field undo. */
456 
457  /* post activate */
461 
462 typedef struct uiAfterFunc {
463  struct uiAfterFunc *next, *prev;
464 
466  void *func_arg1;
467  void *func_arg2;
468 
470  void *func_argN;
471 
473  void *rename_arg1;
474  void *rename_orig;
475 
478  int retval;
479 
482  int a2;
483 
488 
491 
492  void *search_arg;
494 
497 
499 
503 
504 static void button_activate_init(bContext *C,
505  ARegion *region,
506  uiBut *but,
509 static void button_activate_exit(
510  bContext *C, uiBut *but, uiHandleButtonData *data, const bool mousemove, const bool onfree);
511 static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *userdata);
513  ARegion *region,
514  uiBut *but,
517  uiBut *but,
519  const wmEvent *event);
522  const wmEvent *event);
524 
525 #ifdef USE_DRAG_MULTINUM
528 #endif
529 
530 /* buttons clipboard */
533 static bool but_copypaste_curve_alive = false;
535 static bool but_copypaste_profile_alive = false;
536 
539 /* -------------------------------------------------------------------- */
543 bool ui_but_is_editing(const uiBut *but)
544 {
545  const uiHandleButtonData *data = but->active;
547 }
548 
549 void ui_pan_to_scroll(const wmEvent *event, int *type, int *val)
550 {
551  static int lastdy = 0;
552  const int dy = WM_event_absolute_delta_y(event);
553 
554  /* This event should be originally from event->type,
555  * converting wrong event into wheel is bad, see T33803. */
556  BLI_assert(*type == MOUSEPAN);
557 
558  /* sign differs, reset */
559  if ((dy > 0 && lastdy < 0) || (dy < 0 && lastdy > 0)) {
560  lastdy = dy;
561  }
562  else {
563  lastdy += dy;
564 
565  if (abs(lastdy) > (int)UI_UNIT_Y) {
566  *val = KM_PRESS;
567 
568  if (dy > 0) {
569  *type = WHEELUPMOUSE;
570  }
571  else {
572  *type = WHEELDOWNMOUSE;
573  }
574 
575  lastdy = 0;
576  }
577  }
578 }
579 
580 static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b)
581 {
582  return ((but_a->type == but_b->type) && (but_a->alignnr == but_b->alignnr) &&
583  (but_a->poin == but_b->poin) && (but_a->rnapoin.type == but_b->rnapoin.type) &&
584  (but_a->rnaprop == but_b->rnaprop));
585 }
586 
588 {
589  uiBut *but_iter = but;
590  uiBut *but_found = NULL;
591  BLI_assert(ELEM(direction, -1, 1));
592 
593  while ((but_iter->prev) && ui_but_find_select_in_enum__cmp(but_iter->prev, but)) {
594  but_iter = but_iter->prev;
595  }
596 
597  while (but_iter && ui_but_find_select_in_enum__cmp(but_iter, but)) {
598  if (but_iter->flag & UI_SELECT) {
599  but_found = but_iter;
600  if (direction == 1) {
601  break;
602  }
603  }
604  but_iter = but_iter->next;
605  }
606 
607  return but_found;
608 }
609 
610 static float ui_mouse_scale_warp_factor(const bool shift)
611 {
612  return shift ? 0.05f : 1.0f;
613 }
614 
616  const float mx,
617  const float my,
618  float *r_mx,
619  float *r_my,
620  const bool shift)
621 {
622  const float fac = ui_mouse_scale_warp_factor(shift);
623 
624  /* slow down the mouse, this is fairly picky */
625  *r_mx = (data->dragstartx * (1.0f - fac) + mx * fac);
626  *r_my = (data->dragstarty * (1.0f - fac) + my * fac);
627 }
628 
631 /* -------------------------------------------------------------------- */
639 {
640  if (mx == data->draglastx) {
641  return false;
642  }
643 
644  if (data->draglock) {
645  if (abs(mx - data->dragstartx) <= BUTTON_DRAGLOCK_THRESH) {
646  return false;
647  }
648 #ifdef USE_DRAG_MULTINUM
649  if (ELEM(data->multi_data.init, BUTTON_MULTI_INIT_UNSET, BUTTON_MULTI_INIT_SETUP)) {
650  return false;
651  }
652 #endif
653  data->draglock = false;
654  data->dragstartx = mx; /* ignore mouse movement within drag-lock */
655  }
656 
657  return true;
658 }
659 
661 {
662  /* Not very elegant, but ensures preference changes force re-save. */
663 
664  if (!prop) {
665  return false;
666  }
668  return false;
669  }
670 
671  StructRNA *base = RNA_struct_base(ptr->type);
672  if (base == NULL) {
673  base = ptr->type;
674  }
675  return ELEM(base,
676  &RNA_AddonPreferences,
677  &RNA_KeyConfigPreferences,
678  &RNA_KeyMapItem,
679  &RNA_UserAssetLibrary);
680 }
681 
682 bool UI_but_is_userdef(const uiBut *but)
683 {
684  /* This is read-only, RNA API isn't using const when it could. */
685  return ui_rna_is_userdef((PointerRNA *)&but->rnapoin, but->rnaprop);
686 }
687 
689 {
690  if (ui_rna_is_userdef(ptr, prop)) {
691  U.runtime.is_dirty = true;
693  }
694 }
695 
697 {
699 }
700 
702 {
704 }
705 
708 /* -------------------------------------------------------------------- */
712 enum eSnapType {
713  SNAP_OFF = 0,
716 };
717 
718 static enum eSnapType ui_event_to_snap(const wmEvent *event)
719 {
720  return (event->modifier & KM_CTRL) ? (event->modifier & KM_SHIFT) ? SNAP_ON_SMALL : SNAP_ON :
721  SNAP_OFF;
722 }
723 
724 static bool ui_event_is_snap(const wmEvent *event)
725 {
726  return (ELEM(event->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) ||
728 }
729 
730 static void ui_color_snap_hue(const enum eSnapType snap, float *r_hue)
731 {
732  const float snap_increment = (snap == SNAP_ON_SMALL) ? 24 : 12;
734  *r_hue = roundf((*r_hue) * snap_increment) / snap_increment;
735 }
736 
739 /* -------------------------------------------------------------------- */
744 
746 {
747  uiAfterFunc *after = MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
748 
749  BLI_addtail(&UIAfterFuncs, after);
750 
751  return after;
752 }
753 
765  PointerRNA **properties,
767  const uiBut *context_but)
768 {
769  uiAfterFunc *after = ui_afterfunc_new();
770 
771  after->optype = ot;
772  after->opcontext = opcontext;
773  if (properties) {
774  after->opptr = *properties;
775  *properties = NULL;
776  }
777 
778  if (context_but && context_but->context) {
779  after->context = CTX_store_copy(context_but->context);
780  }
781 
782  if (context_but) {
783  ui_but_drawstr_without_sep_char(context_but, after->drawstr, sizeof(after->drawstr));
784  }
785 }
786 
788 {
790 }
791 
792 static void popup_check(bContext *C, wmOperator *op)
793 {
794  if (op && op->type->check) {
795  op->type->check(C, op);
796  }
797 }
798 
802 static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but)
803 {
804  return (but->func || but->funcN || but->rename_func || but->optype || but->rnaprop ||
805  block->handle_func || (but->type == UI_BTYPE_BUT_MENU && block->butm_func) ||
806  (block->handle && block->handle->popup_op));
807 }
808 
814 static void ui_apply_but_func(bContext *C, uiBut *but)
815 {
816  uiBlock *block = but->block;
817  if (!ui_afterfunc_check(block, but)) {
818  return;
819  }
820 
821  uiAfterFunc *after = ui_afterfunc_new();
822 
823  if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) {
824  /* exception, this will crash due to removed button otherwise */
825  but->func(C, but->func_arg1, but->func_arg2);
826  }
827  else {
828  after->func = but->func;
829  }
830 
831  after->func_arg1 = but->func_arg1;
832  after->func_arg2 = but->func_arg2;
833 
834  after->funcN = but->funcN;
835  after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : NULL;
836 
837  after->rename_func = but->rename_func;
838  after->rename_arg1 = but->rename_arg1;
839  after->rename_orig = but->rename_orig; /* needs free! */
840 
841  after->handle_func = block->handle_func;
842  after->handle_func_arg = block->handle_func_arg;
843  after->retval = but->retval;
844 
845  if (but->type == UI_BTYPE_BUT_MENU) {
846  after->butm_func = block->butm_func;
847  after->butm_func_arg = block->butm_func_arg;
848  after->a2 = but->a2;
849  }
850 
851  if (block->handle) {
852  after->popup_op = block->handle->popup_op;
853  }
854 
855  after->optype = but->optype;
856  after->opcontext = but->opcontext;
857  after->opptr = but->opptr;
858 
859  after->rnapoin = but->rnapoin;
860  after->rnaprop = but->rnaprop;
861 
862  if (but->type == UI_BTYPE_SEARCH_MENU) {
863  uiButSearch *search_but = (uiButSearch *)but;
864  after->search_arg_free_fn = search_but->arg_free_fn;
865  after->search_arg = search_but->arg;
866  search_but->arg_free_fn = NULL;
867  search_but->arg = NULL;
868  }
869 
870  if (but->active != NULL) {
872  if (data->custom_interaction_handle != NULL) {
874  after->custom_interaction_handle = data->custom_interaction_handle;
875 
876  /* Ensure this callback runs once and last. */
877  uiAfterFunc *after_prev = after->prev;
878  if (after_prev &&
879  (after_prev->custom_interaction_handle == data->custom_interaction_handle)) {
880  after_prev->custom_interaction_handle = NULL;
881  memset(&after_prev->custom_interaction_callbacks,
882  0x0,
883  sizeof(after_prev->custom_interaction_callbacks));
884  }
885  else {
887  }
888  }
889  }
890 
891  if (but->context) {
892  after->context = CTX_store_copy(but->context);
893  }
894 
895  ui_but_drawstr_without_sep_char(but, after->drawstr, sizeof(after->drawstr));
896 
897  but->optype = NULL;
898  but->opcontext = 0;
899  but->opptr = NULL;
900 }
901 
902 /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */
903 static void ui_apply_but_undo(uiBut *but)
904 {
905  if (!(but->flag & UI_BUT_UNDO)) {
906  return;
907  }
908 
909  const char *str = NULL;
910  size_t str_len_clip = SIZE_MAX - 1;
911  bool skip_undo = false;
912 
913  /* define which string to use for undo */
914  if (but->type == UI_BTYPE_MENU) {
915  str = but->drawstr;
916  str_len_clip = ui_but_drawstr_len_without_sep_char(but);
917  }
918  else if (but->drawstr[0]) {
919  str = but->drawstr;
920  str_len_clip = ui_but_drawstr_len_without_sep_char(but);
921  }
922  else {
923  str = but->tip;
924  str_len_clip = ui_but_tip_len_only_first_line(but);
925  }
926 
927  /* fallback, else we don't get an undo! */
928  if (str == NULL || str[0] == '\0' || str_len_clip == 0) {
929  str = "Unknown Action";
930  str_len_clip = strlen(str);
931  }
932 
933  /* Optionally override undo when undo system doesn't support storing properties. */
934  if (but->rnapoin.owner_id) {
935  /* Exception for renaming ID data, we always need undo pushes in this case,
936  * because undo systems track data by their ID, see: T67002. */
937  /* Exception for active shape-key, since changing this in edit-mode updates
938  * the shape key from object mode data. */
939  if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) {
940  /* pass */
941  }
942  else {
943  ID *id = but->rnapoin.owner_id;
945  skip_undo = true;
946  }
947  }
948  }
949 
950  if (skip_undo == false) {
951  /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo
952  * steps to be written which cause lag: T71434. */
954  skip_undo = true;
955  }
956  }
957 
958  if (skip_undo) {
959  str = "";
960  }
961 
962  /* delayed, after all other funcs run, popups are closed, etc */
963  uiAfterFunc *after = ui_afterfunc_new();
964  BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr)));
965 }
966 
968 {
970 
971  /* try autokey */
973 
974  if (!but->rnaprop) {
975  return;
976  }
977 
979  return;
980  }
981 
982  /* make a little report about what we've done! */
983  char *buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex);
984  if (buf) {
986  MEM_freeN(buf);
987 
989  }
990 }
991 
993 {
994  /* copy to avoid recursive calls */
995  ListBase funcs = UIAfterFuncs;
997 
998  LISTBASE_FOREACH_MUTABLE (uiAfterFunc *, afterf, &funcs) {
999  uiAfterFunc after = *afterf; /* copy to avoid memleak on exit() */
1000  BLI_freelinkN(&funcs, afterf);
1001 
1002  if (after.context) {
1003  CTX_store_set(C, after.context);
1004  }
1005 
1006  if (after.popup_op) {
1007  popup_check(C, after.popup_op);
1008  }
1009 
1010  PointerRNA opptr;
1011  if (after.opptr) {
1012  /* free in advance to avoid leak on exit */
1013  opptr = *after.opptr;
1014  MEM_freeN(after.opptr);
1015  }
1016 
1017  if (after.optype) {
1019  C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL, NULL, after.drawstr);
1020  }
1021 
1022  if (after.opptr) {
1024  }
1025 
1026  if (after.rnapoin.data) {
1027  RNA_property_update(C, &after.rnapoin, after.rnaprop);
1028  }
1029 
1030  if (after.context) {
1031  CTX_store_set(C, NULL);
1032  CTX_store_free(after.context);
1033  }
1034 
1035  if (after.func) {
1036  after.func(C, after.func_arg1, after.func_arg2);
1037  }
1038  if (after.funcN) {
1039  after.funcN(C, after.func_argN, after.func_arg2);
1040  }
1041  if (after.func_argN) {
1042  MEM_freeN(after.func_argN);
1043  }
1044 
1045  if (after.handle_func) {
1046  after.handle_func(C, after.handle_func_arg, after.retval);
1047  }
1048  if (after.butm_func) {
1049  after.butm_func(C, after.butm_func_arg, after.a2);
1050  }
1051 
1052  if (after.rename_func) {
1053  after.rename_func(C, after.rename_arg1, after.rename_orig);
1054  }
1055  if (after.rename_orig) {
1056  MEM_freeN(after.rename_orig);
1057  }
1058 
1059  if (after.search_arg_free_fn) {
1060  after.search_arg_free_fn(after.search_arg);
1061  }
1062 
1063  if (after.custom_interaction_handle != NULL) {
1066  if (after.custom_interaction_handle->user_count == 0) {
1071  }
1073  }
1074 
1076 
1077  if (after.undostr[0]) {
1078  ED_undo_push(C, after.undostr);
1079  }
1080  }
1081 }
1082 
1084 {
1085  ui_apply_but_func(C, but);
1086 
1087  data->retval = but->retval;
1088  data->applied = true;
1089 }
1090 
1092 {
1093  ui_but_value_set(but, but->hardmin);
1094  ui_apply_but_func(C, but);
1095 
1096  data->retval = but->retval;
1097  data->applied = true;
1098 }
1099 
1101 {
1102  if (but->type == UI_BTYPE_MENU) {
1103  ui_but_value_set(but, data->value);
1104  }
1105 
1106  ui_but_update_edited(but);
1107  ui_apply_but_func(C, but);
1108  data->retval = but->retval;
1109  data->applied = true;
1110 }
1111 
1113 {
1114  const double value = ui_but_value_get(but);
1115  int value_toggle;
1116  if (but->bit) {
1117  value_toggle = UI_BITBUT_VALUE_TOGGLED((int)value, but->bitnr);
1118  }
1119  else {
1120  value_toggle = (value == 0.0);
1122  value_toggle = !value_toggle;
1123  }
1124  }
1125 
1126  ui_but_value_set(but, (double)value_toggle);
1128  ui_but_update_edited(but);
1129  }
1130 
1131  ui_apply_but_func(C, but);
1132 
1133  data->retval = but->retval;
1134  data->applied = true;
1135 }
1136 
1138 {
1139  ui_but_value_set(but, but->hardmax);
1140 
1141  ui_apply_but_func(C, but);
1142 
1143  /* states of other row buttons */
1144  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
1145  if (bt != but && bt->poin == but->poin && ELEM(bt->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) {
1147  }
1148  }
1149 
1150  data->retval = but->retval;
1151  data->applied = true;
1152 }
1153 
1155  uiBlock *block,
1156  uiBut *but,
1158 {
1159  if (data->apply_through_extra_icon) {
1160  /* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons.
1161  */
1162  return;
1163  }
1164  ui_apply_but_ROW(C, block, but, data);
1165 }
1166 
1175  const uiBut *context_but,
1176  wmOperatorType *ot,
1177  PointerRNA **properties)
1178 {
1179  if (!ui_but_context_poll_operator(C, ot, context_but)) {
1180  return false;
1181  }
1182 
1183  /* Allow the context to be set from the hovered button, so the list item draw callback can set
1184  * context for the operators. */
1185  ui_handle_afterfunc_add_operator_ex(ot, properties, WM_OP_INVOKE_DEFAULT, context_but);
1186  return true;
1187 }
1188 
1190 {
1191  uiBut *listbox = ui_list_find_from_row(data->region, but);
1192  if (listbox) {
1193  uiList *list = listbox->custom_data;
1194  if (list && list->dyn_data->custom_activate_optype) {
1197  }
1198  }
1199 
1200  ui_apply_but_ROW(C, block, but, data);
1201 }
1202 
1204 {
1205  if (!data->str) {
1206  return;
1207  }
1208 
1209  ui_but_string_set(C, but, data->str);
1210  ui_but_update_edited(but);
1211 
1212  /* give butfunc a copy of the original text too.
1213  * feature used for bone renaming, channels, etc.
1214  * afterfunc frees rename_orig */
1215  if (data->origstr && (but->flag & UI_BUT_TEXTEDIT_UPDATE)) {
1216  /* In this case, we need to keep origstr available,
1217  * to restore real org string in case we cancel after having typed something already. */
1218  but->rename_orig = BLI_strdup(data->origstr);
1219  }
1220  /* only if there are afterfuncs, otherwise 'renam_orig' isn't freed */
1221  else if (ui_afterfunc_check(but->block, but)) {
1222  but->rename_orig = data->origstr;
1223  data->origstr = NULL;
1224  }
1225 
1226  void *orig_arg2 = but->func_arg2;
1227 
1228  /* If arg2 isn't in use already, pass the active search item through it. */
1229  if ((but->func_arg2 == NULL) && (but->type == UI_BTYPE_SEARCH_MENU)) {
1230  uiButSearch *search_but = (uiButSearch *)but;
1231  but->func_arg2 = search_but->item_active;
1232  }
1233 
1234  ui_apply_but_func(C, but);
1235 
1236  but->func_arg2 = orig_arg2;
1237 
1238  data->retval = but->retval;
1239  data->applied = true;
1240 }
1241 
1243 {
1244  if (data->str) {
1245  ui_but_string_set(C, but, data->str);
1246  ui_but_update_edited(but);
1247  }
1248  else {
1249  ui_but_value_set(but, but->hardmax);
1250  ui_apply_but_func(C, but);
1251  }
1252 
1253  data->retval = but->retval;
1254  data->applied = true;
1255 }
1256 
1258 {
1259  if (data->str) {
1260  /* This is intended to avoid unnecessary updates when the value stays the same, however there
1261  * are issues with the current implementation. It does not work with multi-button editing
1262  * (T89996) or operator popups where a number button requires an update even if the value is
1263  * unchanged (T89996).
1264  *
1265  * Trying to detect changes at this level is not reliable. Instead it could be done at the
1266  * level of RNA update/set, skipping RNA update if RNA set did not change anything, instead
1267  * of skipping all button updates. */
1268 #if 0
1269  double value;
1270  /* Check if the string value is a number and cancel if it's equal to the startvalue. */
1271  if (ui_but_string_eval_number(C, but, data->str, &value) && (value == data->startvalue)) {
1272  data->cancel = true;
1273  return;
1274  }
1275 #endif
1276 
1277  if (ui_but_string_set(C, but, data->str)) {
1278  data->value = ui_but_value_get(but);
1279  }
1280  else {
1281  data->cancel = true;
1282  return;
1283  }
1284  }
1285  else {
1286  ui_but_value_set(but, data->value);
1287  }
1288 
1289  ui_but_update_edited(but);
1290  ui_apply_but_func(C, but);
1291 
1292  data->retval = but->retval;
1293  data->applied = true;
1294 }
1295 
1297 {
1298  ui_but_v3_set(but, data->vec);
1299  ui_but_update_edited(but);
1300  ui_apply_but_func(C, but);
1301 
1302  data->retval = but->retval;
1303  data->applied = true;
1304 }
1305 
1307 {
1308  ui_apply_but_func(C, but);
1309  data->retval = but->retval;
1310  data->applied = true;
1311 }
1312 
1314 {
1315  ui_apply_but_func(C, but);
1316  data->retval = but->retval;
1317  data->applied = true;
1318 }
1319 
1321 {
1322  ui_apply_but_func(C, but);
1323  data->retval = but->retval;
1324  data->applied = true;
1325 }
1326 
1329 /* -------------------------------------------------------------------- */
1333 #ifdef USE_DRAG_MULTINUM
1334 
1335 /* small multi-but api */
1337 {
1339  BLI_assert(data->multi_data.has_mbuts);
1340 
1341  uiButMultiState *mbut_state = MEM_callocN(sizeof(*mbut_state), __func__);
1342  mbut_state->but = but;
1343  mbut_state->origvalue = ui_but_value_get(but);
1344 # ifdef USE_ALLSELECT
1345  mbut_state->select_others.is_copy = data->select_others.is_copy;
1346 # endif
1347 
1348  BLI_linklist_prepend(&data->multi_data.mbuts, mbut_state);
1349 
1350  UI_butstore_register(data->multi_data.bs_mbuts, &mbut_state->but);
1351 }
1352 
1354 {
1355  for (LinkNode *l = data->multi_data.mbuts; l; l = l->next) {
1356  uiButMultiState *mbut_state = l->link;
1357 
1358  if (mbut_state->but == but) {
1359  return mbut_state;
1360  }
1361  }
1362 
1363  return NULL;
1364 }
1365 
1367 {
1368  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
1369  if (but->flag & UI_BUT_DRAG_MULTI) {
1370  uiButMultiState *mbut_state = ui_multibut_lookup(data, but);
1371  if (mbut_state) {
1372  ui_but_value_set(but, mbut_state->origvalue);
1373 
1374 # ifdef USE_ALLSELECT
1375  if (mbut_state->select_others.elems_len > 0) {
1377  C, but, &mbut_state->select_others, mbut_state->origvalue, mbut_state->origvalue);
1378  }
1379 # else
1380  UNUSED_VARS(C);
1381 # endif
1382  }
1383  }
1384  }
1385 }
1386 
1388 {
1389 # ifdef USE_ALLSELECT
1390  if (data->multi_data.mbuts) {
1391  LinkNode *list = data->multi_data.mbuts;
1392  while (list) {
1393  LinkNode *next = list->next;
1394  uiButMultiState *mbut_state = list->link;
1395 
1396  if (mbut_state->select_others.elems) {
1397  MEM_freeN(mbut_state->select_others.elems);
1398  }
1399 
1400  MEM_freeN(list->link);
1401  MEM_freeN(list);
1402  list = next;
1403  }
1404  }
1405 # else
1406  BLI_linklist_freeN(data->multi_data.mbuts);
1407 # endif
1408 
1409  data->multi_data.mbuts = NULL;
1410 
1411  if (data->multi_data.bs_mbuts) {
1412  UI_butstore_free(block, data->multi_data.bs_mbuts);
1413  data->multi_data.bs_mbuts = NULL;
1414  }
1415 }
1416 
1417 static bool ui_multibut_states_tag(uiBut *but_active,
1419  const wmEvent *event)
1420 {
1421  float seg[2][2];
1422  bool changed = false;
1423 
1424  seg[0][0] = data->multi_data.drag_start[0];
1425  seg[0][1] = data->multi_data.drag_start[1];
1426 
1427  seg[1][0] = event->xy[0];
1428  seg[1][1] = event->xy[1];
1429 
1430  BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_SETUP);
1431 
1432  ui_window_to_block_fl(data->region, but_active->block, &seg[0][0], &seg[0][1]);
1433  ui_window_to_block_fl(data->region, but_active->block, &seg[1][0], &seg[1][1]);
1434 
1435  data->multi_data.has_mbuts = false;
1436 
1437  /* follow ui_but_find_mouse_over_ex logic */
1438  LISTBASE_FOREACH (uiBut *, but, &but_active->block->buttons) {
1439  bool drag_prev = false;
1440  bool drag_curr = false;
1441 
1442  /* re-set each time */
1443  if (but->flag & UI_BUT_DRAG_MULTI) {
1444  but->flag &= ~UI_BUT_DRAG_MULTI;
1445  drag_prev = true;
1446  }
1447 
1448  if (ui_but_is_interactive(but, false)) {
1449 
1450  /* drag checks */
1451  if (but_active != but) {
1452  if (ui_but_is_compatible(but_active, but)) {
1453 
1454  BLI_assert(but->active == NULL);
1455 
1456  /* finally check for overlap */
1457  if (BLI_rctf_isect_segment(&but->rect, seg[0], seg[1])) {
1458 
1459  but->flag |= UI_BUT_DRAG_MULTI;
1460  data->multi_data.has_mbuts = true;
1461  drag_curr = true;
1462  }
1463  }
1464  }
1465  }
1466 
1467  changed |= (drag_prev != drag_curr);
1468  }
1469 
1470  return changed;
1471 }
1472 
1474 {
1475  BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_SETUP);
1476  BLI_assert(data->multi_data.has_mbuts);
1477 
1478  data->multi_data.bs_mbuts = UI_butstore_create(but_active->block);
1479 
1480  LISTBASE_FOREACH (uiBut *, but, &but_active->block->buttons) {
1481  if (but->flag & UI_BUT_DRAG_MULTI) {
1482  ui_multibut_add(data, but);
1483  }
1484  }
1485 
1486  /* Edit buttons proportionally to each other.
1487  * NOTE: if we mix buttons which are proportional and others which are not,
1488  * this may work a bit strangely. */
1489  if ((but_active->rnaprop && (RNA_property_flag(but_active->rnaprop) & PROP_PROPORTIONAL)) ||
1491  if (data->origvalue != 0.0) {
1492  data->multi_data.is_proportional = true;
1493  }
1494  }
1495 }
1496 
1498 {
1499  ARegion *region = data->region;
1500  const double value_delta = data->value - data->origvalue;
1501  const double value_scale = data->multi_data.is_proportional ? (data->value / data->origvalue) :
1502  0.0;
1503 
1504  BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_ENABLE);
1505  BLI_assert(data->multi_data.skip == false);
1506 
1507  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
1508  if (!(but->flag & UI_BUT_DRAG_MULTI)) {
1509  continue;
1510  }
1511 
1512  uiButMultiState *mbut_state = ui_multibut_lookup(data, but);
1513 
1514  if (mbut_state == NULL) {
1515  /* Highly unlikely. */
1516  printf("%s: Can't find button\n", __func__);
1517  /* While this avoids crashing, multi-button dragging will fail,
1518  * which is still a bug from the user perspective. See T83651. */
1519  continue;
1520  }
1521 
1522  void *active_back;
1523  ui_but_execute_begin(C, region, but, &active_back);
1524 
1525 # ifdef USE_ALLSELECT
1526  if (data->select_others.is_enabled) {
1527  /* init once! */
1528  if (mbut_state->select_others.elems_len == 0) {
1529  ui_selectcontext_begin(C, but, &mbut_state->select_others);
1530  }
1531  if (mbut_state->select_others.elems_len == 0) {
1532  mbut_state->select_others.elems_len = -1;
1533  }
1534  }
1535 
1536  /* Needed so we apply the right deltas. */
1537  but->active->origvalue = mbut_state->origvalue;
1538  but->active->select_others = mbut_state->select_others;
1539  but->active->select_others.do_free = false;
1540 # endif
1541 
1542  BLI_assert(active_back == NULL);
1543  /* No need to check 'data->state' here. */
1544  if (data->str) {
1545  /* Entering text (set all). */
1546  but->active->value = data->value;
1547  ui_but_string_set(C, but, data->str);
1548  }
1549  else {
1550  /* Dragging (use delta). */
1551  if (data->multi_data.is_proportional) {
1552  but->active->value = mbut_state->origvalue * value_scale;
1553  }
1554  else {
1555  but->active->value = mbut_state->origvalue + value_delta;
1556  }
1557 
1558  /* Clamp based on soft limits, see T40154. */
1559  CLAMP(but->active->value, (double)but->softmin, (double)but->softmax);
1560  }
1561 
1562  ui_but_execute_end(C, region, but, active_back);
1563  }
1564 }
1565 
1566 #endif /* USE_DRAG_MULTINUM */
1567 
1570 /* -------------------------------------------------------------------- */
1574 #ifdef USE_DRAG_TOGGLE
1575 
1576 /* Helpers that wrap boolean functions, to support different kinds of buttons. */
1577 
1579 {
1580  if (but->flag & UI_BUT_DISABLED) {
1581  return false;
1582  }
1583  if (ui_but_is_bool(but)) {
1584  return true;
1585  }
1586  if (UI_but_is_decorator(but)) {
1587  return ELEM(but->icon,
1588  ICON_DECORATE,
1589  ICON_DECORATE_KEYFRAME,
1590  ICON_DECORATE_ANIMATE,
1591  ICON_DECORATE_OVERRIDE);
1592  }
1593  return false;
1594 }
1595 
1596 /* Button pushed state to compare if other buttons match. Can be more
1597  * then just true or false for toggle buttons with more than 2 states. */
1599 {
1600  if (but->rnapoin.data == NULL && but->poin == NULL && but->icon) {
1601  /* Assume icon identifies a unique state, for buttons that
1602  * work through functions callbacks and don't have an boolean
1603  * value that indicates the state. */
1604  return but->icon + but->iconadd;
1605  }
1606  if (ui_but_is_bool(but)) {
1607  return ui_but_is_pushed(but);
1608  }
1609  return 0;
1610 }
1611 
1612 typedef struct uiDragToggleHandle {
1613  /* init */
1615  float but_cent_start[2];
1616 
1618  bool xy_lock[2];
1619 
1620  int xy_init[2];
1621  int xy_last[2];
1623 
1625  bContext *C, ARegion *region, const int pushed_state, const int xy_src[2], const int xy_dst[2])
1626 {
1627  /* popups such as layers won't re-evaluate on redraw */
1628  const bool do_check = (region->regiontype == RGN_TYPE_TEMPORARY);
1629  bool changed = false;
1630 
1631  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
1632  float xy_a_block[2] = {UNPACK2(xy_src)};
1633  float xy_b_block[2] = {UNPACK2(xy_dst)};
1634 
1635  ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]);
1636  ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]);
1637 
1638  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
1639  /* NOTE: ctrl is always true here because (at least for now)
1640  * we always want to consider text control in this case, even when not embossed. */
1641 
1642  if (!ui_but_is_interactive(but, true)) {
1643  continue;
1644  }
1645  if (!BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) {
1646  continue;
1647  }
1648  if (!ui_drag_toggle_but_is_supported(but)) {
1649  continue;
1650  }
1651  /* is it pressed? */
1652  const int pushed_state_but = ui_drag_toggle_but_pushed_state(but);
1653  if (pushed_state_but == pushed_state) {
1654  continue;
1655  }
1656 
1657  /* execute the button */
1658  UI_but_execute(C, region, but);
1659  if (do_check) {
1660  ui_but_update_edited(but);
1661  }
1662  if (U.runtime.is_dirty == false) {
1664  }
1665  changed = true;
1666  }
1667  }
1668 
1669  if (changed) {
1670  /* apply now, not on release (or if handlers are canceled for whatever reason) */
1672  }
1673 
1674  return changed;
1675 }
1676 
1677 static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const int xy_input[2])
1678 {
1679  ARegion *region = CTX_wm_region(C);
1680  bool do_draw = false;
1681 
1688  if (drag_info->is_xy_lock_init == false) {
1689  /* first store the buttons original coords */
1690  uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, false, NULL, NULL);
1691 
1692  if (but) {
1693  if (but->flag & UI_BUT_DRAG_LOCK) {
1694  const float but_cent_new[2] = {
1695  BLI_rctf_cent_x(&but->rect),
1696  BLI_rctf_cent_y(&but->rect),
1697  };
1698 
1699  /* check if this is a different button,
1700  * chances are high the button won't move about :) */
1701  if (len_manhattan_v2v2(drag_info->but_cent_start, but_cent_new) > 1.0f) {
1702  if (fabsf(drag_info->but_cent_start[0] - but_cent_new[0]) <
1703  fabsf(drag_info->but_cent_start[1] - but_cent_new[1])) {
1704  drag_info->xy_lock[0] = true;
1705  }
1706  else {
1707  drag_info->xy_lock[1] = true;
1708  }
1709  drag_info->is_xy_lock_init = true;
1710  }
1711  }
1712  else {
1713  drag_info->is_xy_lock_init = true;
1714  }
1715  }
1716  }
1717  /* done with axis locking */
1718 
1719  int xy[2];
1720  xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : drag_info->xy_last[0];
1721  xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : drag_info->xy_last[1];
1722 
1723  /* touch all buttons between last mouse coord and this one */
1724  do_draw = ui_drag_toggle_set_xy_xy(C, region, drag_info->pushed_state, drag_info->xy_last, xy);
1725 
1726  if (do_draw) {
1727  ED_region_tag_redraw(region);
1728  }
1729 
1730  copy_v2_v2_int(drag_info->xy_last, xy);
1731 }
1732 
1734 {
1735  uiDragToggleHandle *drag_info = userdata;
1736  MEM_freeN(drag_info);
1737 }
1738 
1739 static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void *userdata)
1740 {
1741  uiDragToggleHandle *drag_info = userdata;
1742  bool done = false;
1743 
1744  switch (event->type) {
1745  case LEFTMOUSE: {
1746  if (event->val == KM_RELEASE) {
1747  done = true;
1748  }
1749  break;
1750  }
1751  case MOUSEMOVE: {
1752  ui_drag_toggle_set(C, drag_info, event->xy);
1753  break;
1754  }
1755  }
1756 
1757  if (done) {
1758  wmWindow *win = CTX_wm_window(C);
1759  const ARegion *region = CTX_wm_region(C);
1760  uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, false, NULL, NULL);
1761 
1762  if (but) {
1763  ui_apply_but_undo(but);
1764  }
1765 
1769  drag_info,
1770  false);
1772 
1774  return WM_UI_HANDLER_BREAK;
1775  }
1776  return WM_UI_HANDLER_CONTINUE;
1777 }
1778 
1779 static bool ui_but_is_drag_toggle(const uiBut *but)
1780 {
1781  return ((ui_drag_toggle_but_is_supported(but) == true) &&
1782  /* Menu check is important so the button dragged over isn't removed instantly. */
1783  (ui_block_is_menu(but->block) == false));
1784 }
1785 
1786 #endif /* USE_DRAG_TOGGLE */
1787 
1788 #ifdef USE_ALLSELECT
1789 
1791 {
1792  PointerRNA lptr;
1793  PropertyRNA *lprop;
1794  bool success = false;
1795 
1796  char *path = NULL;
1797  ListBase lb = {NULL};
1798 
1799  PointerRNA ptr = but->rnapoin;
1800  PropertyRNA *prop = but->rnaprop;
1801  const int index = but->rnaindex;
1802 
1803  /* for now don't support whole colors */
1804  if (index == -1) {
1805  return false;
1806  }
1807 
1808  /* if there is a valid property that is editable... */
1809  if (ptr.data && prop) {
1810  bool use_path_from_id;
1811 
1812  /* some facts we want to know */
1813  const bool is_array = RNA_property_array_check(prop);
1814  const int rna_type = RNA_property_type(prop);
1815 
1816  if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) &&
1817  !BLI_listbase_is_empty(&lb)) {
1818  selctx_data->elems_len = BLI_listbase_count(&lb);
1819  selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len,
1820  __func__);
1821  int i;
1822  LISTBASE_FOREACH_INDEX (CollectionPointerLink *, link, &lb, i) {
1823  if (i >= selctx_data->elems_len) {
1824  break;
1825  }
1826 
1828  &ptr, &link->ptr, prop, path, use_path_from_id, &lptr, &lprop)) {
1829  selctx_data->elems_len -= 1;
1830  i -= 1;
1831  continue;
1832  }
1833 
1834  uiSelectContextElem *other = &selctx_data->elems[i];
1835  other->ptr = lptr;
1836  if (is_array) {
1837  if (rna_type == PROP_FLOAT) {
1838  other->val_f = RNA_property_float_get_index(&lptr, lprop, index);
1839  }
1840  else if (rna_type == PROP_INT) {
1841  other->val_i = RNA_property_int_get_index(&lptr, lprop, index);
1842  }
1843  /* ignored for now */
1844 # if 0
1845  else if (rna_type == PROP_BOOLEAN) {
1846  other->val_b = RNA_property_boolean_get_index(&lptr, lprop, index);
1847  }
1848 # endif
1849  }
1850  else {
1851  if (rna_type == PROP_FLOAT) {
1852  other->val_f = RNA_property_float_get(&lptr, lprop);
1853  }
1854  else if (rna_type == PROP_INT) {
1855  other->val_i = RNA_property_int_get(&lptr, lprop);
1856  }
1857  /* ignored for now */
1858 # if 0
1859  else if (rna_type == PROP_BOOLEAN) {
1860  other->val_b = RNA_property_boolean_get(&lptr, lprop);
1861  }
1862  else if (rna_type == PROP_ENUM) {
1863  other->val_i = RNA_property_enum_get(&lptr, lprop);
1864  }
1865 # endif
1866  }
1867  }
1868  success = (selctx_data->elems_len != 0);
1869  }
1870  }
1871 
1872  if (selctx_data->elems_len == 0) {
1873  MEM_SAFE_FREE(selctx_data->elems);
1874  }
1875 
1876  MEM_SAFE_FREE(path);
1877  BLI_freelistN(&lb);
1878 
1879  /* caller can clear */
1880  selctx_data->do_free = true;
1881 
1882  if (success) {
1884  }
1885 
1886  return success;
1887 }
1888 
1889 static void ui_selectcontext_end(uiBut *but, uiSelectContextStore *selctx_data)
1890 {
1891  if (selctx_data->do_free) {
1892  if (selctx_data->elems) {
1893  MEM_freeN(selctx_data->elems);
1894  }
1895  }
1896 
1897  but->flag &= ~UI_BUT_IS_SELECT_CONTEXT;
1898 }
1899 
1901  uiBut *but,
1902  uiSelectContextStore *selctx_data,
1903  const double value,
1904  const double value_orig)
1905 {
1906  if (selctx_data->elems) {
1907  PropertyRNA *prop = but->rnaprop;
1908  PropertyRNA *lprop = but->rnaprop;
1909  const int index = but->rnaindex;
1910  const bool use_delta = (selctx_data->is_copy == false);
1911 
1912  union {
1913  bool b;
1914  int i;
1915  float f;
1916  PointerRNA p;
1917  } delta, min, max;
1918 
1919  const bool is_array = RNA_property_array_check(prop);
1920  const int rna_type = RNA_property_type(prop);
1921 
1922  if (rna_type == PROP_FLOAT) {
1923  delta.f = use_delta ? (value - value_orig) : value;
1924  RNA_property_float_range(&but->rnapoin, prop, &min.f, &max.f);
1925  }
1926  else if (rna_type == PROP_INT) {
1927  delta.i = use_delta ? ((int)value - (int)value_orig) : (int)value;
1928  RNA_property_int_range(&but->rnapoin, prop, &min.i, &max.i);
1929  }
1930  else if (rna_type == PROP_ENUM) {
1931  /* Not a delta in fact. */
1932  delta.i = RNA_property_enum_get(&but->rnapoin, prop);
1933  }
1934  else if (rna_type == PROP_BOOLEAN) {
1935  if (is_array) {
1936  /* Not a delta in fact. */
1937  delta.b = RNA_property_boolean_get_index(&but->rnapoin, prop, index);
1938  }
1939  else {
1940  /* Not a delta in fact. */
1941  delta.b = RNA_property_boolean_get(&but->rnapoin, prop);
1942  }
1943  }
1944  else if (rna_type == PROP_POINTER) {
1945  /* Not a delta in fact. */
1946  delta.p = RNA_property_pointer_get(&but->rnapoin, prop);
1947  }
1948 
1949 # ifdef USE_ALLSELECT_LAYER_HACK
1950  /* make up for not having 'handle_layer_buttons' */
1951  {
1952  const PropertySubType subtype = RNA_property_subtype(prop);
1953 
1954  if ((rna_type == PROP_BOOLEAN) && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER) && is_array &&
1955  /* could check for 'handle_layer_buttons' */
1956  but->func) {
1957  wmWindow *win = CTX_wm_window(C);
1958  if ((win->eventstate->modifier & KM_SHIFT) == 0) {
1959  const int len = RNA_property_array_length(&but->rnapoin, prop);
1960  bool *tmparray = MEM_callocN(sizeof(bool) * len, __func__);
1961 
1962  tmparray[index] = true;
1963 
1964  for (int i = 0; i < selctx_data->elems_len; i++) {
1965  uiSelectContextElem *other = &selctx_data->elems[i];
1966  PointerRNA lptr = other->ptr;
1967  RNA_property_boolean_set_array(&lptr, lprop, tmparray);
1968  RNA_property_update(C, &lptr, lprop);
1969  }
1970 
1971  MEM_freeN(tmparray);
1972 
1973  return;
1974  }
1975  }
1976  }
1977 # endif
1978 
1979  for (int i = 0; i < selctx_data->elems_len; i++) {
1980  uiSelectContextElem *other = &selctx_data->elems[i];
1981  PointerRNA lptr = other->ptr;
1982 
1983  if (rna_type == PROP_FLOAT) {
1984  float other_value = use_delta ? (other->val_f + delta.f) : delta.f;
1985  CLAMP(other_value, min.f, max.f);
1986  if (is_array) {
1987  RNA_property_float_set_index(&lptr, lprop, index, other_value);
1988  }
1989  else {
1990  RNA_property_float_set(&lptr, lprop, other_value);
1991  }
1992  }
1993  else if (rna_type == PROP_INT) {
1994  int other_value = use_delta ? (other->val_i + delta.i) : delta.i;
1995  CLAMP(other_value, min.i, max.i);
1996  if (is_array) {
1997  RNA_property_int_set_index(&lptr, lprop, index, other_value);
1998  }
1999  else {
2000  RNA_property_int_set(&lptr, lprop, other_value);
2001  }
2002  }
2003  else if (rna_type == PROP_BOOLEAN) {
2004  const bool other_value = delta.b;
2005  if (is_array) {
2006  RNA_property_boolean_set_index(&lptr, lprop, index, other_value);
2007  }
2008  else {
2009  RNA_property_boolean_set(&lptr, lprop, delta.b);
2010  }
2011  }
2012  else if (rna_type == PROP_ENUM) {
2013  const int other_value = delta.i;
2014  BLI_assert(!is_array);
2015  RNA_property_enum_set(&lptr, lprop, other_value);
2016  }
2017  else if (rna_type == PROP_POINTER) {
2018  const PointerRNA other_value = delta.p;
2019  RNA_property_pointer_set(&lptr, lprop, other_value, NULL);
2020  }
2021 
2022  RNA_property_update(C, &lptr, prop);
2023  }
2024  }
2025 }
2026 
2027 #endif /* USE_ALLSELECT */
2028 
2031 /* -------------------------------------------------------------------- */
2036  uiBut *but,
2038  const wmEvent *event)
2039 {
2040  /* prevent other WM gestures to start while we try to drag */
2042 
2043  /* Clamp the maximum to half the UI unit size so a high user preference
2044  * doesn't require the user to drag more than half the default button height. */
2045  const int drag_threshold = min_ii(
2046  WM_event_drag_threshold(event),
2047  (int)((UI_UNIT_Y / 2) * ui_block_to_window_scale(data->region, but->block)));
2048 
2049  if (abs(data->dragstartx - event->xy[0]) + abs(data->dragstarty - event->xy[1]) >
2050  drag_threshold) {
2052  data->cancel = true;
2053 #ifdef USE_DRAG_TOGGLE
2055  uiDragToggleHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__);
2056  ARegion *region_prev;
2057 
2058  /* call here because regular mouse-up event won't run,
2059  * typically 'button_activate_exit()' handles this */
2060  ui_apply_but_autokey(C, but);
2061 
2063  drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect);
2064  drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect);
2065  copy_v2_v2_int(drag_info->xy_init, event->xy);
2066  copy_v2_v2_int(drag_info->xy_last, event->xy);
2067 
2068  /* needed for toggle drag on popups */
2069  region_prev = CTX_wm_region(C);
2070  CTX_wm_region_set(C, data->region);
2071 
2073  &data->window->modalhandlers,
2076  drag_info,
2078 
2079  CTX_wm_region_set(C, region_prev);
2080 
2081  /* Initialize alignment for single row/column regions,
2082  * otherwise we use the relative position of the first other button dragged over. */
2083  if (ELEM(data->region->regiontype,
2087  RGN_TYPE_FOOTER)) {
2088  const int region_alignment = RGN_ALIGN_ENUM_FROM_MASK(data->region->alignment);
2089  int lock_axis = -1;
2090 
2091  if (ELEM(region_alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
2092  lock_axis = 0;
2093  }
2094  else if (ELEM(region_alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
2095  lock_axis = 1;
2096  }
2097  if (lock_axis != -1) {
2098  drag_info->xy_lock[lock_axis] = true;
2099  drag_info->is_xy_lock_init = true;
2100  }
2101  }
2102  }
2103  else
2104 #endif
2105  if (but->type == UI_BTYPE_COLOR) {
2106  bool valid = false;
2107  uiDragColorHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__);
2108 
2109  /* TODO: support more button pointer types. */
2110  if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2111  ui_but_v3_get(but, drag_info->color);
2112  drag_info->gamma_corrected = true;
2113  valid = true;
2114  }
2115  else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
2116  ui_but_v3_get(but, drag_info->color);
2117  drag_info->gamma_corrected = false;
2118  valid = true;
2119  }
2120  else if (ELEM(but->pointype, UI_BUT_POIN_FLOAT, UI_BUT_POIN_CHAR)) {
2121  ui_but_v3_get(but, drag_info->color);
2122  copy_v3_v3(drag_info->color, (float *)but->poin);
2123  valid = true;
2124  }
2125 
2126  if (valid) {
2127  WM_event_start_drag(C, ICON_COLOR, WM_DRAG_COLOR, drag_info, 0.0, WM_DRAG_FREE_DATA);
2128  }
2129  else {
2130  MEM_freeN(drag_info);
2131  return false;
2132  }
2133  }
2134  else if (but->type == UI_BTYPE_VIEW_ITEM) {
2135  const uiButViewItem *view_item_but = (uiButViewItem *)but;
2136  if (view_item_but->view_item) {
2137  UI_view_item_drag_start(C, view_item_but->view_item);
2138  }
2139  }
2140  else {
2141  ui_but_drag_start(C, but);
2142  }
2143  return true;
2144  }
2145 
2146  return false;
2147 }
2148 
2151 /* -------------------------------------------------------------------- */
2156 {
2157  ui_apply_but_func(C, but);
2158  data->retval = but->retval;
2159  data->applied = true;
2160 }
2161 
2163 {
2164  ui_apply_but_func(C, but);
2165  data->retval = but->retval;
2166  data->applied = true;
2167 }
2168 
2170 {
2171  ui_apply_but_func(C, but);
2172  data->retval = but->retval;
2173  data->applied = true;
2174 }
2175 
2177 {
2178  ui_apply_but_func(C, but);
2179  data->retval = but->retval;
2180  data->applied = true;
2181 }
2182 
2183 static void ui_apply_but(
2184  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const bool interactive)
2185 {
2186  const eButType but_type = but->type; /* Store as const to quiet maybe uninitialized warning. */
2187 
2188  data->retval = 0;
2189 
2190  /* if we cancel and have not applied yet, there is nothing to do,
2191  * otherwise we have to restore the original value again */
2192  if (data->cancel) {
2193  if (!data->applied) {
2194  return;
2195  }
2196 
2197  if (data->str) {
2198  MEM_freeN(data->str);
2199  }
2200  data->str = data->origstr;
2201  data->origstr = NULL;
2202  data->value = data->origvalue;
2203  copy_v3_v3(data->vec, data->origvec);
2204  /* postpone clearing origdata */
2205  }
2206  else {
2207  /* We avoid applying interactive edits a second time
2208  * at the end with the #uiHandleButtonData.applied_interactive flag. */
2209  if (interactive) {
2210  data->applied_interactive = true;
2211  }
2212  else if (data->applied_interactive) {
2213  return;
2214  }
2215 
2216 #ifdef USE_ALLSELECT
2217 # ifdef USE_DRAG_MULTINUM
2218  if (but->flag & UI_BUT_DRAG_MULTI) {
2219  /* pass */
2220  }
2221  else
2222 # endif
2223  if (data->select_others.elems_len == 0) {
2224  wmWindow *win = CTX_wm_window(C);
2225  /* may have been enabled before activating */
2226  if (data->select_others.is_enabled || IS_ALLSELECT_EVENT(win->eventstate)) {
2227  ui_selectcontext_begin(C, but, &data->select_others);
2228  data->select_others.is_enabled = true;
2229  }
2230  }
2231  if (data->select_others.elems_len == 0) {
2232  /* Don't check again. */
2233  data->select_others.elems_len = -1;
2234  }
2235 #endif
2236  }
2237 
2238  /* ensures we are writing actual values */
2239  char *editstr = but->editstr;
2240  double *editval = but->editval;
2241  float *editvec = but->editvec;
2242  ColorBand *editcoba;
2243  CurveMapping *editcumap;
2244  CurveProfile *editprofile;
2245  if (but_type == UI_BTYPE_COLORBAND) {
2246  uiButColorBand *but_coba = (uiButColorBand *)but;
2247  editcoba = but_coba->edit_coba;
2248  }
2249  else if (but_type == UI_BTYPE_CURVE) {
2250  uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
2251  editcumap = but_cumap->edit_cumap;
2252  }
2253  else if (but_type == UI_BTYPE_CURVEPROFILE) {
2254  uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
2255  editprofile = but_profile->edit_profile;
2256  }
2257  but->editstr = NULL;
2258  but->editval = NULL;
2259  but->editvec = NULL;
2260  if (but_type == UI_BTYPE_COLORBAND) {
2261  uiButColorBand *but_coba = (uiButColorBand *)but;
2262  but_coba->edit_coba = NULL;
2263  }
2264  else if (but_type == UI_BTYPE_CURVE) {
2265  uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
2266  but_cumap->edit_cumap = NULL;
2267  }
2268  else if (but_type == UI_BTYPE_CURVEPROFILE) {
2269  uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
2270  but_profile->edit_profile = NULL;
2271  }
2272 
2273  /* handle different types */
2274  switch (but_type) {
2275  case UI_BTYPE_BUT:
2276  case UI_BTYPE_DECORATOR:
2277  ui_apply_but_BUT(C, but, data);
2278  break;
2279  case UI_BTYPE_TEXT:
2280  case UI_BTYPE_SEARCH_MENU:
2281  ui_apply_but_TEX(C, but, data);
2282  break;
2283  case UI_BTYPE_BUT_TOGGLE:
2284  case UI_BTYPE_TOGGLE:
2285  case UI_BTYPE_TOGGLE_N:
2286  case UI_BTYPE_ICON_TOGGLE:
2288  case UI_BTYPE_CHECKBOX:
2289  case UI_BTYPE_CHECKBOX_N:
2290  ui_apply_but_TOG(C, but, data);
2291  break;
2292  case UI_BTYPE_ROW:
2293  ui_apply_but_ROW(C, block, but, data);
2294  break;
2295  case UI_BTYPE_VIEW_ITEM:
2296  ui_apply_but_VIEW_ITEM(C, block, but, data);
2297  break;
2298  case UI_BTYPE_LISTROW:
2299  ui_apply_but_LISTROW(C, block, but, data);
2300  break;
2301  case UI_BTYPE_TAB:
2302  ui_apply_but_TAB(C, but, data);
2303  break;
2304  case UI_BTYPE_SCROLL:
2305  case UI_BTYPE_GRIP:
2306  case UI_BTYPE_NUM:
2307  case UI_BTYPE_NUM_SLIDER:
2308  ui_apply_but_NUM(C, but, data);
2309  break;
2310  case UI_BTYPE_MENU:
2311  case UI_BTYPE_BLOCK:
2312  case UI_BTYPE_PULLDOWN:
2313  ui_apply_but_BLOCK(C, but, data);
2314  break;
2315  case UI_BTYPE_COLOR:
2316  if (data->cancel) {
2317  ui_apply_but_VEC(C, but, data);
2318  }
2319  else {
2320  ui_apply_but_BLOCK(C, but, data);
2321  }
2322  break;
2323  case UI_BTYPE_BUT_MENU:
2324  ui_apply_but_BUTM(C, but, data);
2325  break;
2326  case UI_BTYPE_UNITVEC:
2327  case UI_BTYPE_HSVCUBE:
2328  case UI_BTYPE_HSVCIRCLE:
2329  ui_apply_but_VEC(C, but, data);
2330  break;
2331  case UI_BTYPE_COLORBAND:
2332  ui_apply_but_COLORBAND(C, but, data);
2333  break;
2334  case UI_BTYPE_CURVE:
2335  ui_apply_but_CURVE(C, but, data);
2336  break;
2337  case UI_BTYPE_CURVEPROFILE:
2339  break;
2340  case UI_BTYPE_KEY_EVENT:
2341  case UI_BTYPE_HOTKEY_EVENT:
2342  ui_apply_but_BUT(C, but, data);
2343  break;
2344  case UI_BTYPE_IMAGE:
2345  ui_apply_but_IMAGE(C, but, data);
2346  break;
2347  case UI_BTYPE_HISTOGRAM:
2348  ui_apply_but_HISTOGRAM(C, but, data);
2349  break;
2350  case UI_BTYPE_WAVEFORM:
2351  ui_apply_but_WAVEFORM(C, but, data);
2352  break;
2355  break;
2356  default:
2357  break;
2358  }
2359 
2360 #ifdef USE_DRAG_MULTINUM
2361  if (data->multi_data.has_mbuts) {
2362  if ((data->multi_data.init == BUTTON_MULTI_INIT_ENABLE) && (data->multi_data.skip == false)) {
2363  if (data->cancel) {
2364  ui_multibut_restore(C, data, block);
2365  }
2366  else {
2367  ui_multibut_states_apply(C, data, block);
2368  }
2369  }
2370  }
2371 #endif
2372 
2373 #ifdef USE_ALLSELECT
2374  ui_selectcontext_apply(C, but, &data->select_others, data->value, data->origvalue);
2375 #endif
2376 
2377  if (data->cancel) {
2378  data->origvalue = 0.0;
2379  zero_v3(data->origvec);
2380  }
2381 
2382  but->editstr = editstr;
2383  but->editval = editval;
2384  but->editvec = editvec;
2385  if (but_type == UI_BTYPE_COLORBAND) {
2386  uiButColorBand *but_coba = (uiButColorBand *)but;
2387  but_coba->edit_coba = editcoba;
2388  }
2389  else if (but_type == UI_BTYPE_CURVE) {
2390  uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
2391  but_cumap->edit_cumap = editcumap;
2392  }
2393  else if (but_type == UI_BTYPE_CURVEPROFILE) {
2394  uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
2395  but_profile->edit_profile = editprofile;
2396  }
2397 
2398  if (data->custom_interaction_handle != NULL) {
2400  C, &block->custom_interaction_callbacks, data->custom_interaction_handle);
2401  }
2402 }
2403 
2406 /* -------------------------------------------------------------------- */
2410 static void ui_but_get_pasted_text_from_clipboard(char **buf_paste, int *buf_len)
2411 {
2412  /* get only first line even if the clipboard contains multiple lines */
2413  int length;
2414  char *text = WM_clipboard_text_get_firstline(false, &length);
2415 
2416  if (text) {
2417  *buf_paste = text;
2418  *buf_len = length;
2419  }
2420  else {
2421  *buf_paste = MEM_callocN(sizeof(char), __func__);
2422  *buf_len = 0;
2423  }
2424 }
2425 
2427 {
2428  return RNA_property_array_length(&but->rnapoin, but->rnaprop);
2429 }
2430 
2432  bContext *C, uiBut *but, uiHandleButtonData *data, const float *values, const int values_len)
2433 {
2435 
2436  for (int i = 0; i < values_len; i++) {
2437  RNA_property_float_set_index(&but->rnapoin, but->rnaprop, i, values[i]);
2438  }
2439  if (data) {
2440  if (but->type == UI_BTYPE_UNITVEC) {
2441  BLI_assert(values_len == 3);
2442  copy_v3_v3(data->vec, values);
2443  }
2444  else {
2445  data->value = values[but->rnaindex];
2446  }
2447  }
2448 
2450 }
2451 
2452 static void float_array_to_string(const float *values,
2453  const int values_len,
2454  char *output,
2455  int output_len_max)
2456 {
2457  const int values_end = values_len - 1;
2458  int ofs = 0;
2459  output[ofs++] = '[';
2460  for (int i = 0; i < values_len; i++) {
2461  ofs += BLI_snprintf_rlen(
2462  output + ofs, output_len_max - ofs, (i != values_end) ? "%f, " : "%f]", values[i]);
2463  }
2464 }
2465 
2466 static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max)
2467 {
2468  const int values_len = get_but_property_array_length(but);
2469  float *values = alloca(values_len * sizeof(float));
2470  RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values);
2471  float_array_to_string(values, values_len, output, output_len_max);
2472 }
2473 
2474 static bool parse_float_array(char *text, float *values, int values_len_expected)
2475 {
2476  /* can parse max 4 floats for now */
2477  BLI_assert(0 <= values_len_expected && values_len_expected <= 4);
2478 
2479  float v[5];
2480  const int values_len_actual = sscanf(
2481  text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]);
2482 
2483  if (values_len_actual == values_len_expected) {
2484  memcpy(values, v, sizeof(float) * values_len_expected);
2485  return true;
2486  }
2487  return false;
2488 }
2489 
2491  uiBut *but,
2493  char *buf_paste)
2494 {
2495  const int values_len = get_but_property_array_length(but);
2496  if (values_len > 4) {
2497  /* not supported for now */
2498  return;
2499  }
2500 
2501  float *values = alloca(sizeof(float) * values_len);
2502 
2503  if (parse_float_array(buf_paste, values, values_len)) {
2504  ui_but_set_float_array(C, but, data, values, values_len);
2505  }
2506  else {
2507  WM_report(RPT_ERROR, "Expected an array of numbers: [n, n, ...]");
2508  }
2509 }
2510 
2511 static void ui_but_copy_numeric_value(uiBut *but, char *output, int output_len_max)
2512 {
2513  /* Get many decimal places, then strip trailing zeros.
2514  * NOTE: too high values start to give strange results. */
2515  ui_but_string_get_ex(but, output, output_len_max, UI_PRECISION_FLOAT_MAX, false, NULL);
2517 }
2518 
2520  uiBut *but,
2522  char *buf_paste)
2523 {
2524  double value;
2525  if (ui_but_string_eval_number(C, but, buf_paste, &value)) {
2527  data->value = value;
2528  ui_but_string_set(C, but, buf_paste);
2530  }
2531  else {
2532  WM_report(RPT_ERROR, "Expected a number");
2533  }
2534 }
2535 
2537  uiBut *but,
2539  char *buf_paste)
2540 {
2541  float xyz[3];
2542  if (parse_float_array(buf_paste, xyz, 3)) {
2543  if (normalize_v3(xyz) == 0.0f) {
2544  /* better set Z up then have a zero vector */
2545  xyz[2] = 1.0;
2546  }
2547  ui_but_set_float_array(C, but, data, xyz, 3);
2548  }
2549  else {
2550  WM_report(RPT_ERROR, "Paste expected 3 numbers, formatted: '[n, n, n]'");
2551  }
2552 }
2553 
2554 static void ui_but_copy_color(uiBut *but, char *output, int output_len_max)
2555 {
2556  float rgba[4];
2557 
2558  if (but->rnaprop && get_but_property_array_length(but) == 4) {
2559  rgba[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
2560  }
2561  else {
2562  rgba[3] = 1.0f;
2563  }
2564 
2565  ui_but_v3_get(but, rgba);
2566 
2567  /* convert to linear color to do compatible copy between gamma and non-gamma */
2568  if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
2570  }
2571 
2572  float_array_to_string(rgba, 4, output, output_len_max);
2573 }
2574 
2575 static void ui_but_paste_color(bContext *C, uiBut *but, char *buf_paste)
2576 {
2577  float rgba[4];
2578  if (parse_float_array(buf_paste, rgba, 4)) {
2579  if (but->rnaprop) {
2580  /* Assume linear colors in buffer. */
2583  }
2584 
2585  /* Some color properties are RGB, not RGBA. */
2586  const int array_len = get_but_property_array_length(but);
2587  BLI_assert(ELEM(array_len, 3, 4));
2588  ui_but_set_float_array(C, but, NULL, rgba, array_len);
2589  }
2590  }
2591  else {
2592  WM_report(RPT_ERROR, "Paste expected 4 numbers, formatted: '[n, n, n, n]'");
2593  }
2594 }
2595 
2596 static void ui_but_copy_text(uiBut *but, char *output, int output_len_max)
2597 {
2598  ui_but_string_get(but, output, output_len_max);
2599 }
2600 
2601 static void ui_but_paste_text(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste)
2602 {
2603  BLI_assert(but->active == data);
2605  ui_but_set_string_interactive(C, but, buf_paste);
2606 }
2607 
2608 static void ui_but_copy_colorband(uiBut *but)
2609 {
2610  if (but->poin != NULL) {
2611  memcpy(&but_copypaste_coba, but->poin, sizeof(ColorBand));
2612  }
2613 }
2614 
2616 {
2617  if (but_copypaste_coba.tot != 0) {
2618  if (!but->poin) {
2619  but->poin = MEM_callocN(sizeof(ColorBand), "colorband");
2620  }
2621 
2623  memcpy(data->coba, &but_copypaste_coba, sizeof(ColorBand));
2625  }
2626 }
2627 
2629 {
2630  if (but->poin != NULL) {
2634  }
2635 }
2636 
2638 {
2640  if (!but->poin) {
2641  but->poin = MEM_callocN(sizeof(CurveMapping), "curvemapping");
2642  }
2643 
2648  }
2649 }
2650 
2652 {
2653  if (but->poin != NULL) {
2657  }
2658 }
2659 
2661 {
2663  if (!but->poin) {
2664  but->poin = MEM_callocN(sizeof(CurveProfile), "CurveProfile");
2665  }
2666 
2671  }
2672 }
2673 
2674 static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_len_max)
2675 {
2676  PointerRNA *opptr = UI_but_operator_ptr_get(but);
2677 
2678  char *str;
2679  str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr);
2680  BLI_strncpy(output, str, output_len_max);
2681  MEM_freeN(str);
2682 }
2683 
2684 static bool ui_but_copy_menu(uiBut *but, char *output, int output_len_max)
2685 {
2686  MenuType *mt = UI_but_menutype_get(but);
2687  if (mt) {
2688  BLI_snprintf(output, output_len_max, "bpy.ops.wm.call_menu(name=\"%s\")", mt->idname);
2689  return true;
2690  }
2691  return false;
2692 }
2693 
2694 static bool ui_but_copy_popover(uiBut *but, char *output, int output_len_max)
2695 {
2696  PanelType *pt = UI_but_paneltype_get(but);
2697  if (pt) {
2698  BLI_snprintf(output, output_len_max, "bpy.ops.wm.call_panel(name=\"%s\")", pt->idname);
2699  return true;
2700  }
2701  return false;
2702 }
2703 
2704 static void ui_but_copy(bContext *C, uiBut *but, const bool copy_array)
2705 {
2706  if (ui_but_contains_password(but)) {
2707  return;
2708  }
2709 
2710  /* Arbitrary large value (allow for paths: 'PATH_MAX') */
2711  char buf[4096] = {0};
2712  const int buf_max_len = sizeof(buf);
2713 
2714  /* Left false for copying internal data (color-band for eg). */
2715  bool is_buf_set = false;
2716 
2717  const bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL);
2718 
2719  switch (but->type) {
2720  case UI_BTYPE_NUM:
2721  case UI_BTYPE_NUM_SLIDER:
2722  if (!has_required_data) {
2723  break;
2724  }
2725  if (copy_array && ui_but_has_array_value(but)) {
2726  ui_but_copy_numeric_array(but, buf, buf_max_len);
2727  }
2728  else {
2729  ui_but_copy_numeric_value(but, buf, buf_max_len);
2730  }
2731  is_buf_set = true;
2732  break;
2733 
2734  case UI_BTYPE_UNITVEC:
2735  if (!has_required_data) {
2736  break;
2737  }
2738  ui_but_copy_numeric_array(but, buf, buf_max_len);
2739  is_buf_set = true;
2740  break;
2741 
2742  case UI_BTYPE_COLOR:
2743  if (!has_required_data) {
2744  break;
2745  }
2746  ui_but_copy_color(but, buf, buf_max_len);
2747  is_buf_set = true;
2748  break;
2749 
2750  case UI_BTYPE_TEXT:
2751  case UI_BTYPE_SEARCH_MENU:
2752  if (!has_required_data) {
2753  break;
2754  }
2755  ui_but_copy_text(but, buf, buf_max_len);
2756  is_buf_set = true;
2757  break;
2758 
2759  case UI_BTYPE_COLORBAND:
2760  ui_but_copy_colorband(but);
2761  break;
2762 
2763  case UI_BTYPE_CURVE:
2765  break;
2766 
2767  case UI_BTYPE_CURVEPROFILE:
2769  break;
2770 
2771  case UI_BTYPE_BUT:
2772  if (!but->optype) {
2773  break;
2774  }
2775  ui_but_copy_operator(C, but, buf, buf_max_len);
2776  is_buf_set = true;
2777  break;
2778 
2779  case UI_BTYPE_MENU:
2780  case UI_BTYPE_PULLDOWN:
2781  if (ui_but_copy_menu(but, buf, buf_max_len)) {
2782  is_buf_set = true;
2783  }
2784  break;
2785  case UI_BTYPE_POPOVER:
2786  if (ui_but_copy_popover(but, buf, buf_max_len)) {
2787  is_buf_set = true;
2788  }
2789  break;
2790 
2791  default:
2792  break;
2793  }
2794 
2795  if (is_buf_set) {
2796  WM_clipboard_text_set(buf, 0);
2797  }
2798 }
2799 
2800 static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, const bool paste_array)
2801 {
2802  BLI_assert((but->flag & UI_BUT_DISABLED) == 0); /* caller should check */
2803 
2804  int buf_paste_len = 0;
2805  char *buf_paste;
2806  ui_but_get_pasted_text_from_clipboard(&buf_paste, &buf_paste_len);
2807 
2808  const bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL);
2809 
2810  switch (but->type) {
2811  case UI_BTYPE_NUM:
2812  case UI_BTYPE_NUM_SLIDER:
2813  if (!has_required_data) {
2814  break;
2815  }
2816  if (paste_array && ui_but_has_array_value(but)) {
2817  ui_but_paste_numeric_array(C, but, data, buf_paste);
2818  }
2819  else {
2820  ui_but_paste_numeric_value(C, but, data, buf_paste);
2821  }
2822  break;
2823 
2824  case UI_BTYPE_UNITVEC:
2825  if (!has_required_data) {
2826  break;
2827  }
2828  ui_but_paste_normalized_vector(C, but, data, buf_paste);
2829  break;
2830 
2831  case UI_BTYPE_COLOR:
2832  if (!has_required_data) {
2833  break;
2834  }
2835  ui_but_paste_color(C, but, buf_paste);
2836  break;
2837 
2838  case UI_BTYPE_TEXT:
2839  case UI_BTYPE_SEARCH_MENU:
2840  if (!has_required_data) {
2841  break;
2842  }
2843  ui_but_paste_text(C, but, data, buf_paste);
2844  break;
2845 
2846  case UI_BTYPE_COLORBAND:
2847  ui_but_paste_colorband(C, but, data);
2848  break;
2849 
2850  case UI_BTYPE_CURVE:
2852  break;
2853 
2854  case UI_BTYPE_CURVEPROFILE:
2856  break;
2857 
2858  default:
2859  break;
2860  }
2861 
2862  MEM_freeN((void *)buf_paste);
2863 }
2864 
2866 {
2869 }
2870 
2873 /* -------------------------------------------------------------------- */
2887 {
2888  const char *butstr = (but->editstr) ? but->editstr : but->drawstr;
2889  const char *strpos = butstr;
2890  const char *str_end = butstr + strlen(butstr);
2891  for (int i = 0; i < pos; i++) {
2892  strpos = BLI_str_find_next_char_utf8(strpos, str_end);
2893  }
2894 
2895  return (strpos - butstr);
2896 }
2897 
2898 static int ui_text_position_to_hidden(uiBut *but, int pos)
2899 {
2900  const char *butstr = (but->editstr) ? but->editstr : but->drawstr;
2901  return BLI_strnlen_utf8(butstr, pos);
2902 }
2903 
2905  uiBut *but,
2906  const bool restore)
2907 {
2908  if (!(but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) {
2909  return;
2910  }
2911 
2912  char *butstr = (but->editstr) ? but->editstr : but->drawstr;
2913 
2914  if (restore) {
2915  /* restore original string */
2916  BLI_strncpy(butstr, password_str, UI_MAX_PASSWORD_STR);
2917 
2918  /* remap cursor positions */
2919  if (but->pos >= 0) {
2920  but->pos = ui_text_position_from_hidden(but, but->pos);
2921  but->selsta = ui_text_position_from_hidden(but, but->selsta);
2922  but->selend = ui_text_position_from_hidden(but, but->selend);
2923  }
2924  }
2925  else {
2926  /* convert text to hidden text using asterisks (e.g. pass -> ****) */
2927  const size_t len = BLI_strlen_utf8(butstr);
2928 
2929  /* remap cursor positions */
2930  if (but->pos >= 0) {
2931  but->pos = ui_text_position_to_hidden(but, but->pos);
2932  but->selsta = ui_text_position_to_hidden(but, but->selsta);
2933  but->selend = ui_text_position_to_hidden(but, but->selend);
2934  }
2935 
2936  /* save original string */
2937  BLI_strncpy(password_str, butstr, UI_MAX_PASSWORD_STR);
2938  memset(butstr, '*', len);
2939  butstr[len] = '\0';
2940  }
2941 }
2942 
2945 /* -------------------------------------------------------------------- */
2949 void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value)
2950 {
2951  /* Caller should check. */
2952  BLI_assert((but->flag & UI_BUT_DISABLED) == 0);
2953 
2955  ui_textedit_string_set(but, but->active, value);
2956 
2957  if (but->type == UI_BTYPE_SEARCH_MENU && but->active) {
2958  but->changed = true;
2959  ui_searchbox_update(C, but->active->searchbox, but, true);
2960  }
2961 
2963 }
2964 
2966 {
2967  if (!but->active) {
2968  return;
2969  }
2970 
2971  /* most likely NULL, but let's check, and give it temp zero string */
2972  if (!but->active->str) {
2973  but->active->str = MEM_callocN(1, "temp str");
2974  }
2975  but->active->str[0] = 0;
2976 
2977  ui_apply_but_TEX(C, but, but->active);
2979 }
2980 
2982 {
2983  BLI_assert(data->is_str_dynamic);
2984  BLI_assert(data->str == but->editstr);
2985 
2986  if (maxlen > data->maxlen) {
2987  data->str = but->editstr = MEM_reallocN(data->str, sizeof(char) * maxlen);
2988  data->maxlen = maxlen;
2989  }
2990 }
2991 
2992 static void ui_textedit_string_set(uiBut *but, uiHandleButtonData *data, const char *str)
2993 {
2994  if (data->is_str_dynamic) {
2995  ui_textedit_string_ensure_max_length(but, data, strlen(str) + 1);
2996  }
2997 
2998  if (UI_but_is_utf8(but)) {
2999  BLI_strncpy_utf8(data->str, str, data->maxlen);
3000  }
3001  else {
3002  BLI_strncpy(data->str, str, data->maxlen);
3003  }
3004 }
3005 
3007 {
3008  char *str = data->str;
3009  const int len = strlen(str);
3010  bool changed = false;
3011  if (but->selsta != but->selend && len) {
3012  memmove(str + but->selsta, str + but->selend, (len - but->selend) + 1);
3013  changed = true;
3014  }
3015 
3016  but->pos = but->selend = but->selsta;
3017  return changed;
3018 }
3019 
3021  const size_t str_step_ofs,
3022  const rcti *glyph_step_bounds,
3023  const int UNUSED(glyph_advance_x),
3024  const rcti *glyph_bounds,
3025  const int UNUSED(glyph_bearing[2]),
3026  void *user_data)
3027 {
3028  int *cursor_data = user_data;
3029  const int center = glyph_step_bounds->xmin + (BLI_rcti_size_x(glyph_bounds) / 2.0f);
3030  if (cursor_data[0] < center) {
3031  cursor_data[1] = str_step_ofs;
3032  return false;
3033  }
3034  return true;
3035 }
3036 
3043 {
3044  /* XXX pass on as arg. */
3045  uiFontStyle fstyle = UI_style_get()->widget;
3046  const float aspect = but->block->aspect;
3047 
3048  float startx = but->rect.xmin;
3049  float starty_dummy = 0.0f;
3050  char password_str[UI_MAX_PASSWORD_STR];
3051  /* treat 'str_last' as null terminator for str, no need to modify in-place */
3052  const char *str = but->editstr, *str_last;
3053 
3054  ui_block_to_window_fl(data->region, but->block, &startx, &starty_dummy);
3055 
3056  ui_fontscale(&fstyle.points, aspect);
3057 
3058  UI_fontstyle_set(&fstyle);
3059 
3060  ui_but_text_password_hide(password_str, but, false);
3061 
3063  if (but->flag & UI_HAS_ICON) {
3064  startx += UI_DPI_ICON_SIZE / aspect;
3065  }
3066  }
3067  startx += (UI_TEXT_MARGIN_X * U.widget_unit) / aspect;
3068 
3069  /* mouse dragged outside the widget to the left */
3070  if (x < startx) {
3071  int i = but->ofs;
3072 
3073  str_last = &str[but->ofs];
3074 
3075  while (i > 0) {
3076  if (BLI_str_cursor_step_prev_utf8(str, but->ofs, &i)) {
3077  /* 0.25 == scale factor for less sensitivity */
3078  if (BLF_width(fstyle.uifont_id, str + i, (str_last - str) - i) > (startx - x) * 0.25f) {
3079  break;
3080  }
3081  }
3082  else {
3083  break; /* unlikely but possible */
3084  }
3085  }
3086  but->ofs = i;
3087  but->pos = but->ofs;
3088  }
3089  /* mouse inside the widget, mouse coords mapped in widget space */
3090  else {
3091  str_last = &str[but->ofs];
3092  const int str_last_len = strlen(str_last);
3093  const int x_pos = (int)(x - startx);
3094  int glyph_data[2] = {
3095  x_pos, /* horizontal position to test. */
3096  -1, /* Write the character offset here. */
3097  };
3099  str + but->ofs,
3100  INT_MAX,
3102  glyph_data);
3103  /* If value untouched then we are to the right. */
3104  if (glyph_data[1] == -1) {
3105  glyph_data[1] = str_last_len;
3106  }
3107  but->pos = glyph_data[1] + but->ofs;
3108  }
3109 
3110  ui_but_text_password_hide(password_str, but, true);
3111 }
3112 
3114 {
3116 
3117  but->selsta = but->pos;
3118  but->selend = data->sel_pos_init;
3119  if (but->selend < but->selsta) {
3120  SWAP(short, but->selsta, but->selend);
3121  }
3122 
3123  ui_but_update(but);
3124 }
3125 
3133  const char *buf,
3134  int buf_len)
3135 {
3136  int len = strlen(data->str);
3137  const int len_new = len - (but->selend - but->selsta) + 1;
3138  bool changed = false;
3139 
3140  if (data->is_str_dynamic) {
3141  ui_textedit_string_ensure_max_length(but, data, len_new + buf_len);
3142  }
3143 
3144  if (len_new <= data->maxlen) {
3145  char *str = data->str;
3146  size_t step = buf_len;
3147 
3148  /* type over the current selection */
3149  if ((but->selend - but->selsta) > 0) {
3150  changed = ui_textedit_delete_selection(but, data);
3151  len = strlen(str);
3152  }
3153 
3154  if ((len + step >= data->maxlen) && (data->maxlen - (len + 1) > 0)) {
3155  if (UI_but_is_utf8(but)) {
3156  /* Shorten 'step' to a utf8 aligned size that fits. */
3157  BLI_strnlen_utf8_ex(buf, data->maxlen - (len + 1), &step);
3158  }
3159  else {
3160  step = data->maxlen - (len + 1);
3161  }
3162  }
3163 
3164  if (step && (len + step < data->maxlen)) {
3165  memmove(&str[but->pos + step], &str[but->pos], (len + 1) - but->pos);
3166  memcpy(&str[but->pos], buf, step * sizeof(char));
3167  but->pos += step;
3168  changed = true;
3169  }
3170  }
3171 
3172  return changed;
3173 }
3174 
3175 static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii)
3176 {
3177  BLI_assert(isascii(ascii));
3178  const char buf[2] = {ascii, '\0'};
3179  return ui_textedit_insert_buf(but, data, buf, sizeof(buf) - 1);
3180 }
3181 
3182 static void ui_textedit_move(uiBut *but,
3184  eStrCursorJumpDirection direction,
3185  const bool select,
3187 {
3188  const char *str = data->str;
3189  const int len = strlen(str);
3190  const int pos_prev = but->pos;
3191  const bool has_sel = (but->selend - but->selsta) > 0;
3192 
3193  ui_but_update(but);
3194 
3195  /* special case, quit selection and set cursor */
3196  if (has_sel && !select) {
3197  if (jump == STRCUR_JUMP_ALL) {
3198  but->selsta = but->selend = but->pos = direction ? len : 0;
3199  }
3200  else {
3201  if (direction) {
3202  but->selsta = but->pos = but->selend;
3203  }
3204  else {
3205  but->pos = but->selend = but->selsta;
3206  }
3207  }
3208  data->sel_pos_init = but->pos;
3209  }
3210  else {
3211  int pos_i = but->pos;
3212  BLI_str_cursor_step_utf8(str, len, &pos_i, direction, jump, true);
3213  but->pos = pos_i;
3214 
3215  if (select) {
3216  if (has_sel == false) {
3217  data->sel_pos_init = pos_prev;
3218  }
3219  but->selsta = but->pos;
3220  but->selend = data->sel_pos_init;
3221  }
3222  if (but->selend < but->selsta) {
3223  SWAP(short, but->selsta, but->selend);
3224  }
3225  }
3226 }
3227 
3228 static bool ui_textedit_delete(uiBut *but,
3230  int direction,
3232 {
3233  char *str = data->str;
3234  const int len = strlen(str);
3235 
3236  bool changed = false;
3237 
3238  if (jump == STRCUR_JUMP_ALL) {
3239  if (len) {
3240  changed = true;
3241  }
3242  str[0] = '\0';
3243  but->pos = 0;
3244  }
3245  else if (direction) { /* delete */
3246  if ((but->selend - but->selsta) > 0) {
3247  changed = ui_textedit_delete_selection(but, data);
3248  }
3249  else if (but->pos >= 0 && but->pos < len) {
3250  int pos = but->pos;
3251  int step;
3252  BLI_str_cursor_step_utf8(str, len, &pos, direction, jump, true);
3253  step = pos - but->pos;
3254  memmove(&str[but->pos], &str[but->pos + step], (len + 1) - (but->pos + step));
3255  changed = true;
3256  }
3257  }
3258  else { /* backspace */
3259  if (len != 0) {
3260  if ((but->selend - but->selsta) > 0) {
3261  changed = ui_textedit_delete_selection(but, data);
3262  }
3263  else if (but->pos > 0) {
3264  int pos = but->pos;
3265  int step;
3266 
3267  BLI_str_cursor_step_utf8(str, len, &pos, direction, jump, true);
3268  step = but->pos - pos;
3269  memmove(&str[but->pos - step], &str[but->pos], (len + 1) - but->pos);
3270  but->pos -= step;
3271  changed = true;
3272  }
3273  }
3274  }
3275 
3276  return changed;
3277 }
3278 
3280 {
3281  char *str = data->str;
3282 
3283  int changed;
3284  if (data->searchbox) {
3285  changed = ui_searchbox_autocomplete(C, data->searchbox, but, data->str);
3286  }
3287  else {
3288  changed = but->autocomplete_func(C, str, but->autofunc_arg);
3289  }
3290 
3291  but->pos = strlen(str);
3292  but->selsta = but->selend = but->pos;
3293 
3294  return changed;
3295 }
3296 
3297 /* mode for ui_textedit_copypaste() */
3298 enum {
3302 };
3303 
3304 static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const int mode)
3305 {
3306  bool changed = false;
3307 
3308  /* paste */
3309  if (mode == UI_TEXTEDIT_PASTE) {
3310  /* extract the first line from the clipboard */
3311  int buf_len;
3312  char *pbuf = WM_clipboard_text_get_firstline(false, &buf_len);
3313 
3314  if (pbuf) {
3315  if (UI_but_is_utf8(but)) {
3316  buf_len -= BLI_str_utf8_invalid_strip(pbuf, (size_t)buf_len);
3317  }
3318 
3319  ui_textedit_insert_buf(but, data, pbuf, buf_len);
3320 
3321  changed = true;
3322 
3323  MEM_freeN(pbuf);
3324  }
3325  }
3326  /* cut & copy */
3327  else if (ELEM(mode, UI_TEXTEDIT_COPY, UI_TEXTEDIT_CUT)) {
3328  /* copy the contents to the copypaste buffer */
3329  const int sellen = but->selend - but->selsta;
3330  char *buf = MEM_mallocN(sizeof(char) * (sellen + 1), "ui_textedit_copypaste");
3331 
3332  BLI_strncpy(buf, data->str + but->selsta, sellen + 1);
3333  WM_clipboard_text_set(buf, 0);
3334  MEM_freeN(buf);
3335 
3336  /* for cut only, delete the selection afterwards */
3337  if (mode == UI_TEXTEDIT_CUT) {
3338  if ((but->selend - but->selsta) > 0) {
3339  changed = ui_textedit_delete_selection(but, data);
3340  }
3341  }
3342  }
3343 
3344  return changed;
3345 }
3346 
3347 #ifdef WITH_INPUT_IME
3348 /* Enable IME, and setup #uiBut IME data. */
3349 static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but))
3350 {
3351  /* XXX Is this really needed? */
3352  int x, y;
3353 
3354  BLI_assert(win->ime_data == NULL);
3355 
3356  /* enable IME and position to cursor, it's a trick */
3357  x = win->eventstate->xy[0];
3358  /* flip y and move down a bit, prevent the IME panel cover the edit button */
3359  y = win->eventstate->xy[1] - 12;
3360 
3361  wm_window_IME_begin(win, x, y, 0, 0, true);
3362 }
3363 
3364 /* Disable IME, and clear #uiBut IME data. */
3365 static void ui_textedit_ime_end(wmWindow *win, uiBut *UNUSED(but))
3366 {
3367  wm_window_IME_end(win);
3368 }
3369 
3370 void ui_but_ime_reposition(uiBut *but, int x, int y, bool complete)
3371 {
3372  BLI_assert(but->active);
3373 
3374  ui_region_to_window(but->active->region, &x, &y);
3375  wm_window_IME_begin(but->active->window, x, y - 4, 0, 0, complete);
3376 }
3377 
3378 wmIMEData *ui_but_ime_data_get(uiBut *but)
3379 {
3380  if (but->active && but->active->window) {
3381  return but->active->window->ime_data;
3382  }
3383  else {
3384  return NULL;
3385  }
3386 }
3387 #endif /* WITH_INPUT_IME */
3388 
3390 {
3391  wmWindow *win = data->window;
3392  const bool is_num_but = ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER);
3393  bool no_zero_strip = false;
3394 
3395  MEM_SAFE_FREE(data->str);
3396 
3397 #ifdef USE_DRAG_MULTINUM
3398  /* this can happen from multi-drag */
3399  if (data->applied_interactive) {
3400  /* remove any small changes so canceling edit doesn't restore invalid value: T40538 */
3401  data->cancel = true;
3402  ui_apply_but(C, but->block, but, data, true);
3403  data->cancel = false;
3404 
3405  data->applied_interactive = false;
3406  }
3407 #endif
3408 
3409 #ifdef USE_ALLSELECT
3410  if (is_num_but) {
3411  if (IS_ALLSELECT_EVENT(win->eventstate)) {
3412  data->select_others.is_enabled = true;
3413  data->select_others.is_copy = true;
3414  }
3415  }
3416 #endif
3417 
3418  /* retrieve string */
3419  data->maxlen = ui_but_string_get_max_length(but);
3420  if (data->maxlen != 0) {
3421  data->str = MEM_callocN(sizeof(char) * data->maxlen, "textedit str");
3422  /* We do not want to truncate precision to default here, it's nice to show value,
3423  * not to edit it - way too much precision is lost then. */
3425  but, data->str, data->maxlen, UI_PRECISION_FLOAT_MAX, true, &no_zero_strip);
3426  }
3427  else {
3428  data->is_str_dynamic = true;
3429  data->str = ui_but_string_get_dynamic(but, &data->maxlen);
3430  }
3431 
3432  if (ui_but_is_float(but) && !ui_but_is_unit(but) && !ui_but_anim_expression_get(but, NULL, 0) &&
3433  !no_zero_strip) {
3434  BLI_str_rstrip_float_zero(data->str, '\0');
3435  }
3436 
3437  if (is_num_but) {
3438  BLI_assert(data->is_str_dynamic == false);
3439  ui_but_convert_to_unit_alt_name(but, data->str, data->maxlen);
3440 
3442  }
3443 
3444  /* won't change from now on */
3445  const int len = strlen(data->str);
3446 
3447  data->origstr = BLI_strdupn(data->str, len);
3448  data->sel_pos_init = 0;
3449 
3450  /* set cursor pos to the end of the text */
3451  but->editstr = data->str;
3452  but->pos = len;
3453  but->selsta = 0;
3454  but->selend = len;
3455 
3456  /* Initialize undo history tracking. */
3457  data->undo_stack_text = ui_textedit_undo_stack_create();
3458  ui_textedit_undo_push(data->undo_stack_text, but->editstr, but->pos);
3459 
3460  /* optional searchbox */
3461  if (but->type == UI_BTYPE_SEARCH_MENU) {
3462  uiButSearch *search_but = (uiButSearch *)but;
3463 
3464  data->searchbox = search_but->popup_create_fn(C, data->region, search_but);
3465  ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */
3466  }
3467 
3468  /* reset alert flag (avoid confusion, will refresh on exit) */
3469  but->flag &= ~UI_BUT_REDALERT;
3470 
3471  ui_but_update(but);
3472 
3473  /* Make sure the edited button is in view. */
3474  if (data->searchbox) {
3475  /* Popup blocks don't support moving after creation, so don't change the view for them. */
3476  }
3477  else if (UI_block_layout_needs_resolving(but->block)) {
3478  /* Layout isn't resolved yet (may happen when activating while drawing through
3479  * #UI_but_active_only()), so can't move it into view yet. This causes
3480  * #ui_but_update_view_for_active() to run after the layout is resolved. */
3481  but->changed = true;
3482  }
3483  else {
3484  UI_but_ensure_in_view(C, data->region, but);
3485  }
3486 
3488 
3489 #ifdef WITH_INPUT_IME
3490  if (!is_num_but) {
3491  ui_textedit_ime_begin(win, but);
3492  }
3493 #endif
3494 }
3495 
3497 {
3498  wmWindow *win = data->window;
3499 
3500  if (but) {
3501  if (UI_but_is_utf8(but)) {
3502  const int strip = BLI_str_utf8_invalid_strip(but->editstr, strlen(but->editstr));
3503  /* not a file?, strip non utf-8 chars */
3504  if (strip) {
3505  /* won't happen often so isn't that annoying to keep it here for a while */
3506  printf("%s: invalid utf8 - stripped chars %d\n", __func__, strip);
3507  }
3508  }
3509 
3510  if (data->searchbox) {
3511  if (data->cancel == false) {
3513  uiButSearch *but_search = (uiButSearch *)but;
3514 
3515  if ((ui_searchbox_apply(but, data->searchbox) == false) &&
3516  (ui_searchbox_find_index(data->searchbox, but->editstr) == -1) &&
3517  !but_search->results_are_suggestions) {
3518 
3519  if (but->flag & UI_BUT_VALUE_CLEAR) {
3520  /* It is valid for _VALUE_CLEAR flavor to have no active element
3521  * (it's a valid way to unlink). */
3522  but->editstr[0] = '\0';
3523  }
3524  data->cancel = true;
3525 
3526  /* ensure menu (popup) too is closed! */
3527  data->escapecancel = true;
3528 
3529  WM_reportf(RPT_ERROR, "Failed to find '%s'", but->editstr);
3531  }
3532  }
3533 
3534  ui_searchbox_free(C, data->searchbox);
3535  data->searchbox = NULL;
3536  }
3537 
3538  but->editstr = NULL;
3539  but->pos = -1;
3540  }
3541 
3543 
3544  /* Free text undo history text blocks. */
3545  ui_textedit_undo_stack_destroy(data->undo_stack_text);
3546  data->undo_stack_text = NULL;
3547 
3548 #ifdef WITH_INPUT_IME
3549  if (win->ime_data) {
3550  ui_textedit_ime_end(win, but);
3551  }
3552 #endif
3553 }
3554 
3556 {
3557  /* label and roundbox can overlap real buttons (backdrops...) */
3558  if (ELEM(actbut->type,
3560  UI_BTYPE_SEPR,
3563  UI_BTYPE_LISTBOX)) {
3564  return;
3565  }
3566 
3567  for (uiBut *but = actbut->next; but; but = but->next) {
3568  if (ui_but_is_editable_as_text(but)) {
3569  if (!(but->flag & UI_BUT_DISABLED)) {
3570  data->postbut = but;
3571  data->posttype = BUTTON_ACTIVATE_TEXT_EDITING;
3572  return;
3573  }
3574  }
3575  }
3576  for (uiBut *but = block->buttons.first; but != actbut; but = but->next) {
3577  if (ui_but_is_editable_as_text(but)) {
3578  if (!(but->flag & UI_BUT_DISABLED)) {
3579  data->postbut = but;
3580  data->posttype = BUTTON_ACTIVATE_TEXT_EDITING;
3581  return;
3582  }
3583  }
3584  }
3585 }
3586 
3588 {
3589  /* label and roundbox can overlap real buttons (backdrops...) */
3590  if (ELEM(actbut->type,
3592  UI_BTYPE_SEPR,
3595  UI_BTYPE_LISTBOX)) {
3596  return;
3597  }
3598 
3599  for (uiBut *but = actbut->prev; but; but = but->prev) {
3600  if (ui_but_is_editable_as_text(but)) {
3601  if (!(but->flag & UI_BUT_DISABLED)) {
3602  data->postbut = but;
3603  data->posttype = BUTTON_ACTIVATE_TEXT_EDITING;
3604  return;
3605  }
3606  }
3607  }
3608  for (uiBut *but = block->buttons.last; but != actbut; but = but->prev) {
3609  if (ui_but_is_editable_as_text(but)) {
3610  if (!(but->flag & UI_BUT_DISABLED)) {
3611  data->postbut = but;
3612  data->posttype = BUTTON_ACTIVATE_TEXT_EDITING;
3613  return;
3614  }
3615  }
3616  }
3617 }
3618 
3620  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
3621 {
3622  int retval = WM_UI_HANDLER_CONTINUE;
3623  bool changed = false, inbox = false, update = false, skip_undo_push = false;
3624 
3625 #ifdef WITH_INPUT_IME
3626  wmWindow *win = CTX_wm_window(C);
3627  wmIMEData *ime_data = win->ime_data;
3628  const bool is_ime_composing = ime_data && ime_data->is_ime_composing;
3629 #else
3630  const bool is_ime_composing = false;
3631 #endif
3632 
3633  switch (event->type) {
3634  case MOUSEMOVE:
3635  case MOUSEPAN:
3636  if (data->searchbox) {
3637 #ifdef USE_KEYNAV_LIMIT
3638  if ((event->type == MOUSEMOVE) &&
3639  ui_mouse_motion_keynav_test(&data->searchbox_keynav_state, event)) {
3640  /* pass */
3641  }
3642  else {
3643  ui_searchbox_event(C, data->searchbox, but, data->region, event);
3644  }
3645 #else
3646  ui_searchbox_event(C, data->searchbox, but, data->region, event);
3647 #endif
3648  }
3650 
3651  break;
3652  case RIGHTMOUSE:
3653  case EVT_ESCKEY:
3654  if (event->val == KM_PRESS) {
3655  /* Support search context menu. */
3656  if (event->type == RIGHTMOUSE) {
3657  if (data->searchbox) {
3658  if (ui_searchbox_event(C, data->searchbox, but, data->region, event)) {
3659  /* Only break if the event was handled. */
3660  break;
3661  }
3662  }
3663  }
3664 
3665 #ifdef WITH_INPUT_IME
3666  /* skips button handling since it is not wanted */
3667  if (is_ime_composing) {
3668  break;
3669  }
3670 #endif
3671  data->cancel = true;
3672  data->escapecancel = true;
3674  retval = WM_UI_HANDLER_BREAK;
3675  }
3676  break;
3677  case LEFTMOUSE: {
3678  /* Allow clicks on extra icons while editing. */
3679  if (ui_do_but_extra_operator_icon(C, but, data, event)) {
3680  break;
3681  }
3682 
3683  const bool had_selection = but->selsta != but->selend;
3684 
3685  /* exit on LMB only on RELEASE for searchbox, to mimic other popups,
3686  * and allow multiple menu levels */
3687  if (data->searchbox) {
3688  inbox = ui_searchbox_inside(data->searchbox, event->xy);
3689  }
3690 
3691  /* for double click: we do a press again for when you first click on button
3692  * (selects all text, no cursor pos) */
3693  if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) {
3694  float mx = event->xy[0];
3695  float my = event->xy[1];
3696  ui_window_to_block_fl(data->region, block, &mx, &my);
3697 
3698  if (ui_but_contains_pt(but, mx, my)) {
3699  ui_textedit_set_cursor_pos(but, data, event->xy[0]);
3700  but->selsta = but->selend = but->pos;
3701  data->sel_pos_init = but->pos;
3702 
3704  retval = WM_UI_HANDLER_BREAK;
3705  }
3706  else if (inbox == false) {
3707  /* if searchbox, click outside will cancel */
3708  if (data->searchbox) {
3709  data->cancel = data->escapecancel = true;
3710  }
3712  retval = WM_UI_HANDLER_BREAK;
3713  }
3714  }
3715 
3716  /* only select a word in button if there was no selection before */
3717  if (event->val == KM_DBL_CLICK && had_selection == false) {
3720  retval = WM_UI_HANDLER_BREAK;
3721  changed = true;
3722  }
3723  else if (inbox) {
3724  /* if we allow activation on key press,
3725  * it gives problems launching operators T35713. */
3726  if (event->val == KM_RELEASE) {
3728  retval = WM_UI_HANDLER_BREAK;
3729  }
3730  }
3731  break;
3732  }
3733  }
3734 
3735  if (event->val == KM_PRESS && !is_ime_composing) {
3736  switch (event->type) {
3737  case EVT_VKEY:
3738  case EVT_XKEY:
3739  case EVT_CKEY:
3740 #if defined(__APPLE__)
3741  if (ELEM(event->modifier, KM_OSKEY, KM_CTRL))
3742 #else
3743  if (event->modifier == KM_CTRL)
3744 #endif
3745  {
3746  if (event->type == EVT_VKEY) {
3747  changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_PASTE);
3748  }
3749  else if (event->type == EVT_CKEY) {
3750  changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_COPY);
3751  }
3752  else if (event->type == EVT_XKEY) {
3753  changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_CUT);
3754  }
3755 
3756  retval = WM_UI_HANDLER_BREAK;
3757  }
3758  break;
3759  case EVT_RIGHTARROWKEY:
3760  ui_textedit_move(but,
3761  data,
3763  event->modifier & KM_SHIFT,
3765  retval = WM_UI_HANDLER_BREAK;
3766  break;
3767  case EVT_LEFTARROWKEY:
3768  ui_textedit_move(but,
3769  data,
3771  event->modifier & KM_SHIFT,
3773  retval = WM_UI_HANDLER_BREAK;
3774  break;
3775  case WHEELDOWNMOUSE:
3776  case EVT_DOWNARROWKEY:
3777  if (data->searchbox) {
3778 #ifdef USE_KEYNAV_LIMIT
3779  ui_mouse_motion_keynav_init(&data->searchbox_keynav_state, event);
3780 #endif
3781  ui_searchbox_event(C, data->searchbox, but, data->region, event);
3782  break;
3783  }
3784  if (event->type == WHEELDOWNMOUSE) {
3785  break;
3786  }
3788  case EVT_ENDKEY:
3790  retval = WM_UI_HANDLER_BREAK;
3791  break;
3792  case WHEELUPMOUSE:
3793  case EVT_UPARROWKEY:
3794  if (data->searchbox) {
3795 #ifdef USE_KEYNAV_LIMIT
3796  ui_mouse_motion_keynav_init(&data->searchbox_keynav_state, event);
3797 #endif
3798  ui_searchbox_event(C, data->searchbox, but, data->region, event);
3799  break;
3800  }
3801  if (event->type == WHEELUPMOUSE) {
3802  break;
3803  }
3805  case EVT_HOMEKEY:
3807  retval = WM_UI_HANDLER_BREAK;
3808  break;
3809  case EVT_PADENTER:
3810  case EVT_RETKEY:
3812  retval = WM_UI_HANDLER_BREAK;
3813  break;
3814  case EVT_DELKEY:
3815  changed = ui_textedit_delete(
3816  but, data, 1, (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
3817  retval = WM_UI_HANDLER_BREAK;
3818  break;
3819 
3820  case EVT_BACKSPACEKEY:
3821  changed = ui_textedit_delete(
3822  but, data, 0, (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE);
3823  retval = WM_UI_HANDLER_BREAK;
3824  break;
3825 
3826  case EVT_AKEY:
3827 
3828  /* Ctrl-A: Select all. */
3829 #if defined(__APPLE__)
3830  /* OSX uses Command-A system-wide, so add it. */
3831  if (ELEM(event->modifier, KM_OSKEY, KM_CTRL))
3832 #else
3833  if (event->modifier == KM_CTRL)
3834 #endif
3835  {
3838  retval = WM_UI_HANDLER_BREAK;
3839  }
3840  break;
3841 
3842  case EVT_TABKEY:
3843  /* There is a key conflict here, we can't tab with auto-complete. */
3844  if (but->autocomplete_func || data->searchbox) {
3845  const int autocomplete = ui_textedit_autocomplete(C, but, data);
3846  changed = autocomplete != AUTOCOMPLETE_NO_MATCH;
3847 
3848  if (autocomplete == AUTOCOMPLETE_FULL_MATCH) {
3850  }
3851  }
3852  else if ((event->modifier & (KM_CTRL | KM_ALT | KM_OSKEY)) == 0) {
3853  /* Use standard keys for cycling through buttons Tab, Shift-Tab to reverse. */
3854  if (event->modifier & KM_SHIFT) {
3855  ui_textedit_prev_but(block, but, data);
3856  }
3857  else {
3858  ui_textedit_next_but(block, but, data);
3859  }
3861  }
3862  retval = WM_UI_HANDLER_BREAK;
3863  break;
3864  case EVT_ZKEY: {
3865  /* Ctrl-Z or Ctrl-Shift-Z: Undo/Redo (allowing for OS-Key on Apple). */
3866 
3867  const bool is_redo = (event->modifier & KM_SHIFT);
3868  if (
3869 #if defined(__APPLE__)
3870  ((event->modifier & KM_OSKEY) && ((event->modifier & (KM_ALT | KM_CTRL)) == 0)) ||
3871 #endif
3872  ((event->modifier & KM_CTRL) && ((event->modifier & (KM_ALT | KM_OSKEY)) == 0))) {
3873  int undo_pos;
3874  const char *undo_str = ui_textedit_undo(
3875  data->undo_stack_text, is_redo ? 1 : -1, &undo_pos);
3876  if (undo_str != NULL) {
3877  ui_textedit_string_set(but, data, undo_str);
3878 
3879  /* Set the cursor & clear selection. */
3880  but->pos = undo_pos;
3881  but->selsta = but->pos;
3882  but->selend = but->pos;
3883  changed = true;
3884  }
3885  retval = WM_UI_HANDLER_BREAK;
3886  skip_undo_push = true;
3887  }
3888  break;
3889  }
3890  }
3891 
3892  if ((event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)
3893 #ifdef WITH_INPUT_IME
3894  && !is_ime_composing && !WM_event_is_ime_switch(event)
3895 #endif
3896  ) {
3897  char utf8_buf_override[2] = {'\0', '\0'};
3898  const char *utf8_buf = event->utf8_buf;
3899 
3900  /* Exception that's useful for number buttons, some keyboard
3901  * numpads have a comma instead of a period. */
3902  if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */
3903  if ((event->type == EVT_PADPERIOD) && (utf8_buf[0] == ',')) {
3904  utf8_buf_override[0] = '.';
3905  utf8_buf = utf8_buf_override;
3906  }
3907  }
3908 
3909  if (utf8_buf[0]) {
3910  const int utf8_buf_len = BLI_str_utf8_size(utf8_buf);
3911  BLI_assert(utf8_buf_len != -1);
3912  changed = ui_textedit_insert_buf(but, data, utf8_buf, utf8_buf_len);
3913  }
3914 
3915  retval = WM_UI_HANDLER_BREAK;
3916  }
3917  /* textbutton with this flag: do live update (e.g. for search buttons) */
3918  if (but->flag & UI_BUT_TEXTEDIT_UPDATE) {
3919  update = true;
3920  }
3921  }
3922 
3923 #ifdef WITH_INPUT_IME
3925  changed = true;
3926 
3927  if (event->type == WM_IME_COMPOSITE_START && but->selend > but->selsta) {
3929  }
3930  if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) {
3931  if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
3932  strcmp(ime_data->str_result, "\xE3\x80\x82") == 0) {
3933  /* Convert Ideographic Full Stop (U+3002) to decimal point when entering numbers. */
3934  ui_textedit_insert_ascii(but, data, '.');
3935  }
3936  else {
3937  ui_textedit_insert_buf(but, data, ime_data->str_result, ime_data->result_len);
3938  }
3939  }
3940  }
3941  else if (event->type == WM_IME_COMPOSITE_END) {
3942  changed = true;
3943  }
3944 #else
3945  /* Prevent the function from being unused. */
3947 #endif
3948 
3949  if (changed) {
3950  /* The undo stack may be NULL if an event exits editing. */
3951  if ((skip_undo_push == false) && (data->undo_stack_text != NULL)) {
3952  ui_textedit_undo_push(data->undo_stack_text, data->str, but->pos);
3953  }
3954 
3955  /* only do live update when but flag request it (UI_BUT_TEXTEDIT_UPDATE). */
3956  if (update && data->interactive) {
3957  ui_apply_but(C, block, but, data, true);
3958  }
3959  else {
3960  ui_but_update_edited(but);
3961  }
3962  but->changed = true;
3963 
3964  if (data->searchbox) {
3965  ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */
3966  }
3967  }
3968 
3969  if (changed || (retval == WM_UI_HANDLER_BREAK)) {
3970  ED_region_tag_redraw(data->region);
3971  }
3972 }
3973 
3975  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
3976 {
3977  int retval = WM_UI_HANDLER_CONTINUE;
3978 
3979  switch (event->type) {
3980  case MOUSEMOVE: {
3981  int mx = event->xy[0];
3982  int my = event->xy[1];
3983  ui_window_to_block(data->region, block, &mx, &my);
3984 
3985  ui_textedit_set_cursor_select(but, data, event->xy[0]);
3986  retval = WM_UI_HANDLER_BREAK;
3987  break;
3988  }
3989  case LEFTMOUSE:
3990  if (event->val == KM_RELEASE) {
3992  }
3993  retval = WM_UI_HANDLER_BREAK;
3994  break;
3995  }
3996 
3997  if (retval == WM_UI_HANDLER_BREAK) {
3998  ui_but_update(but);
3999  ED_region_tag_redraw(data->region);
4000  }
4001 }
4002 
4005 /* -------------------------------------------------------------------- */
4010 {
4011  data->startvalue = ui_but_value_get(but);
4012  data->origvalue = data->startvalue;
4013  data->value = data->origvalue;
4014 }
4015 
4017 {
4018  if (but->type == UI_BTYPE_CURVE) {
4019  uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
4020  but_cumap->edit_cumap = (CurveMapping *)but->poin;
4021  }
4022  else if (but->type == UI_BTYPE_CURVEPROFILE) {
4023  uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
4024  but_profile->edit_profile = (CurveProfile *)but->poin;
4025  }
4026  else if (but->type == UI_BTYPE_COLORBAND) {
4027  uiButColorBand *but_coba = (uiButColorBand *)but;
4028  data->coba = (ColorBand *)but->poin;
4029  but_coba->edit_coba = data->coba;
4030  }
4031  else if (ELEM(but->type,
4035  UI_BTYPE_COLOR)) {
4036  ui_but_v3_get(but, data->origvec);
4037  copy_v3_v3(data->vec, data->origvec);
4038  but->editvec = data->vec;
4039  }
4040  else {
4042  but->editval = &data->value;
4043 
4044  float softmin = but->softmin;
4045  float softmax = but->softmax;
4046  float softrange = softmax - softmin;
4047  const PropertyScaleType scale_type = ui_but_scale_type(but);
4048 
4049  float log_min = (scale_type == PROP_SCALE_LOG) ? max_ff(softmin, UI_PROP_SCALE_LOG_MIN) : 0.0f;
4050 
4051  if ((but->type == UI_BTYPE_NUM) && (ui_but_is_cursor_warp(but) == false)) {
4052  uiButNumber *number_but = (uiButNumber *)but;
4053 
4054  if (scale_type == PROP_SCALE_LOG) {
4055  log_min = max_ff(log_min, powf(10, -number_but->precision) * 0.5f);
4056  }
4057  /* Use a minimum so we have a predictable range,
4058  * otherwise some float buttons get a large range. */
4059  const float value_step_float_min = 0.1f;
4060  const bool is_float = ui_but_is_float(but);
4061  const double value_step = is_float ?
4062  (double)(number_but->step_size * UI_PRECISION_FLOAT_SCALE) :
4063  (int)number_but->step_size;
4064  const float drag_map_softrange_max = UI_DRAG_MAP_SOFT_RANGE_PIXEL_MAX * UI_DPI_FAC;
4065  const float softrange_max = min_ff(
4066  softrange,
4067  2 * (is_float ? min_ff(value_step, value_step_float_min) *
4068  (drag_map_softrange_max / value_step_float_min) :
4069  drag_map_softrange_max));
4070 
4071  if (softrange > softrange_max) {
4072  /* Center around the value, keeping in the real soft min/max range. */
4073  softmin = data->origvalue - (softrange_max / 2);
4074  softmax = data->origvalue + (softrange_max / 2);
4075  if (!isfinite(softmin)) {
4076  softmin = (data->origvalue > 0.0f ? FLT_MAX : -FLT_MAX);
4077  }
4078  if (!isfinite(softmax)) {
4079  softmax = (data->origvalue > 0.0f ? FLT_MAX : -FLT_MAX);
4080  }
4081 
4082  if (softmin < but->softmin) {
4083  softmin = but->softmin;
4084  softmax = softmin + softrange_max;
4085  }
4086  else if (softmax > but->softmax) {
4087  softmax = but->softmax;
4088  softmin = softmax - softrange_max;
4089  }
4090 
4091  /* Can happen at extreme values. */
4092  if (UNLIKELY(softmin == softmax)) {
4093  if (data->origvalue > 0.0) {
4094  softmin = nextafterf(softmin, -FLT_MAX);
4095  }
4096  else {
4097  softmax = nextafterf(softmax, FLT_MAX);
4098  }
4099  }
4100 
4101  softrange = softmax - softmin;
4102  }
4103  }
4104 
4105  if (softrange == 0.0f) {
4106  data->dragfstart = 0.0f;
4107  }
4108  else {
4109  switch (scale_type) {
4110  case PROP_SCALE_LINEAR: {
4111  data->dragfstart = ((float)data->value - softmin) / softrange;
4112  break;
4113  }
4114  case PROP_SCALE_LOG: {
4115  BLI_assert(log_min != 0.0f);
4116  const float base = softmax / log_min;
4117  data->dragfstart = logf((float)data->value / log_min) / logf(base);
4118  break;
4119  }
4120  case PROP_SCALE_CUBIC: {
4121  const float cubic_min = cube_f(softmin);
4122  const float cubic_max = cube_f(softmax);
4123  const float cubic_range = cubic_max - cubic_min;
4124  const float f = ((float)data->value - softmin) * cubic_range / softrange + cubic_min;
4125  data->dragfstart = (cbrtf(f) - softmin) / softrange;
4126  break;
4127  }
4128  }
4129  }
4130  data->dragf = data->dragfstart;
4131 
4132  data->drag_map_soft_min = softmin;
4133  data->drag_map_soft_max = softmax;
4134  }
4135 
4136  data->dragchange = false;
4137  data->draglock = true;
4138 }
4139 
4141 {
4142  but->editval = NULL;
4143  but->editvec = NULL;
4144  if (but->type == UI_BTYPE_COLORBAND) {
4145  uiButColorBand *but_coba = (uiButColorBand *)but;
4146  but_coba->edit_coba = NULL;
4147  }
4148  else if (but->type == UI_BTYPE_CURVE) {
4149  uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
4150  but_cumap->edit_cumap = NULL;
4151  }
4152  else if (but->type == UI_BTYPE_CURVEPROFILE) {
4153  uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
4154  but_profile->edit_profile = NULL;
4155  }
4156  data->dragstartx = 0;
4157  data->draglastx = 0;
4158  data->dragchange = false;
4159  data->dragcbd = NULL;
4160  data->dragsel = 0;
4161 }
4162 
4164 {
4165  if (data->interactive) {
4166  ui_apply_but(C, block, but, data, true);
4167  }
4168  else {
4169  ui_but_update(but);
4170  }
4171 
4172  ED_region_tag_redraw(data->region);
4173 }
4174 
4176 {
4177  but->active->apply_through_extra_icon = true;
4178 
4179  if (but->active->interactive) {
4180  ui_apply_but(C, but->block, but, but->active, true);
4181  }
4184  op_icon->optype_params->optype,
4185  op_icon->optype_params->opcontext,
4186  op_icon->optype_params->opptr,
4187  NULL,
4188  NULL);
4189 
4190  /* Force recreation of extra operator icons (pseudo update). */
4192 
4194 }
4195 
4198 /* -------------------------------------------------------------------- */
4203 {
4204  uiBlockCreateFunc func = NULL;
4205  uiBlockHandleCreateFunc handlefunc = NULL;
4206  uiMenuCreateFunc menufunc = NULL;
4207  uiMenuCreateFunc popoverfunc = NULL;
4208  void *arg = NULL;
4209 
4210  switch (but->type) {
4211  case UI_BTYPE_BLOCK:
4212  case UI_BTYPE_PULLDOWN:
4213  if (but->menu_create_func) {
4214  menufunc = but->menu_create_func;
4215  arg = but->poin;
4216  }
4217  else {
4218  func = but->block_create_func;
4219  arg = but->poin ? but->poin : but->func_argN;
4220  }
4221  break;
4222  case UI_BTYPE_MENU:
4223  case UI_BTYPE_POPOVER:
4225  if ((but->type == UI_BTYPE_POPOVER) || ui_but_menu_draw_as_popover(but)) {
4226  popoverfunc = but->menu_create_func;
4227  }
4228  else {
4229  menufunc = but->menu_create_func;
4230  }
4231  arg = but->poin;
4232  break;
4233  case UI_BTYPE_COLOR:
4234  ui_but_v3_get(but, data->origvec);
4235  copy_v3_v3(data->vec, data->origvec);
4236  but->editvec = data->vec;
4237 
4238  if (ui_but_menu_draw_as_popover(but)) {
4239  popoverfunc = but->menu_create_func;
4240  }
4241  else {
4242  handlefunc = ui_block_func_COLOR;
4243  }
4244  arg = but;
4245  break;
4246 
4247  /* quiet warnings for unhandled types */
4248  default:
4249  break;
4250  }
4251 
4252  if (func || handlefunc) {
4253  data->menu = ui_popup_block_create(C, data->region, but, func, handlefunc, arg, NULL);
4254  if (but->block->handle) {
4255  data->menu->popup = but->block->handle->popup;
4256  }
4257  }
4258  else if (menufunc) {
4259  data->menu = ui_popup_menu_create(C, data->region, but, menufunc, arg);
4260  if (but->block->handle) {
4261  data->menu->popup = but->block->handle->popup;
4262  }
4263  }
4264  else if (popoverfunc) {
4265  data->menu = ui_popover_panel_create(C, data->region, but, popoverfunc, arg);
4266  if (but->block->handle) {
4267  data->menu->popup = but->block->handle->popup;
4268  }
4269  }
4270 
4271 #ifdef USE_ALLSELECT
4272  {
4273  if (IS_ALLSELECT_EVENT(data->window->eventstate)) {
4274  data->select_others.is_enabled = true;
4275  }
4276  }
4277 #endif
4278 
4279  /* this makes adjacent blocks auto open from now on */
4280  // if (but->block->auto_open == 0) {
4281  // but->block->auto_open = 1;
4282  //}
4283 }
4284 
4286 {
4287  if (but) {
4288  but->editval = NULL;
4289  but->editvec = NULL;
4290 
4292  }
4293 
4294  if (data->menu) {
4295  ui_popup_block_free(C, data->menu);
4296  data->menu = NULL;
4297  }
4298 }
4299 
4301 {
4302  uiHandleButtonData *data = but->active;
4303 
4304  if (data && data->menu) {
4305  return data->menu->direction;
4306  }
4307 
4308  return 0;
4309 }
4310 
4316  uiBut *but,
4318  const wmEvent *event,
4319  uiButtonActivateType activate_type)
4320 {
4321  ARegion *region = data->region;
4322  uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, false, NULL, NULL);
4323 
4324  if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) {
4325  /* exit listrow */
4326  data->cancel = true;
4327  button_activate_exit(C, but, data, false, false);
4328 
4329  /* Activate the text button. */
4330  button_activate_init(C, region, labelbut, activate_type);
4331 
4332  return labelbut;
4333  }
4334  return NULL;
4335 }
4336 
4339 /* -------------------------------------------------------------------- */
4344  ARegion *region,
4345  const wmEvent *event)
4346 {
4347  float xmax = but->rect.xmax;
4348  const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */
4349  int x = event->xy[0], y = event->xy[1];
4350 
4351  ui_window_to_block(region, but->block, &x, &y);
4352  if (!BLI_rctf_isect_pt(&but->rect, x, y)) {
4353  return NULL;
4354  }
4355 
4356  /* Same as in 'widget_draw_extra_icons', icon padding from the right edge. */
4357  xmax -= 0.2 * icon_size;
4358 
4359  /* Handle the padding space from the right edge as the last button. */
4360  if (x > xmax) {
4361  return but->extra_op_icons.last;
4362  }
4363 
4364  /* Inverse order, from right to left. */
4366  if ((x > (xmax - icon_size)) && x <= xmax) {
4367  return op_icon;
4368  }
4369  xmax -= icon_size;
4370  }
4371 
4372  return NULL;
4373 }
4374 
4376  uiBut *but,
4378  const wmEvent *event)
4379 {
4380  uiButExtraOpIcon *op_icon = ui_but_extra_operator_icon_mouse_over_get(but, data->region, event);
4381 
4382  if (!op_icon) {
4383  return false;
4384  }
4385 
4386  /* Only act on release, avoids some glitches. */
4387  if (event->val != KM_RELEASE) {
4388  /* Still swallow events on the icon. */
4389  return true;
4390  }
4391 
4392  ED_region_tag_redraw(data->region);
4394 
4395  ui_but_extra_operator_icon_apply(C, but, op_icon);
4396  /* NOTE: 'but', 'data' may now be freed, don't access. */
4397 
4398  return true;
4399 }
4400 
4403  const wmEvent *event)
4404 {
4405  uiButExtraOpIcon *old_highlighted = NULL;
4406 
4407  /* Unset highlighting of all first. */
4408  LISTBASE_FOREACH (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) {
4409  if (op_icon->highlighted) {
4410  old_highlighted = op_icon;
4411  }
4412  op_icon->highlighted = false;
4413  }
4414 
4415  uiButExtraOpIcon *hovered = ui_but_extra_operator_icon_mouse_over_get(but, data->region, event);
4416 
4417  if (hovered) {
4418  hovered->highlighted = true;
4419  }
4420 
4421  if (old_highlighted != hovered) {
4423  }
4424 }
4425 
4426 #ifdef USE_DRAG_TOGGLE
4427 /* Shared by any button that supports drag-toggle. */
4429  bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event, int *r_retval)
4430 {
4431  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4432  if (event->type == LEFTMOUSE && event->val == KM_PRESS && ui_but_is_drag_toggle(but)) {
4433  ui_apply_but(C, but->block, but, data, true);
4435  data->dragstartx = event->xy[0];
4436  data->dragstarty = event->xy[1];
4437  *r_retval = WM_UI_HANDLER_BREAK;
4438  return true;
4439  }
4440  }
4441  else if (data->state == BUTTON_STATE_WAIT_DRAG) {
4442  /* NOTE: the 'BUTTON_STATE_WAIT_DRAG' part of 'ui_do_but_EXIT' could be refactored into
4443  * its own function */
4444  data->applied = false;
4445  *r_retval = ui_do_but_EXIT(C, but, data, event);
4446  return true;
4447  }
4448  return false;
4449 }
4450 #endif /* USE_DRAG_TOGGLE */
4451 
4452 static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
4453 {
4454 #ifdef USE_DRAG_TOGGLE
4455  {
4456  int retval;
4457  if (ui_do_but_ANY_drag_toggle(C, but, data, event, &retval)) {
4458  return retval;
4459  }
4460  }
4461 #endif
4462 
4463  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4464  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
4466  return WM_UI_HANDLER_BREAK;
4467  }
4468  if (event->type == LEFTMOUSE && event->val == KM_RELEASE && but->block->handle) {
4469  /* regular buttons will be 'UI_SELECT', menu items 'UI_ACTIVE' */
4470  if (!(but->flag & (UI_SELECT | UI_ACTIVE))) {
4471  data->cancel = true;
4472  }
4474  return WM_UI_HANDLER_BREAK;
4475  }
4476  if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
4478  return WM_UI_HANDLER_BREAK;
4479  }
4480  }
4481  else if (data->state == BUTTON_STATE_WAIT_RELEASE) {
4482  if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
4483  if (!(but->flag & UI_SELECT)) {
4484  data->cancel = true;
4485  }
4487  return WM_UI_HANDLER_BREAK;
4488  }
4489  }
4490 
4491  return WM_UI_HANDLER_CONTINUE;
4492 }
4493 
4495  uiBut *but,
4497  const wmEvent *event)
4498 {
4499  uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but;
4501 
4502  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4504  (event->val == KM_PRESS)) {
4505  but->drawstr[0] = 0;
4506  hotkey_but->modifier_key = 0;
4508  return WM_UI_HANDLER_BREAK;
4509  }
4510  }
4511  else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
4512  if (ISMOUSE_MOTION(event->type)) {
4513  return WM_UI_HANDLER_CONTINUE;
4514  }
4515  if (event->type == EVT_UNKNOWNKEY) {
4516  WM_report(RPT_WARNING, "Unsupported key: Unknown");
4517  return WM_UI_HANDLER_CONTINUE;
4518  }
4519  if (event->type == EVT_CAPSLOCKKEY) {
4520  WM_report(RPT_WARNING, "Unsupported key: CapsLock");
4521  return WM_UI_HANDLER_CONTINUE;
4522  }
4523 
4524  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
4525  /* only cancel if click outside the button */
4526  if (ui_but_contains_point_px(but, but->active->region, event->xy) == false) {
4527  data->cancel = true;
4528  /* Close the containing popup (if any). */
4529  data->escapecancel = true;
4531  return WM_UI_HANDLER_BREAK;
4532  }
4533  }
4534 
4535  /* always set */
4536  hotkey_but->modifier_key = event->modifier;
4537 
4538  ui_but_update(but);
4539  ED_region_tag_redraw(data->region);
4540 
4541  if (event->val == KM_PRESS) {
4542  if (ISHOTKEY(event->type) && (event->type != EVT_ESCKEY)) {
4543  if (WM_key_event_string(event->type, false)[0]) {
4544  ui_but_value_set(but, event->type);
4545  }
4546  else {
4547  data->cancel = true;
4548  }
4549 
4551  return WM_UI_HANDLER_BREAK;
4552  }
4553  if (event->type == EVT_ESCKEY) {
4554  if (event->val == KM_PRESS) {
4555  data->cancel = true;
4556  data->escapecancel = true;
4558  }
4559  }
4560  }
4561  }
4562 
4563  return WM_UI_HANDLER_CONTINUE;
4564 }
4565 
4567  uiBut *but,
4569  const wmEvent *event)
4570 {
4571  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4572  if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
4574  return WM_UI_HANDLER_BREAK;
4575  }
4576  }
4577  else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
4578  if (ISMOUSE_MOTION(event->type)) {
4579  return WM_UI_HANDLER_CONTINUE;
4580  }
4581 
4582  if (event->val == KM_PRESS) {
4583  if (WM_key_event_string(event->type, false)[0]) {
4584  ui_but_value_set(but, event->type);
4585  }
4586  else {
4587  data->cancel = true;
4588  }
4589 
4591  }
4592  }
4593 
4594  return WM_UI_HANDLER_CONTINUE;
4595 }
4596 
4597 static int ui_do_but_TAB(
4598  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
4599 {
4600  const bool is_property = (but->rnaprop != NULL);
4601 
4602 #ifdef USE_DRAG_TOGGLE
4603  if (is_property) {
4604  int retval;
4605  if (ui_do_but_ANY_drag_toggle(C, but, data, event, &retval)) {
4606  return retval;
4607  }
4608  }
4609 #endif
4610 
4611  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4612  const int rna_type = but->rnaprop ? RNA_property_type(but->rnaprop) : 0;
4613 
4614  if (is_property && ELEM(rna_type, PROP_POINTER, PROP_STRING) && (but->custom_data != NULL) &&
4615  (event->type == LEFTMOUSE) &&
4616  ((event->val == KM_DBL_CLICK) || (event->modifier & KM_CTRL))) {
4618  return WM_UI_HANDLER_BREAK;
4619  }
4620  if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY)) {
4621  const int event_val = (is_property) ? KM_PRESS : KM_CLICK;
4622  if (event->val == event_val) {
4624  return WM_UI_HANDLER_BREAK;
4625  }
4626  }
4627  }
4628  else if (data->state == BUTTON_STATE_TEXT_EDITING) {
4629  ui_do_but_textedit(C, block, but, data, event);
4630  return WM_UI_HANDLER_BREAK;
4631  }
4632  else if (data->state == BUTTON_STATE_TEXT_SELECTING) {
4633  ui_do_but_textedit_select(C, block, but, data, event);
4634  return WM_UI_HANDLER_BREAK;
4635  }
4636 
4637  return WM_UI_HANDLER_CONTINUE;
4638 }
4639 
4640 static int ui_do_but_TEX(
4641  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
4642 {
4643  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4645  event->val == KM_PRESS) {
4646  if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && (!UI_but_is_utf8(but))) {
4647  /* pass - allow filesel, enter to execute */
4648  }
4650  ((event->modifier & KM_CTRL) == 0)) {
4651  /* pass */
4652  }
4653  else {
4654  if (!ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) {
4656  }
4657  return WM_UI_HANDLER_BREAK;
4658  }
4659  }
4660  }
4661  else if (data->state == BUTTON_STATE_TEXT_EDITING) {
4662  ui_do_but_textedit(C, block, but, data, event);
4663  return WM_UI_HANDLER_BREAK;
4664  }
4665  else if (data->state == BUTTON_STATE_TEXT_SELECTING) {
4666  ui_do_but_textedit_select(C, block, but, data, event);
4667  return WM_UI_HANDLER_BREAK;
4668  }
4669 
4670  return WM_UI_HANDLER_CONTINUE;
4671 }
4672 
4674  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
4675 {
4676  /* unlink icon is on right */
4678  /* doing this on KM_PRESS calls eyedropper after clicking unlink icon */
4679  if ((event->val == KM_RELEASE) && ui_do_but_extra_operator_icon(C, but, data, event)) {
4680  return WM_UI_HANDLER_BREAK;
4681  }
4682  }
4683  return ui_do_but_TEX(C, block, but, data, event);
4684 }
4685 
4686 static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
4687 {
4688 #ifdef USE_DRAG_TOGGLE
4689  {
4690  int retval;
4691  if (ui_do_but_ANY_drag_toggle(C, but, data, event, &retval)) {
4692  return retval;
4693  }
4694  }
4695 #endif
4696 
4697  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4698  bool do_activate = false;
4699  if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY)) {
4700  if (event->val == KM_PRESS) {
4701  do_activate = true;
4702  }
4703  }
4704  else if (event->type == LEFTMOUSE) {
4705  if (ui_block_is_menu(but->block)) {
4706  /* Behave like other menu items. */
4707  do_activate = (event->val == KM_RELEASE);
4708  }
4709  else if (!ui_do_but_extra_operator_icon(C, but, data, event)) {
4710  /* Also use double-clicks to prevent fast clicks to leak to other handlers (T76481). */
4711  do_activate = ELEM(event->val, KM_PRESS, KM_DBL_CLICK);
4712  }
4713  }
4714 
4715  if (do_activate) {
4717  return WM_UI_HANDLER_BREAK;
4718  }
4719  if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && (event->modifier & KM_CTRL)) {
4720  /* Support Ctrl-Wheel to cycle values on expanded enum rows. */
4721  if (but->type == UI_BTYPE_ROW) {
4722  int type = event->type;
4723  int val = event->val;
4724 
4725  /* Convert pan to scroll-wheel. */
4726  if (type == MOUSEPAN) {
4727  ui_pan_to_scroll(event, &type, &val);
4728 
4729  if (type == MOUSEPAN) {
4730  return WM_UI_HANDLER_BREAK;
4731  }
4732  }
4733 
4734  const int direction = (type == WHEELDOWNMOUSE) ? -1 : 1;
4735  uiBut *but_select = ui_but_find_select_in_enum(but, direction);
4736  if (but_select) {
4737  uiBut *but_other = (direction == -1) ? but_select->next : but_select->prev;
4738  if (but_other && ui_but_find_select_in_enum__cmp(but, but_other)) {
4739  ARegion *region = data->region;
4740 
4741  data->cancel = true;
4742  button_activate_exit(C, but, data, false, false);
4743 
4744  /* Activate the text button. */
4745  button_activate_init(C, region, but_other, BUTTON_ACTIVATE_OVER);
4746  data = but_other->active;
4747  if (data) {
4748  ui_apply_but(C, but->block, but_other, but_other->active, true);
4749  button_activate_exit(C, but_other, data, false, false);
4750 
4751  /* restore active button */
4753  }
4754  else {
4755  /* shouldn't happen */
4756  BLI_assert(0);
4757  }
4758  }
4759  }
4760  return WM_UI_HANDLER_BREAK;
4761  }
4762  }
4763  }
4764  return WM_UI_HANDLER_CONTINUE;
4765 }
4766 
4768  uiBut *but,
4770  const wmEvent *event)
4771 {
4772  uiButViewItem *view_item_but = (uiButViewItem *)but;
4773  BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM);
4774 
4775  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4776  if (event->type == LEFTMOUSE) {
4777  switch (event->val) {
4778  case KM_PRESS:
4779  /* Extra icons have priority, don't mess with them. */
4780  if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) {
4781  return WM_UI_HANDLER_BREAK;
4782  }
4784  data->dragstartx = event->xy[0];
4785  data->dragstarty = event->xy[1];
4786  return WM_UI_HANDLER_CONTINUE;
4787 
4788  case KM_CLICK:
4790  return WM_UI_HANDLER_BREAK;
4791 
4792  case KM_DBL_CLICK:
4793  data->cancel = true;
4794  UI_view_item_begin_rename(view_item_but->view_item);
4796  return WM_UI_HANDLER_BREAK;
4797  }
4798  }
4799  }
4800  else if (data->state == BUTTON_STATE_WAIT_DRAG) {
4801  /* Let "default" button handling take care of the drag logic. */
4802  return ui_do_but_EXIT(C, but, data, event);
4803  }
4804 
4805  return WM_UI_HANDLER_CONTINUE;
4806 }
4807 
4808 static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
4809 {
4810  if (data->state == BUTTON_STATE_HIGHLIGHT) {
4811 
4812  /* First handle click on icon-drag type button. */
4813  if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && ui_but_drag_is_draggable(but)) {
4814  if (ui_but_contains_point_px_icon(but, data->region, event)) {
4815 
4816  /* tell the button to wait and keep checking further events to
4817  * see if it should start dragging */
4819  data->dragstartx = event->xy[0];
4820  data->dragstarty = event->xy[1];
4821  return WM_UI_HANDLER_CONTINUE;
4822  }
4823  }
4824 #ifdef USE_DRAG_TOGGLE
4825  if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && ui_but_is_drag_toggle(but)) {
4827  data->dragstartx = event->xy[0];
4828  data->dragstarty = event->xy[1];
4829  return WM_UI_HANDLER_CONTINUE;
4830  }
4831 #endif
4832 
4833  if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
4834  int ret = WM_UI_HANDLER_BREAK;
4835  /* XXX: (a bit ugly) Special case handling for file-browser drag button. */
4836  if (ui_but_drag_is_draggable(but) && but->imb &&
4837  ui_but_contains_point_px_icon(but, data->region, event)) {
4839  }
4840  /* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event
4841  * will be sent for the list to work with. */
4842  const uiBut *listbox = ui_list_find_mouse_over(data->region, event);
4843  if (listbox) {
4844  const uiList *ui_list = listbox->custom_data;
4845  if (ui_list && ui_list->dyn_data->custom_drag_optype) {
4847  }
4848  }
4849  const uiBut *view_but = ui_view_item_find_mouse_over(data->region, event->xy);
4850  if (view_but) {
4852  }
4854  return ret;
4855  }
4856  }
4857  else if (data->state == BUTTON_STATE_WAIT_DRAG) {
4858 
4859  /* this function also ends state */
4860  if (ui_but_drag_init(C, but, data, event)) {
4861  return WM_UI_HANDLER_BREAK;
4862  }
4863 
4864  /* If the mouse has been pressed and released, getting to
4865  * this point without triggering a drag, then clear the
4866  * drag state for this button and continue to pass on the event */
4867  if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
4869  return WM_UI_HANDLER_CONTINUE;
4870  }
4871 
4872  /* while waiting for a drag to be triggered, always block
4873  * other events from getting handled */
4874  return WM_UI_HANDLER_BREAK;
4875  }
4876 
4877  return WM_UI_HANDLER_CONTINUE;
4878 }
4879 
4880 /* var names match ui_numedit_but_NUM */
4882  uiBut *but, float tempf, float softmin, float softmax, const enum eSnapType snap)
4883 {
4884  if (tempf == softmin || tempf == softmax || snap == SNAP_OFF) {
4885  /* pass */
4886  }
4887  else {
4888  const PropertyScaleType scale_type = ui_but_scale_type(but);
4889  float softrange = softmax - softmin;
4890  float fac = 1.0f;
4891 
4892  if (ui_but_is_unit(but)) {
4893  UnitSettings *unit = but->block->unit;
4894  const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
4895 
4896  if (BKE_unit_is_valid(unit->system, unit_type)) {
4897  fac = (float)BKE_unit_base_scalar(unit->system, unit_type);
4898  if (ELEM(unit_type, B_UNIT_LENGTH, B_UNIT_AREA, B_UNIT_VOLUME)) {
4899  fac /= unit->scale_length;
4900  }
4901  }
4902  }
4903 
4904  if (fac != 1.0f) {
4905  /* snap in unit-space */
4906  tempf /= fac;
4907  /* softmin /= fac; */ /* UNUSED */
4908  /* softmax /= fac; */ /* UNUSED */
4909  softrange /= fac;
4910  }
4911 
4912  /* workaround, too high snapping values */
4913  /* snapping by 10's for float buttons is quite annoying (location, scale...),
4914  * but allow for rotations */
4915  if (softrange >= 21.0f) {
4916  UnitSettings *unit = but->block->unit;
4917  const int unit_type = UI_but_unit_type_get(but);
4918  if ((unit_type == PROP_UNIT_ROTATION) && (unit->system_rotation != USER_UNIT_ROT_RADIANS)) {
4919  /* Pass (degrees). */
4920  }
4921  else {
4922  softrange = 20.0f;
4923  }
4924  }
4925 
4927  switch (scale_type) {
4928  case PROP_SCALE_LINEAR:
4929  case PROP_SCALE_CUBIC: {
4930  const float snap_fac = (snap == SNAP_ON_SMALL ? 0.1f : 1.0f);
4931  if (softrange < 2.10f) {
4932  tempf = roundf(tempf * 10.0f / snap_fac) * 0.1f * snap_fac;
4933  }
4934  else if (softrange < 21.0f) {
4935  tempf = roundf(tempf / snap_fac) * snap_fac;
4936  }
4937  else {
4938  tempf = roundf(tempf * 0.1f / snap_fac) * 10.0f * snap_fac;
4939  }
4940  break;
4941  }
4942  case PROP_SCALE_LOG: {
4943  const float snap_fac = powf(10.0f,
4944  roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) -
4945  (snap == SNAP_ON_SMALL ? 2.0f : 1.0f));
4946  tempf = roundf(tempf / snap_fac) * snap_fac;
4947  break;
4948  }
4949  }
4950 
4951  if (fac != 1.0f) {
4952  tempf *= fac;
4953  }
4954  }
4955 
4956  return tempf;
4957 }
4958 
4959 static float ui_numedit_apply_snap(int temp,
4960  float softmin,
4961  float softmax,
4962  const enum eSnapType snap)
4963 {
4964  if (ELEM(temp, softmin, softmax)) {
4965  return temp;
4966  }
4967 
4968  switch (snap) {
4969  case SNAP_OFF:
4970  break;
4971  case SNAP_ON:
4972  temp = 10 * (temp / 10);
4973  break;
4974  case SNAP_ON_SMALL:
4975  temp = 100 * (temp / 100);
4976  break;
4977  }
4978 
4979  return temp;
4980 }
4981 
4982 static bool ui_numedit_but_NUM(uiButNumber *number_but,
4984  int mx,
4985  const bool is_motion,
4986  const enum eSnapType snap,
4987  float fac)
4988 {
4989  uiBut *but = &number_but->but;
4990  float deler, tempf;
4991  int lvalue, temp;
4992  bool changed = false;
4993  const bool is_float = ui_but_is_float(but);
4994  const PropertyScaleType scale_type = ui_but_scale_type(but);
4995 
4996  /* prevent unwanted drag adjustments, test motion so modifier keys refresh. */
4997  if ((is_motion || data->draglock) && (ui_but_dragedit_update_mval(data, mx) == false)) {
4998  return changed;
4999  }
5000 
5002 
5003  if (ui_but_is_cursor_warp(but)) {
5004  const float softmin = but->softmin;
5005  const float softmax = but->softmax;
5006  const float softrange = softmax - softmin;
5007 
5008  const float log_min = (scale_type == PROP_SCALE_LOG) ?
5010  powf(10, -number_but->precision) * 0.5f) :
5011  0;
5012 
5013  /* Mouse location isn't screen clamped to the screen so use a linear mapping
5014  * 2px == 1-int, or 1px == 1-ClickStep */
5015  if (is_float) {
5016  fac *= 0.01f * number_but->step_size;
5017  switch (scale_type) {
5018  case PROP_SCALE_LINEAR: {
5019  tempf = (float)data->startvalue + (float)(mx - data->dragstartx) * fac;
5020  break;
5021  }
5022  case PROP_SCALE_LOG: {
5023  const float startvalue = max_ff((float)data->startvalue, log_min);
5024  tempf = expf((float)(mx - data->dragstartx) * fac) * startvalue;
5025  if (tempf <= log_min) {
5026  tempf = 0.0f;
5027  }
5028  break;
5029  }
5030  case PROP_SCALE_CUBIC: {
5031  tempf = cbrtf((float)data->startvalue) + (float)(mx - data->dragstartx) * fac;
5032  tempf *= tempf * tempf;
5033  break;
5034  }
5035  }
5036 
5037  tempf = ui_numedit_apply_snapf(but, tempf, softmin, softmax, snap);
5038 
5039 #if 1 /* fake moving the click start, nicer for dragging back after passing the limit */
5040  switch (scale_type) {
5041  case PROP_SCALE_LINEAR: {
5042  if (tempf < softmin) {
5043  data->dragstartx -= (softmin - tempf) / fac;
5044  tempf = softmin;
5045  }
5046  else if (tempf > softmax) {
5047  data->dragstartx -= (softmax - tempf) / fac;
5048  tempf = softmax;
5049  }
5050  break;
5051  }
5052  case PROP_SCALE_LOG: {
5053  if (tempf < log_min) {
5054  data->dragstartx -= logf(log_min / (float)data->startvalue) / fac -
5055  (float)(mx - data->dragstartx);
5056  tempf = softmin;
5057  }
5058  else if (tempf > softmax) {
5059  data->dragstartx -= logf(softmax / (float)data->startvalue) / fac -
5060  (float)(mx - data->dragstartx);
5061  tempf = softmax;
5062  }
5063  break;
5064  }
5065  case PROP_SCALE_CUBIC: {
5066  if (tempf < softmin) {
5067  data->dragstartx = mx - (int)((cbrtf(softmin) - cbrtf((float)data->startvalue)) / fac);
5068  tempf = softmin;
5069  }
5070  else if (tempf > softmax) {
5071  data->dragstartx = mx - (int)((cbrtf(softmax) - cbrtf((float)data->startvalue)) / fac);
5072  tempf = softmax;
5073  }
5074  break;
5075  }
5076  }
5077 #else
5078  CLAMP(tempf, softmin, softmax);
5079 #endif
5080 
5081  if (tempf != (float)data->value) {
5082  data->dragchange = true;
5083  data->value = tempf;
5084  changed = true;
5085  }
5086  }
5087  else {
5088  if (softrange > 256) {
5089  fac = 1.0;
5090  } /* 1px == 1 */
5091  else if (softrange > 32) {
5092  fac = 1.0 / 2.0;
5093  } /* 2px == 1 */
5094  else {
5095  fac = 1.0 / 16.0;
5096  } /* 16px == 1? */
5097 
5098  temp = data->startvalue + (((double)mx - data->dragstartx) * (double)fac);
5099  temp = ui_numedit_apply_snap(temp, softmin, softmax, snap);
5100 
5101 #if 1 /* fake moving the click start, nicer for dragging back after passing the limit */
5102  if (temp < softmin) {
5103  data->dragstartx -= (softmin - temp) / fac;
5104  temp = softmin;
5105  }
5106  else if (temp > softmax) {
5107  data->dragstartx += (temp - softmax) / fac;
5108  temp = softmax;
5109  }
5110 #else
5111  CLAMP(temp, softmin, softmax);
5112 #endif
5113 
5114  if (temp != data->value) {
5115  data->dragchange = true;
5116  data->value = temp;
5117  changed = true;
5118  }
5119  }
5120 
5121  data->draglastx = mx;
5122  }
5123  else {
5124  /* Use 'but->softmin', 'but->softmax' when clamping values. */
5125  const float softmin = data->drag_map_soft_min;
5126  const float softmax = data->drag_map_soft_max;
5127  const float softrange = softmax - softmin;
5128 
5129  float non_linear_range_limit;
5130  float non_linear_pixel_map;
5131  float non_linear_scale;
5132 
5133  /* Use a non-linear mapping of the mouse drag especially for large floats
5134  * (normal behavior) */
5135  deler = 500;
5136  if (is_float) {
5137  /* not needed for smaller float buttons */
5138  non_linear_range_limit = 11.0f;
5139  non_linear_pixel_map = 500.0f;
5140  }
5141  else {
5142  /* only scale large int buttons */
5143  non_linear_range_limit = 129.0f;
5144  /* Larger for ints, we don't need to fine tune them. */
5145  non_linear_pixel_map = 250.0f;
5146 
5147  /* prevent large ranges from getting too out of control */
5148  if (softrange > 600) {
5149  deler = powf(softrange, 0.75f);
5150  }
5151  else if (softrange < 25) {
5152  deler = 50.0;
5153  }
5154  else if (softrange < 100) {
5155  deler = 100.0;
5156  }
5157  }
5158  deler /= fac;
5159 
5160  if (softrange > non_linear_range_limit) {
5161  non_linear_scale = (float)abs(mx - data->dragstartx) / non_linear_pixel_map;
5162  }
5163  else {
5164  non_linear_scale = 1.0f;
5165  }
5166 
5167  if (is_float == false) {
5168  /* at minimum, moving cursor 2 pixels should change an int button. */
5169  CLAMP_MIN(non_linear_scale, 0.5f * UI_DPI_FAC);
5170  }
5171 
5172  data->dragf += (((float)(mx - data->draglastx)) / deler) * non_linear_scale;
5173 
5174  if (but->softmin == softmin) {
5175  CLAMP_MIN(data->dragf, 0.0f);
5176  }
5177  if (but->softmax == softmax) {
5178  CLAMP_MAX(data->dragf, 1.0f);
5179  }
5180 
5181  data->draglastx = mx;
5182 
5183  switch (scale_type) {
5184  case PROP_SCALE_LINEAR: {
5185  tempf = (softmin + data->dragf * softrange);
5186  break;
5187  }
5188  case PROP_SCALE_LOG: {
5189  const float log_min = max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN),
5190  powf(10.0f, -number_but->precision) * 0.5f);
5191  const float base = softmax / log_min;
5192  tempf = powf(base, data->dragf) * log_min;
5193  if (tempf <= log_min) {
5194  tempf = 0.0f;
5195  }
5196  break;
5197  }
5198  case PROP_SCALE_CUBIC: {
5199  tempf = (softmin + data->dragf * softrange);
5200  tempf *= tempf * tempf;
5201  float cubic_min = softmin * softmin * softmin;
5202  float cubic_max = softmax * softmax * softmax;
5203  tempf = (tempf - cubic_min) / (cubic_max - cubic_min) * softrange + softmin;
5204  break;
5205  }
5206  }
5207 
5208  if (!is_float) {
5209  temp = round_fl_to_int(tempf);
5210 
5211  temp = ui_numedit_apply_snap(temp, but->softmin, but->softmax, snap);
5212 
5213  CLAMP(temp, but->softmin, but->softmax);
5214  lvalue = (int)data->value;
5215 
5216  if (temp != lvalue) {
5217  data->dragchange = true;
5218  data->value = (double)temp;
5219  changed = true;
5220  }
5221  }
5222  else {
5223  temp = 0;
5224  tempf = ui_numedit_apply_snapf(but, tempf, but->softmin, but->softmax, snap);
5225 
5226  CLAMP(tempf, but->softmin, but->softmax);
5227 
5228  if (tempf != (float)data->value) {
5229  data->dragchange = true;
5230  data->value = tempf;
5231  changed = true;
5232  }
5233  }
5234  }
5235 
5236  return changed;
5237 }
5238 
5239 static void ui_numedit_set_active(uiBut *but)
5240 {
5241  const int oldflag = but->drawflag;
5243 
5244  uiHandleButtonData *data = but->active;
5245  if (!data) {
5246  return;
5247  }
5248 
5249  /* Ignore once we start dragging. */
5250  if (data->dragchange == false) {
5251  const float handle_width = min_ff(BLI_rctf_size_x(&but->rect) / 3,
5252  BLI_rctf_size_y(&but->rect) * 0.7f);
5253  /* we can click on the side arrows to increment/decrement,
5254  * or click inside to edit the value directly */
5255  int mx = data->window->eventstate->xy[0];
5256  int my = data->window->eventstate->xy[1];
5257  ui_window_to_block(data->region, but->block, &mx, &my);
5258 
5259  if (mx < (but->rect.xmin + handle_width)) {
5260  but->drawflag |= UI_BUT_ACTIVE_LEFT;
5261  }
5262  else if (mx > (but->rect.xmax - handle_width)) {
5263  but->drawflag |= UI_BUT_ACTIVE_RIGHT;
5264  }
5265  }
5266 
5267  /* Don't change the cursor once pressed. */
5268  if ((but->flag & UI_SELECT) == 0) {
5269  if ((but->drawflag & UI_BUT_ACTIVE_LEFT) || (but->drawflag & UI_BUT_ACTIVE_RIGHT)) {
5270  if (data->changed_cursor) {
5271  WM_cursor_modal_restore(data->window);
5272  data->changed_cursor = false;
5273  }
5274  }
5275  else {
5276  if (data->changed_cursor == false) {
5278  data->changed_cursor = true;
5279  }
5280  }
5281  }
5282 
5283  if (but->drawflag != oldflag) {
5284  ED_region_tag_redraw(data->region);
5285  }
5286 }
5287 
5288 static int ui_do_but_NUM(
5289  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
5290 {
5291  uiButNumber *number_but = (uiButNumber *)but;
5292  int click = 0;
5293  int retval = WM_UI_HANDLER_CONTINUE;
5294 
5295  /* mouse location scaled to fit the UI */
5296  int mx = event->xy[0];
5297  int my = event->xy[1];
5298  /* mouse location kept at screen pixel coords */
5299  const int screen_mx = event->xy[0];
5300 
5301  BLI_assert(but->type == UI_BTYPE_NUM);
5302 
5303  ui_window_to_block(data->region, block, &mx, &my);
5304  ui_numedit_set_active(but);
5305 
5306  if (data->state == BUTTON_STATE_HIGHLIGHT) {
5307  int type = event->type, val = event->val;
5308 
5309  if (type == MOUSEPAN) {
5310  ui_pan_to_scroll(event, &type, &val);
5311  }
5312 
5313  /* XXX hardcoded keymap check.... */
5314  if (type == MOUSEPAN && (event->modifier & KM_CTRL)) {
5315  /* allow accumulating values, otherwise scrolling gets preference */
5316  retval = WM_UI_HANDLER_BREAK;
5317  }
5318  else if (type == WHEELDOWNMOUSE && (event->modifier & KM_CTRL)) {
5319  mx = but->rect.xmin;
5320  but->drawflag &= ~UI_BUT_ACTIVE_RIGHT;
5321  but->drawflag |= UI_BUT_ACTIVE_LEFT;
5322  click = 1;
5323  }
5324  else if ((type == WHEELUPMOUSE) && (event->modifier & KM_CTRL)) {
5325  mx = but->rect.xmax;
5326  but->drawflag &= ~UI_BUT_ACTIVE_LEFT;
5327  but->drawflag |= UI_BUT_ACTIVE_RIGHT;
5328  click = 1;
5329  }
5330  else if (event->val == KM_PRESS) {
5331  if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->modifier & KM_CTRL)) {
5333  retval = WM_UI_HANDLER_BREAK;
5334  }
5335  else if (event->type == LEFTMOUSE) {
5336  data->dragstartx = data->draglastx = ui_but_is_cursor_warp(but) ? screen_mx : mx;
5338  retval = WM_UI_HANDLER_BREAK;
5339  }
5340  else if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
5341  click = 1;
5342  }
5343  else if (event->type == EVT_MINUSKEY && event->val == KM_PRESS) {
5345  data->value = -data->value;
5347  retval = WM_UI_HANDLER_BREAK;
5348  }
5349 
5350 #ifdef USE_DRAG_MULTINUM
5351  copy_v2_v2_int(data->multi_data.drag_start, event->xy);
5352 #endif
5353  }
5354  }
5355  else if (data->state == BUTTON_STATE_NUM_EDITING) {
5356  if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
5357  if (event->val == KM_PRESS) {
5358  data->cancel = true;
5359  data->escapecancel = true;
5361  }
5362  }
5363  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
5364  if (data->dragchange) {
5365 #ifdef USE_DRAG_MULTINUM
5366  /* If we started multi-button but didn't drag, then edit. */
5367  if (data->multi_data.init == BUTTON_MULTI_INIT_SETUP) {
5368  click = 1;
5369  }
5370  else
5371 #endif
5372  {
5374  }
5375  }
5376  else {
5377  click = 1;
5378  }
5379  }
5380  else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) {
5381  const bool is_motion = (event->type == MOUSEMOVE);
5382  const enum eSnapType snap = ui_event_to_snap(event);
5383  float fac;
5384 
5385 #ifdef USE_DRAG_MULTINUM
5386  data->multi_data.drag_dir[0] += abs(data->draglastx - mx);
5387  data->multi_data.drag_dir[1] += abs(data->draglasty - my);
5388 #endif
5389 
5390  fac = 1.0f;
5391  if (event->modifier & KM_SHIFT) {
5392  fac /= 10.0f;
5393  }
5394 
5395  if (ui_numedit_but_NUM(number_but,
5396  data,
5397  (ui_but_is_cursor_warp(but) ? screen_mx : mx),
5398  is_motion,
5399  snap,
5400  fac)) {
5401  ui_numedit_apply(C, block, but, data);
5402  }
5403 #ifdef USE_DRAG_MULTINUM
5404  else if (data->multi_data.has_mbuts) {
5405  if (data->multi_data.init == BUTTON_MULTI_INIT_ENABLE) {
5406  ui_multibut_states_apply(C, data, block);
5407  }
5408  }
5409 #endif
5410  }
5411  retval = WM_UI_HANDLER_BREAK;
5412  }
5413  else if (data->state == BUTTON_STATE_TEXT_EDITING) {
5414  ui_do_but_textedit(C, block, but, data, event);
5415  retval = WM_UI_HANDLER_BREAK;
5416  }
5417  else if (data->state == BUTTON_STATE_TEXT_SELECTING) {
5418  ui_do_but_textedit_select(C, block, but, data, event);
5419  retval = WM_UI_HANDLER_BREAK;
5420  }
5421 
5422  if (click) {
5423  /* we can click on the side arrows to increment/decrement,
5424  * or click inside to edit the value directly */
5425 
5426  if (!ui_but_is_float(but)) {
5427  /* Integer Value. */
5430 
5431  const int value_step = (int)number_but->step_size;
5432  BLI_assert(value_step > 0);
5433  const int softmin = round_fl_to_int_clamp(but->softmin);
5434  const int softmax = round_fl_to_int_clamp(but->softmax);
5435  const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ?
5436  (double)max_ii(softmin, (int)data->value - value_step) :
5437  (double)min_ii(softmax, (int)data->value + value_step);
5438  if (value_test != data->value) {
5439  data->value = (double)value_test;
5440  }
5441  else {
5442  data->cancel = true;
5443  }
5445  }
5446  else {
5448  }
5449  }
5450  else {
5451  /* Float Value. */
5453  const PropertyScaleType scale_type = ui_but_scale_type(but);
5454 
5456 
5457  double value_step;
5458  if (scale_type == PROP_SCALE_LOG) {
5459  value_step = powf(10.0f,
5460  (roundf(log10f(data->value) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f) +
5461  log10f(number_but->step_size));
5462  }
5463  else {
5464  value_step = (double)(number_but->step_size * UI_PRECISION_FLOAT_SCALE);
5465  }
5466  BLI_assert(value_step > 0.0f);
5467  const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ?
5468  (double)max_ff(but->softmin,
5469  (float)(data->value - value_step)) :
5470  (double)min_ff(but->softmax,
5471  (float)(data->value + value_step));
5472  if (value_test != data->value) {
5473  data->value = value_test;
5474  }
5475  else {
5476  data->cancel = true;
5477  }
5479  }
5480  else {
5482  }
5483  }
5484 
5485  retval = WM_UI_HANDLER_BREAK;
5486  }
5487 
5488  data->draglastx = mx;
5489  data->draglasty = my;
5490 
5491  return retval;
5492 }
5493 
5494 static bool ui_numedit_but_SLI(uiBut *but,
5496  int mx,
5497  const bool is_horizontal,
5498  const bool is_motion,
5499  const bool snap,
5500  const bool shift)
5501 {
5502  float cursor_x_range, f, tempf, softmin, softmax, softrange;
5503  int temp, lvalue;
5504  bool changed = false;
5505  float mx_fl, my_fl;
5506 
5507  /* prevent unwanted drag adjustments, test motion so modifier keys refresh. */
5508  if ((but->type != UI_BTYPE_SCROLL) && (is_motion || data->draglock) &&
5509  (ui_but_dragedit_update_mval(data, mx) == false)) {
5510  return changed;
5511  }
5512 
5514 
5515  const PropertyScaleType scale_type = ui_but_scale_type(but);
5516 
5517  softmin = but->softmin;
5518  softmax = but->softmax;
5519  softrange = softmax - softmin;
5520 
5521  /* yes, 'mx' as both x/y is intentional */
5522  ui_mouse_scale_warp(data, mx, mx, &mx_fl, &my_fl, shift);
5523 
5524  if (but->type == UI_BTYPE_NUM_SLIDER) {
5525  cursor_x_range = BLI_rctf_size_x(&but->rect);
5526  }
5527  else if (but->type == UI_BTYPE_SCROLL) {
5528  const float size = (is_horizontal) ? BLI_rctf_size_x(&but->rect) :
5529  -BLI_rctf_size_y(&but->rect);
5530  cursor_x_range = size * (but->softmax - but->softmin) /
5531  (but->softmax - but->softmin + but->a1);
5532  }
5533  else {
5534  const float ofs = (BLI_rctf_size_y(&but->rect) / 2.0f);
5535  cursor_x_range = (BLI_rctf_size_x(&but->rect) - ofs);
5536  }
5537 
5538  f = (mx_fl - data->dragstartx) / cursor_x_range + data->dragfstart;
5539  CLAMP(f, 0.0f, 1.0f);
5540 
5541  /* deal with mouse correction */
5542 #ifdef USE_CONT_MOUSE_CORRECT
5543  if (ui_but_is_cursor_warp(but)) {
5544  /* OK but can go outside bounds */
5545  if (is_horizontal) {
5546  data->ungrab_mval[0] = but->rect.xmin + (f * cursor_x_range);
5547  data->ungrab_mval[1] = BLI_rctf_cent_y(&but->rect);
5548  }
5549  else {
5550  data->ungrab_mval[1] = but->rect.ymin + (f * cursor_x_range);
5551  data->ungrab_mval[0] = BLI_rctf_cent_x(&but->rect);
5552  }
5553  BLI_rctf_clamp_pt_v(&but->rect, data->ungrab_mval);
5554  }
5555 #endif
5556  /* done correcting mouse */
5557 
5558  switch (scale_type) {
5559  case PROP_SCALE_LINEAR: {
5560  tempf = softmin + f * softrange;
5561  break;
5562  }
5563  case PROP_SCALE_LOG: {
5564  tempf = powf(softmax / softmin, f) * softmin;
5565  break;
5566  }
5567  case PROP_SCALE_CUBIC: {
5568  const float cubicmin = cube_f(softmin);
5569  const float cubicmax = cube_f(softmax);
5570  const float cubicrange = cubicmax - cubicmin;
5571  tempf = cube_f(softmin + f * softrange);
5572  tempf = (tempf - cubicmin) / cubicrange * softrange + softmin;
5573  break;
5574  }
5575  }
5576  temp = round_fl_to_int(tempf);
5577 
5578  if (snap) {
5579  if (ELEM(tempf, softmin, softmax)) {
5580  /* pass */
5581  }
5582  else if (ui_but_is_float(but)) {
5583 
5584  if (shift) {
5585  if (ELEM(tempf, softmin, softmax)) {
5586  }
5587  else if (softrange < 2.10f) {
5588  tempf = roundf(tempf * 100.0f) * 0.01f;
5589  }
5590  else if (softrange < 21.0f) {
5591  tempf = roundf(tempf * 10.0f) * 0.1f;
5592  }
5593  else {
5594  tempf = roundf(tempf);
5595  }
5596  }
5597  else {
5598  if (softrange < 2.10f) {
5599  tempf = roundf(tempf * 10.0f) * 0.1f;
5600  }
5601  else if (softrange < 21.0f) {
5602  tempf = roundf(tempf);
5603  }
5604  else {
5605  tempf = roundf(tempf * 0.1f) * 10.0f;
5606  }
5607  }
5608  }
5609  else {
5610  temp = 10 * (temp / 10);
5611  tempf = temp;
5612  }
5613  }
5614 
5615  if (!ui_but_is_float(but)) {
5616  lvalue = round(data->value);
5617 
5618  CLAMP(temp, softmin, softmax);
5619 
5620  if (temp != lvalue) {
5621  data->value = temp;
5622  data->dragchange = true;
5623  changed = true;
5624  }
5625  }
5626  else {
5627  CLAMP(tempf, softmin, softmax);
5628 
5629  if (tempf != (float)data->value) {
5630  data->value = tempf;
5631  data->dragchange = true;
5632  changed = true;
5633  }
5634  }
5635 
5636  return changed;
5637 }
5638 
5639 static int ui_do_but_SLI(
5640  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
5641 {
5642  int click = 0;
5643  int retval = WM_UI_HANDLER_CONTINUE;
5644 
5645  int mx = event->xy[0];
5646  int my = event->xy[1];
5647  ui_window_to_block(data->region, block, &mx, &my);
5648 
5649  if (data->state == BUTTON_STATE_HIGHLIGHT) {
5650  int type = event->type, val = event->val;
5651 
5652  if (type == MOUSEPAN) {
5653  ui_pan_to_scroll(event, &type, &val);
5654  }
5655 
5656  /* XXX hardcoded keymap check.... */
5657  if ((type == MOUSEPAN) && (event->modifier & KM_CTRL)) {
5658  /* allow accumulating values, otherwise scrolling gets preference */
5659  retval = WM_UI_HANDLER_BREAK;
5660  }
5661  else if ((type == WHEELDOWNMOUSE) && (event->modifier & KM_CTRL)) {
5662  mx = but->rect.xmin;
5663  click = 2;
5664  }
5665  else if ((type == WHEELUPMOUSE) && (event->modifier & KM_CTRL)) {
5666  mx = but->rect.xmax;
5667  click = 2;
5668  }
5669  else if (event->val == KM_PRESS) {
5670  if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->modifier & KM_CTRL)) {
5672  retval = WM_UI_HANDLER_BREAK;
5673  }
5674 #ifndef USE_ALLSELECT
5675  /* alt-click on sides to get "arrows" like in UI_BTYPE_NUM buttons,
5676  * and match wheel usage above */
5677  else if ((event->type == LEFTMOUSE) && (event->modifier & KM_ALT)) {
5678  int halfpos = BLI_rctf_cent_x(&but->rect);
5679  click = 2;
5680  if (mx < halfpos) {
5681  mx = but->rect.xmin;
5682  }
5683  else {
5684  mx = but->rect.xmax;
5685  }
5686  }
5687 #endif
5688  else if (event->type == LEFTMOUSE) {
5689  data->dragstartx = mx;
5690  data->draglastx = mx;
5692  retval = WM_UI_HANDLER_BREAK;
5693  }
5694  else if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
5695  click = 1;
5696  }
5697  else if (event->type == EVT_MINUSKEY && event->val == KM_PRESS) {
5699  data->value = -data->value;
5701  retval = WM_UI_HANDLER_BREAK;
5702  }
5703  }
5704 #ifdef USE_DRAG_MULTINUM
5705  copy_v2_v2_int(data->multi_data.drag_start, event->xy);
5706 #endif
5707  }
5708  else if (data->state == BUTTON_STATE_NUM_EDITING) {
5709  if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
5710  if (event->val == KM_PRESS) {
5711  data->cancel = true;
5712  data->escapecancel = true;
5714  }
5715  }
5716  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
5717  if (data->dragchange) {
5718 #ifdef USE_DRAG_MULTINUM
5719  /* If we started multi-button but didn't drag, then edit. */
5720  if (data->multi_data.init == BUTTON_MULTI_INIT_SETUP) {
5721  click = 1;
5722  }
5723  else
5724 #endif
5725  {
5727  }
5728  }
5729  else {
5730 #ifdef USE_CONT_MOUSE_CORRECT
5731  /* reset! */
5732  copy_v2_fl(data->ungrab_mval, FLT_MAX);
5733 #endif
5734  click = 1;
5735  }
5736  }
5737  else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) {
5738  const bool is_motion = (event->type == MOUSEMOVE);
5739 #ifdef USE_DRAG_MULTINUM
5740  data->multi_data.drag_dir[0] += abs(data->draglastx - mx);
5741  data->multi_data.drag_dir[1] += abs(data->draglasty - my);
5742 #endif
5743  if (ui_numedit_but_SLI(but,
5744  data,
5745  mx,
5746  true,
5747  is_motion,
5748  event->modifier & KM_CTRL,
5749  event->modifier & KM_SHIFT)) {
5750  ui_numedit_apply(C, block, but, data);
5751  }
5752 
5753 #ifdef USE_DRAG_MULTINUM
5754  else if (data->multi_data.has_mbuts) {
5755  if (data->multi_data.init == BUTTON_MULTI_INIT_ENABLE) {
5756  ui_multibut_states_apply(C, data, block);
5757  }
5758  }
5759 #endif
5760  }
5761  retval = WM_UI_HANDLER_BREAK;
5762  }
5763  else if (data->state == BUTTON_STATE_TEXT_EDITING) {
5764  ui_do_but_textedit(C, block, but, data, event);
5765  retval = WM_UI_HANDLER_BREAK;
5766  }
5767  else if (data->state == BUTTON_STATE_TEXT_SELECTING) {
5768  ui_do_but_textedit_select(C, block, but, data, event);
5769  retval = WM_UI_HANDLER_BREAK;
5770  }
5771 
5772  if (click) {
5773  if (click == 2) {
5774  const PropertyScaleType scale_type = ui_but_scale_type(but);
5775 
5776  /* nudge slider to the left or right */
5777  float f, tempf, softmin, softmax, softrange;
5778  int temp;
5779 
5781 
5782  softmin = but->softmin;
5783  softmax = but->softmax;
5784  softrange = softmax - softmin;
5785 
5786  tempf = data->value;
5787  temp = (int)data->value;
5788 
5789 #if 0
5790  if (but->type == SLI) {
5791  /* same as below */
5792  f = (float)(mx - but->rect.xmin) / (BLI_rctf_size_x(&but->rect));
5793  }
5794  else
5795 #endif
5796  {
5797  f = (float)(mx - but->rect.xmin) / (BLI_rctf_size_x(&but->rect));
5798  }
5799 
5800  if (scale_type == PROP_SCALE_LOG) {
5801  f = powf(softmax / softmin, f) * softmin;
5802  }
5803  else {
5804  f = softmin + f * softrange;
5805  }
5806 
5807  if (!ui_but_is_float(but)) {
5808  int value_step = 1;
5809  if (f < temp) {
5810  temp -= value_step;
5811  }
5812  else {
5813  temp += value_step;
5814  }
5815 
5816  if (temp >= softmin && temp <= softmax) {
5817  data->value = temp;
5818  }
5819  else {
5820  data->cancel = true;
5821  }
5822  }
5823  else {
5824  if (tempf >= softmin && tempf <= softmax) {
5825  float value_step;
5826  if (scale_type == PROP_SCALE_LOG) {
5827  value_step = powf(10.0f, roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f);
5828  }
5829  else {
5830  value_step = 0.01f;
5831  }
5832 
5833  if (f < tempf) {
5834  tempf -= value_step;
5835  }
5836  else {
5837  tempf += value_step;
5838  }
5839 
5840  CLAMP(tempf, softmin, softmax);
5841  data->value = tempf;
5842  }
5843  else {
5844  data->cancel = true;
5845  }
5846  }
5847 
5849  retval = WM_UI_HANDLER_BREAK;
5850  }
5851  else {
5852  /* edit the value directly */
5854  retval = WM_UI_HANDLER_BREAK;
5855  }
5856  }
5857 
5858  data->draglastx = mx;
5859  data->draglasty = my;
5860 
5861  return retval;
5862 }
5863 
5864 static int ui_do_but_SCROLL(
5865  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
5866 {
5867  int retval = WM_UI_HANDLER_CONTINUE;
5868  const bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect));
5869 
5870  int mx = event->xy[0];
5871  int my = event->xy[1];
5872  ui_window_to_block(data->region, block, &mx, &my);
5873 
5874  if (data->state == BUTTON_STATE_HIGHLIGHT) {
5875  if (event->val == KM_PRESS) {
5876  if (event->type == LEFTMOUSE) {
5877  if (horizontal) {
5878  data->dragstartx = mx;
5879  data->draglastx = mx;
5880  }
5881  else {
5882  data->dragstartx = my;
5883  data->draglastx = my;
5884  }
5886  retval = WM_UI_HANDLER_BREAK;
5887  }
5888  }
5889  }
5890  else if (data->state == BUTTON_STATE_NUM_EDITING) {
5891  if (event->type == EVT_ESCKEY) {
5892  if (event->val == KM_PRESS) {
5893  data->cancel = true;
5894  data->escapecancel = true;
5896  }
5897  }
5898  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
5900  }
5901  else if (event->type == MOUSEMOVE) {
5902  const bool is_motion = (event->type == MOUSEMOVE);
5903  if (ui_numedit_but_SLI(
5904  but, data, (horizontal) ? mx : my, horizontal, is_motion, false, false)) {
5905  /* Scrollbars in popups need UI layout refresh to update the right items to show. */
5906  if (ui_block_is_popup_any(but->block)) {
5907  ED_region_tag_refresh_ui(data->region);
5908  }
5909  ui_numedit_apply(C, block, but, data);
5910  }
5911  }
5912 
5913  retval = WM_UI_HANDLER_BREAK;
5914  }
5915 
5916  return retval;
5917 }
5918 
5919 static int ui_do_but_GRIP(
5920  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
5921 {
5922  int retval = WM_UI_HANDLER_CONTINUE;
5923  const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect));
5924 
5925  /* NOTE: Having to store org point in window space and recompute it to block "space" each time
5926  * is not ideal, but this is a way to hack around behavior of ui_window_to_block(), which
5927  * returns different results when the block is inside a panel or not...
5928  * See T37739.
5929  */
5930 
5931  int mx = event->xy[0];
5932  int my = event->xy[1];
5933  ui_window_to_block(data->region, block, &mx, &my);
5934 
5935  if (data->state == BUTTON_STATE_HIGHLIGHT) {
5936  if (event->val == KM_PRESS) {
5937  if (event->type == LEFTMOUSE) {
5938  data->dragstartx = event->xy[0];
5939  data->dragstarty = event->xy[1];
5941  retval = WM_UI_HANDLER_BREAK;
5942  }
5943  }
5944  }
5945  else if (data->state == BUTTON_STATE_NUM_EDITING) {
5946  if (event->type == EVT_ESCKEY) {
5947  if (event->val == KM_PRESS) {
5948  data->cancel = true;
5949  data->escapecancel = true;
5951  }
5952  }
5953  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
5955  }
5956  else if (event->type == MOUSEMOVE) {
5957  int dragstartx = data->dragstartx;
5958  int dragstarty = data->dragstarty;
5959  ui_window_to_block(data->region, block, &dragstartx, &dragstarty);
5960  data->value = data->origvalue + (horizontal ? mx - dragstartx : dragstarty - my);
5961  ui_numedit_apply(C, block, but, data);
5962  }
5963 
5964  retval = WM_UI_HANDLER_BREAK;
5965  }
5966 
5967  return retval;
5968 }
5969 
5971  uiBut *but,
5973  const wmEvent *event)
5974 {
5975  if (data->state == BUTTON_STATE_HIGHLIGHT) {
5976  /* hack to pass on ctrl+click and double click to overlapping text
5977  * editing field for editing list item names
5978  */
5979  if ((ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->val == KM_PRESS) &&
5980  (event->modifier & KM_CTRL)) ||
5981  (event->type == LEFTMOUSE && event->val == KM_DBL_CLICK)) {
5983  C, but, data, event, BUTTON_ACTIVATE_TEXT_EDITING);
5984  if (labelbut) {
5985  /* Nothing else to do. */
5986  return WM_UI_HANDLER_BREAK;
5987  }
5988  }
5989  }
5990 
5991  return ui_do_but_EXIT(C, but, data, event);
5992 }
5993 
5994 static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
5995 {
5996  if (data->state == BUTTON_STATE_HIGHLIGHT) {
5997 
5998  /* First handle click on icon-drag type button. */
5999  if (event->type == LEFTMOUSE && ui_but_drag_is_draggable(but) && event->val == KM_PRESS) {
6000  if (ui_but_contains_point_px_icon(but, data->region, event)) {
6002  data->dragstartx = event->xy[0];
6003  data->dragstarty = event->xy[1];
6004  return WM_UI_HANDLER_BREAK;
6005  }
6006  }
6007 #ifdef USE_DRAG_TOGGLE
6008  if (event->type == LEFTMOUSE && event->val == KM_PRESS && (ui_but_is_drag_toggle(but))) {
6010  data->dragstartx = event->xy[0];
6011  data->dragstarty = event->xy[1];
6012  return WM_UI_HANDLER_BREAK;
6013  }
6014 #endif
6015  /* regular open menu */
6016  if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
6018  return WM_UI_HANDLER_BREAK;
6019  }
6020  if (ui_but_supports_cycling(but)) {
6021  if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) &&
6022  (event->modifier & KM_CTRL)) {
6023  int type = event->type;
6024  int val = event->val;
6025 
6026  /* Convert pan to scroll-wheel. */
6027  if (type == MOUSEPAN) {
6028  ui_pan_to_scroll(event, &type, &val);
6029 
6030  if (type == MOUSEPAN) {
6031  return WM_UI_HANDLER_BREAK;
6032  }
6033  }
6034 
6035  const int direction = (type == WHEELDOWNMOUSE) ? 1 : -1;
6036 
6037  data->value = ui_but_menu_step(but, direction);
6038 
6040  ui_apply_but(C, but->block, but, data, true);
6041 
6042  /* Button's state need to be changed to EXIT so moving mouse away from this mouse
6043  * wouldn't lead to cancel changes made to this button, but changing state to EXIT also
6044  * makes no button active for a while which leads to triggering operator when doing fast
6045  * scrolling mouse wheel. using post activate stuff from button allows to make button be
6046  * active again after checking for all that mouse leave and cancel stuff, so quick
6047  * scroll wouldn't be an issue anymore. Same goes for scrolling wheel in another
6048  * direction below (sergey).
6049  */
6050  data->postbut = but;
6051  data->posttype = BUTTON_ACTIVATE_OVER;
6052 
6053  /* without this, a new interface that draws as result of the menu change
6054  * won't register that the mouse is over it, eg:
6055  * Alt+MouseWheel over the render slots, without this,
6056  * the slot menu fails to switch a second time.
6057  *
6058  * The active state of the button could be maintained some other way
6059  * and remove this mouse-move event.
6060  */
6061  WM_event_add_mousemove(data->window);
6062 
6063  return WM_UI_HANDLER_BREAK;
6064  }
6065  }
6066  }
6067  else if (data->state == BUTTON_STATE_WAIT_DRAG) {
6068 
6069  /* this function also ends state */
6070  if (ui_but_drag_init(C, but, data, event)) {
6071  return WM_UI_HANDLER_BREAK;
6072  }
6073 
6074  /* outside icon quit, not needed if drag activated */
6075  if (0 == ui_but_contains_point_px_icon(but, data->region, event)) {
6077  data->cancel = true;
6078  return WM_UI_HANDLER_BREAK;
6079  }
6080 
6081  if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
6083  return WM_UI_HANDLER_BREAK;
6084  }
6085  }
6086 
6087  return WM_UI_HANDLER_CONTINUE;
6088 }
6089 
6091  uiBut *but, uiHandleButtonData *data, int mx, int my, const enum eSnapType snap)
6092 {
6093  float mrad;
6094  bool changed = true;
6095 
6096  /* button is presumed square */
6097  /* if mouse moves outside of sphere, it does negative normal */
6098 
6099  /* note that both data->vec and data->origvec should be normalized
6100  * else we'll get a harmless but annoying jump when first clicking */
6101 
6102  float *fp = data->origvec;
6103  const float rad = BLI_rctf_size_x(&but->rect);
6104  const float radsq = rad * rad;
6105 
6106  int mdx, mdy;
6107  if (fp[2] > 0.0f) {
6108  mdx = (rad * fp[0]);
6109  mdy = (rad * fp[1]);
6110  }
6111  else if (fp[2] > -1.0f) {
6112  mrad = rad / sqrtf(fp[0] * fp[0] + fp[1] * fp[1]);
6113 
6114  mdx = 2.0f * mrad * fp[0] - (rad * fp[0]);
6115  mdy = 2.0f * mrad * fp[1] - (rad * fp[1]);
6116  }
6117  else {
6118  mdx = mdy = 0;
6119  }
6120 
6121  float dx = (float)(mx + mdx - data->dragstartx);
6122  float dy = (float)(my + mdy - data->dragstarty);
6123 
6124  fp = data->vec;
6125  mrad = dx * dx + dy * dy;
6126  if (mrad < radsq) { /* inner circle */
6127  fp[0] = dx;
6128  fp[1] = dy;
6129  fp[2] = sqrtf(radsq - dx * dx - dy * dy);
6130  }
6131  else { /* outer circle */
6132 
6133  mrad = rad / sqrtf(mrad); /* veclen */
6134 
6135  dx *= (2.0f * mrad - 1.0f);
6136  dy *= (2.0f * mrad - 1.0f);
6137 
6138  mrad = dx * dx + dy * dy;
6139  if (mrad < radsq) {
6140  fp[0] = dx;
6141  fp[1] = dy;
6142  fp[2] = -sqrtf(radsq - dx * dx - dy * dy);
6143  }
6144  }
6145  normalize_v3(fp);
6146 
6147  if (snap != SNAP_OFF) {
6148  const int snap_steps = (snap == SNAP_ON) ? 4 : 12; /* 45 or 15 degree increments */
6149  const float snap_steps_angle = M_PI / snap_steps;
6150  float angle, angle_snap;
6151 
6152  /* round each axis of 'fp' to the next increment
6153  * do this in "angle" space - this gives increments of same size */
6154  for (int i = 0; i < 3; i++) {
6155  angle = asinf(fp[i]);
6156  angle_snap = roundf((angle / snap_steps_angle)) * snap_steps_angle;
6157  fp[i] = sinf(angle_snap);
6158  }
6159  normalize_v3(fp);
6160  changed = !compare_v3v3(fp, data->origvec, FLT_EPSILON);
6161  }
6162 
6163  data->draglastx = mx;
6164  data->draglasty = my;
6165 
6166  return changed;
6167 }
6168 
6169 static void ui_palette_set_active(uiButColor *color_but)
6170 {
6171  if (color_but->is_pallete_color) {
6172  Palette *palette = (Palette *)color_but->but.rnapoin.owner_id;
6173  PaletteColor *color = color_but->but.rnapoin.data;
6174  palette->active_color = BLI_findindex(&palette->colors, color);
6175  }
6176 }
6177 
6178 static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
6179 {
6180  BLI_assert(but->type == UI_BTYPE_COLOR);
6181  uiButColor *color_but = (uiButColor *)but;
6182 
6183  if (data->state == BUTTON_STATE_HIGHLIGHT) {
6184  /* First handle click on icon-drag type button. */
6185  if (event->type == LEFTMOUSE && ui_but_drag_is_draggable(but) && event->val == KM_PRESS) {
6186  ui_palette_set_active(color_but);
6187  if (ui_but_contains_point_px_icon(but, data->region, event)) {
6189  data->dragstartx = event->xy[0];
6190  data->dragstarty = event->xy[1];
6191  return WM_UI_HANDLER_BREAK;
6192  }
6193  }
6194 #ifdef USE_DRAG_TOGGLE
6195  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
6196  ui_palette_set_active(color_but);
6198  data->dragstartx = event->xy[0];
6199  data->dragstarty = event->xy[1];
6200  return WM_UI_HANDLER_BREAK;
6201  }
6202 #endif
6203  /* regular open menu */
6204  if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
6205  ui_palette_set_active(color_but);
6207  return WM_UI_HANDLER_BREAK;
6208  }
6209  if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && (event->modifier & KM_CTRL)) {
6210  ColorPicker *cpicker = but->custom_data;
6211  float hsv_static[3] = {0.0f};
6212  float *hsv = cpicker ? cpicker->hsv_perceptual : hsv_static;
6213  float col[3];
6214 
6215  ui_but_v3_get(but, col);
6216  rgb_to_hsv_compat_v(col, hsv);
6217 
6218  if (event->type == WHEELDOWNMOUSE) {
6219  hsv[2] = clamp_f(hsv[2] - 0.05f, 0.0f, 1.0f);
6220  }
6221  else if (event->type == WHEELUPMOUSE) {
6222  hsv[2] = clamp_f(hsv[2] + 0.05f, 0.0f, 1.0f);
6223  }
6224  else {
6225  const float fac = 0.005 * (event->xy[1] - event->prev_xy[1]);
6226  hsv[2] = clamp_f(hsv[2] + fac, 0.0f, 1.0f);
6227  }
6228 
6229  hsv_to_rgb_v(hsv, data->vec);
6230  ui_but_v3_set(but, data->vec);
6231 
6233  ui_apply_but(C, but->block, but, data, true);
6234  return WM_UI_HANDLER_BREAK;
6235  }
6236  if (color_but->is_pallete_color && (event->type == EVT_DELKEY) && (event->val == KM_PRESS)) {
6237  Palette *palette = (Palette *)but->rnapoin.owner_id;
6238  PaletteColor *color = but->rnapoin.data;
6239 
6240  BKE_palette_color_remove(palette, color);
6241 
6243 
6244  /* this is risky. it works OK for now,
6245  * but if it gives trouble we should delay execution */
6246  but->rnapoin = PointerRNA_NULL;
6247  but->rnaprop = NULL;
6248 
6249  return WM_UI_HANDLER_BREAK;
6250  }
6251  }
6252  else if (data->state == BUTTON_STATE_WAIT_DRAG) {
6253 
6254  /* this function also ends state */
6255  if (ui_but_drag_init(C, but, data, event)) {
6256  return WM_UI_HANDLER_BREAK;
6257  }
6258 
6259  /* outside icon quit, not needed if drag activated */
6260  if (0 == ui_but_contains_point_px_icon(but, data->region, event)) {
6262  data->cancel = true;
6263  return WM_UI_HANDLER_BREAK;
6264  }
6265 
6266  if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
6267  if (color_but->is_pallete_color) {
6268  if ((event->modifier & KM_CTRL) == 0) {
6269  float color[3];
6271  if (paint != NULL) {
6272  Brush *brush = BKE_paint_brush(paint);
6273 
6274  if (brush->flag & BRUSH_USE_GRADIENT) {
6275  float *target = &brush->gradient->data[brush->gradient->cur].r;
6276 
6277  if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
6278  RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target);
6280  }
6281  else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
6282  RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target);
6283  }
6284  }
6285  else {
6287  bool updated = false;
6288 
6289  if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
6291  BKE_brush_color_set(scene, brush, color);
6292  updated = true;
6293  }
6294  else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
6297  BKE_brush_color_set(scene, brush, color);
6298  updated = true;
6299  }
6300 
6301  if (updated) {
6302  PointerRNA brush_ptr;
6303  PropertyRNA *brush_color_prop;
6304 
6305  RNA_id_pointer_create(&brush->id, &brush_ptr);
6306  brush_color_prop = RNA_struct_find_property(&brush_ptr, "color");
6307  RNA_property_update(C, &brush_ptr, brush_color_prop);
6308  }
6309  }
6310  }
6311 
6313  }
6314  else {
6316  }
6317  }
6318  else {
6320  }
6321  return WM_UI_HANDLER_BREAK;
6322  }
6323  }
6324 
6325  return WM_UI_HANDLER_CONTINUE;
6326 }
6327 
6329  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
6330 {
6331  int mx = event->xy[0];
6332  int my = event->xy[1];
6333  ui_window_to_block(data->region, block, &mx, &my);
6334 
6335  if (data->state == BUTTON_STATE_HIGHLIGHT) {
6336  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
6337  const enum eSnapType snap = ui_event_to_snap(event);
6338  data->dragstartx = mx;
6339  data->dragstarty = my;
6340  data->draglastx = mx;
6341  data->draglasty = my;
6343 
6344  /* also do drag the first time */
6345  if (ui_numedit_but_UNITVEC(but, data, mx, my, snap)) {
6346  ui_numedit_apply(C, block, but, data);
6347  }
6348 
6349  return WM_UI_HANDLER_BREAK;
6350  }
6351  }
6352  else if (data->state == BUTTON_STATE_NUM_EDITING) {
6353  if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) {
6354  if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) {
6355  const enum eSnapType snap = ui_event_to_snap(event);
6356  if (ui_numedit_but_UNITVEC(but, data, mx, my, snap)) {
6357  ui_numedit_apply(C, block, but, data);
6358  }
6359  }
6360  }
6361  else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
6362  if (event->val == KM_PRESS) {
6363  data->cancel = true;
6364  data->escapecancel = true;
6366  }
6367  }
6368  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
6370  }
6371 
6372  return WM_UI_HANDLER_BREAK;
6373  }
6374 
6375  return WM_UI_HANDLER_CONTINUE;
6376 }
6377 
6378 /* scales a vector so no axis exceeds max
6379  * (could become BLI_math func) */
6380 static void clamp_axis_max_v3(float v[3], const float max)
6381 {
6382  const float v_max = max_fff(v[0], v[1], v[2]);
6383  if (v_max > max) {
6384  mul_v3_fl(v, max / v_max);
6385  if (v[0] > max) {
6386  v[0] = max;
6387  }
6388  if (v[1] > max) {
6389  v[1] = max;
6390  }
6391  if (v[2] > max) {
6392  v[2] = max;
6393  }
6394  }
6395 }
6396 
6398  const float rgb[3],
6399  float hsv[3])
6400 {
6401  if (hsv_but->gradient_type == UI_GRAD_L_ALT) {
6402  rgb_to_hsl_compat_v(rgb, hsv);
6403  }
6404  else {
6405  rgb_to_hsv_compat_v(rgb, hsv);
6406  }
6407 }
6408 
6410  const float rgb[3],
6411  float hsv[3])
6412 {
6413  if (hsv_but->gradient_type == UI_GRAD_L_ALT) {
6414  rgb_to_hsl_v(rgb, hsv);
6415  }
6416  else {
6417  rgb_to_hsv_v(rgb, hsv);
6418  }
6419 }
6420 
6422  const float hsv[3],
6423  float rgb[3])
6424 {
6425  if (hsv_but->gradient_type == UI_GRAD_L_ALT) {
6426  hsl_to_rgb_v(hsv, rgb);
6427  }
6428  else {
6429  hsv_to_rgb_v(hsv, rgb);
6430  }
6431 }
6432 
6435  int mx,
6436  int my,
6437  const enum eSnapType snap,
6438  const bool shift)
6439 {
6440  const uiButHSVCube *hsv_but = (uiButHSVCube *)but;
6441  ColorPicker *cpicker = but->custom_data;
6442  float *hsv = cpicker->hsv_perceptual;
6443  float rgb[3];
6444  float x, y;
6445  float mx_fl, my_fl;
6446  const bool changed = true;
6447 
6448  ui_mouse_scale_warp(data, mx, my, &mx_fl, &my_fl, shift);
6449 
6450 #ifdef USE_CONT_MOUSE_CORRECT
6451  if (ui_but_is_cursor_warp(but)) {
6452  /* OK but can go outside bounds */
6453  data->ungrab_mval[0] = mx_fl;
6454  data->ungrab_mval[1] = my_fl;
6455  BLI_rctf_clamp_pt_v(&but->rect, data->ungrab_mval);
6456  }
6457 #endif
6458 
6459  ui_but_v3_get(but, rgb);
6461 
6463 
6464  /* only apply the delta motion, not absolute */
6465  if (shift) {
6466  rcti rect_i;
6467  float xpos, ypos, hsvo[3];
6468 
6469  BLI_rcti_rctf_copy(&rect_i, &but->rect);
6470 
6471  /* calculate original hsv again */
6472  copy_v3_v3(rgb, data->origvec);
6474 
6475  copy_v3_v3(hsvo, hsv);
6476 
6478 
6479  /* and original position */
6480  ui_hsvcube_pos_from_vals(hsv_but, &rect_i, hsvo, &xpos, &ypos);
6481 
6482  mx_fl = xpos - (data->dragstartx - mx_fl);
6483  my_fl = ypos - (data->dragstarty - my_fl);
6484  }
6485 
6486  /* relative position within box */
6487  x = ((float)mx_fl - but->rect.xmin) / BLI_rctf_size_x(&but->rect);
6488  y = ((float)my_fl - but->rect.ymin) / BLI_rctf_size_y(&but->rect);
6489  CLAMP(x, 0.0f, 1.0f);
6490  CLAMP(y, 0.0f, 1.0f);
6491 
6492  switch (hsv_but->gradient_type) {
6493  case UI_GRAD_SV:
6494  hsv[1] = x;
6495  hsv[2] = y;
6496  break;
6497  case UI_GRAD_HV:
6498  hsv[0] = x;
6499  hsv[2] = y;
6500  break;
6501  case UI_GRAD_HS:
6502  hsv[0] = x;
6503  hsv[1] = y;
6504  break;
6505  case UI_GRAD_H:
6506  hsv[0] = x;
6507  break;
6508  case UI_GRAD_S:
6509  hsv[1] = x;
6510  break;
6511  case UI_GRAD_V:
6512  hsv[2] = x;
6513  break;
6514  case UI_GRAD_L_ALT:
6515  hsv[2] = y;
6516  break;
6517  case UI_GRAD_V_ALT: {
6518  /* vertical 'value' strip */
6519  const float min = but->softmin, max = but->softmax;
6520  /* exception only for value strip - use the range set in but->min/max */
6521  hsv[2] = y * (max - min) + min;
6522  break;
6523  }
6524  default:
6525  BLI_assert(0);
6526  break;
6527  }
6528 
6529  if (snap != SNAP_OFF) {
6530  if (ELEM(hsv_but->gradient_type, UI_GRAD_HV, UI_GRAD_HS, UI_GRAD_H)) {
6531  ui_color_snap_hue(snap, &hsv[0]);
6532  }
6533  }
6534 
6535  ui_color_picker_to_rgb_HSVCUBE_v(hsv_but, hsv, rgb);
6537 
6538  /* clamp because with color conversion we can exceed range T34295. */
6539  if (hsv_but->gradient_type == UI_GRAD_V_ALT) {
6540  clamp_axis_max_v3(rgb, but->softmax);
6541  }
6542 
6543  copy_v3_v3(data->vec, rgb);
6544 
6545  data->draglastx = mx;
6546  data->draglasty = my;
6547 
6548  return changed;
6549 }
6550 
6551 #ifdef WITH_INPUT_NDOF
6552 static void ui_ndofedit_but_HSVCUBE(uiButHSVCube *hsv_but,
6554  const wmNDOFMotionData *ndof,
6555  const enum eSnapType snap,
6556  const bool shift)
6557 {
6558  ColorPicker *cpicker = hsv_but->but.custom_data;
6559  float *hsv = cpicker->hsv_perceptual;
6560  const float hsv_v_max = max_ff(hsv[2], hsv_but->but.softmax);
6561  float rgb[3];
6562  const float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt;
6563 
6564  ui_but_v3_get(&hsv_but->but, rgb);
6567 
6568  switch (hsv_but->gradient_type) {
6569  case UI_GRAD_SV:
6570  hsv[1] += ndof->rvec[2] * sensitivity;
6571  hsv[2] += ndof->rvec[0] * sensitivity;
6572  break;
6573  case UI_GRAD_HV:
6574  hsv[0] += ndof->rvec[2] * sensitivity;
6575  hsv[2] += ndof->rvec[0] * sensitivity;
6576  break;
6577  case UI_GRAD_HS:
6578  hsv[0] += ndof->rvec[2] * sensitivity;
6579  hsv[1] += ndof->rvec[0] * sensitivity;
6580  break;
6581  case UI_GRAD_H:
6582  hsv[0] += ndof->rvec[2] * sensitivity;
6583  break;
6584  case UI_GRAD_S:
6585  hsv[1] += ndof->rvec[2] * sensitivity;
6586  break;
6587  case UI_GRAD_V:
6588  hsv[2] += ndof->rvec[2] * sensitivity;
6589  break;
6590  case UI_GRAD_V_ALT:
6591  case UI_GRAD_L_ALT:
6592  /* vertical 'value' strip */
6593 
6594  /* exception only for value strip - use the range set in but->min/max */
6595  hsv[2] += ndof->rvec[0] * sensitivity;
6596 
6597  CLAMP(hsv[2], hsv_but->but.softmin, hsv_but->but.softmax);
6598  break;
6599  default:
6600  BLI_assert_msg(0, "invalid hsv type");
6601  break;
6602  }
6603 
6604  if (snap != SNAP_OFF) {
6605  if (ELEM(hsv_but->gradient_type, UI_GRAD_HV, UI_GRAD_HS, UI_GRAD_H)) {
6606  ui_color_snap_hue(snap, &hsv[0]);
6607  }
6608  }
6609 
6610  /* ndof specific: the changes above aren't clamping */
6611  hsv_clamp_v(hsv, hsv_v_max);
6612 
6613  ui_color_picker_to_rgb_HSVCUBE_v(hsv_but, hsv, rgb);
6615 
6616  copy_v3_v3(data->vec, rgb);
6617  ui_but_v3_set(&hsv_but->but, data->vec);
6618 }
6619 #endif /* WITH_INPUT_NDOF */
6620 
6622  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
6623 {
6624  uiButHSVCube *hsv_but = (uiButHSVCube *)but;
6625  int mx = event->xy[0];
6626  int my = event->xy[1];
6627  ui_window_to_block(data->region, block, &mx, &my);
6628 
6629  if (data->state == BUTTON_STATE_HIGHLIGHT) {
6630  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
6631  const enum eSnapType snap = ui_event_to_snap(event);
6632 
6633  data->dragstartx = mx;
6634  data->dragstarty = my;
6635  data->draglastx = mx;
6636  data->draglasty = my;
6638 
6639  /* also do drag the first time */
6640  if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) {
6641  ui_numedit_apply(C, block, but, data);
6642  }
6643 
6644  return WM_UI_HANDLER_BREAK;
6645  }
6646 #ifdef WITH_INPUT_NDOF
6647  if (event->type == NDOF_MOTION) {
6648  const wmNDOFMotionData *ndof = event->customdata;
6649  const enum eSnapType snap = ui_event_to_snap(event);
6650 
6651  ui_ndofedit_but_HSVCUBE(hsv_but, data, ndof, snap, event->modifier & KM_SHIFT);
6652 
6654  ui_apply_but(C, but->block, but, data, true);
6655 
6656  return WM_UI_HANDLER_BREAK;
6657  }
6658 #endif /* WITH_INPUT_NDOF */
6659  /* XXX hardcoded keymap check.... */
6660  if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
6661  if (ELEM(hsv_but->gradient_type, UI_GRAD_V_ALT, UI_GRAD_L_ALT)) {
6662  int len;
6663 
6664  /* reset only value */
6665 
6667  if (ELEM(len, 3, 4)) {
6668  float rgb[3], def_hsv[3];
6669  float def[4];
6670  ColorPicker *cpicker = but->custom_data;
6671  float *hsv = cpicker->hsv_perceptual;
6672 
6674  ui_rgb_to_color_picker_HSVCUBE_v(hsv_but, def, def_hsv);
6675 
6676  ui_but_v3_get(but, rgb);
6678 
6679  def_hsv[0] = hsv[0];
6680  def_hsv[1] = hsv[1];
6681 
6682  ui_color_picker_to_rgb_HSVCUBE_v(hsv_but, def_hsv, rgb);
6683  ui_but_v3_set(but, rgb);
6684 
6685  RNA_property_update(C, &but->rnapoin, but->rnaprop);
6686  return WM_UI_HANDLER_BREAK;
6687  }
6688  }
6689  }
6690  }
6691  else if (data->state == BUTTON_STATE_NUM_EDITING) {
6692  if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
6693  if (event->val == KM_PRESS) {
6694  data->cancel = true;
6695  data->escapecancel = true;
6697  }
6698  }
6699  else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) {
6700  if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) {
6701  const enum eSnapType snap = ui_event_to_snap(event);
6702 
6703  if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) {
6704  ui_numedit_apply(C, block, but, data);
6705  }
6706  }
6707  }
6708  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
6710  }
6711 
6712  return WM_UI_HANDLER_BREAK;
6713  }
6714 
6715  return WM_UI_HANDLER_CONTINUE;
6716 }
6717 
6720  float mx,
6721  float my,
6722  const enum eSnapType snap,
6723  const bool shift)
6724 {
6725  const bool changed = true;
6726  ColorPicker *cpicker = but->custom_data;
6727  float *hsv = cpicker->hsv_perceptual;
6728 
6729  float mx_fl, my_fl;
6730  ui_mouse_scale_warp(data, mx, my, &mx_fl, &my_fl, shift);
6731 
6732 #ifdef USE_CONT_MOUSE_CORRECT
6733  if (ui_but_is_cursor_warp(but)) {
6734  /* OK but can go outside bounds */
6735  data->ungrab_mval[0] = mx_fl;
6736  data->ungrab_mval[1] = my_fl;
6737  { /* clamp */
6738  const float radius = min_ff(BLI_rctf_size_x(&but->rect), BLI_rctf_size_y(&but->rect)) / 2.0f;
6739  const float cent[2] = {BLI_rctf_cent_x(&but->rect), BLI_rctf_cent_y(&but->rect)};
6740  const float len = len_v2v2(cent, data->ungrab_mval);
6741  if (len > radius) {
6742  dist_ensure_v2_v2fl(data->ungrab_mval, cent, radius);
6743  }
6744  }
6745  }
6746 #endif
6747 
6748  rcti rect;
6749  BLI_rcti_rctf_copy(&rect, &but->rect);
6750 
6751  float rgb[3];
6752  ui_but_v3_get(but, rgb);
6755 
6756  /* exception, when using color wheel in 'locked' value state:
6757  * allow choosing a hue for black values, by giving a tiny increment */
6758  if (cpicker->use_color_lock) {
6759  if (U.color_picker_type == USER_CP_CIRCLE_HSV) { /* lock */
6760  if (hsv[2] == 0.0f) {
6761  hsv[2] = 0.0001f;
6762  }
6763  }
6764  else {
6765  if (hsv[2] == 0.0f) {
6766  hsv[2] = 0.0001f;
6767  }
6768  if (hsv[2] >= 0.9999f) {
6769  hsv[2] = 0.9999f;
6770  }
6771  }
6772  }
6773 
6774  /* only apply the delta motion, not absolute */
6775  if (shift) {
6776  float xpos, ypos, hsvo[3], rgbo[3];
6777 
6778  /* calculate original hsv again */
6779  copy_v3_v3(hsvo, hsv);
6780  copy_v3_v3(rgbo, data->origvec);
6783 
6784  /* and original position */
6785  ui_hsvcircle_pos_from_vals(cpicker, &rect, hsvo, &xpos, &ypos);
6786 
6787  mx_fl = xpos - (data->dragstartx - mx_fl);
6788  my_fl = ypos - (data->dragstarty - my_fl);
6789  }
6790 
6791  ui_hsvcircle_vals_from_pos(&rect, mx_fl, my_fl, hsv, hsv + 1);
6792 
6793  if ((cpicker->use_color_cubic) && (U.color_picker_type == USER_CP_CIRCLE_HSV)) {
6794  hsv[1] = 1.0f - sqrt3f(1.0f - hsv[1]);
6795  }
6796 
6797  if (snap != SNAP_OFF) {
6798  ui_color_snap_hue(snap, &hsv[0]);
6799  }
6800 
6802 
6803  if (cpicker->use_luminosity_lock) {
6804  if (!is_zero_v3(rgb)) {
6806  }
6807  }
6808 
6810  ui_but_v3_set(but, rgb);
6811 
6812  data->draglastx = mx;
6813  data->draglasty = my;
6814 
6815  return changed;
6816 }
6817 
6818 #ifdef WITH_INPUT_NDOF
6819 static void ui_ndofedit_but_HSVCIRCLE(uiBut *but,
6821  const wmNDOFMotionData *ndof,
6822  const enum eSnapType snap,
6823  const bool shift)
6824 {
6825  ColorPicker *cpicker = but->custom_data;
6826  float *hsv = cpicker->hsv_perceptual;
6827  float rgb[3];
6828  float phi, r /*, sqr */ /* UNUSED */, v[2];
6829  const float sensitivity = (shift ? 0.06f : 0.3f) * ndof->dt;
6830 
6831  ui_but_v3_get(but, rgb);
6834 
6835  /* Convert current color on hue/sat disc to circular coordinates phi, r */
6836  phi = fmodf(hsv[0] + 0.25f, 1.0f) * -2.0f * (float)M_PI;
6837  r = hsv[1];
6838  /* sqr = r > 0.0f ? sqrtf(r) : 1; */ /* UNUSED */
6839 
6840  /* Convert to 2d vectors */
6841  v[0] = r * cosf(phi);
6842  v[1] = r * sinf(phi);
6843 
6844  /* Use ndof device y and x rotation to move the vector in 2d space */
6845  v[0] += ndof->rvec[2] * sensitivity;
6846  v[1] += ndof->rvec[0] * sensitivity;
6847 
6848  /* convert back to polar coords on circle */
6849  phi = atan2f(v[0], v[1]) / (2.0f * (float)M_PI) + 0.5f;
6850 
6851  /* use ndof Y rotation to additionally rotate hue */
6852  phi += ndof->rvec[1] * sensitivity * 0.5f;
6853  r = len_v2(v);
6854 
6855  /* convert back to hsv values, in range [0,1] */
6856  hsv[0] = phi;
6857  hsv[1] = r;
6858 
6859  /* exception, when using color wheel in 'locked' value state:
6860  * allow choosing a hue for black values, by giving a tiny increment */
6861  if (cpicker->use_color_lock) {
6862  if (U.color_picker_type == USER_CP_CIRCLE_HSV) { /* lock */
6863  if (hsv[2] == 0.0f) {
6864  hsv[2] = 0.0001f;
6865  }
6866  }
6867  else {
6868  if (hsv[2] == 0.0f) {
6869  hsv[2] = 0.0001f;
6870  }
6871  if (hsv[2] == 1.0f) {
6872  hsv[2] = 0.9999f;
6873  }
6874  }
6875  }
6876 
6877  if (snap != SNAP_OFF) {
6878  ui_color_snap_hue(snap, &hsv[0]);
6879  }
6880 
6881  hsv_clamp_v(hsv, FLT_MAX);
6882 
6883  ui_color_picker_hsv_to_rgb(hsv, data->vec);
6884 
6885  if (cpicker->use_luminosity_lock) {
6886  if (!is_zero_v3(data->vec)) {
6888  }
6889  }
6890 
6892  ui_but_v3_set(but, data->vec);
6893 }
6894 #endif /* WITH_INPUT_NDOF */
6895 
6897  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
6898 {
6899  ColorPicker *cpicker = but->custom_data;
6900  float *hsv = cpicker->hsv_perceptual;
6901  int mx = event->xy[0];
6902  int my = event->xy[1];
6903  ui_window_to_block(data->region, block, &mx, &my);
6904 
6905  if (data->state == BUTTON_STATE_HIGHLIGHT) {
6906  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
6907  const enum eSnapType snap = ui_event_to_snap(event);
6908  data->dragstartx = mx;
6909  data->dragstarty = my;
6910  data->draglastx = mx;
6911  data->draglasty = my;
6913 
6914  /* also do drag the first time */
6915  if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) {
6916  ui_numedit_apply(C, block, but, data);
6917  }
6918 
6919  return WM_UI_HANDLER_BREAK;
6920  }
6921 #ifdef WITH_INPUT_NDOF
6922  if (event->type == NDOF_MOTION) {
6923  const enum eSnapType snap = ui_event_to_snap(event);
6924  const wmNDOFMotionData *ndof = event->customdata;
6925 
6926  ui_ndofedit_but_HSVCIRCLE(but, data, ndof, snap, event->modifier & KM_SHIFT);
6927 
6929  ui_apply_but(C, but->block, but, data, true);
6930 
6931  return WM_UI_HANDLER_BREAK;
6932  }
6933 #endif /* WITH_INPUT_NDOF */
6934  /* XXX hardcoded keymap check.... */
6935  if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
6936  int len;
6937 
6938  /* reset only saturation */
6939 
6941  if (len >= 3) {
6942  float rgb[3], def_hsv[3];
6943  float *def;
6944  def = MEM_callocN(sizeof(float) * len, "reset_defaults - float");
6945 
6947  ui_color_picker_hsv_to_rgb(def, def_hsv);
6948 
6949  ui_but_v3_get(but, rgb);
6951 
6952  def_hsv[0] = hsv[0];
6953  def_hsv[2] = hsv[2];
6954 
6955  hsv_to_rgb_v(def_hsv, rgb);
6956  ui_but_v3_set(but, rgb);
6957 
6958  RNA_property_update(C, &but->rnapoin, but->rnaprop);
6959 
6960  MEM_freeN(def);
6961  }
6962  return WM_UI_HANDLER_BREAK;
6963  }
6964  }
6965  else if (data->state == BUTTON_STATE_NUM_EDITING) {
6966  if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
6967  if (event->val == KM_PRESS) {
6968  data->cancel = true;
6969  data->escapecancel = true;
6971  }
6972  }
6973  /* XXX hardcoded keymap check.... */
6974  else if (event->type == WHEELDOWNMOUSE) {
6975  hsv[2] = clamp_f(hsv[2] - 0.05f, 0.0f, 1.0f);
6976  ui_but_hsv_set(but); /* converts to rgb */
6977  ui_numedit_apply(C, block, but, data);
6978  }
6979  else if (event->type == WHEELUPMOUSE) {
6980  hsv[2] = clamp_f(hsv[2] + 0.05f, 0.0f, 1.0f);
6981  ui_but_hsv_set(but); /* converts to rgb */
6982  ui_numedit_apply(C, block, but, data);
6983  }
6984  else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) {
6985  if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) {
6986  const enum eSnapType snap = ui_event_to_snap(event);
6987 
6988  if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) {
6989  ui_numedit_apply(C, block, but, data);
6990  }
6991  }
6992  }
6993  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
6995  }
6996  return WM_UI_HANDLER_BREAK;
6997  }
6998 
6999  return WM_UI_HANDLER_CONTINUE;
7000 }
7001 
7003 {
7004  bool changed = false;
7005 
7006  if (data->draglastx == mx) {
7007  return changed;
7008  }
7009 
7010  if (data->coba->tot == 0) {
7011  return changed;
7012  }
7013 
7014  const float dx = ((float)(mx - data->draglastx)) / BLI_rctf_size_x(&but->rect);
7015  data->dragcbd->pos += dx;
7016  CLAMP(data->dragcbd->pos, 0.0f, 1.0f);
7017 
7019  data->dragcbd = data->coba->data + data->coba->cur; /* because qsort */
7020 
7021  data->draglastx = mx;
7022  changed = true;
7023 
7024  return changed;
7025 }
7026 
7028  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
7029 {
7030  int mx = event->xy[0];
7031  int my = event->xy[1];
7032  ui_window_to_block(data->region, block, &mx, &my);
7033 
7034  if (data->state == BUTTON_STATE_HIGHLIGHT) {
7035  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
7036  ColorBand *coba = (ColorBand *)but->poin;
7037 
7038  if (event->modifier & KM_CTRL) {
7039  /* insert new key on mouse location */
7040  const float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect);
7043  }
7044  else {
7045  CBData *cbd;
7046  /* ignore zoom-level for mindist */
7047  int mindist = (50 * UI_DPI_FAC) * block->aspect;
7048  int xco;
7049  data->dragstartx = mx;
7050  data->dragstarty = my;
7051  data->draglastx = mx;
7052  data->draglasty = my;
7053 
7054  /* activate new key when mouse is close */
7055  int a;
7056  for (a = 0, cbd = coba->data; a < coba->tot; a++, cbd++) {
7057  xco = but->rect.xmin + (cbd->pos * BLI_rctf_size_x(&but->rect));
7058  xco = abs(xco - mx);
7059  if (a == coba->cur) {
7060  /* Selected one disadvantage. */
7061  xco += 5;
7062  }
7063  if (xco < mindist) {
7064  coba->cur = a;
7065  mindist = xco;
7066  }
7067  }
7068 
7069  data->dragcbd = coba->data + coba->cur;
7070  data->dragfstart = data->dragcbd->pos;
7072  }
7073 
7074  return WM_UI_HANDLER_BREAK;
7075  }
7076  }
7077  else if (data->state == BUTTON_STATE_NUM_EDITING) {
7078  if (event->type == MOUSEMOVE) {
7079  if (mx != data->draglastx || my != data->draglasty) {
7080  if (ui_numedit_but_COLORBAND(but, data, mx)) {
7081  ui_numedit_apply(C, block, but, data);
7082  }
7083  }
7084  }
7085  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
7087  }
7088  else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
7089  if (event->val == KM_PRESS) {
7090  data->dragcbd->pos = data->dragfstart;
7092  data->cancel = true;
7093  data->escapecancel = true;
7095  }
7096  }
7097  return WM_UI_HANDLER_BREAK;
7098  }
7099 
7100  return WM_UI_HANDLER_CONTINUE;
7101 }
7102 
7103 static bool ui_numedit_but_CURVE(uiBlock *block,
7104  uiBut *but,
7106  int evtx,
7107  int evty,
7108  bool snap,
7109  const bool shift)
7110 {
7111  CurveMapping *cumap = (CurveMapping *)but->poin;
7112  CurveMap *cuma = cumap->cm + cumap->cur;
7113  CurveMapPoint *cmp = cuma->curve;
7114  bool changed = false;
7115 
7116  /* evtx evty and drag coords are absolute mouse-coords,
7117  * prevents errors when editing when layout changes. */
7118  int mx = evtx;
7119  int my = evty;
7120  ui_window_to_block(data->region, block, &mx, &my);
7121  int dragx = data->draglastx;
7122  int dragy = data->draglasty;
7123  ui_window_to_block(data->region, block, &dragx, &dragy);
7124 
7125  const float zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&cumap->curr);
7126  const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&cumap->curr);
7127 
7128  if (snap) {
7129  float d[2];
7130 
7131  d[0] = mx - data->dragstartx;
7132  d[1] = my - data->dragstarty;
7133 
7134  if (len_squared_v2(d) < (3.0f * 3.0f)) {
7135  snap = false;
7136  }
7137  }
7138 
7139  float fx = (mx - dragx) / zoomx;
7140  float fy = (my - dragy) / zoomy;
7141 
7142  if (data->dragsel != -1) {
7143  CurveMapPoint *cmp_last = NULL;
7144  const float mval_factor = ui_mouse_scale_warp_factor(shift);
7145  bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */
7146 
7147  fx *= mval_factor;
7148  fy *= mval_factor;
7149 
7150  for (int a = 0; a < cuma->totpoint; a++) {
7151  if (cmp[a].flag & CUMA_SELECT) {
7152  const float origx = cmp[a].x, origy = cmp[a].y;
7153  cmp[a].x += fx;
7154  cmp[a].y += fy;
7155  if (snap) {
7156  cmp[a].x = 0.125f * roundf(8.0f * cmp[a].x);
7157  cmp[a].y = 0.125f * roundf(8.0f * cmp[a].y);
7158  }
7159  if (cmp[a].x != origx || cmp[a].y != origy) {
7160  moved_point = true;
7161  }
7162 
7163  cmp_last = &cmp[a];
7164  }
7165  }
7166 
7167  BKE_curvemapping_changed(cumap, false);
7168 
7169  if (moved_point) {
7170  data->draglastx = evtx;
7171  data->draglasty = evty;
7172  changed = true;
7173 
7174 #ifdef USE_CONT_MOUSE_CORRECT
7175  /* NOTE: using 'cmp_last' is weak since there may be multiple points selected,
7176  * but in practice this isn't really an issue */
7177  if (ui_but_is_cursor_warp(but)) {
7178  /* OK but can go outside bounds */
7179  data->ungrab_mval[0] = but->rect.xmin + ((cmp_last->x - cumap->curr.xmin) * zoomx);
7180  data->ungrab_mval[1] = but->rect.ymin + ((cmp_last->y - cumap->curr.ymin) * zoomy);
7181  BLI_rctf_clamp_pt_v(&but->rect, data->ungrab_mval);
7182  }
7183 #endif
7184  }
7185 
7186  data->dragchange = true; /* mark for selection */
7187  }
7188  else {
7189  /* clamp for clip */
7190  if (cumap->flag & CUMA_DO_CLIP) {
7191  if (cumap->curr.xmin - fx < cumap->clipr.xmin) {
7192  fx = cumap->curr.xmin - cumap->clipr.xmin;
7193  }
7194  else if (cumap->curr.xmax - fx > cumap->clipr.xmax) {
7195  fx = cumap->curr.xmax - cumap->clipr.xmax;
7196  }
7197  if (cumap->curr.ymin - fy < cumap->clipr.ymin) {
7198  fy = cumap->curr.ymin - cumap->clipr.ymin;
7199  }
7200  else if (cumap->curr.ymax - fy > cumap->clipr.ymax) {
7201  fy = cumap->curr.ymax - cumap->clipr.ymax;
7202  }
7203  }
7204 
7205  cumap->curr.xmin -= fx;
7206  cumap->curr.ymin -= fy;
7207  cumap->curr.xmax -= fx;
7208  cumap->curr.ymax -= fy;
7209 
7210  data->draglastx = evtx;
7211  data->draglasty = evty;
7212 
7213  changed = true;
7214  }
7215 
7216  return changed;
7217 }
7218 
7219 static int ui_do_but_CURVE(
7220  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
7221 {
7222  bool changed = false;
7224  ViewLayer *view_layer = CTX_data_view_layer(C);
7225 
7226  int mx = event->xy[0];
7227  int my = event->xy[1];
7228  ui_window_to_block(data->region, block, &mx, &my);
7229 
7230  if (data->state == BUTTON_STATE_HIGHLIGHT) {
7231  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
7232  CurveMapping *cumap = (CurveMapping *)but->poin;
7233  CurveMap *cuma = cumap->cm + cumap->cur;
7234  const float m_xy[2] = {mx, my};
7235  float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius */
7236  int sel = -1;
7237 
7238  if (event->modifier & KM_CTRL) {
7239  float f_xy[2];
7240  BLI_rctf_transform_pt_v(&cumap->curr, &but->rect, f_xy, m_xy);
7241 
7242  BKE_curvemap_insert(cuma, f_xy[0], f_xy[1]);
7243  BKE_curvemapping_changed(cumap, false);
7244  changed = true;
7245  }
7246 
7247  /* check for selecting of a point */
7248  CurveMapPoint *cmp = cuma->curve; /* ctrl adds point, new malloc */
7249  for (int a = 0; a < cuma->totpoint; a++) {
7250  float f_xy[2];
7251  BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[a].x);
7252  const float dist_sq = len_squared_v2v2(m_xy, f_xy);
7253  if (dist_sq < dist_min_sq) {
7254  sel = a;
7255  dist_min_sq = dist_sq;
7256  }
7257  }
7258 
7259  if (sel == -1) {
7260  float f_xy[2], f_xy_prev[2];
7261 
7262  /* if the click didn't select anything, check if it's clicked on the
7263  * curve itself, and if so, add a point */
7264  cmp = cuma->table;
7265 
7266  BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[0].x);
7267 
7268  /* with 160px height 8px should translate to the old 0.05 coefficient at no zoom */
7269  dist_min_sq = square_f(U.dpi_fac * 8.0f);
7270 
7271  /* loop through the curve segment table and find what's near the mouse. */
7272  for (int i = 1; i <= CM_TABLE; i++) {
7273  copy_v2_v2(f_xy_prev, f_xy);
7274  BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[i].x);
7275 
7276  if (dist_squared_to_line_segment_v2(m_xy, f_xy_prev, f_xy) < dist_min_sq) {
7277  BLI_rctf_transform_pt_v(&cumap->curr, &but->rect, f_xy, m_xy);
7278 
7279  BKE_curvemap_insert(cuma, f_xy[0], f_xy[1]);
7280  BKE_curvemapping_changed(cumap, false);
7281 
7282  changed = true;
7283 
7284  /* reset cmp back to the curve points again,
7285  * rather than drawing segments */
7286  cmp = cuma->curve;
7287 
7288  /* find newly added point and make it 'sel' */
7289  for (int a = 0; a < cuma->totpoint; a++) {
7290  if (cmp[a].x == f_xy[0]) {
7291  sel = a;
7292  }
7293  }
7294  break;
7295  }
7296  }
7297  }
7298 
7299  if (sel != -1) {
7300  /* ok, we move a point */
7301  /* deselect all if this one is deselect. except if we hold shift */
7302  if ((event->modifier & KM_SHIFT) == 0) {
7303  for (int a = 0; a < cuma->totpoint; a++) {
7304  cmp[a].flag &= ~CUMA_SELECT;
7305  }
7306  cmp[sel].flag |= CUMA_SELECT;
7307  }
7308  else {
7309  cmp[sel].flag ^= CUMA_SELECT;
7310  }
7311  }
7312  else {
7313  /* move the view */
7314  data->cancel = true;
7315  }
7316 
7317  data->dragsel = sel;
7318 
7319  data->dragstartx = event->xy[0];
7320  data->dragstarty = event->xy[1];
7321  data->draglastx = event->xy[0];
7322  data->draglasty = event->xy[1];
7323 
7325  return WM_UI_HANDLER_BREAK;
7326  }
7327  }
7328  else if (data->state == BUTTON_STATE_NUM_EDITING) {
7329  if (event->type == MOUSEMOVE) {
7330  if (event->xy[0] != data->draglastx || event->xy[1] != data->draglasty) {
7331 
7332  if (ui_numedit_but_CURVE(block,
7333  but,
7334  data,
7335  event->xy[0],
7336  event->xy[1],
7337  event->modifier & KM_CTRL,
7338  event->modifier & KM_SHIFT)) {
7339  ui_numedit_apply(C, block, but, data);
7340  }
7341  }
7342  }
7343  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
7344  if (data->dragsel != -1) {
7345  CurveMapping *cumap = (CurveMapping *)but->poin;
7346  CurveMap *cuma = cumap->cm + cumap->cur;
7347  CurveMapPoint *cmp = cuma->curve;
7348 
7349  if (data->dragchange == false) {
7350  /* deselect all, select one */
7351  if ((event->modifier & KM_SHIFT) == 0) {
7352  for (int a = 0; a < cuma->totpoint; a++) {
7353  cmp[a].flag &= ~CUMA_SELECT;
7354  }
7355  cmp[data->dragsel].flag |= CUMA_SELECT;
7356  }
7357  }
7358  else {
7359  BKE_curvemapping_changed(cumap, true); /* remove doubles */
7360  BKE_paint_invalidate_cursor_overlay(scene, view_layer, cumap);
7361  }
7362  }
7363 
7365  }
7366 
7367  return WM_UI_HANDLER_BREAK;
7368  }
7369 
7370  /* UNUSED but keep for now */
7371  (void)changed;
7372 
7373  return WM_UI_HANDLER_CONTINUE;
7374 }
7375 
7376 /* Same as ui_numedit_but_CURVE with some smaller changes. */
7378  uiBut *but,
7380  int evtx,
7381  int evty,
7382  bool snap,
7383  const bool shift)
7384 {
7385  CurveProfile *profile = (CurveProfile *)but->poin;
7386  CurveProfilePoint *pts = profile->path;
7387  bool changed = false;
7388 
7389  /* evtx evty and drag coords are absolute mouse-coords,
7390  * prevents errors when editing when layout changes. */
7391  int mx = evtx;
7392  int my = evty;
7393  ui_window_to_block(data->region, block, &mx, &my);
7394  int dragx = data->draglastx;
7395  int dragy = data->draglasty;
7396  ui_window_to_block(data->region, block, &dragx, &dragy);
7397 
7398  const float zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect);
7399  const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect);
7400 
7401  if (snap) {
7402  const float d[2] = {mx - data->dragstartx, data->dragstarty};
7403  if (len_squared_v2(d) < (9.0f * U.dpi_fac)) {
7404  snap = false;
7405  }
7406  }
7407 
7408  float fx = (mx - dragx) / zoomx;
7409  float fy = (my - dragy) / zoomy;
7410 
7411  if (data->dragsel != -1) {
7412  float last_x, last_y;
7413  const float mval_factor = ui_mouse_scale_warp_factor(shift);
7414  bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */
7415 
7416  fx *= mval_factor;
7417  fy *= mval_factor;
7418 
7419  /* Move all selected points. */
7420  const float delta[2] = {fx, fy};
7421  for (int a = 0; a < profile->path_len; a++) {
7422  /* Don't move the last and first control points. */
7423  if (pts[a].flag & PROF_SELECT) {
7424  moved_point |= BKE_curveprofile_move_point(profile, &pts[a], snap, delta);
7425  last_x = pts[a].x;
7426  last_y = pts[a].y;
7427  }
7428  else {
7429  /* Move handles when they're selected but the control point isn't. */
7430  if (ELEM(pts[a].h2, HD_FREE, HD_ALIGN) && pts[a].flag == PROF_H1_SELECT) {
7431  moved_point |= BKE_curveprofile_move_handle(&pts[a], true, snap, delta);
7432  last_x = pts[a].h1_loc[0];
7433  last_y = pts[a].h1_loc[1];
7434  }
7435  if (ELEM(pts[a].h2, HD_FREE, HD_ALIGN) && pts[a].flag == PROF_H2_SELECT) {
7436  moved_point |= BKE_curveprofile_move_handle(&pts[a], false, snap, delta);
7437  last_x = pts[a].h2_loc[0];
7438  last_y = pts[a].h2_loc[1];
7439  }
7440  }
7441  }
7442 
7444 
7445  if (moved_point) {
7446  data->draglastx = evtx;
7447  data->draglasty = evty;
7448  changed = true;
7449 #ifdef USE_CONT_MOUSE_CORRECT
7450  /* NOTE: using 'cmp_last' is weak since there may be multiple points selected,
7451  * but in practice this isn't really an issue */
7452  if (ui_but_is_cursor_warp(but)) {
7453  /* OK but can go outside bounds */
7454  data->ungrab_mval[0] = but->rect.xmin + ((last_x - profile->view_rect.xmin) * zoomx);
7455  data->ungrab_mval[1] = but->rect.ymin + ((last_y - profile->view_rect.ymin) * zoomy);
7456  BLI_rctf_clamp_pt_v(&but->rect, data->ungrab_mval);
7457  }
7458 #endif
7459  }
7460  data->dragchange = true; /* mark for selection */
7461  }
7462  else {
7463  /* Clamp the view rect when clipping is on. */
7464  if (profile->flag & PROF_USE_CLIP) {
7465  if (profile->view_rect.xmin - fx < profile->clip_rect.xmin) {
7466  fx = profile->view_rect.xmin - profile->clip_rect.xmin;
7467  }
7468  else if (profile->view_rect.xmax - fx > profile->clip_rect.xmax) {
7469  fx = profile->view_rect.xmax - profile->clip_rect.xmax;
7470  }
7471  if (profile->view_rect.ymin - fy < profile->clip_rect.ymin) {
7472  fy = profile->view_rect.ymin - profile->clip_rect.ymin;
7473  }
7474  else if (profile->view_rect.ymax - fy > profile->clip_rect.ymax) {
7475  fy = profile->view_rect.ymax - profile->clip_rect.ymax;
7476  }
7477  }
7478 
7479  profile->view_rect.xmin -= fx;
7480  profile->view_rect.ymin -= fy;
7481  profile->view_rect.xmax -= fx;
7482  profile->view_rect.ymax -= fy;
7483 
7484  data->draglastx = evtx;
7485  data->draglasty = evty;
7486 
7487  changed = true;
7488  }
7489 
7490  return changed;
7491 }
7492 
7497 {
7498  return (point->flag & PROF_SELECT &&
7499  (ELEM(point->h1, HD_FREE, HD_ALIGN) || ELEM(point->h2, HD_FREE, HD_ALIGN))) ||
7501 }
7502 
7508  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
7509 {
7510  CurveProfile *profile = (CurveProfile *)but->poin;
7511  int mx = event->xy[0];
7512  int my = event->xy[1];
7513 
7514  ui_window_to_block(data->region, block, &mx, &my);
7515 
7516  /* Move selected control points. */
7517  if (event->type == EVT_GKEY && event->val == KM_RELEASE) {
7518  data->dragstartx = mx;
7519  data->dragstarty = my;
7520  data->draglastx = mx;
7521  data->draglasty = my;
7523  return WM_UI_HANDLER_BREAK;
7524  }
7525 
7526  /* Delete selected control points. */
7527  if (event->type == EVT_XKEY && event->val == KM_RELEASE) {
7531  return WM_UI_HANDLER_BREAK;
7532  }
7533 
7534  /* Selecting, adding, and starting point movements. */
7535  if (data->state == BUTTON_STATE_HIGHLIGHT) {
7536  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
7537  const float m_xy[2] = {mx, my};
7538 
7539  if (event->modifier & KM_CTRL) {
7540  float f_xy[2];
7541  BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
7542 
7543  BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]);
7545  }
7546 
7547  /* Check for selecting of a point by finding closest point in radius. */
7548  CurveProfilePoint *pts = profile->path;
7549  float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius for selecting points. */
7550  int i_selected = -1;
7551  short selection_type = 0; /* For handle selection. */
7552  for (int i = 0; i < profile->path_len; i++) {
7553  float f_xy[2];
7554  BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x);
7555  float dist_sq = len_squared_v2v2(m_xy, f_xy);
7556  if (dist_sq < dist_min_sq) {
7557  i_selected = i;
7558  selection_type = PROF_SELECT;
7559  dist_min_sq = dist_sq;
7560  }
7561 
7562  /* Also select handles if the point is selected and it has the right handle type. */
7563  if (point_draw_handles(&pts[i])) {
7564  if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) {
7565  BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, pts[i].h1_loc);
7566  dist_sq = len_squared_v2v2(m_xy, f_xy);
7567  if (dist_sq < dist_min_sq) {
7568  i_selected = i;
7569  selection_type = PROF_H1_SELECT;
7570  dist_min_sq = dist_sq;
7571  }
7572  }
7573  if (ELEM(profile->path[i].h2, HD_FREE, HD_ALIGN)) {
7574  BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, pts[i].h2_loc);
7575  dist_sq = len_squared_v2v2(m_xy, f_xy);
7576  if (dist_sq < dist_min_sq) {
7577  i_selected = i;
7578  selection_type = PROF_H2_SELECT;
7579  dist_min_sq = dist_sq;
7580  }
7581  }
7582  }
7583  }
7584 
7585  /* Add a point if the click was close to the path but not a control point or handle. */
7586  if (i_selected == -1) {
7587  float f_xy[2], f_xy_prev[2];
7588  CurveProfilePoint *table = profile->table;
7589  BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[0].x);
7590 
7591  dist_min_sq = square_f(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */
7592 
7593  /* Loop through the path's high resolution table and find what's near the click. */
7594  for (int i = 1; i <= BKE_curveprofile_table_size(profile); i++) {
7595  copy_v2_v2(f_xy_prev, f_xy);
7596  BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[i].x);
7597 
7598  if (dist_squared_to_line_segment_v2(m_xy, f_xy_prev, f_xy) < dist_min_sq) {
7599  BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
7600 
7601  CurveProfilePoint *new_pt = BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]);
7603 
7604  /* Get the index of the newly added point. */
7605  i_selected = (int)(new_pt - profile->path);
7606  BLI_assert(i_selected >= 0 && i_selected <= profile->path_len);
7607  selection_type = PROF_SELECT;
7608  break;
7609  }
7610  }
7611  }
7612 
7613  /* Change the flag for the point(s) if one was selected or added. */
7614  if (i_selected != -1) {
7615  /* Deselect all if this one is deselected, except if we hold shift. */
7616  if (event->modifier & KM_SHIFT) {
7617  pts[i_selected].flag ^= selection_type;
7618  }
7619  else {
7620  for (int i = 0; i < profile->path_len; i++) {
7621  // pts[i].flag &= ~(PROF_SELECT | PROF_H1_SELECT | PROF_H2_SELECT);
7622  profile->path[i].flag &= ~(PROF_SELECT | PROF_H1_SELECT | PROF_H2_SELECT);
7623  }
7624  profile->path[i_selected].flag |= selection_type;
7625  }
7626  }
7627  else {
7628  /* Move the view. */
7629  data->cancel = true;
7630  }
7631 
7632  data->dragsel = i_selected;
7633 
7634  data->dragstartx = mx;
7635  data->dragstarty = my;
7636  data->draglastx = mx;
7637  data->draglasty = my;
7638 
7640  return WM_UI_HANDLER_BREAK;
7641  }
7642  }
7643  else if (data->state == BUTTON_STATE_NUM_EDITING) { /* Do control point movement. */
7644  if (event->type == MOUSEMOVE) {
7645  if (mx != data->draglastx || my != data->draglasty) {
7647  block, but, data, mx, my, event->modifier & KM_CTRL, event->modifier & KM_SHIFT)) {
7648  ui_numedit_apply(C, block, but, data);
7649  }
7650  }
7651  }
7652  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
7653  /* Finish move. */
7654  if (data->dragsel != -1) {
7655 
7656  if (data->dragchange == false) {
7657  /* Deselect all, select one. */
7658  }
7659  else {
7660  /* Remove doubles, clip after move. */
7662  }
7663  }
7665  }
7666  return WM_UI_HANDLER_BREAK;
7667  }
7668 
7669  return WM_UI_HANDLER_CONTINUE;
7670 }
7671 
7672 static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx, int my)
7673 {
7674  Histogram *hist = (Histogram *)but->poin;
7675  const bool changed = true;
7676  const float dy = my - data->draglasty;
7677 
7678  /* scale histogram values (dy / 10 for better control) */
7679  const float yfac = min_ff(pow2f(hist->ymax), 1.0f) * 0.5f;
7680  hist->ymax += (dy * 0.1f) * yfac;
7681 
7682  /* 0.1 allows us to see HDR colors up to 10 */
7683  CLAMP(hist->ymax, 0.1f, 100.0f);
7684 
7685  data->draglastx = mx;
7686  data->draglasty = my;
7687 
7688  return changed;
7689 }
7690 
7692  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
7693 {
7694  int mx = event->xy[0];
7695  int my = event->xy[1];
7696  ui_window_to_block(data->region, block, &mx, &my);
7697 
7698  if (data->state == BUTTON_STATE_HIGHLIGHT) {
7699  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
7700  data->dragstartx = mx;
7701  data->dragstarty = my;
7702  data->draglastx = mx;
7703  data->draglasty = my;
7705 
7706  /* also do drag the first time */
7707  if (ui_numedit_but_HISTOGRAM(but, data, mx, my)) {
7708  ui_numedit_apply(C, block, but, data);
7709  }
7710 
7711  return WM_UI_HANDLER_BREAK;
7712  }
7713  /* XXX hardcoded keymap check.... */
7714  if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
7715  Histogram *hist = (Histogram *)but->poin;
7716  hist->ymax = 1.0f;
7717 
7719  return WM_UI_HANDLER_BREAK;
7720  }
7721  }
7722  else if (data->state == BUTTON_STATE_NUM_EDITING) {
7723  if (event->type == EVT_ESCKEY) {
7724  if (event->val == KM_PRESS) {
7725  data->cancel = true;
7726  data->escapecancel = true;
7728  }
7729  }
7730  else if (event->type == MOUSEMOVE) {
7731  if (mx != data->draglastx || my != data->draglasty) {
7732  if (ui_numedit_but_HISTOGRAM(but, data, mx, my)) {
7733  ui_numedit_apply(C, block, but, data);
7734  }
7735  }
7736  }
7737  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
7739  }
7740  return WM_UI_HANDLER_BREAK;
7741  }
7742 
7743  return WM_UI_HANDLER_CONTINUE;
7744 }
7745 
7746 static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx, int my)
7747 {
7748  Scopes *scopes = (Scopes *)but->poin;
7749  const bool changed = true;
7750 
7751  const float dy = my - data->draglasty;
7752 
7753  /* scale waveform values */
7754  scopes->wavefrm_yfac += dy / 200.0f;
7755 
7756  CLAMP(scopes->wavefrm_yfac, 0.5f, 2.0f);
7757 
7758  data->draglastx = mx;
7759  data->draglasty = my;
7760 
7761  return changed;
7762 }
7763 
7765  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
7766 {
7767  int mx = event->xy[0];
7768  int my = event->xy[1];
7769  ui_window_to_block(data->region, block, &mx, &my);
7770 
7771  if (data->state == BUTTON_STATE_HIGHLIGHT) {
7772  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
7773  data->dragstartx = mx;
7774  data->dragstarty = my;
7775  data->draglastx = mx;
7776  data->draglasty = my;
7778 
7779  /* also do drag the first time */
7780  if (ui_numedit_but_WAVEFORM(but, data, mx, my)) {
7781  ui_numedit_apply(C, block, but, data);
7782  }
7783 
7784  return WM_UI_HANDLER_BREAK;
7785  }
7786  /* XXX hardcoded keymap check.... */
7787  if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
7788  Scopes *scopes = (Scopes *)but->poin;
7789  scopes->wavefrm_yfac = 1.0f;
7790 
7792  return WM_UI_HANDLER_BREAK;
7793  }
7794  }
7795  else if (data->state == BUTTON_STATE_NUM_EDITING) {
7796  if (event->type == EVT_ESCKEY) {
7797  if (event->val == KM_PRESS) {
7798  data->cancel = true;
7799  data->escapecancel = true;
7801  }
7802  }
7803  else if (event->type == MOUSEMOVE) {
7804  if (mx != data->draglastx || my != data->draglasty) {
7805  if (ui_numedit_but_WAVEFORM(but, data, mx, my)) {
7806  ui_numedit_apply(C, block, but, data);
7807  }
7808  }
7809  }
7810  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
7812  }
7813  return WM_UI_HANDLER_BREAK;
7814  }
7815 
7816  return WM_UI_HANDLER_CONTINUE;
7817 }
7818 
7820  bContext *C, uiBut *but, uiHandleButtonData *data, int mx, int my, const bool shift)
7821 {
7822  MovieClipScopes *scopes = (MovieClipScopes *)but->poin;
7823  const bool changed = true;
7824 
7825  float dx = mx - data->draglastx;
7826  float dy = my - data->draglasty;
7827 
7828  if (shift) {
7829  dx /= 5.0f;
7830  dy /= 5.0f;
7831  }
7832 
7833  if (!scopes->track_locked) {
7834  const MovieClip *clip = CTX_data_edit_movieclip(C);
7835  const int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, scopes->scene_framenr);
7836  if (scopes->marker->framenr != clip_framenr) {
7837  scopes->marker = BKE_tracking_marker_ensure(scopes->track, clip_framenr);
7838  }
7839 
7840  scopes->marker->flag &= ~(MARKER_DISABLED | MARKER_TRACKED);
7841  scopes->marker->pos[0] += -dx * scopes->slide_scale[0] / BLI_rctf_size_x(&but->block->rect);
7842  scopes->marker->pos[1] += -dy * scopes->slide_scale[1] / BLI_rctf_size_y(&but->block->rect);
7843 
7845  }
7846 
7847  scopes->ok = 0;
7848 
7849  data->draglastx = mx;
7850  data->draglasty = my;
7851 
7852  return changed;
7853 }
7854 
7856  bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
7857 {
7858  int mx = event->xy[0];
7859  int my = event->xy[1];
7860  ui_window_to_block(data->region, block, &mx, &my);
7861 
7862  if (data->state == BUTTON_STATE_HIGHLIGHT) {
7863  if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
7864  data->dragstartx = mx;
7865  data->dragstarty = my;
7866  data->draglastx = mx;
7867  data->draglasty = my;
7869 
7870  /* also do drag the first time */
7871  if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->modifier & KM_SHIFT)) {
7872  ui_numedit_apply(C, block, but, data);
7873  }
7874 
7875  return WM_UI_HANDLER_BREAK;
7876  }
7877  }
7878  else if (data->state == BUTTON_STATE_NUM_EDITING) {
7879  if (event->type == EVT_ESCKEY) {
7880  if (event->val == KM_PRESS) {
7881  data->cancel = true;
7882  data->escapecancel = true;
7884  }
7885  }
7886  else if (event->type == MOUSEMOVE) {
7887  if (mx != data->draglastx || my != data->draglasty) {
7888  if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->modifier & KM_SHIFT)) {
7889  ui_numedit_apply(C, block, but, data);
7890  }
7891  }
7892  }
7893  else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
7895  }
7896  return WM_UI_HANDLER_BREAK;
7897  }
7898 
7899  return WM_UI_HANDLER_CONTINUE;
7900 }
7901 
7902 static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *event)
7903 {
7904  uiHandleButtonData *data = but->active;
7905  int retval = WM_UI_HANDLER_CONTINUE;
7906 
7907  const bool is_disabled = but->flag & UI_BUT_DISABLED || data->disable_force;
7908 
7909  /* if but->pointype is set, but->poin should be too */
7910  BLI_assert(!but->pointype || but->poin);
7911 
7912  /* Only hard-coded stuff here, button interactions with configurable
7913  * keymaps are handled using operators (see #ED_keymap_ui). */
7914 
7915  if (data->state == BUTTON_STATE_HIGHLIGHT) {
7916 
7917  /* handle copy and paste */
7918  bool is_press_ctrl_but_no_shift = (event->val == KM_PRESS) &&
7919  (event->modifier & (KM_CTRL | KM_OSKEY)) &&
7920  (event->modifier & KM_SHIFT) == 0;
7921  const bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift;
7922  const bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift;
7923 
7924  /* Specific handling for list-rows, we try to find their overlapping text button. */
7925  if ((do_copy || do_paste) && but->type == UI_BTYPE_LISTROW) {
7926  uiBut *labelbut = ui_but_list_row_text_activate(C, but, data, event, BUTTON_ACTIVATE_OVER);
7927  if (labelbut) {
7928  but = labelbut;
7929  data = but->active;
7930  }
7931  }
7932 
7933  /* do copy first, because it is the only allowed operator when disabled */
7934  if (do_copy) {
7935  ui_but_copy(C, but, event->modifier & KM_ALT);
7936  return WM_UI_HANDLER_BREAK;
7937  }
7938 
7939  /* handle menu */
7940 
7941  if ((event->type == RIGHTMOUSE) &&
7942  (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0 &&
7943  (event->val == KM_PRESS)) {
7944  /* For some button types that are typically representing entire sets of data, right-clicking
7945  * to spawn the context menu should also activate the item. This makes it clear which item
7946  * will be operated on.
7947  * Apply the button immediately, so context menu polls get the right active item. */
7948  if (ELEM(but->type, UI_BTYPE_VIEW_ITEM)) {
7949  ui_apply_but(C, but->block, but, but->active, true);
7950  }
7951 
7952  /* RMB has two options now */
7953  if (ui_popup_context_menu_for_button(C, but, event)) {
7954  return WM_UI_HANDLER_BREAK;
7955  }
7956  }
7957 
7958  if (is_disabled) {
7959  return WM_UI_HANDLER_CONTINUE;
7960  }
7961 
7962  if (do_paste) {
7963  ui_but_paste(C, but, data, event->modifier & KM_ALT);
7964  return WM_UI_HANDLER_BREAK;
7965  }
7966 
7967  if ((data->state == BUTTON_STATE_HIGHLIGHT) &&
7969  (event->val == KM_RELEASE) &&
7970  /* Only returns true if the event was handled. */
7971  ui_do_but_extra_operator_icon(C, but, data, event)) {
7972  return WM_UI_HANDLER_BREAK;
7973  }
7974  }
7975 
7976  if (but->flag & UI_BUT_DISABLED) {
7977  /* It's important to continue here instead of breaking since breaking causes the event to be
7978  * considered "handled", preventing further click/drag events from being generated.
7979  *
7980  * An example of where this is needed is dragging node-sockets, where dragging a node-socket
7981  * could exit the button before the drag threshold was reached, disable the button then break
7982  * handling of the #MOUSEMOVE event preventing the socket being dragged entirely, see: T96255.
7983  *
7984  * Region level event handling is responsible for preventing events being passed
7985  * through to parts of the UI that are logically behind this button, see: T92364. */
7986  return WM_UI_HANDLER_CONTINUE;
7987  }
7988 
7989  switch (but->type) {
7990  case UI_BTYPE_BUT:
7991  case UI_BTYPE_DECORATOR:
7992  retval = ui_do_but_BUT(C, but, data, event);
7993  break;
7994  case UI_BTYPE_KEY_EVENT:
7995  retval = ui_do_but_KEYEVT(C, but, data, event);
7996  break;
7997  case UI_BTYPE_HOTKEY_EVENT:
7998  retval = ui_do_but_HOTKEYEVT(C, but, data, event);
7999  break;
8000  case UI_BTYPE_TAB:
8001  retval = ui_do_but_TAB(C, block, but, data, event);
8002  break;
8003  case UI_BTYPE_BUT_TOGGLE:
8004  case UI_BTYPE_TOGGLE:
8005  case UI_BTYPE_ICON_TOGGLE:
8007  case UI_BTYPE_TOGGLE_N:
8008  case UI_BTYPE_CHECKBOX:
8009  case UI_BTYPE_CHECKBOX_N:
8010  case UI_BTYPE_ROW:
8011  retval = ui_do_but_TOG(C, but, data, event);
8012  break;
8013  case UI_BTYPE_VIEW_ITEM:
8014  retval = ui_do_but_VIEW_ITEM(C, but, data, event);
8015  break;
8016  case UI_BTYPE_SCROLL:
8017  retval = ui_do_but_SCROLL(C, block, but, data, event);
8018  break;
8019  case UI_BTYPE_GRIP:
8020  retval = ui_do_but_GRIP(C, block, but, data, event);
8021  break;
8022  case UI_BTYPE_NUM:
8023  retval = ui_do_but_NUM(C, block, but, data, event);
8024  break;
8025  case UI_BTYPE_NUM_SLIDER:
8026  retval = ui_do_but_SLI(C, block, but, data, event);
8027  break;
8028  case UI_BTYPE_LISTBOX:
8029  /* Nothing to do! */
8030  break;
8031  case UI_BTYPE_LISTROW:
8032  retval = ui_do_but_LISTROW(C, but, data, event);
8033  break;
8034  case UI_BTYPE_ROUNDBOX:
8035  case UI_BTYPE_LABEL:
8036  case UI_BTYPE_IMAGE:
8037  case UI_BTYPE_PROGRESS_BAR:
8038  case UI_BTYPE_NODE_SOCKET:
8039  case UI_BTYPE_PREVIEW_TILE:
8040  retval = ui_do_but_EXIT(C, but, data, event);
8041  break;
8042  case UI_BTYPE_HISTOGRAM:
8043  retval = ui_do_but_HISTOGRAM(C, block, but, data, event);
8044  break;
8045  case UI_BTYPE_WAVEFORM:
8046  retval = ui_do_but_WAVEFORM(C, block, but, data, event);
8047  break;
8048  case UI_BTYPE_VECTORSCOPE:
8049  /* Nothing to do! */
8050  break;
8051  case UI_BTYPE_TEXT:
8052  case UI_BTYPE_SEARCH_MENU:
8053  if ((but->type == UI_BTYPE_SEARCH_MENU) && (but->flag & UI_BUT_VALUE_CLEAR)) {
8054  retval = ui_do_but_SEARCH_UNLINK(C, block, but, data, event);
8055  if (retval & WM_UI_HANDLER_BREAK) {
8056  break;
8057  }
8058  }
8059  retval = ui_do_but_TEX(C, block, but, data, event);
8060  break;
8061  case UI_BTYPE_MENU:
8062  case UI_BTYPE_POPOVER:
8063  case UI_BTYPE_BLOCK:
8064  case UI_BTYPE_PULLDOWN:
8065  retval = ui_do_but_BLOCK(C, but, data, event);
8066  break;
8067  case UI_BTYPE_BUT_MENU:
8068  retval = ui_do_but_BUT(C, but, data, event);
8069  break;
8070  case UI_BTYPE_COLOR:
8071  retval = ui_do_but_COLOR(C, but, data, event);
8072  break;
8073  case UI_BTYPE_UNITVEC:
8074  retval = ui_do_but_UNITVEC(C, block, but, data, event);
8075  break;
8076  case UI_BTYPE_COLORBAND:
8077  retval = ui_do_but_COLORBAND(C, block, but, data, event);
8078  break;
8079  case UI_BTYPE_CURVE:
8080  retval = ui_do_but_CURVE(C, block, but, data, event);
8081  break;
8082  case UI_BTYPE_CURVEPROFILE:
8083  retval = ui_do_but_CURVEPROFILE(C, block, but, data, event);
8084  break;
8085  case UI_BTYPE_HSVCUBE:
8086  retval = ui_do_but_HSVCUBE(C, block, but, data, event);
8087  break;
8088  case UI_BTYPE_HSVCIRCLE:
8089  retval = ui_do_but_HSVCIRCLE(C, block, but, data, event);
8090  break;
8092  retval = ui_do_but_TRACKPREVIEW(C, block, but, data, event);
8093  break;
8094 
8095  /* quiet warnings for unhandled types */
8096  case UI_BTYPE_SEPR:
8097  case UI_BTYPE_SEPR_LINE:
8098  case UI_BTYPE_SEPR_SPACER:
8099  case UI_BTYPE_EXTRA:
8100  break;
8101  }
8102 
8103 #ifdef USE_DRAG_MULTINUM
8104  data = but->active;
8105  if (data) {
8106  if (ISMOUSE_MOTION(event->type) ||
8107  /* if we started dragging, progress on any event */
8108  (data->multi_data.init == BUTTON_MULTI_INIT_SETUP)) {
8109  if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
8111  /* initialize! */
8112  if (data->multi_data.init == BUTTON_MULTI_INIT_UNSET) {
8113  /* --> (BUTTON_MULTI_INIT_SETUP | BUTTON_MULTI_INIT_DISABLE) */
8114 
8115  const float margin_y = DRAG_MULTINUM_THRESHOLD_DRAG_Y / sqrtf(block->aspect);
8116 
8117  /* check if we have a vertical gesture */
8118  if (len_squared_v2(data->multi_data.drag_dir) > (margin_y * margin_y)) {
8119  const float dir_nor_y[2] = {0.0, 1.0f};
8120  float dir_nor_drag[2];
8121 
8122  normalize_v2_v2(dir_nor_drag, data->multi_data.drag_dir);
8123 
8124  if (fabsf(dot_v2v2(dir_nor_drag, dir_nor_y)) > DRAG_MULTINUM_THRESHOLD_VERTICAL) {
8125  data->multi_data.init = BUTTON_MULTI_INIT_SETUP;
8126  data->multi_data.drag_lock_x = event->xy[0];
8127  }
8128  else {
8129  data->multi_data.init = BUTTON_MULTI_INIT_DISABLE;
8130  }
8131  }
8132  }
8133  else if (data->multi_data.init == BUTTON_MULTI_INIT_SETUP) {
8134  /* --> (BUTTON_MULTI_INIT_ENABLE) */
8135  const float margin_x = DRAG_MULTINUM_THRESHOLD_DRAG_X / sqrtf(block->aspect);
8136  /* Check if we're don't setting buttons. */
8137  if ((data->str &&
8139  ((abs(data->multi_data.drag_lock_x - event->xy[0]) > margin_x) &&
8140  /* Just to be sure, check we're dragging more horizontally then vertically. */
8141  abs(event->prev_xy[0] - event->xy[0]) > abs(event->prev_xy[1] - event->xy[1]))) {
8142  if (data->multi_data.has_mbuts) {
8144  data->multi_data.init = BUTTON_MULTI_INIT_ENABLE;
8145  }
8146  else {
8147  data->multi_data.init = BUTTON_MULTI_INIT_DISABLE;
8148  }
8149  }
8150  }
8151 
8152  if (data->multi_data.init == BUTTON_MULTI_INIT_SETUP) {
8153  if (ui_multibut_states_tag(but, data, event)) {
8154  ED_region_tag_redraw(data->region);
8155  }
8156  }
8157  }
8158  }
8159  }
8160 #endif /* USE_DRAG_MULTINUM */
8161 
8162  return retval;
8163 }
8164 
8167 /* -------------------------------------------------------------------- */
8171 static void ui_blocks_set_tooltips(ARegion *region, const bool enable)
8172 {
8173  if (!region) {
8174  return;
8175  }
8176 
8177  /* We disabled buttons when they were already shown, and re-enable them on mouse move. */
8178  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
8179  block->tooltipdisabled = !enable;
8180  }
8181 }
8182 
8184 {
8185  uiHandleButtonData *data = but->active;
8186  if (data) {
8187  bScreen *screen = WM_window_get_active_screen(data->window);
8188  if (screen->tool_tip && screen->tool_tip->region) {
8189  WM_tooltip_refresh(C, data->window);
8190  }
8191  }
8192 }
8193 
8195 {
8196  uiHandleButtonData *data = but->active;
8197  if (data) {
8198  if (data->autoopentimer) {
8199  WM_event_remove_timer(data->wm, data->window, data->autoopentimer);
8200  data->autoopentimer = NULL;
8201  }
8202 
8203  if (data->window) {
8204  WM_tooltip_clear(C, data->window);
8205  }
8206  }
8207 }
8208 
8210  bContext *C, ARegion *region, int *pass, double *r_pass_delay, bool *r_exit_on_event)
8211 {
8212  bool is_label = false;
8213  if (*pass == 1) {
8214  is_label = true;
8215  (*pass)--;
8216  (*r_pass_delay) = UI_TOOLTIP_DELAY - UI_TOOLTIP_DELAY_LABEL;
8217  }
8218 
8219  uiBut *but = UI_region_active_but_get(region);
8220  *r_exit_on_event = false;
8221  if (but) {
8222  const wmWindow *win = CTX_wm_window(C);
8224  but, but->active ? but->active->region : region, win->eventstate);
8225 
8226  return UI_tooltip_create_from_button_or_extra_icon(C, region, but, extra_icon, is_label);
8227  }
8228  return NULL;
8229 }
8230 
8232 {
8234  uiHandleButtonData *data = but->active;
8235 
8236  WM_tooltip_timer_clear(C, data->window);
8237 
8238  if ((U.flag & USER_TOOLTIPS) || (data->tooltip_force)) {
8239  if (!but->block->tooltipdisabled) {
8240  if (!wm->drags.first) {
8241  const bool is_label = UI_but_has_tooltip_label(but);
8242  const double delay = is_label ? UI_TOOLTIP_DELAY_LABEL : UI_TOOLTIP_DELAY;
8244  C, data->window, data->area, data->region, ui_but_tooltip_init, delay);
8245  if (is_label) {
8246  bScreen *screen = WM_window_get_active_screen(data->window);
8247  if (screen->tool_tip) {
8248  screen->tool_tip->pass = 1;
8249  }
8250  }
8251  }
8252  }
8253  }
8254 }
8255 
8258 /* -------------------------------------------------------------------- */
8263 {
8264  return ELEM(state,
8271 }
8272 
8274 {
8275  uiHandleButtonData *data = but->active;
8276  if (data->state == state) {
8277  return;
8278  }
8279 
8280  /* Highlight has timers for tool-tips and auto open. */
8281  if (state == BUTTON_STATE_HIGHLIGHT) {
8282  but->flag &= ~UI_SELECT;
8283 
8285 
8286  /* Automatic open pull-down block timer. */
8288  /* Menu button types may draw as popovers, check for this case
8289  * ignoring other kinds of menus (mainly enums). (see T66538). */
8290  ((but->type == UI_BTYPE_MENU) &&
8292  if (data->used_mouse && !data->autoopentimer) {
8293  int time;
8294 
8295  if (but->block->auto_open == true) { /* test for toolbox */
8296  time = 1;
8297  }
8298  else if ((but->block->flag & UI_BLOCK_LOOP && but->type != UI_BTYPE_BLOCK) ||
8299  (but->block->auto_open == true)) {
8300  time = 5 * U.menuthreshold2;
8301  }
8302  else if (U.uiflag & USER_MENUOPENAUTO) {
8303  time = 5 * U.menuthreshold1;
8304  }
8305  else {
8306  time = -1; /* do nothing */
8307  }
8308 
8309  if (time >= 0) {
8310  data->autoopentimer = WM_event_add_timer(
8311  data->wm, data->window, TIMER, 0.02 * (double)time);
8312  }
8313  }
8314  }
8315  }
8316  else {
8317  but->flag |= UI_SELECT;
8319  }
8320 
8321  /* text editing */
8323  ui_textedit_begin(C, but, data);
8324  }
8326  ui_textedit_end(C, but, data);
8327  }
8329  ui_textedit_end(C, but, data);
8330  }
8331 
8332  /* number editing */
8334  if (ui_but_is_cursor_warp(but)) {
8336  }
8337  ui_numedit_begin(but, data);
8338  }
8339  else if (data->state == BUTTON_STATE_NUM_EDITING) {
8340  ui_numedit_end(but, data);
8341 
8342  if (but->flag & UI_BUT_DRIVEN) {
8343  /* Only warn when editing stepping/dragging the value.
8344  * No warnings should show for editing driver expressions though!
8345  */
8348  "Can't edit driven number value, see graph editor for the driver setup.");
8349  }
8350  }
8351 
8352  if (ui_but_is_cursor_warp(but)) {
8353 
8354 #ifdef USE_CONT_MOUSE_CORRECT
8355  /* stereo3d has issues with changing cursor location so rather avoid */
8356  if (data->ungrab_mval[0] != FLT_MAX && !WM_stereo3d_enabled(data->window, false)) {
8357  int mouse_ungrab_xy[2];
8359  data->region, but->block, &data->ungrab_mval[0], &data->ungrab_mval[1]);
8360  mouse_ungrab_xy[0] = data->ungrab_mval[0];
8361  mouse_ungrab_xy[1] = data->ungrab_mval[1];
8362 
8363  WM_cursor_grab_disable(data->window, mouse_ungrab_xy);
8364  }
8365  else {
8366  WM_cursor_grab_disable(data->window, NULL);
8367  }
8368 #else
8369  WM_cursor_grab_disable(data->window, NULL);
8370 #endif
8371  }
8372  }
8373  /* menu open */
8374  if (state == BUTTON_STATE_MENU_OPEN) {
8375  ui_block_open_begin(C, but, data);
8376  }
8377  else if (data->state == BUTTON_STATE_MENU_OPEN) {
8378  ui_block_open_end(C, but, data);
8379  }
8380 
8381  /* add a short delay before exiting, to ensure there is some feedback */
8382  if (state == BUTTON_STATE_WAIT_FLASH) {
8383  data->flashtimer = WM_event_add_timer(data->wm, data->window, TIMER, BUTTON_FLASH_DELAY);
8384  }
8385  else if (data->flashtimer) {
8386  WM_event_remove_timer(data->wm, data->window, data->flashtimer);
8387  data->flashtimer = NULL;
8388  }
8389 
8390  /* add hold timer if it's used */
8391  if (state == BUTTON_STATE_WAIT_RELEASE && (but->hold_func != NULL)) {
8392  data->hold_action_timer = WM_event_add_timer(
8393  data->wm, data->window, TIMER, BUTTON_AUTO_OPEN_THRESH);
8394  }
8395  else if (data->hold_action_timer) {
8396  WM_event_remove_timer(data->wm, data->window, data->hold_action_timer);
8397  data->hold_action_timer = NULL;
8398  }
8399 
8400  /* Add a blocking ui handler at the window handler for blocking, modal states
8401  * but not for popups, because we already have a window level handler. */
8402  if (!(but->block->handle && but->block->handle->popup)) {
8403  if (button_modal_state(state)) {
8404  if (!button_modal_state(data->state)) {
8406  C, &data->window->modalhandlers, ui_handler_region_menu, NULL, data, 0);
8407  }
8408  }
8409  else {
8410  if (button_modal_state(data->state)) {
8411  /* true = postpone free */
8413  &data->window->modalhandlers, ui_handler_region_menu, NULL, data, true);
8414  }
8415  }
8416  }
8417 
8418  /* Wait for mouse-move to enable drag. */
8419  if (state == BUTTON_STATE_WAIT_DRAG) {
8420  but->flag &= ~UI_SELECT;
8421  }
8422 
8425  }
8426  else if (state == BUTTON_STATE_EXIT) {
8427  if (data->state == BUTTON_STATE_NUM_EDITING) {
8428  /* This happens on pasting values for example. */
8430  }
8431  }
8432 
8433  data->state = state;
8434 
8435  if (state != BUTTON_STATE_EXIT) {
8436  /* When objects for eg. are removed, running ui_but_update() can access
8437  * the removed data - so disable update on exit. Also in case of
8438  * highlight when not in a popup menu, we remove because data used in
8439  * button below popup might have been removed by action of popup. Needs
8440  * a more reliable solution... */
8441  if (state != BUTTON_STATE_HIGHLIGHT || (but->block->flag & UI_BLOCK_LOOP)) {
8442  ui_but_update(but);
8443  }
8444  }
8445 
8446  /* redraw */
8448 }
8449 
8451  ARegion *region,
8452  uiBut *but,
8454 {
8455  /* Only ever one active button! */
8457 
8458  /* setup struct */
8459  uiHandleButtonData *data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData");
8460  data->wm = CTX_wm_manager(C);
8461  data->window = CTX_wm_window(C);
8462  data->area = CTX_wm_area(C);
8463  BLI_assert(region != NULL);
8464  data->region = region;
8465 
8466 #ifdef USE_CONT_MOUSE_CORRECT
8467  copy_v2_fl(data->ungrab_mval, FLT_MAX);
8468 #endif
8469 
8471  /* XXX curve is temp */
8472  }
8473  else {
8474  if ((but->flag & UI_BUT_UPDATE_DELAY) == 0) {
8475  data->interactive = true;
8476  }
8477  }
8478 
8479  data->state = BUTTON_STATE_INIT;
8480 
8481  /* activate button */
8482  but->flag |= UI_ACTIVE;
8483 
8484  but->active = data;
8485 
8486  /* we disable auto_open in the block after a threshold, because we still
8487  * want to allow auto opening adjacent menus even if no button is activated
8488  * in between going over to the other button, but only for a short while */
8489  if (type == BUTTON_ACTIVATE_OVER && but->block->auto_open == true) {
8491  but->block->auto_open = false;
8492  }
8493  }
8494 
8495  if (type == BUTTON_ACTIVATE_OVER) {
8496  data->used_mouse = true;
8497  }
8499 
8500  if (type == BUTTON_ACTIVATE_OPEN) {
8502 
8503  /* activate first button in submenu */
8504  if (data->menu && data->menu->region) {
8505  ARegion *subar = data->menu->region;
8506  uiBlock *subblock = subar->uiblocks.first;
8507  uiBut *subbut;
8508 
8509  if (subblock) {
8510  subbut = ui_but_first(subblock);
8511 
8512  if (subbut) {
8513  ui_handle_button_activate(C, subar, subbut, BUTTON_ACTIVATE);
8514  }
8515  }
8516  }
8517  }
8518  else if (type == BUTTON_ACTIVATE_TEXT_EDITING) {
8520  }
8521  else if (type == BUTTON_ACTIVATE_APPLY) {
8523  }
8524 
8525  if (but->type == UI_BTYPE_GRIP) {
8526  const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect));
8527  WM_cursor_modal_set(data->window, horizontal ? WM_CURSOR_X_MOVE : WM_CURSOR_Y_MOVE);
8528  }
8529  else if (but->type == UI_BTYPE_NUM) {
8530  ui_numedit_set_active(but);
8531  }
8532 
8533  if (UI_but_has_tooltip_label(but)) {
8534  /* Show a label for this button. */
8535  bScreen *screen = WM_window_get_active_screen(data->window);
8536  if ((PIL_check_seconds_timer() - WM_tooltip_time_closed()) < 0.1) {
8538  if (screen->tool_tip) {
8539  screen->tool_tip->pass = 1;
8540  }
8541  }
8542  }
8543 }
8544 
8546  bContext *C, uiBut *but, uiHandleButtonData *data, const bool mousemove, const bool onfree)
8547 {
8548  wmWindow *win = data->window;
8549  uiBlock *block = but->block;
8550 
8551  if (but->type == UI_BTYPE_GRIP) {
8553  }
8554 
8555  /* ensure we are in the exit state */
8556  if (data->state != BUTTON_STATE_EXIT) {
8558  }
8559 
8560  /* apply the button action or value */
8561  if (!onfree) {
8562  ui_apply_but(C, block, but, data, false);
8563  }
8564 
8565 #ifdef USE_DRAG_MULTINUM
8566  if (data->multi_data.has_mbuts) {
8567  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
8568  if (bt->flag & UI_BUT_DRAG_MULTI) {
8569  bt->flag &= ~UI_BUT_DRAG_MULTI;
8570 
8571  if (!data->cancel) {
8572  ui_apply_but_autokey(C, bt);
8573  }
8574  }
8575  }
8576 
8577  ui_multibut_free(data, block);
8578  }
8579 #endif
8580 
8581  /* if this button is in a menu, this will set the button return
8582  * value to the button value and the menu return value to ok, the
8583  * menu return value will be picked up and the menu will close */
8584  if (block->handle && !(block->flag & UI_BLOCK_KEEP_OPEN)) {
8585  if (!data->cancel || data->escapecancel) {
8586  uiPopupBlockHandle *menu;
8587 
8588  menu = block->handle;
8589  menu->butretval = data->retval;
8590  menu->menuretval = (data->cancel) ? UI_RETURN_CANCEL : UI_RETURN_OK;
8591  }
8592  }
8593 
8594  if (!onfree && !data->cancel) {
8595  /* autokey & undo push */
8596  ui_apply_but_undo(but);
8597  ui_apply_but_autokey(C, but);
8598 
8599 #ifdef USE_ALLSELECT
8600  {
8601  /* only RNA from this button is used */
8602  uiBut but_temp = *but;
8603  uiSelectContextStore *selctx_data = &data->select_others;
8604  for (int i = 0; i < selctx_data->elems_len; i++) {
8605  uiSelectContextElem *other = &selctx_data->elems[i];
8606  but_temp.rnapoin = other->ptr;
8607  ui_apply_but_autokey(C, &but_temp);
8608  }
8609  }
8610 #endif
8611 
8612  /* popup menu memory */
8613  if (block->flag & UI_BLOCK_POPUP_MEMORY) {
8614  ui_popup_menu_memory_set(block, but);
8615  }
8616 
8617  if (U.runtime.is_dirty == false) {
8619  }
8620  }
8621 
8622  /* Disable tool-tips until mouse-move + last active flag. */
8623  LISTBASE_FOREACH (uiBlock *, block_iter, &data->region->uiblocks) {
8624  LISTBASE_FOREACH (uiBut *, bt, &block_iter->buttons) {
8625  bt->flag &= ~UI_BUT_LAST_ACTIVE;
8626  }
8627 
8628  block_iter->tooltipdisabled = true;
8629  }
8630 
8631  ui_blocks_set_tooltips(data->region, false);
8632 
8633  /* clean up */
8634  if (data->str) {
8635  MEM_freeN(data->str);
8636  }
8637  if (data->origstr) {
8638  MEM_freeN(data->origstr);
8639  }
8640 
8641 #ifdef USE_ALLSELECT
8642  ui_selectcontext_end(but, &data->select_others);
8643 #endif
8644 
8645  if (data->changed_cursor) {
8646  WM_cursor_modal_restore(data->window);
8647  }
8648 
8649  /* redraw and refresh (for popups) */
8651  ED_region_tag_refresh_ui(data->region);
8652 
8653  if ((but->flag & UI_BUT_DRAG_MULTI) == 0) {
8654  if (data->custom_interaction_handle != NULL) {
8655  /* Should only set when the button is modal. */
8656  BLI_assert(but->active != NULL);
8657  data->custom_interaction_handle->user_count--;
8658 
8659  BLI_assert(data->custom_interaction_handle->user_count >= 0);
8660  if (data->custom_interaction_handle->user_count == 0) {
8662  C, &but->block->custom_interaction_callbacks, data->custom_interaction_handle);
8663  }
8664  data->custom_interaction_handle = NULL;
8665  }
8666  }
8667 
8668  /* clean up button */
8669  MEM_SAFE_FREE(but->active);
8670 
8671  but->flag &= ~(UI_ACTIVE | UI_SELECT);
8672  but->flag |= UI_BUT_LAST_ACTIVE;
8673  if (!onfree) {
8674  ui_but_update(but);
8675  }
8676 
8677  /* Adds empty mouse-move in queue for re-initialize handler, in case mouse is
8678  * still over a button. We cannot just check for this ourselves because
8679  * at this point the mouse may be over a button in another region. */
8680  if (mousemove) {
8682  }
8683 }
8684 
8686 {
8687  /* this gets called when the button somehow disappears while it is still
8688  * active, this is bad for user interaction, but we need to handle this
8689  * case cleanly anyway in case it happens */
8690  if (but->active) {
8691  uiHandleButtonData *data = but->active;
8692  data->cancel = true;
8693  button_activate_exit((bContext *)C, but, data, false, true);
8694  }
8695 }
8696 
8697 /* returns the active button with an optional checking function */
8698 static uiBut *ui_context_button_active(const ARegion *region, bool (*but_check_cb)(const uiBut *))
8699 {
8700  uiBut *but_found = NULL;
8701 
8702  while (region) {
8703  /* Follow this exact priority (from highest to lowest priority):
8704  * 1) Active-override button (#UI_BUT_ACTIVE_OVERRIDE).
8705  * 2) The real active button.
8706  * 3) The previously active button (#UI_BUT_LAST_ACTIVE).
8707  */
8708  uiBut *active_but_override = NULL;
8709  uiBut *active_but_real = NULL;
8710  uiBut *active_but_last = NULL;
8711 
8712  /* find active button */
8713  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
8714  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
8715  if (but->flag & UI_BUT_ACTIVE_OVERRIDE) {
8716  active_but_override = but;
8717  }
8718  if (but->active) {
8719  active_but_real = but;
8720  }
8721  if (but->flag & UI_BUT_LAST_ACTIVE) {
8722  active_but_last = but;
8723  }
8724  }
8725  }
8726 
8727  uiBut *activebut = active_but_override;
8728  if (!activebut) {
8729  activebut = active_but_real;
8730  }
8731  if (!activebut) {
8732  activebut = active_but_last;
8733  }
8734 
8735  if (activebut && (but_check_cb == NULL || but_check_cb(activebut))) {
8736  uiHandleButtonData *data = activebut->active;
8737 
8738  but_found = activebut;
8739 
8740  /* Recurse into opened menu, like color-picker case. */
8741  if (data && data->menu && (region != data->menu->region)) {
8742  region = data->menu->region;
8743  }
8744  else {
8745  return but_found;
8746  }
8747  }
8748  else {
8749  /* no active button */
8750  return but_found;
8751  }
8752  }
8753 
8754  return but_found;
8755 }
8756 
8758 {
8759  return (but->rnapoin.data != NULL);
8760 }
8762 {
8764 }
8765 
8767 {
8769 }
8770 
8772 {
8773  ARegion *region_menu = CTX_wm_menu(C);
8774  return ui_context_button_active(region_menu ? region_menu : CTX_wm_region(C), NULL);
8775 }
8776 
8778 {
8779  return ui_context_button_active(region, NULL);
8780 }
8781 
8782 uiBut *UI_region_but_find_rect_over(const ARegion *region, const rcti *rect_px)
8783 {
8784  return ui_but_find_rect_over(region, rect_px);
8785 }
8786 
8788  const int xy[2],
8789  bool only_clip)
8790 {
8791  return ui_block_find_mouse_over_ex(region, xy, only_clip);
8792 }
8793 
8795  struct PointerRNA *r_ptr,
8796  struct PropertyRNA **r_prop,
8797  int *r_index)
8798 {
8799  uiBut *activebut = ui_context_rna_button_active(C);
8800 
8801  if (activebut && activebut->rnapoin.data) {
8802  *r_ptr = activebut->rnapoin;
8803  *r_prop = activebut->rnaprop;
8804  *r_index = activebut->rnaindex;
8805  }
8806  else {
8807  memset(r_ptr, 0, sizeof(*r_ptr));
8808  *r_prop = NULL;
8809  *r_index = 0;
8810  }
8811 
8812  return activebut;
8813 }
8814 
8815 void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo)
8816 {
8817  uiBut *activebut = ui_context_rna_button_active(C);
8818  if (activebut) {
8819  /* TODO(campbell): look into a better way to handle the button change
8820  * currently this is mainly so reset defaults works for the
8821  * operator redo panel. */
8822  uiBlock *block = activebut->block;
8823  if (block->handle_func) {
8824  block->handle_func(C, block->handle_func_arg, activebut->retval);
8825  }
8826  if (handle_undo) {
8827  /* Update the button so the undo text uses the correct value. */
8828  ui_but_update(activebut);
8829  ui_apply_but_undo(activebut);
8830  }
8831  }
8832 }
8833 
8835 {
8836  wm_event_handler_ui_cancel_ex(C, win, region, false);
8837 }
8838 
8840 {
8841  ARegion *region_ctx = CTX_wm_region(C);
8842 
8843  /* background mode */
8844  if (region_ctx == NULL) {
8845  return NULL;
8846  }
8847 
8848  /* scan active regions ui */
8849  LISTBASE_FOREACH (uiBlock *, block, &region_ctx->uiblocks) {
8850  if (block->ui_operator) {
8851  return block->ui_operator;
8852  }
8853  }
8854 
8855  /* scan popups */
8856  {
8857  bScreen *screen = CTX_wm_screen(C);
8858 
8859  LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
8860  if (region == region_ctx) {
8861  continue;
8862  }
8863  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
8864  if (block->ui_operator) {
8865  return block->ui_operator;
8866  }
8867  }
8868  }
8869  }
8870 
8871  return NULL;
8872 }
8873 
8875 {
8876  uiBut *but = UI_region_active_but_get(button_region);
8877  return (but != NULL) ? but->active->searchbox : NULL;
8878 }
8879 
8881 {
8883  ARegion *region = CTX_wm_region(C);
8885  const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
8886  depsgraph, (scene) ? scene->r.cfra : 0.0f);
8887 
8888  while (region) {
8889  /* find active button */
8890  uiBut *activebut = NULL;
8891 
8892  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
8893  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
8894  ui_but_anim_flag(but, &anim_eval_context);
8896  if (UI_but_is_decorator(but)) {
8898  }
8899 
8900  ED_region_tag_redraw(region);
8901 
8902  if (but->active) {
8903  activebut = but;
8904  }
8905  else if (!activebut && (but->flag & UI_BUT_LAST_ACTIVE)) {
8906  activebut = but;
8907  }
8908  }
8909  }
8910 
8911  if (activebut) {
8912  /* Always recurse into opened menu, so all buttons update (like color-picker). */
8913  uiHandleButtonData *data = activebut->active;
8914  if (data && data->menu) {
8915  region = data->menu->region;
8916  }
8917  else {
8918  return;
8919  }
8920  }
8921  else {
8922  /* no active button */
8923  return;
8924  }
8925  }
8926 }
8927 
8929 {
8930  uiBut *active_but = ui_block_active_but_get(block);
8931  if (!active_but || !active_but->active || !active_but->changed || active_but->block != block) {
8932  return;
8933  }
8934  /* If there is a search popup attached to the button, don't change the view. The popups don't
8935  * support updating the position to the button position nicely. */
8936  uiHandleButtonData *data = active_but->active;
8937  if (data->searchbox) {
8938  return;
8939  }
8940 
8941  UI_but_ensure_in_view(C, active_but->active->region, active_but);
8942 }
8943 
8946 /* -------------------------------------------------------------------- */
8950 static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event)
8951 {
8952  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
8953  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
8954  if (but == event->customdata) {
8955  return but;
8956  }
8957  }
8958  }
8959  return NULL;
8960 }
8961 
8962 static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *region)
8963 {
8964  if (event->type == MOUSEMOVE) {
8965  const bool labeledit = event->modifier & KM_CTRL;
8966  /* Allow buttons to be activated to show the tool-tip,
8967  * then force-disable them if they're not considered interactive
8968  * so they don't swallow events but can still display tips. */
8969  const bool for_tooltip = true;
8970  uiBut *but = ui_but_find_mouse_over_ex(region, event->xy, labeledit, for_tooltip, NULL, NULL);
8971  if (but) {
8973 
8974  if ((event->modifier & KM_ALT) && but->active) {
8975  /* Display tool-tips if holding Alt on mouse-over when tool-tips are disabled in the
8976  * preferences. */
8977  but->active->tooltip_force = true;
8978  }
8979 
8980  if (but->active && !ui_but_is_interactive(but, labeledit)) {
8981  but->active->disable_force = true;
8982  }
8983  }
8984  }
8985  else if (event->type == EVT_BUT_OPEN) {
8986  uiBut *but = ui_but_find_open_event(region, event);
8987  if (but) {
8989  ui_do_button(C, but->block, but, event);
8990  }
8991  }
8992 
8993  return WM_UI_HANDLER_CONTINUE;
8994 }
8995 
8997 {
8998  wmWindow *win = CTX_wm_window(C);
8999 
9001 
9002  wmEvent event;
9003  wm_event_init_from_window(win, &event);
9004  event.type = EVT_BUT_OPEN;
9005  event.val = KM_PRESS;
9006  event.flag = 0;
9007  event.customdata = but;
9008  event.customdata_free = false;
9009 
9010  ui_do_button(C, but->block, but, &event);
9011 }
9012 
9014 {
9016 }
9017 
9019  struct ARegion *region,
9020  uiBut *but,
9021  void **active_back)
9022 {
9023  BLI_assert(region != NULL);
9024  BLI_assert(BLI_findindex(&region->uiblocks, but->block) != -1);
9025  /* NOTE: ideally we would not have to change 'but->active' however
9026  * some functions we call don't use data (as they should be doing) */
9028  *active_back = but->active;
9029  data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData_Fake");
9030  but->active = data;
9031  BLI_assert(region != NULL);
9032  data->region = region;
9033 }
9034 
9036  struct ARegion *UNUSED(region),
9037  uiBut *but,
9038  void *active_back)
9039 {
9040  ui_apply_but(C, but->block, but, but->active, true);
9041 
9042  if ((but->flag & UI_BUT_DRAG_MULTI) == 0) {
9043  ui_apply_but_autokey(C, but);
9044  }
9045  /* use onfree event so undo is handled by caller and apply is already done above */
9046  button_activate_exit((bContext *)C, but, but->active, false, true);
9047  but->active = active_back;
9048 }
9049 
9051  ARegion *region,
9052  uiBut *but,
9054 {
9055  uiBut *oldbut = ui_region_find_active_but(region);
9056  if (oldbut) {
9057  uiHandleButtonData *data = oldbut->active;
9058  data->cancel = true;
9059  button_activate_exit(C, oldbut, data, false, false);
9060  }
9061 
9062  button_activate_init(C, region, but, type);
9063 }
9064 
9069 {
9070  if (but->type == UI_BTYPE_BUT_MENU) {
9071  /* mainly for operator buttons */
9073  }
9074  else if (ELEM(but->type, UI_BTYPE_BLOCK, UI_BTYPE_PULLDOWN)) {
9075  /* open sub-menus (like right arrow key) */
9077  }
9078  else if (but->type == UI_BTYPE_MENU) {
9079  /* activate menu items */
9081  }
9082  else {
9083 #ifdef DEBUG
9084  printf("%s: error, unhandled type: %u\n", __func__, but->type);
9085 #endif
9086  return false;
9087  }
9088  return true;
9089 }
9090 
9093 /* -------------------------------------------------------------------- */
9097 static bool ui_button_value_default(uiBut *but, double *r_value)
9098 {
9099  if (but->rnaprop != NULL && ui_but_is_rna_valid(but)) {
9100  const int type = RNA_property_type(but->rnaprop);
9101  if (ELEM(type, PROP_FLOAT, PROP_INT)) {
9102  double default_value;
9103  switch (type) {
9104  case PROP_INT:
9105  if (RNA_property_array_check(but->rnaprop)) {
9106  default_value = (double)RNA_property_int_get_default_index(
9107  &but->rnapoin, but->rnaprop, but->rnaindex);
9108  }
9109  else {
9110  default_value = (double)RNA_property_int_get_default(&but->rnapoin, but->rnaprop);
9111  }
9112  break;
9113  case PROP_FLOAT:
9114  if (RNA_property_array_check(but->rnaprop)) {
9116  &but->rnapoin, but->rnaprop, but->rnaindex);
9117  }
9118  else {
9119  default_value = (double)RNA_property_float_get_default(&but->rnapoin, but->rnaprop);
9120  }
9121  break;
9122  }
9123  *r_value = default_value;
9124  return true;
9125  }
9126  }
9127  return false;
9128 }
9129 
9130 static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
9131 {
9132  uiHandleButtonData *data = but->active;
9133  const uiHandleButtonState state_orig = data->state;
9134 
9135  uiBlock *block = but->block;
9136  ARegion *region = data->region;
9137 
9138  int retval = WM_UI_HANDLER_CONTINUE;
9139 
9140  if (data->state == BUTTON_STATE_HIGHLIGHT) {
9141  switch (event->type) {
9142  case WINDEACTIVATE:
9143  case EVT_BUT_CANCEL:
9144  data->cancel = true;
9146  break;
9147 #ifdef USE_UI_POPOVER_ONCE
9148  case LEFTMOUSE: {
9149  if (event->val == KM_RELEASE) {
9150  if (block->flag & UI_BLOCK_POPOVER_ONCE) {
9151  if (!(but->flag & UI_BUT_DISABLED)) {
9152  if (ui_but_is_popover_once_compat(but)) {
9153  data->cancel = false;
9155  retval = WM_UI_HANDLER_BREAK;
9156  /* Cancel because this `but` handles all events and we don't want
9157  * the parent button's update function to do anything.
9158  *
9159  * Causes issues with buttons defined by #uiItemFullR_with_popover. */
9161  }
9162  else if (ui_but_is_editable_as_text(but)) {
9164  retval = WM_UI_HANDLER_BREAK;
9165  }
9166  }
9167  }
9168  }
9169  break;
9170  }
9171 #endif
9172  case MOUSEMOVE: {
9173  uiBut *but_other = ui_but_find_mouse_over(region, event);
9174  bool exit = false;
9175 
9176  /* always deactivate button for pie menus,
9177  * else moving to blank space will leave activated */
9178  if ((!ui_block_is_menu(block) || ui_block_is_pie_menu(block)) &&
9179  !ui_but_contains_point_px(but, region, event->xy)) {
9180  exit = true;
9181  }
9182  else if (but_other && ui_but_is_editable(but_other) && (but_other != but)) {
9183  exit = true;
9184  }
9185 
9186  if (exit) {
9187  data->cancel = true;
9189  }
9190  else if (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1]) {
9191  /* Re-enable tool-tip on mouse move. */
9192  ui_blocks_set_tooltips(region, true);
9194  }
9195 
9196  /* Update extra icons states. */
9198 
9199  break;
9200  }
9201  case TIMER: {
9202  /* Handle menu auto open timer. */
9203  if (event->customdata == data->autoopentimer) {
9204  WM_event_remove_timer(data->wm, data->window, data->autoopentimer);
9205  data->autoopentimer = NULL;
9206 
9207  if (ui_but_contains_point_px(but, region, event->xy) || but->active) {
9209  }
9210  }
9211 
9212  break;
9213  }
9214  /* XXX hardcoded keymap check... but anyway,
9215  * while view changes, tool-tips should be removed */
9216  case WHEELUPMOUSE:
9217  case WHEELDOWNMOUSE:
9218  case MIDDLEMOUSE:
9219  case MOUSEPAN:
9222  default:
9223  break;
9224  }
9225 
9226  /* handle button type specific events */
9227  retval = ui_do_button(C, block, but, event);
9228  }
9229  else if (data->state == BUTTON_STATE_WAIT_RELEASE) {
9230  switch (event->type) {
9231  case WINDEACTIVATE:
9232  data->cancel = true;
9234  break;
9235 
9236  case TIMER: {
9237  if (event->customdata == data->hold_action_timer) {
9238  if (true) {
9239  data->cancel = true;
9241  }
9242  else {
9243  /* Do this so we can still mouse-up, closing the menu and running the button.
9244  * This is nice to support but there are times when the button gets left pressed.
9245  * Keep disabled for now. */
9246  WM_event_remove_timer(data->wm, data->window, data->hold_action_timer);
9247  data->hold_action_timer = NULL;
9248  }
9249  retval = WM_UI_HANDLER_CONTINUE;
9250  but->hold_func(C, data->region, but);
9251  }
9252  break;
9253  }
9254  case MOUSEMOVE: {
9255  /* deselect the button when moving the mouse away */
9256  /* also de-activate for buttons that only show highlights */
9257  if (ui_but_contains_point_px(but, region, event->xy)) {
9258 
9259  /* Drag on a hold button (used in the toolbar) now opens it immediately. */
9260  if (data->hold_action_timer) {
9261  if (but->flag & UI_SELECT) {
9262  if (len_manhattan_v2v2_int(event->xy, event->prev_xy) <=
9264  /* pass */
9265  }
9266  else {
9267  WM_event_remove_timer(data->wm, data->window, data->hold_action_timer);
9268  data->hold_action_timer = WM_event_add_timer(data->wm, data->window, TIMER, 0.0f);
9269  }
9270  }
9271  }
9272 
9273  if (!(but->flag & UI_SELECT)) {
9274  but->flag |= (UI_SELECT | UI_ACTIVE);
9275  data->cancel = false;
9277  }
9278  }
9279  else {
9280  if (but->flag & UI_SELECT) {
9281  but->flag &= ~(UI_SELECT | UI_ACTIVE);
9282  data->cancel = true;
9284  }
9285  }
9286  break;
9287  }
9288  default:
9289  /* otherwise catch mouse release event */
9290  ui_do_button(C, block, but, event);
9291  break;
9292  }
9293 
9294  retval = WM_UI_HANDLER_BREAK;
9295  }
9296  else if (data->state == BUTTON_STATE_WAIT_FLASH) {
9297  switch (event->type) {
9298  case TIMER: {
9299  if (event->customdata == data->flashtimer) {
9301  }
9302  break;
9303  }
9304  }
9305 
9306  retval = WM_UI_HANDLER_CONTINUE;
9307  }
9308  else if (data->state == BUTTON_STATE_MENU_OPEN) {
9309  /* check for exit because of mouse-over another button */
9310  switch (event->type) {
9311  case MOUSEMOVE: {
9312  uiBut *bt;
9313 
9314  if (data->menu && data->menu->region) {
9315  if (ui_region_contains_point_px(data->menu->region, event->xy)) {
9316  break;
9317  }
9318  }
9319 
9320  bt = ui_but_find_mouse_over(region, event);
9321 
9322  if (bt && bt->active != data) {
9323  if (but->type != UI_BTYPE_COLOR) { /* exception */
9324  data->cancel = true;
9325  }
9327  }
9328  break;
9329  }
9330  case RIGHTMOUSE: {
9331  if (event->val == KM_PRESS) {
9332  uiBut *bt = ui_but_find_mouse_over(region, event);
9333  if (bt && bt->active == data) {
9335  }
9336  }
9337  break;
9338  }
9339  }
9340 
9341  ui_do_button(C, block, but, event);
9342  retval = WM_UI_HANDLER_CONTINUE;
9343  }
9344  else {
9345  retval = ui_do_button(C, block, but, event);
9346  // retval = WM_UI_HANDLER_BREAK; XXX why ?
9347  }
9348 
9349  /* may have been re-allocated above (eyedropper for eg) */
9350  data = but->active;
9351  if (data && data->state == BUTTON_STATE_EXIT) {
9352  uiBut *post_but = data->postbut;
9353  const uiButtonActivateType post_type = data->posttype;
9354 
9355  /* Reset the button value when empty text is typed. */
9356  if ((data->cancel == false) && (data->str != NULL) && (data->str[0] == '\0') &&
9358  MEM_SAFE_FREE(data->str);
9359  ui_button_value_default(but, &data->value);
9360 
9361 #ifdef USE_DRAG_MULTINUM
9362  if (data->multi_data.mbuts) {
9363  for (LinkNode *l = data->multi_data.mbuts; l; l = l->next) {
9364  uiButMultiState *state = l->link;
9365  uiBut *but_iter = state->but;
9366  double default_value;
9367 
9368  if (ui_button_value_default(but_iter, &default_value)) {
9369  ui_but_value_set(but_iter, default_value);
9370  }
9371  }
9372  }
9373  data->multi_data.skip = true;
9374 #endif
9375  }
9376 
9377  button_activate_exit(C, but, data, (post_but == NULL), false);
9378 
9379  /* for jumping to the next button with tab while text editing */
9380  if (post_but) {
9381  /* The post_but still has previous ranges (without the changes in active button considered),
9382  * needs refreshing the ranges. */
9383  ui_but_range_set_soft(post_but);
9384  ui_but_range_set_hard(post_but);
9385 
9386  button_activate_init(C, region, post_but, post_type);
9387  }
9388  else if (!((event->type == EVT_BUT_CANCEL) && (event->val == 1))) {
9389  /* XXX issue is because WM_event_add_mousemove(wm) is a bad hack and not reliable,
9390  * if that gets coded better this bypass can go away too.
9391  *
9392  * This is needed to make sure if a button was active,
9393  * it stays active while the mouse is over it.
9394  * This avoids adding mouse-moves, see: T33466. */
9396  if (ui_but_find_mouse_over(region, event) == but) {
9398  }
9399  }
9400  }
9401  }
9402 
9403  return retval;
9404 }
9405 
9413  ARegion *region,
9414  const uiList *ui_list,
9415  const wmEvent *event,
9416  bool activate_dragging)
9417 {
9418  const bool do_drag = activate_dragging && ui_list->dyn_data->custom_drag_optype;
9419 
9420  if (do_drag) {
9421  const uiBut *hovered_but = ui_but_find_mouse_over(region, event);
9423  hovered_but,
9424  ui_list->dyn_data->custom_drag_optype,
9425  &ui_list->dyn_data->custom_drag_opptr)) {
9426  return WM_UI_HANDLER_CONTINUE;
9427  }
9428  }
9429 
9430  int mouse_xy[2];
9431  WM_event_drag_start_xy(event, mouse_xy);
9432 
9433  uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy);
9434  if (listrow) {
9435  wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype;
9436 
9437  /* Hacky: Ensure the custom activate operator is not called when the custom drag operator
9438  * was. Only one should run! */
9439  if (activate_dragging && do_drag) {
9440  ((uiList *)ui_list)->dyn_data->custom_activate_optype = NULL;
9441  }
9442 
9443  /* Simulate click on listrow button itself (which may be overlapped by another button). Also
9444  * calls the custom activate operator (#uiListDyn::custom_activate_optype). */
9445  UI_but_execute(C, region, listrow);
9446 
9447  ((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype;
9448  }
9449 
9450  return WM_UI_HANDLER_BREAK;
9451 }
9452 
9454  const uiList *list,
9455  const ARegion *region,
9456  const wmEvent *event)
9457 {
9458  /* On a tweak event, uses the coordinates from where tweaking was started. */
9459  int mouse_xy[2];
9460  WM_event_drag_start_xy(event, mouse_xy);
9461 
9462  const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, false, NULL, NULL);
9463 
9464  if (list->dyn_data->custom_drag_optype) {
9465  if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) {
9466  return true;
9467  }
9468  }
9469 
9470  return (hovered_but && ui_but_drag_is_draggable(hovered_but));
9471 }
9472 
9474  const uiList *ui_list,
9475  ARegion *region,
9476  const wmEvent *event)
9477 {
9478  if (event->type != LEFTMOUSE) {
9479  return WM_HANDLER_CONTINUE;
9480  }
9481 
9482  int retval = WM_HANDLER_CONTINUE;
9483 
9484  const bool is_draggable = ui_list_is_hovering_draggable_but(C, ui_list, region, event);
9485  bool activate = false;
9486  bool activate_dragging = false;
9487 
9488  if (event->val == KM_CLICK_DRAG) {
9489  if (is_draggable) {
9490  activate_dragging = true;
9491  activate = true;
9492  }
9493  }
9494  /* #KM_CLICK is only sent after an uncaught release event, so the foreground button gets all
9495  * regular events (including mouse presses to start dragging) and this part only kicks in if it
9496  * hasn't handled the release event. Note that if there's no overlaid button, the row selects
9497  * on the press event already via regular #UI_BTYPE_LISTROW handling. */
9498  else if (event->val == KM_CLICK) {
9499  activate = true;
9500  }
9501 
9502  if (activate) {
9503  retval = ui_list_activate_hovered_row(C, region, ui_list, event, activate_dragging);
9504  }
9505 
9506  return retval;
9507 }
9508 
9510  bContext *C, ARegion *region, uiBut *listbox, uiList *ui_list, int index)
9511 {
9512  uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox);
9513  if (new_active_row) {
9514  /* Preferred way to update the active item, also calls the custom activate operator
9515  * (#uiListDyn::custom_activate_optype). */
9516  UI_but_execute(C, region, new_active_row);
9517  }
9518  else {
9519  /* A bit ugly, set the active index in RNA directly. That's because a button that's
9520  * scrolled away in the list box isn't created at all.
9521  * The custom activate operator (#uiListDyn::custom_activate_optype) is not called in this case
9522  * (which may need the row button context). */
9523  RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index);
9524  RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop);
9525  ui_apply_but_undo(listbox);
9526  }
9527 
9528  ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
9529 }
9530 
9531 static int ui_list_get_increment(const uiList *ui_list, const int type, const int columns)
9532 {
9533  int increment = 0;
9534 
9535  /* Handle column offsets for grid layouts. */
9538  increment = (type == EVT_UPARROWKEY) ? -columns : columns;
9539  }
9540  else {
9541  /* Left or right in grid layouts or any direction in single column layouts increments by 1. */
9542  increment = ELEM(type, EVT_UPARROWKEY, EVT_LEFTARROWKEY, WHEELUPMOUSE) ? -1 : 1;
9543  }
9544 
9545  if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) {
9546  increment *= -1;
9547  }
9548 
9549  return increment;
9550 }
9551 
9552 static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox)
9553 {
9554  int retval = WM_UI_HANDLER_CONTINUE;
9555  int type = event->type, val = event->val;
9556  int scroll_dir = 1;
9557  bool redraw = false;
9558 
9559  uiList *ui_list = listbox->custom_data;
9560  if (!ui_list || !ui_list->dyn_data) {
9561  return retval;
9562  }
9563  uiListDyn *dyn_data = ui_list->dyn_data;
9564 
9565  int mx = event->xy[0];
9566  int my = event->xy[1];
9567  ui_window_to_block(region, listbox->block, &mx, &my);
9568 
9569  /* Convert pan to scroll-wheel. */
9570  if (type == MOUSEPAN) {
9571  ui_pan_to_scroll(event, &type, &val);
9572 
9573  /* 'ui_pan_to_scroll' gives the absolute direction. */
9574  if (event->flag & WM_EVENT_SCROLL_INVERT) {
9575  scroll_dir = -1;
9576  }
9577 
9578  /* If type still is mouse-pan, we call it handled, since delta-y accumulate. */
9579  /* also see wm_event_system.c do_wheel_ui hack */
9580  if (type == MOUSEPAN) {
9581  retval = WM_UI_HANDLER_BREAK;
9582  }
9583  }
9584 
9585  if (event->type == LEFTMOUSE) {
9586  retval = ui_list_handle_click_drag(C, ui_list, region, event);
9587  }
9588  else if (val == KM_PRESS) {
9590  (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0) ||
9591  ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && (event->modifier & KM_CTRL) &&
9592  (event->modifier & (KM_SHIFT | KM_ALT | KM_OSKEY)) == 0))) {
9593  const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop);
9594  int value, min, max;
9595 
9596  value = value_orig;
9597  const int inc = ui_list_get_increment(ui_list, type, dyn_data->columns);
9598 
9599  if (dyn_data->items_filter_neworder || dyn_data->items_filter_flags) {
9600  /* If we have a display order different from
9601  * collection order, we have some work! */
9602  int *org_order = MEM_mallocN(dyn_data->items_shown * sizeof(int), __func__);
9603  const int *new_order = dyn_data->items_filter_neworder;
9604  int org_idx = -1, len = dyn_data->items_len;
9605  int current_idx = -1;
9606  const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
9607 
9608  for (int i = 0; i < len; i++) {
9609  if (!dyn_data->items_filter_flags ||
9610  ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) {
9611  org_order[new_order ? new_order[++org_idx] : ++org_idx] = i;
9612  if (i == value) {
9613  current_idx = new_order ? new_order[org_idx] : org_idx;
9614  }
9615  }
9616  else if (i == value && org_idx >= 0) {
9617  current_idx = -(new_order ? new_order[org_idx] : org_idx) - 1;
9618  }
9619  }
9620  /* Now, org_order maps displayed indices to real indices,
9621  * and current_idx either contains the displayed index of active value (positive),
9622  * or its more-nearest one (negated).
9623  */
9624  if (current_idx < 0) {
9625  current_idx = (current_idx * -1) + (inc < 0 ? inc : inc - 1);
9626  }
9627  else {
9628  current_idx += inc;
9629  }
9630  CLAMP(current_idx, 0, dyn_data->items_shown - 1);
9631  value = org_order[current_idx];
9632  MEM_freeN(org_order);
9633  }
9634  else {
9635  value += inc;
9636  }
9637 
9638  CLAMP(value, 0, dyn_data->items_len - 1);
9639 
9640  RNA_property_int_range(&listbox->rnapoin, listbox->rnaprop, &min, &max);
9641  CLAMP(value, min, max);
9642 
9643  if (value != value_orig) {
9644  ui_list_activate_row_from_index(C, region, listbox, ui_list, value);
9645  redraw = true;
9646  }
9647  retval = WM_UI_HANDLER_BREAK;
9648  }
9649  else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && (event->modifier & KM_SHIFT)) {
9650  /* We now have proper grip, but keep this anyway! */
9651  if (ui_list->list_grip < (dyn_data->visual_height_min - UI_LIST_AUTO_SIZE_THRESHOLD)) {
9652  ui_list->list_grip = dyn_data->visual_height;
9653  }
9654  ui_list->list_grip += (type == WHEELUPMOUSE) ? -1 : 1;
9655 
9656  ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
9657 
9658  redraw = true;
9659  retval = WM_UI_HANDLER_BREAK;
9660  }
9661  else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
9662  if (dyn_data->height > dyn_data->visual_height) {
9663  /* list template will clamp */
9664  ui_list->list_scroll += scroll_dir * ((type == WHEELUPMOUSE) ? -1 : 1);
9665 
9666  redraw = true;
9667  retval = WM_UI_HANDLER_BREAK;
9668  }
9669  }
9670  }
9671 
9672  if (redraw) {
9673  ED_region_tag_redraw(region);
9674  ED_region_tag_refresh_ui(region);
9675  }
9676 
9677  return retval;
9678 }
9679 
9680 static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *region)
9681 {
9682  bool has_view_item = false;
9683  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
9684  /* Avoid unnecessary work: view item buttons are assumed to be inside views. */
9685  if (BLI_listbase_is_empty(&block->views)) {
9686  continue;
9687  }
9688 
9689  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
9690  if (but->type == UI_BTYPE_VIEW_ITEM) {
9691  but->flag &= ~UI_ACTIVE;
9692  has_view_item = true;
9693  }
9694  }
9695  }
9696 
9697  if (!has_view_item) {
9698  /* Avoid unnecessary lookup. */
9699  return WM_UI_HANDLER_CONTINUE;
9700  }
9701 
9702  /* Always highlight the hovered view item, even if the mouse hovers another button inside of it.
9703  */
9704  uiBut *hovered_row_but = ui_view_item_find_mouse_over(region, event->xy);
9705  if (hovered_row_but) {
9706  hovered_row_but->flag |= UI_ACTIVE;
9707  }
9708 
9709  return WM_UI_HANDLER_CONTINUE;
9710 }
9711 
9713  const wmEvent *event,
9714  ARegion *region,
9715  uiBut *view_but)
9716 {
9717  BLI_assert(view_but->type == UI_BTYPE_VIEW_ITEM);
9718  if (event->type == LEFTMOUSE) {
9719  /* Will free active button if there already is one. */
9720  ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER);
9721  return ui_do_button(C, view_but->block, view_but, event);
9722  }
9723 
9724  return WM_UI_HANDLER_CONTINUE;
9725 }
9726 
9727 static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but)
9728 {
9729  uiHandleButtonData *data = but->active;
9730  uiPopupBlockHandle *menu = data->menu;
9731 
9732  /* copy over return values from the closing menu */
9733  if ((menu->menuretval & UI_RETURN_OK) || (menu->menuretval & UI_RETURN_UPDATE)) {
9734  if (but->type == UI_BTYPE_COLOR) {
9735  copy_v3_v3(data->vec, menu->retvec);
9736  }
9737  else if (but->type == UI_BTYPE_MENU) {
9738  data->value = menu->retvalue;
9739  }
9740  }
9741 
9742  if (menu->menuretval & UI_RETURN_UPDATE) {
9743  if (data->interactive) {
9744  ui_apply_but(C, but->block, but, data, true);
9745  }
9746  else {
9747  ui_but_update(but);
9748  }
9749 
9750  menu->menuretval = 0;
9751  }
9752 
9753  /* now change button state or exit, which will close the submenu */
9754  if ((menu->menuretval & UI_RETURN_OK) || (menu->menuretval & UI_RETURN_CANCEL)) {
9755  if (menu->menuretval != UI_RETURN_OK) {
9756  data->cancel = true;
9757  }
9758 
9759  button_activate_exit(C, but, data, true, false);
9760  }
9761  else if (menu->menuretval & UI_RETURN_OUT) {
9762  if (event->type == MOUSEMOVE && ui_but_contains_point_px(but, data->region, event->xy)) {
9764  }
9765  else {
9766  if (ISKEYBOARD(event->type)) {
9767  /* keyboard menu hierarchy navigation, going back to previous level */
9768  but->active->used_mouse = false;
9770  }
9771  else {
9772  data->cancel = true;
9773  button_activate_exit(C, but, data, true, false);
9774  }
9775  }
9776  }
9777 }
9778 
9781 /* -------------------------------------------------------------------- */
9796  const int xy[2],
9797  const bool force)
9798 {
9799  BLI_assert(((uiBlock *)menu->region->uiblocks.first)->flag &
9801 
9802  if (!menu->dotowards || force) {
9803  menu->dotowards = true;
9804  menu->towards_xy[0] = xy[0];
9805  menu->towards_xy[1] = xy[1];
9806 
9807  if (force) {
9808  menu->towardstime = DBL_MAX; /* unlimited time */
9809  }
9810  else {
9812  }
9813  }
9814 }
9815 
9816 static void ui_mouse_motion_towards_init(uiPopupBlockHandle *menu, const int xy[2])
9817 {
9818  ui_mouse_motion_towards_init_ex(menu, xy, false);
9819 }
9820 
9821 static void ui_mouse_motion_towards_reinit(uiPopupBlockHandle *menu, const int xy[2])
9822 {
9823  ui_mouse_motion_towards_init_ex(menu, xy, true);
9824 }
9825 
9827  uiPopupBlockHandle *menu,
9828  const int xy[2],
9829  const bool use_wiggle_room)
9830 {
9832 
9833  /* annoying fix for T36269, this is a bit odd but in fact works quite well
9834  * don't mouse-out of a menu if another menu has been created after it.
9835  * if this causes problems we could remove it and check on a different fix - campbell */
9836  if (menu->region->next) {
9837  /* am I the last menu (test) */
9838  ARegion *region = menu->region->next;
9839  do {
9840  uiBlock *block_iter = region->uiblocks.first;
9841  if (block_iter && ui_block_is_menu(block_iter)) {
9842  return true;
9843  }
9844  } while ((region = region->next));
9845  }
9846  /* annoying fix end! */
9847 
9848  if (!menu->dotowards) {
9849  return false;
9850  }
9851 
9852  float oldp[2] = {menu->towards_xy[0], menu->towards_xy[1]};
9853  const float newp[2] = {xy[0], xy[1]};
9854  if (len_squared_v2v2(oldp, newp) < (4.0f * 4.0f)) {
9855  return menu->dotowards;
9856  }
9857 
9858  /* verify that we are moving towards one of the edges of the
9859  * menu block, in other words, in the triangle formed by the
9860  * initial mouse location and two edge points. */
9861  rctf rect_px;
9862  ui_block_to_window_rctf(menu->region, block, &rect_px, &block->rect);
9863 
9864  const float margin = MENU_TOWARDS_MARGIN;
9865 
9866  const float p1[2] = {rect_px.xmin - margin, rect_px.ymin - margin};
9867  const float p2[2] = {rect_px.xmax + margin, rect_px.ymin - margin};
9868  const float p3[2] = {rect_px.xmax + margin, rect_px.ymax + margin};
9869  const float p4[2] = {rect_px.xmin - margin, rect_px.ymax + margin};
9870 
9871  /* allow for some wiggle room, if the user moves a few pixels away,
9872  * don't immediately quit (only for top level menus) */
9873  if (use_wiggle_room) {
9874  const float cent[2] = {BLI_rctf_cent_x(&rect_px), BLI_rctf_cent_y(&rect_px)};
9875  float delta[2];
9876 
9877  sub_v2_v2v2(delta, oldp, cent);
9879  add_v2_v2(oldp, delta);
9880  }
9881 
9882  bool closer = (isect_point_tri_v2(newp, oldp, p1, p2) ||
9883  isect_point_tri_v2(newp, oldp, p2, p3) ||
9884  isect_point_tri_v2(newp, oldp, p3, p4) || isect_point_tri_v2(newp, oldp, p4, p1));
9885 
9886  if (!closer) {
9887  menu->dotowards = false;
9888  }
9889 
9890  /* 1 second timer */
9892  menu->dotowards = false;
9893  }
9894 
9895  return menu->dotowards;
9896 }
9897 
9898 #ifdef USE_KEYNAV_LIMIT
9899 static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event)
9900 {
9901  keynav->is_keynav = true;
9902  copy_v2_v2_int(keynav->event_xy, event->xy);
9903 }
9908 static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event)
9909 {
9910  if (keynav->is_keynav &&
9912  keynav->is_keynav = false;
9913  }
9914 
9915  return keynav->is_keynav;
9916 }
9917 #endif /* USE_KEYNAV_LIMIT */
9918 
9921 /* -------------------------------------------------------------------- */
9925 static char ui_menu_scroll_test(uiBlock *block, int my)
9926 {
9927  if (block->flag & (UI_BLOCK_CLIPTOP | UI_BLOCK_CLIPBOTTOM)) {
9928  if (block->flag & UI_BLOCK_CLIPTOP) {
9929  if (my > block->rect.ymax - UI_MENU_SCROLL_MOUSE) {
9930  return 't';
9931  }
9932  }
9933  if (block->flag & UI_BLOCK_CLIPBOTTOM) {
9934  if (my < block->rect.ymin + UI_MENU_SCROLL_MOUSE) {
9935  return 'b';
9936  }
9937  }
9938  }
9939  return 0;
9940 }
9941 
9942 static void ui_menu_scroll_apply_offset_y(ARegion *region, uiBlock *block, float dy)
9943 {
9944  BLI_assert(dy != 0.0f);
9945 
9946  const int scroll_pad = ui_block_is_menu(block) ? UI_MENU_SCROLL_PAD : UI_UNIT_Y * 0.5f;
9947 
9948  if (dy < 0.0f) {
9949  /* Stop at top item, extra 0.5 UI_UNIT_Y makes it snap nicer. */
9950  float ymax = -FLT_MAX;
9951  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
9952  ymax = max_ff(ymax, bt->rect.ymax);
9953  }
9954  if (ymax + dy - UI_UNIT_Y * 0.5f < block->rect.ymax - scroll_pad) {
9955  dy = block->rect.ymax - ymax - scroll_pad;
9956  }
9957  }
9958  else {
9959  /* Stop at bottom item, extra 0.5 UI_UNIT_Y makes it snap nicer. */
9960  float ymin = FLT_MAX;
9961  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
9962  ymin = min_ff(ymin, bt->rect.ymin);
9963  }
9964  if (ymin + dy + UI_UNIT_Y * 0.5f > block->rect.ymin + scroll_pad) {
9965  dy = block->rect.ymin - ymin + scroll_pad;
9966  }
9967  }
9968 
9969  /* remember scroll offset for refreshes */
9970  block->handle->scrolloffset += dy;
9971 
9972  /* apply scroll offset */
9973  LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
9974  bt->rect.ymin += dy;
9975  bt->rect.ymax += dy;
9976  }
9977 
9978  /* set flags again */
9980 
9981  ED_region_tag_redraw(region);
9982 }
9983 
9985 static bool ui_menu_scroll_to_but(ARegion *region, uiBlock *block, uiBut *but_target)
9986 {
9987  float dy = 0.0;
9988  if (block->flag & UI_BLOCK_CLIPTOP) {
9989  if (but_target->rect.ymax > block->rect.ymax - UI_MENU_SCROLL_ARROW) {
9990  dy = block->rect.ymax - but_target->rect.ymax - UI_MENU_SCROLL_ARROW;
9991  }
9992  }
9993  if (block->flag & UI_BLOCK_CLIPBOTTOM) {
9994  if (but_target->rect.ymin < block->rect.ymin + UI_MENU_SCROLL_ARROW) {
9995  dy = block->rect.ymin - but_target->rect.ymin + UI_MENU_SCROLL_ARROW;
9996  }
9997  }
9998  if (dy != 0.0f) {
9999  ui_menu_scroll_apply_offset_y(region, block, dy);
10000  return true;
10001  }
10002  return false;
10003 }
10004 
10006 static bool ui_menu_scroll_to_y(ARegion *region, uiBlock *block, int y)
10007 {
10008  const char test = ui_menu_scroll_test(block, y);
10009  float dy = 0.0f;
10010  if (test == 't') {
10011  dy = -UI_UNIT_Y; /* scroll to the top */
10012  }
10013  else if (test == 'b') {
10014  dy = UI_UNIT_Y; /* scroll to the bottom */
10015  }
10016  if (dy != 0.0f) {
10017  ui_menu_scroll_apply_offset_y(region, block, dy);
10018  return true;
10019  }
10020  return false;
10021 }
10022 
10023 static bool ui_menu_scroll_step(ARegion *region, uiBlock *block, const int scroll_dir)
10024 {
10025  int my;
10026  if (scroll_dir == 1) {
10027  if ((block->flag & UI_BLOCK_CLIPTOP) == 0) {
10028  return false;
10029  }
10030  my = block->rect.ymax + UI_UNIT_Y;
10031  }
10032  else if (scroll_dir == -1) {
10033  if ((block->flag & UI_BLOCK_CLIPBOTTOM) == 0) {
10034  return false;
10035  }
10036  my = block->rect.ymin - UI_UNIT_Y;
10037  }
10038  else {
10039  BLI_assert(0);
10040  return false;
10041  }
10042 
10043  return ui_menu_scroll_to_y(region, block, my);
10044 }
10045 
10048 /* -------------------------------------------------------------------- */
10053 {
10054  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
10055  block->auto_open = false;
10056  }
10057 }
10058 
10069  const uiBut *but,
10070  const int level,
10071  const int retval)
10072 {
10073  if ((level != 0) && (but == NULL)) {
10075  (void)retval; /* so release builds with strict flags are happy as well */
10077  return true;
10078  }
10079  return false;
10080 }
10081 
10082 static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu)
10083 {
10084  ARegion *region = menu->region;
10085  uiBut *but = ui_region_find_active_but(region);
10086 
10087  if (but) {
10088  /* Its possible there is an active menu item NOT under the mouse,
10089  * in this case ignore mouse clicks outside the button (but Enter etc is accepted) */
10090  if (event->val == KM_RELEASE) {
10091  /* pass, needed so we can exit active menu-items when click-dragging out of them */
10092  }
10093  else if (but->type == UI_BTYPE_SEARCH_MENU) {
10094  /* Pass, needed so search popup can have RMB context menu.
10095  * This may be useful for other interactions which happen in the search popup
10096  * without being directly over the search button. */
10097  }
10098  else if (!ui_block_is_menu(but->block) || ui_block_is_pie_menu(but->block)) {
10099  /* pass, skip for dialogs */
10100  }
10101  else if (!ui_region_contains_point_px(but->active->region, event->xy)) {
10102  /* Pass, needed to click-exit outside of non-floating menus. */
10104  }
10105  else if (ISMOUSE_BUTTON(event->type)) {
10106  if (!ui_but_contains_point_px(but, but->active->region, event->xy)) {
10107  but = NULL;
10108  }
10109  }
10110  }
10111 
10112  int retval;
10113  if (but) {
10114  ScrArea *ctx_area = CTX_wm_area(C);
10115  ARegion *ctx_region = CTX_wm_region(C);
10116 
10117  if (menu->ctx_area) {
10118  CTX_wm_area_set(C, menu->ctx_area);
10119  }
10120  if (menu->ctx_region) {
10121  CTX_wm_region_set(C, menu->ctx_region);
10122  }
10123 
10124  retval = ui_handle_button_event(C, event, but);
10125 
10126  if (menu->ctx_area) {
10127  CTX_wm_area_set(C, ctx_area);
10128  }
10129  if (menu->ctx_region) {
10130  CTX_wm_region_set(C, ctx_region);
10131  }
10132  }
10133  else {
10134  retval = ui_handle_button_over(C, event, region);
10135  }
10136 
10137  return retval;
10138 }
10139 
10140 float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2])
10141 {
10142  float seg1[2];
10143 
10144  if (block->pie_data.flags & UI_PIE_INITIAL_DIRECTION) {
10145  copy_v2_v2(seg1, block->pie_data.pie_center_init);
10146  }
10147  else {
10148  copy_v2_v2(seg1, block->pie_data.pie_center_spawned);
10149  }
10150 
10151  float seg2[2];
10152  sub_v2_v2v2(seg2, event_xy, seg1);
10153 
10154  const float len = normalize_v2_v2(block->pie_data.pie_dir, seg2);
10155 
10156  if (len < U.pie_menu_threshold * U.dpi_fac) {
10157  block->pie_data.flags |= UI_PIE_INVALID_DIR;
10158  }
10159  else {
10160  block->pie_data.flags &= ~UI_PIE_INVALID_DIR;
10161  }
10162 
10163  return len;
10164 }
10165 
10167  const wmEvent *event,
10168  uiPopupBlockHandle *menu,
10169  int level,
10170  const bool is_parent_inside,
10171  const bool is_parent_menu,
10172  const bool is_floating)
10173 {
10174  uiBut *but;
10175  ARegion *region = menu->region;
10176  uiBlock *block = region->uiblocks.first;
10177 
10178  int retval = WM_UI_HANDLER_CONTINUE;
10179 
10180  int mx = event->xy[0];
10181  int my = event->xy[1];
10182  ui_window_to_block(region, block, &mx, &my);
10183 
10184  /* check if mouse is inside block */
10185  const bool inside = BLI_rctf_isect_pt(&block->rect, mx, my);
10186  /* check for title dragging */
10187  const bool inside_title = inside && ((my + (UI_UNIT_Y * 1.5f)) > block->rect.ymax);
10188 
10189  /* if there's an active modal button, don't check events or outside, except for search menu */
10190  but = ui_region_find_active_but(region);
10191 
10192 #ifdef USE_DRAG_POPUP
10193  if (menu->is_grab) {
10194  if (event->type == LEFTMOUSE) {
10195  menu->is_grab = false;
10196  retval = WM_UI_HANDLER_BREAK;
10197  }
10198  else {
10199  if (event->type == MOUSEMOVE) {
10200  int mdiff[2];
10201 
10202  sub_v2_v2v2_int(mdiff, event->xy, menu->grab_xy_prev);
10203  copy_v2_v2_int(menu->grab_xy_prev, event->xy);
10204 
10206 
10207  ui_popup_translate(region, mdiff);
10208  }
10209 
10210  return retval;
10211  }
10212  }
10213 #endif
10214 
10215  if (but && button_modal_state(but->active->state)) {
10216  if (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)) {
10217  /* if a button is activated modal, always reset the start mouse
10218  * position of the towards mechanism to avoid losing focus,
10219  * and don't handle events */
10220  ui_mouse_motion_towards_reinit(menu, event->xy);
10221  }
10222  }
10223  else if (event->type == TIMER) {
10224  if (event->customdata == menu->scrolltimer) {
10225  ui_menu_scroll_to_y(region, block, my);
10226  }
10227  }
10228  else {
10229  /* for ui_mouse_motion_towards_block */
10230  if (event->type == MOUSEMOVE) {
10231  if (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)) {
10232  ui_mouse_motion_towards_init(menu, event->xy);
10233  }
10234 
10235  /* add menu scroll timer, if needed */
10236  if (ui_menu_scroll_test(block, my)) {
10237  if (menu->scrolltimer == NULL) {
10240  }
10241  }
10242  }
10243 
10244  /* first block own event func */
10245  if (block->block_event_func && block->block_event_func(C, block, event)) {
10246  /* pass */
10247  } /* events not for active search menu button */
10248  else {
10249  int act = 0;
10250 
10251  switch (event->type) {
10252 
10253  /* Closing sub-levels of pull-downs.
10254  *
10255  * The actual event is handled by the button under the cursor.
10256  * This is done so we can right click on menu items even when they have sub-menus open.
10257  */
10258  case RIGHTMOUSE:
10259  if (inside == false) {
10260  if (event->val == KM_PRESS && (block->flag & UI_BLOCK_LOOP)) {
10261  if (block->saferct.first) {
10262  /* Currently right clicking on a top level pull-down (typically in the header)
10263  * just closes the menu and doesn't support immediately handling the RMB event.
10264  *
10265  * To support we would need UI_RETURN_OUT_PARENT to be handled by
10266  * top-level buttons, not just menus. Note that this isn't very important
10267  * since it's easy to manually close these menus by clicking on them. */
10268  menu->menuretval = (level > 0 && is_parent_inside) ? UI_RETURN_OUT_PARENT :
10269  UI_RETURN_OUT;
10270  }
10271  }
10272  retval = WM_UI_HANDLER_BREAK;
10273  }
10274  break;
10275 
10276  /* Closing sub-levels of pull-downs. */
10277  case EVT_LEFTARROWKEY:
10278  if (event->val == KM_PRESS && (block->flag & UI_BLOCK_LOOP)) {
10279  if (block->saferct.first) {
10280  menu->menuretval = UI_RETURN_OUT;
10281  }
10282  }
10283 
10284  retval = WM_UI_HANDLER_BREAK;
10285  break;
10286 
10287  /* Opening sub-levels of pull-downs. */
10288  case EVT_RIGHTARROWKEY:
10289  if (event->val == KM_PRESS && (block->flag & UI_BLOCK_LOOP)) {
10290 
10291  if (ui_menu_pass_event_to_parent_if_nonactive(menu, but, level, retval)) {
10292  break;
10293  }
10294 
10295  but = ui_region_find_active_but(region);
10296 
10297  if (!but) {
10298  /* no item active, we make first active */
10299  if (block->direction & UI_DIR_UP) {
10300  but = ui_but_last(block);
10301  }
10302  else {
10303  but = ui_but_first(block);
10304  }
10305  }
10306 
10307  if (but && ELEM(but->type, UI_BTYPE_BLOCK, UI_BTYPE_PULLDOWN)) {
10309  }
10310  }
10311 
10312  retval = WM_UI_HANDLER_BREAK;
10313  break;
10314 
10315  /* Smooth scrolling for popovers. */
10316  case MOUSEPAN: {
10317  if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) {
10318  /* pass */
10319  }
10320  else if (!ui_block_is_menu(block)) {
10321  if (block->flag & (UI_BLOCK_CLIPTOP | UI_BLOCK_CLIPBOTTOM)) {
10322  const float dy = event->xy[1] - event->prev_xy[1];
10323  if (dy != 0.0f) {
10324  ui_menu_scroll_apply_offset_y(region, block, dy);
10325 
10326  if (but) {
10327  but->active->cancel = true;
10328  button_activate_exit(C, but, but->active, false, false);
10329  }
10331  }
10332  }
10333  break;
10334  }
10336  }
10337  case WHEELUPMOUSE:
10338  case WHEELDOWNMOUSE: {
10339  if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) {
10340  /* pass */
10341  }
10342  else if (!ui_block_is_menu(block)) {
10343  const int scroll_dir = (event->type == WHEELUPMOUSE) ? 1 : -1;
10344  if (ui_menu_scroll_step(region, block, scroll_dir)) {
10345  if (but) {
10346  but->active->cancel = true;
10347  button_activate_exit(C, but, but->active, false, false);
10348  }
10350  }
10351  break;
10352  }
10354  }
10355  case EVT_UPARROWKEY:
10356  case EVT_DOWNARROWKEY:
10357  case EVT_PAGEUPKEY:
10358  case EVT_PAGEDOWNKEY:
10359  case EVT_HOMEKEY:
10360  case EVT_ENDKEY:
10361  /* Arrow-keys: only handle for block_loop blocks. */
10362  if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) {
10363  /* pass */
10364  }
10365  else if (inside || (block->flag & UI_BLOCK_LOOP)) {
10366  int type = event->type;
10367  int val = event->val;
10368 
10369  /* Convert pan to scroll-wheel. */
10370  if (type == MOUSEPAN) {
10371  ui_pan_to_scroll(event, &type, &val);
10372  }
10373 
10374  if (val == KM_PRESS) {
10375  /* Determine scroll operation. */
10376  uiMenuScrollType scrolltype;
10377  const bool ui_block_flipped = (block->flag & UI_BLOCK_IS_FLIP) != 0;
10378 
10380  scrolltype = ui_block_flipped ? MENU_SCROLL_TOP : MENU_SCROLL_BOTTOM;
10381  }
10382  else if (ELEM(type, EVT_PAGEDOWNKEY, EVT_ENDKEY)) {
10383  scrolltype = ui_block_flipped ? MENU_SCROLL_BOTTOM : MENU_SCROLL_TOP;
10384  }
10385  else if (ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE)) {
10386  scrolltype = ui_block_flipped ? MENU_SCROLL_UP : MENU_SCROLL_DOWN;
10387  }
10388  else {
10389  scrolltype = ui_block_flipped ? MENU_SCROLL_DOWN : MENU_SCROLL_UP;
10390  }
10391 
10392  if (ui_menu_pass_event_to_parent_if_nonactive(menu, but, level, retval)) {
10393  break;
10394  }
10395 
10396 #ifdef USE_KEYNAV_LIMIT
10398 #endif
10399 
10400  but = ui_region_find_active_but(region);
10401  if (but) {
10402  /* Apply scroll operation. */
10403  if (scrolltype == MENU_SCROLL_DOWN) {
10404  but = ui_but_next(but);
10405  }
10406  else if (scrolltype == MENU_SCROLL_UP) {
10407  but = ui_but_prev(but);
10408  }
10409  else if (scrolltype == MENU_SCROLL_TOP) {
10410  but = ui_but_first(block);
10411  }
10412  else if (scrolltype == MENU_SCROLL_BOTTOM) {
10413  but = ui_but_last(block);
10414  }
10415  }
10416 
10417  if (!but) {
10418  /* Wrap button or no active button. */
10419  uiBut *but_wrap = NULL;
10420  if (ELEM(scrolltype, MENU_SCROLL_UP, MENU_SCROLL_BOTTOM)) {
10421  but_wrap = ui_but_last(block);
10422  }
10423  else if (ELEM(scrolltype, MENU_SCROLL_DOWN, MENU_SCROLL_TOP)) {
10424  but_wrap = ui_but_first(block);
10425  }
10426  if (but_wrap) {
10427  but = but_wrap;
10428  }
10429  }
10430 
10431  if (but) {
10433  ui_menu_scroll_to_but(region, block, but);
10434  }
10435  }
10436 
10437  retval = WM_UI_HANDLER_BREAK;
10438  }
10439 
10440  break;
10441 
10442  case EVT_ONEKEY:
10443  case EVT_PAD1:
10444  act = 1;
10446  case EVT_TWOKEY:
10447  case EVT_PAD2:
10448  if (act == 0) {
10449  act = 2;
10450  }
10452  case EVT_THREEKEY:
10453  case EVT_PAD3:
10454  if (act == 0) {
10455  act = 3;
10456  }
10458  case EVT_FOURKEY:
10459  case EVT_PAD4:
10460  if (act == 0) {
10461  act = 4;
10462  }
10464  case EVT_FIVEKEY:
10465  case EVT_PAD5:
10466  if (act == 0) {
10467  act = 5;
10468  }
10470  case EVT_SIXKEY:
10471  case EVT_PAD6:
10472  if (act == 0) {
10473  act = 6;
10474  }
10476  case EVT_SEVENKEY:
10477  case EVT_PAD7:
10478  if (act == 0) {
10479  act = 7;
10480  }
10482  case EVT_EIGHTKEY:
10483  case EVT_PAD8:
10484  if (act == 0) {
10485  act = 8;
10486  }
10488  case EVT_NINEKEY:
10489  case EVT_PAD9:
10490  if (act == 0) {
10491  act = 9;
10492  }
10494  case EVT_ZEROKEY:
10495  case EVT_PAD0:
10496  if (act == 0) {
10497  act = 10;
10498  }
10499 
10500  if ((block->flag & UI_BLOCK_NUMSELECT) && event->val == KM_PRESS) {
10501  int count;
10502 
10503  if (ui_menu_pass_event_to_parent_if_nonactive(menu, but, level, retval)) {
10504  break;
10505  }
10506 
10507  /* Only respond to explicit press to avoid the event that opened the menu
10508  * activating an item when the key is held. */
10509  if (event->flag & WM_EVENT_IS_REPEAT) {
10510  break;
10511  }
10512 
10513  if (event->modifier & KM_ALT) {
10514  act += 10;
10515  }
10516 
10517  count = 0;
10518  for (but = block->buttons.first; but; but = but->next) {
10519  bool doit = false;
10520 
10521  if (!ELEM(but->type,
10523  UI_BTYPE_SEPR,
10525  UI_BTYPE_IMAGE)) {
10526  count++;
10527  }
10528 
10529  /* exception for rna layer buts */
10530  if (but->rnapoin.data && but->rnaprop &&
10532  if (but->rnaindex == act - 1) {
10533  doit = true;
10534  }
10535  }
10536  else if (ELEM(but->type,
10537  UI_BTYPE_BUT,
10539  UI_BTYPE_MENU,
10541  UI_BTYPE_PULLDOWN) &&
10542  count == act) {
10543  doit = true;
10544  }
10545 
10546  if (!(but->flag & UI_BUT_DISABLED) && doit) {
10547  /* activate buttons but open menu's */
10549  if (but->type == UI_BTYPE_PULLDOWN) {
10551  }
10552  else {
10554  }
10555 
10556  ui_handle_button_activate(C, region, but, activate);
10557  break;
10558  }
10559  }
10560 
10561  retval = WM_UI_HANDLER_BREAK;
10562  }
10563  break;
10564 
10565  /* Handle keystrokes on menu items */
10566  case EVT_AKEY:
10567  case EVT_BKEY:
10568  case EVT_CKEY:
10569  case EVT_DKEY:
10570  case EVT_EKEY:
10571  case EVT_FKEY:
10572  case EVT_GKEY:
10573  case EVT_HKEY:
10574  case EVT_IKEY:
10575  case EVT_JKEY:
10576  case EVT_KKEY:
10577  case EVT_LKEY:
10578  case EVT_MKEY:
10579  case EVT_NKEY:
10580  case EVT_OKEY:
10581  case EVT_PKEY:
10582  case EVT_QKEY:
10583  case EVT_RKEY:
10584  case EVT_SKEY:
10585  case EVT_TKEY:
10586  case EVT_UKEY:
10587  case EVT_VKEY:
10588  case EVT_WKEY:
10589  case EVT_XKEY:
10590  case EVT_YKEY:
10591  case EVT_ZKEY: {
10592  if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK) &&
10593  ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0) &&
10594  /* Only respond to explicit press to avoid the event that opened the menu
10595  * activating an item when the key is held. */
10596  (event->flag & WM_EVENT_IS_REPEAT) == 0) {
10597  if (ui_menu_pass_event_to_parent_if_nonactive(menu, but, level, retval)) {
10598  break;
10599  }
10600 
10601  for (but = block->buttons.first; but; but = but->next) {
10602  if (!(but->flag & UI_BUT_DISABLED) && but->menu_key == event->type) {
10603  if (but->type == UI_BTYPE_BUT) {
10604  UI_but_execute(C, region, but);
10605  }
10606  else {
10607  ui_handle_button_activate_by_type(C, region, but);
10608  }
10609  break;
10610  }
10611  }
10612 
10613  retval = WM_UI_HANDLER_BREAK;
10614  }
10615  break;
10616  }
10617  }
10618  }
10619 
10620  /* here we check return conditions for menus */
10621  if (block->flag & UI_BLOCK_LOOP) {
10622  /* If we click outside the block, verify if we clicked on the
10623  * button that opened us, otherwise we need to close,
10624  *
10625  * note that there is an exception for root level menus and
10626  * popups which you can click again to close.
10627  *
10628  * Events handled above may have already set the return value,
10629  * don't overwrite them, see: T61015.
10630  */
10631  if ((inside == false) && (menu->menuretval == 0)) {
10632  uiSafetyRct *saferct = block->saferct.first;
10633 
10634  if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
10635  if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) {
10636  if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) {
10637  /* for root menus, allow clicking to close */
10638  if (block->flag & UI_BLOCK_OUT_1) {
10639  menu->menuretval = UI_RETURN_OK;
10640  }
10641  else {
10642  menu->menuretval = UI_RETURN_OUT;
10643  }
10644  }
10645  else if (saferct && !BLI_rctf_isect_pt(
10646  &saferct->parent, (float)event->xy[0], (float)event->xy[1])) {
10647  if (block->flag & UI_BLOCK_OUT_1) {
10648  menu->menuretval = UI_RETURN_OK;
10649  }
10650  else {
10651  menu->menuretval = UI_RETURN_OUT;
10652  }
10653  }
10654  }
10655  else if (ELEM(event->val, KM_RELEASE, KM_CLICK)) {
10656  /* For buttons that use a hold function,
10657  * exit when mouse-up outside the menu. */
10658  if (block->flag & UI_BLOCK_POPUP_HOLD) {
10659  /* NOTE: we could check the cursor is over the parent button. */
10660  menu->menuretval = UI_RETURN_CANCEL;
10661  retval = WM_UI_HANDLER_CONTINUE;
10662  }
10663  }
10664  }
10665  }
10666 
10667  if (menu->menuretval) {
10668  /* pass */
10669  }
10670 #ifdef USE_KEYNAV_LIMIT
10671  else if ((event->type == MOUSEMOVE) &&
10672  ui_mouse_motion_keynav_test(&menu->keynav_state, event)) {
10673  /* Don't handle the mouse-move if we're using key-navigation. */
10674  retval = WM_UI_HANDLER_BREAK;
10675  }
10676 #endif
10677  else if (event->type == EVT_ESCKEY && event->val == KM_PRESS) {
10678  /* Escape cancels this and all preceding menus. */
10679  menu->menuretval = UI_RETURN_CANCEL;
10680  }
10681  else if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER) && event->val == KM_PRESS) {
10683  region, UI_BUT_ACTIVE_DEFAULT, UI_HIDDEN);
10684  if ((but_default != NULL) && (but_default->active == NULL)) {
10685  if (but_default->type == UI_BTYPE_BUT) {
10686  UI_but_execute(C, region, but_default);
10687  }
10688  else {
10689  ui_handle_button_activate_by_type(C, region, but_default);
10690  }
10691  }
10692  else {
10693  uiBut *but_active = ui_region_find_active_but(region);
10694 
10695  /* enter will always close this block, we let the event
10696  * get handled by the button if it is activated, otherwise we cancel */
10697  if (but_active == NULL) {
10699  }
10700  }
10701  }
10702 #ifdef USE_DRAG_POPUP
10703  else if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) &&
10704  (inside && is_floating && inside_title)) {
10705  if (!but || !ui_but_contains_point_px(but, region, event->xy)) {
10706  if (but) {
10708  }
10709 
10710  menu->is_grab = true;
10711  copy_v2_v2_int(menu->grab_xy_prev, event->xy);
10712  retval = WM_UI_HANDLER_BREAK;
10713  }
10714  }
10715 #endif
10716  else {
10717 
10718  /* check mouse moving outside of the menu */
10719  if (inside == false && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) {
10720  uiSafetyRct *saferct;
10721 
10722  ui_mouse_motion_towards_check(block, menu, event->xy, is_parent_inside == false);
10723 
10724  /* Check for all parent rects, enables arrow-keys to be used. */
10725  for (saferct = block->saferct.first; saferct; saferct = saferct->next) {
10726  /* for mouse move we only check our own rect, for other
10727  * events we check all preceding block rects too to make
10728  * arrow keys navigation work */
10729  if (event->type != MOUSEMOVE || saferct == block->saferct.first) {
10730  if (BLI_rctf_isect_pt(&saferct->parent, (float)event->xy[0], (float)event->xy[1])) {
10731  break;
10732  }
10733  if (BLI_rctf_isect_pt(&saferct->safety, (float)event->xy[0], (float)event->xy[1])) {
10734  break;
10735  }
10736  }
10737  }
10738 
10739  /* strict check, and include the parent rect */
10740  if (!menu->dotowards && !saferct) {
10741  if (block->flag & UI_BLOCK_OUT_1) {
10742  menu->menuretval = UI_RETURN_OK;
10743  }
10744  else {
10745  menu->menuretval = UI_RETURN_OUT;
10746  }
10747  }
10748  else if (menu->dotowards && event->type == MOUSEMOVE) {
10749  retval = WM_UI_HANDLER_BREAK;
10750  }
10751  }
10752  }
10753 
10754  /* end switch */
10755  }
10756  }
10757 
10758  /* if we are didn't handle the event yet, lets pass it on to
10759  * buttons inside this region. disabled inside check .. not sure
10760  * anymore why it was there? but it meant enter didn't work
10761  * for example when mouse was not over submenu */
10762  if ((event->type == TIMER) ||
10763  (/* inside && */ (!menu->menuretval || (menu->menuretval & UI_RETURN_UPDATE)) &&
10764  retval == WM_UI_HANDLER_CONTINUE)) {
10765  retval = ui_handle_menu_button(C, event, menu);
10766  }
10767 
10768 #ifdef USE_UI_POPOVER_ONCE
10769  if (block->flag & UI_BLOCK_POPOVER_ONCE) {
10770  if ((event->type == LEFTMOUSE) && (event->val == KM_RELEASE)) {
10772  block->flag &= ~UI_BLOCK_POPOVER_ONCE;
10773  }
10774  }
10775 #endif
10776 
10777  /* Don't handle double click events, re-handle as regular press/release. */
10778  if (retval == WM_UI_HANDLER_CONTINUE && event->val == KM_DBL_CLICK) {
10779  return retval;
10780  }
10781 
10782  /* if we set a menu return value, ensure we continue passing this on to
10783  * lower menus and buttons, so always set continue then, and if we are
10784  * inside the region otherwise, ensure we swallow the event */
10785  if (menu->menuretval) {
10786  return WM_UI_HANDLER_CONTINUE;
10787  }
10788  if (inside) {
10789  return WM_UI_HANDLER_BREAK;
10790  }
10791  return retval;
10792 }
10793 
10795  const wmEvent *event,
10796  uiPopupBlockHandle *menu)
10797 {
10798  ARegion *region = menu->region;
10799  uiBlock *block = region->uiblocks.first;
10800 
10801  uiBut *but = ui_region_find_active_but(region);
10802 
10803  BLI_assert(but);
10804 
10805  uiHandleButtonData *data = but->active;
10806  uiPopupBlockHandle *submenu = data->menu;
10807 
10808  if (submenu->menuretval) {
10809  bool update;
10810 
10811  /* first decide if we want to close our own menu cascading, if
10812  * so pass on the sub menu return value to our own menu handle */
10813  if ((submenu->menuretval & UI_RETURN_OK) || (submenu->menuretval & UI_RETURN_CANCEL)) {
10814  if (!(block->flag & UI_BLOCK_KEEP_OPEN)) {
10815  menu->menuretval = submenu->menuretval;
10816  menu->butretval = data->retval;
10817  }
10818  }
10819 
10820  update = (submenu->menuretval & UI_RETURN_UPDATE) != 0;
10821 
10822  /* now let activated button in this menu exit, which
10823  * will actually close the submenu too */
10824  ui_handle_button_return_submenu(C, event, but);
10825 
10826  if (update) {
10827  submenu->menuretval = 0;
10828  }
10829  }
10830 
10831  if (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)) {
10832  /* for cases where close does not cascade, allow the user to
10833  * move the mouse back towards the menu without closing */
10834  ui_mouse_motion_towards_reinit(menu, event->xy);
10835  }
10836 
10837  if (menu->menuretval) {
10838  return WM_UI_HANDLER_CONTINUE;
10839  }
10840  return WM_UI_HANDLER_BREAK;
10841 }
10842 
10844 {
10845  return (!ELEM(but->type, UI_BTYPE_NUM_SLIDER, UI_BTYPE_NUM));
10846 }
10847 
10849  uiPopupBlockHandle *menu,
10850  uiBut *but,
10851  bool force_close)
10852 {
10853  const int retval = WM_UI_HANDLER_BREAK;
10854 
10855  if (but && ui_but_pie_menu_supported_apply(but)) {
10856  if (but->type == UI_BTYPE_MENU) {
10857  /* forcing the pie menu to close will not handle menus */
10858  if (!force_close) {
10859  uiBut *active_but = ui_region_find_active_but(menu->region);
10860 
10861  if (active_but) {
10862  button_activate_exit(C, active_but, active_but->active, false, false);
10863  }
10864 
10866  return retval;
10867  }
10868  menu->menuretval = UI_RETURN_CANCEL;
10869  }
10870  else {
10871  button_activate_exit((bContext *)C, but, but->active, false, false);
10872 
10873  menu->menuretval = UI_RETURN_OK;
10874  }
10875  }
10876  else {
10877  menu->menuretval = UI_RETURN_CANCEL;
10878 
10880  }
10881 
10882  return retval;
10883 }
10884 
10886 {
10887  if ((block->flag & UI_BLOCK_NUMSELECT) && event->val == KM_PRESS) {
10888  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
10889  if (but->pie_dir == dir && !ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
10890  return but;
10891  }
10892  }
10893  }
10894 
10895  return NULL;
10896 }
10897 
10899 {
10900  if (but == NULL) {
10901  return WM_UI_HANDLER_BREAK;
10902  }
10903 
10904  uiBut *active_but = ui_region_find_active_but(menu->region);
10905 
10906  if (active_but) {
10907  /* Use onfree to not execute the hovered active_but. */
10908  button_activate_exit(C, active_but, active_but->active, false, true);
10909  }
10910 
10912  return ui_but_pie_menu_apply(C, menu, but, false);
10913 }
10914 
10915 static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu)
10916 {
10917  /* we block all events, this is modal interaction,
10918  * except for drop events which is described below */
10919  int retval = WM_UI_HANDLER_BREAK;
10920 
10921  if (event->type == EVT_DROP) {
10922  /* may want to leave this here for later if we support pie ovens */
10923 
10924  retval = WM_UI_HANDLER_CONTINUE;
10925  }
10926 
10927  ARegion *region = menu->region;
10928  uiBlock *block = region->uiblocks.first;
10929 
10930  const bool is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE);
10931 
10932  /* if there's an active modal button, don't check events or outside, except for search menu */
10933  uiBut *but_active = ui_region_find_active_but(region);
10934 
10935  if (menu->scrolltimer == NULL) {
10938  menu->scrolltimer->duration = 0.0;
10939  }
10940 
10941  const double duration = menu->scrolltimer->duration;
10942 
10943  float event_xy[2] = {UNPACK2(event->xy)};
10944 
10945  ui_window_to_block_fl(region, block, &event_xy[0], &event_xy[1]);
10946 
10947  /* Distance from initial point. */
10948  const float dist = ui_block_calc_pie_segment(block, event_xy);
10949 
10950  if (but_active && button_modal_state(but_active->active->state)) {
10951  retval = ui_handle_menu_button(C, event, menu);
10952  }
10953  else {
10954  if (event->type == TIMER) {
10955  if (event->customdata == menu->scrolltimer) {
10956  /* deactivate initial direction after a while */
10957  if (duration > 0.01 * U.pie_initial_timeout) {
10959  }
10960 
10961  /* handle animation */
10962  if (!(block->pie_data.flags & UI_PIE_ANIMATION_FINISHED)) {
10963  const double final_time = 0.01 * U.pie_animation_timeout;
10964  float fac = duration / final_time;
10965  const float pie_radius = U.pie_menu_radius * UI_DPI_FAC;
10966 
10967  if (fac > 1.0f) {
10968  fac = 1.0f;
10970  }
10971 
10972  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
10973  if (but->pie_dir != UI_RADIAL_NONE) {
10974  float vec[2];
10975  float center[2];
10976 
10977  ui_but_pie_dir(but->pie_dir, vec);
10978 
10979  center[0] = (vec[0] > 0.01f) ? 0.5f : ((vec[0] < -0.01f) ? -0.5f : 0.0f);
10980  center[1] = (vec[1] > 0.99f) ? 0.5f : ((vec[1] < -0.99f) ? -0.5f : 0.0f);
10981 
10982  center[0] *= BLI_rctf_size_x(&but->rect);
10983  center[1] *= BLI_rctf_size_y(&but->rect);
10984 
10985  mul_v2_fl(vec, pie_radius);
10986  add_v2_v2(vec, center);
10987  mul_v2_fl(vec, fac);
10988  add_v2_v2(vec, block->pie_data.pie_center_spawned);
10989 
10990  BLI_rctf_recenter(&but->rect, vec[0], vec[1]);
10991  }
10992  }
10993  block->pie_data.alphafac = fac;
10994 
10995  ED_region_tag_redraw(region);
10996  }
10997  }
10998 
10999  /* Check pie velocity here if gesture has ended. */
11000  if (block->pie_data.flags & UI_PIE_GESTURE_END_WAIT) {
11001  float len_sq = 10;
11002 
11003  /* use a time threshold to ensure we leave time to the mouse to move */
11004  if (duration - block->pie_data.duration_gesture > 0.02) {
11005  len_sq = len_squared_v2v2(event_xy, block->pie_data.last_pos);
11006  copy_v2_v2(block->pie_data.last_pos, event_xy);
11007  block->pie_data.duration_gesture = duration;
11008  }
11009 
11010  if (len_sq < 1.0f) {
11011  uiBut *but = ui_region_find_active_but(menu->region);
11012 
11013  if (but) {
11014  return ui_but_pie_menu_apply(C, menu, but, true);
11015  }
11016  }
11017  }
11018  }
11019 
11020  if (event->type == block->pie_data.event_type && !is_click_style) {
11021  if (event->val != KM_RELEASE) {
11022  ui_handle_menu_button(C, event, menu);
11023 
11025  block->pie_data.flags |= UI_PIE_DRAG_STYLE;
11026  }
11027  /* why redraw here? It's simple, we are getting many double click events here.
11028  * Those operate like mouse move events almost */
11029  ED_region_tag_redraw(region);
11030  }
11031  else {
11032  if ((duration < 0.01 * U.pie_tap_timeout) &&
11033  !(block->pie_data.flags & UI_PIE_DRAG_STYLE)) {
11034  block->pie_data.flags |= UI_PIE_CLICK_STYLE;
11035  }
11036  else {
11037  uiBut *but = ui_region_find_active_but(menu->region);
11038 
11039  if (but && (U.pie_menu_confirm > 0) &&
11040  (dist >= U.dpi_fac * (U.pie_menu_threshold + U.pie_menu_confirm))) {
11041  return ui_but_pie_menu_apply(C, menu, but, true);
11042  }
11043 
11044  retval = ui_but_pie_menu_apply(C, menu, but, true);
11045  }
11046  }
11047  }
11048  else {
11049  /* direction from numpad */
11050  RadialDirection num_dir = UI_RADIAL_NONE;
11051 
11052  switch (event->type) {
11053  case MOUSEMOVE:
11054  if (!is_click_style) {
11055  const float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init);
11056 
11057  /* here we use the initial position explicitly */
11058  if (len_sq > PIE_CLICK_THRESHOLD_SQ) {
11059  block->pie_data.flags |= UI_PIE_DRAG_STYLE;
11060  }
11061 
11062  /* here instead, we use the offset location to account for the initial
11063  * direction timeout */
11064  if ((U.pie_menu_confirm > 0) &&
11065  (dist >= U.dpi_fac * (U.pie_menu_threshold + U.pie_menu_confirm))) {
11067  copy_v2_v2(block->pie_data.last_pos, event_xy);
11068  block->pie_data.duration_gesture = duration;
11069  }
11070  }
11071 
11072  ui_handle_menu_button(C, event, menu);
11073 
11074  /* mouse move should always refresh the area for pie menus */
11075  ED_region_tag_redraw(region);
11076  break;
11077 
11078  case LEFTMOUSE:
11079  if (is_click_style) {
11080  if (block->pie_data.flags & UI_PIE_INVALID_DIR) {
11081  menu->menuretval = UI_RETURN_CANCEL;
11082  }
11083  else {
11084  retval = ui_handle_menu_button(C, event, menu);
11085  }
11086  }
11087  break;
11088 
11089  case EVT_ESCKEY:
11090  case RIGHTMOUSE:
11091  menu->menuretval = UI_RETURN_CANCEL;
11092  break;
11093 
11094  case EVT_AKEY:
11095  case EVT_BKEY:
11096  case EVT_CKEY:
11097  case EVT_DKEY:
11098  case EVT_EKEY:
11099  case EVT_FKEY:
11100  case EVT_GKEY:
11101  case EVT_HKEY:
11102  case EVT_IKEY:
11103  case EVT_JKEY:
11104  case EVT_KKEY:
11105  case EVT_LKEY:
11106  case EVT_MKEY:
11107  case EVT_NKEY:
11108  case EVT_OKEY:
11109  case EVT_PKEY:
11110  case EVT_QKEY:
11111  case EVT_RKEY:
11112  case EVT_SKEY:
11113  case EVT_TKEY:
11114  case EVT_UKEY:
11115  case EVT_VKEY:
11116  case EVT_WKEY:
11117  case EVT_XKEY:
11118  case EVT_YKEY:
11119  case EVT_ZKEY: {
11120  if ((ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) &&
11121  ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0)) {
11122  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
11123  if (but->menu_key == event->type) {
11124  ui_but_pie_button_activate(C, but, menu);
11125  }
11126  }
11127  }
11128  break;
11129  }
11130 
11131 #define CASE_NUM_TO_DIR(n, d) \
11132  case (EVT_ZEROKEY + n): \
11133  case (EVT_PAD0 + n): { \
11134  if (num_dir == UI_RADIAL_NONE) { \
11135  num_dir = d; \
11136  } \
11137  } \
11138  (void)0
11139 
11155  {
11156  uiBut *but = ui_block_pie_dir_activate(block, event, num_dir);
11157  retval = ui_but_pie_button_activate(C, but, menu);
11158  break;
11159  }
11160 #undef CASE_NUM_TO_DIR
11161  default:
11162  retval = ui_handle_menu_button(C, event, menu);
11163  break;
11164  }
11165  }
11166  }
11167 
11168  return retval;
11169 }
11170 
11172  const wmEvent *event,
11173  uiPopupBlockHandle *menu,
11174  int level,
11175  const bool is_parent_inside,
11176  const bool is_parent_menu,
11177  const bool is_floating)
11178 {
11179  int retval = WM_UI_HANDLER_CONTINUE;
11180  bool do_towards_reinit = false;
11181 
11182  /* check if we have a submenu, and handle events for it first */
11183  uiBut *but = ui_region_find_active_but(menu->region);
11184  uiHandleButtonData *data = (but) ? but->active : NULL;
11185  uiPopupBlockHandle *submenu = (data) ? data->menu : NULL;
11186 
11187  if (submenu) {
11188  uiBlock *block = menu->region->uiblocks.first;
11189  const bool is_menu = ui_block_is_menu(block);
11190  bool inside = false;
11191  /* root pie menus accept the key that spawned
11192  * them as double click to improve responsiveness */
11193  const bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) ||
11194  event->type != block->pie_data.event_type);
11195 
11196  if (do_recursion) {
11197  if (is_parent_inside == false) {
11198  int mx = event->xy[0];
11199  int my = event->xy[1];
11200  ui_window_to_block(menu->region, block, &mx, &my);
11201  inside = BLI_rctf_isect_pt(&block->rect, mx, my);
11202  }
11203 
11204  retval = ui_handle_menus_recursive(
11205  C, event, submenu, level + 1, is_parent_inside || inside, is_menu, false);
11206  }
11207  }
11208 
11209  /* now handle events for our own menu */
11210  if (retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) {
11211  const bool do_but_search = (but && (but->type == UI_BTYPE_SEARCH_MENU));
11212  if (submenu && submenu->menuretval) {
11213  const bool do_ret_out_parent = (submenu->menuretval & UI_RETURN_OUT_PARENT) != 0;
11214  retval = ui_handle_menu_return_submenu(C, event, menu);
11215  submenu = NULL; /* hint not to use this, it may be freed by call above */
11216  (void)submenu;
11217  /* we may want to quit the submenu and handle the even in this menu,
11218  * if its important to use it, check 'data->menu' first */
11219  if (((retval == WM_UI_HANDLER_BREAK) && do_ret_out_parent) == false) {
11220  /* skip applying the event */
11221  return retval;
11222  }
11223  }
11224 
11225  if (do_but_search) {
11226  uiBlock *block = menu->region->uiblocks.first;
11227 
11228  retval = ui_handle_menu_button(C, event, menu);
11229 
11230  if (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)) {
11231  /* when there is a active search button and we close it,
11232  * we need to reinit the mouse coords T35346. */
11233  if (ui_region_find_active_but(menu->region) != but) {
11234  do_towards_reinit = true;
11235  }
11236  }
11237  }
11238  else {
11239  uiBlock *block = menu->region->uiblocks.first;
11240  uiBut *listbox = ui_list_find_mouse_over(menu->region, event);
11241 
11242  if (block->flag & UI_BLOCK_RADIAL) {
11243  retval = ui_pie_handler(C, event, menu);
11244  }
11245  else if (event->type == LEFTMOUSE || event->val != KM_DBL_CLICK) {
11246  bool handled = false;
11247 
11248  if (listbox) {
11249  const int retval_test = ui_handle_list_event(C, event, menu->region, listbox);
11250  if (retval_test != WM_UI_HANDLER_CONTINUE) {
11251  retval = retval_test;
11252  handled = true;
11253  }
11254  }
11255 
11256  if (handled == false) {
11257  retval = ui_handle_menu_event(
11258  C, event, menu, level, is_parent_inside, is_parent_menu, is_floating);
11259  }
11260  }
11261  }
11262  }
11263 
11264  if (do_towards_reinit) {
11265  ui_mouse_motion_towards_reinit(menu, event->xy);
11266  }
11267 
11268  return retval;
11269 }
11270 
11271 void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable)
11272 {
11273  uiPopupBlockHandle *menu = block->handle;
11274  if (menu) {
11275  menu->menuretval = enable ? (menu->menuretval | retval) : (menu->menuretval & retval);
11276  }
11277 }
11278 
11281 /* -------------------------------------------------------------------- */
11285 static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(userdata))
11286 {
11287  /* here we handle buttons at the region level, non-modal */
11288  ARegion *region = CTX_wm_region(C);
11289  int retval = WM_UI_HANDLER_CONTINUE;
11290 
11291  if (region == NULL || BLI_listbase_is_empty(&region->uiblocks)) {
11292  return retval;
11293  }
11294 
11295  /* either handle events for already activated button or try to activate */
11296  uiBut *but = ui_region_find_active_but(region);
11297  uiBut *listbox = ui_list_find_mouse_over(region, event);
11298 
11299  retval = ui_handler_panel_region(C, event, region, listbox ? listbox : but);
11300 
11301  if (retval == WM_UI_HANDLER_CONTINUE && listbox) {
11302  retval = ui_handle_list_event(C, event, region, listbox);
11303 
11304  /* interactions with the listbox should disable tips */
11305  if (retval == WM_UI_HANDLER_BREAK) {
11306  if (but) {
11308  }
11309  }
11310  }
11311 
11312  if (retval == WM_UI_HANDLER_CONTINUE) {
11313  if (but) {
11314  retval = ui_handle_button_event(C, event, but);
11315  }
11316  else {
11317  retval = ui_handle_button_over(C, event, region);
11318  }
11319  }
11320 
11321  /* Re-enable tool-tips. */
11322  if (event->type == MOUSEMOVE &&
11323  (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) {
11324  ui_blocks_set_tooltips(region, true);
11325  }
11326 
11327  /* Always do this, to reliably update view item highlighting, even if the mouse hovers a button
11328  * nested in the item (it's an overlapping layout). */
11329  ui_handle_view_items_hover(event, region);
11330  if (retval == WM_UI_HANDLER_CONTINUE) {
11331  uiBut *view_item = ui_view_item_find_mouse_over(region, event->xy);
11332  if (view_item) {
11333  retval = ui_handle_view_item_event(C, event, region, view_item);
11334  }
11335  }
11336 
11337  /* delayed apply callbacks */
11339 
11340  return retval;
11341 }
11342 
11343 static void ui_region_handler_remove(bContext *C, void *UNUSED(userdata))
11344 {
11345  ARegion *region = CTX_wm_region(C);
11346  if (region == NULL) {
11347  return;
11348  }
11349 
11350  UI_blocklist_free(C, region);
11351  bScreen *screen = CTX_wm_screen(C);
11352  if (screen == NULL) {
11353  return;
11354  }
11355 
11356  /* delayed apply callbacks, but not for screen level regions, those
11357  * we rather do at the very end after closing them all, which will
11358  * be done in ui_region_handler/window */
11359  if (BLI_findindex(&screen->regionbase, region) == -1) {
11361  }
11362 }
11363 
11364 /* handle buttons at the window level, modal, for example while
11365  * number sliding, text editing, or when a menu block is open */
11366 static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSED(userdata))
11367 {
11368  ARegion *menu_region = CTX_wm_menu(C);
11369  ARegion *region = menu_region ? menu_region : CTX_wm_region(C);
11370  int retval = WM_UI_HANDLER_CONTINUE;
11371 
11372  uiBut *but = ui_region_find_active_but(region);
11373 
11374  if (but) {
11375  bScreen *screen = CTX_wm_screen(C);
11376  uiBut *but_other;
11377 
11378  /* handle activated button events */
11379  uiHandleButtonData *data = but->active;
11380 
11381  if ((data->state == BUTTON_STATE_MENU_OPEN) &&
11382  /* Make sure this popup isn't dragging a button.
11383  * can happen with popovers (see T67882). */
11384  (ui_region_find_active_but(data->menu->region) == NULL) &&
11385  /* make sure mouse isn't inside another menu (see T43247) */
11386  (ui_screen_region_find_mouse_over(screen, event) == NULL) &&
11388  (but_other = ui_but_find_mouse_over(region, event)) && (but != but_other) &&
11390  /* Hover-opening menu's doesn't work well for buttons over one another
11391  * along the same axis the menu is opening on (see T71719). */
11392  (((data->menu->direction & (UI_DIR_LEFT | UI_DIR_RIGHT)) &&
11393  BLI_rctf_isect_rect_x(&but->rect, &but_other->rect, NULL)) ||
11394  ((data->menu->direction & (UI_DIR_DOWN | UI_DIR_UP)) &&
11395  BLI_rctf_isect_rect_y(&but->rect, &but_other->rect, NULL)))) {
11396  /* if mouse moves to a different root-level menu button,
11397  * open it to replace the current menu */
11398  if ((but_other->flag & UI_BUT_DISABLED) == 0) {
11399  ui_handle_button_activate(C, region, but_other, BUTTON_ACTIVATE_OVER);
11401  retval = WM_UI_HANDLER_BREAK;
11402  }
11403  }
11404  else if (data->state == BUTTON_STATE_MENU_OPEN) {
11405  /* handle events for menus and their buttons recursively,
11406  * this will handle events from the top to the bottom menu */
11407  if (data->menu) {
11408  retval = ui_handle_menus_recursive(C, event, data->menu, 0, false, false, false);
11409  }
11410 
11411  /* handle events for the activated button */
11412  if ((data->menu && (retval == WM_UI_HANDLER_CONTINUE)) || (event->type == TIMER)) {
11413  if (data->menu && data->menu->menuretval) {
11414  ui_handle_button_return_submenu(C, event, but);
11415  retval = WM_UI_HANDLER_BREAK;
11416  }
11417  else {
11418  retval = ui_handle_button_event(C, event, but);
11419  }
11420  }
11421  }
11422  else {
11423  /* handle events for the activated button */
11424  retval = ui_handle_button_event(C, event, but);
11425  }
11426  }
11427 
11428  /* Re-enable tool-tips. */
11429  if (event->type == MOUSEMOVE &&
11430  (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) {
11431  ui_blocks_set_tooltips(region, true);
11432  }
11433 
11434  if (but && but->active && but->active->menu) {
11435  /* Set correct context menu-region. The handling button above breaks if we set the region
11436  * first, so only set it for executing the after-funcs. */
11437  CTX_wm_menu_set(C, but->active->menu->region);
11438  }
11439 
11440  /* delayed apply callbacks */
11442 
11443  /* Reset to previous context region. */
11444  CTX_wm_menu_set(C, menu_region);
11445 
11446  /* Don't handle double-click events,
11447  * these will be converted into regular clicks which we handle. */
11448  if (retval == WM_UI_HANDLER_CONTINUE) {
11449  if (event->val == KM_DBL_CLICK) {
11450  return WM_UI_HANDLER_CONTINUE;
11451  }
11452  }
11453 
11454  /* we block all events, this is modal interaction */
11455  return WM_UI_HANDLER_BREAK;
11456 }
11457 
11458 /* two types of popups, one with operator + enum, other with regular callbacks */
11459 static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata)
11460 {
11461  uiPopupBlockHandle *menu = userdata;
11462  /* we block all events, this is modal interaction,
11463  * except for drop events which is described below */
11464  int retval = WM_UI_HANDLER_BREAK;
11465  bool reset_pie = false;
11466 
11467  ARegion *menu_region = CTX_wm_menu(C);
11468  CTX_wm_menu_set(C, menu->region);
11469 
11470  if (event->type == EVT_DROP || event->val == KM_DBL_CLICK) {
11471  /* EVT_DROP:
11472  * If we're handling drop event we'll want it to be handled by popup callee as well,
11473  * so it'll be possible to perform such operations as opening .blend files by dropping
11474  * them into blender, even if there's opened popup like splash screen (sergey).
11475  * KM_DBL_CLICK:
11476  * Continue in case of double click so wm_handlers_do calls handler again with KM_PRESS
11477  * event. This is needed to ensure correct button handling for fast clicking (T47532).
11478  */
11479 
11480  retval = WM_UI_HANDLER_CONTINUE;
11481  }
11482 
11483  ui_handle_menus_recursive(C, event, menu, 0, false, false, true);
11484 
11485  /* free if done, does not free handle itself */
11486  if (menu->menuretval) {
11487  wmWindow *win = CTX_wm_window(C);
11488  /* copy values, we have to free first (closes region) */
11489  const uiPopupBlockHandle temp = *menu;
11490  uiBlock *block = menu->region->uiblocks.first;
11491 
11492  /* set last pie event to allow chained pie spawning */
11493  if (block->flag & UI_BLOCK_RADIAL) {
11494  win->pie_event_type_last = block->pie_data.event_type;
11495  reset_pie = true;
11496  }
11497 
11498  ui_popup_block_free(C, menu);
11501 
11502 #ifdef USE_DRAG_TOGGLE
11503  {
11505  &win->modalhandlers,
11508  }
11509 #endif
11510 
11511  if ((temp.menuretval & UI_RETURN_OK) || (temp.menuretval & UI_RETURN_POPUP_OK)) {
11512  if (temp.popup_func) {
11513  temp.popup_func(C, temp.popup_arg, temp.retvalue);
11514  }
11515  }
11516  else if (temp.cancel_func) {
11517  temp.cancel_func(C, temp.popup_arg);
11518  }
11519 
11521  }
11522  else {
11523  /* Re-enable tool-tips */
11524  if (event->type == MOUSEMOVE &&
11525  (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) {
11526  ui_blocks_set_tooltips(menu->region, true);
11527  }
11528  }
11529 
11530  /* delayed apply callbacks */
11532 
11533  if (reset_pie) {
11534  /* Reacquire window in case pie invalidates it somehow. */
11535  wmWindow *win = CTX_wm_window(C);
11536 
11537  if (win) {
11539  }
11540  }
11541 
11542  CTX_wm_region_set(C, menu_region);
11543 
11544  return retval;
11545 }
11546 
11547 static void ui_popup_handler_remove(bContext *C, void *userdata)
11548 {
11549  uiPopupBlockHandle *menu = userdata;
11550 
11551  /* More correct would be to expect UI_RETURN_CANCEL here, but not wanting to
11552  * cancel when removing handlers because of file exit is a rare exception.
11553  * So instead of setting cancel flag for all menus before removing handlers,
11554  * just explicitly flag menu with UI_RETURN_OK to avoid canceling it. */
11555  if ((menu->menuretval & UI_RETURN_OK) == 0 && menu->cancel_func) {
11556  menu->cancel_func(C, menu->popup_arg);
11557  }
11558 
11559  /* free menu block if window is closed for some reason */
11560  ui_popup_block_free(C, menu);
11561 
11562  /* delayed apply callbacks */
11564 }
11565 
11567 {
11570 }
11571 
11573  ListBase *handlers,
11574  uiPopupBlockHandle *popup,
11575  const char flag)
11576 {
11578 }
11579 
11581 {
11582  LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
11583  if (handler_base->type == WM_HANDLER_TYPE_UI) {
11584  wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
11585 
11586  if (handler->handle_fn == ui_popup_handler &&
11587  handler->remove_fn == ui_popup_handler_remove && handler->user_data == popup) {
11588  /* tag refresh parent popup */
11589  wmEventHandler_UI *handler_next = (wmEventHandler_UI *)handler->head.next;
11590  if (handler_next && handler_next->head.type == WM_HANDLER_TYPE_UI &&
11591  handler_next->handle_fn == ui_popup_handler &&
11592  handler_next->remove_fn == ui_popup_handler_remove) {
11593  uiPopupBlockHandle *parent_popup = handler_next->user_data;
11594  ED_region_tag_refresh_ui(parent_popup->region);
11595  }
11596  break;
11597  }
11598  }
11599  }
11600 
11602 }
11603 
11605 {
11607 }
11608 
11610  ARegion *region,
11611  const void *rna_poin_data,
11612  const char *rna_prop_id)
11613 {
11614  uiBlock *block_text = NULL;
11615  uiBut *but_text = NULL;
11616 
11617  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
11618  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
11619  if (but->type == UI_BTYPE_TEXT) {
11620  if (but->rnaprop && but->rnapoin.data == rna_poin_data) {
11621  if (STREQ(RNA_property_identifier(but->rnaprop), rna_prop_id)) {
11622  block_text = block;
11623  but_text = but;
11624  break;
11625  }
11626  }
11627  }
11628  }
11629  if (but_text) {
11630  break;
11631  }
11632  }
11633 
11634  if (but_text) {
11635  UI_but_active_only(C, region, block_text, but_text);
11636  return true;
11637  }
11638  return false;
11639 }
11640 
11642 {
11643  ARegion *region = CTX_wm_region(C);
11644  uiBlock *block_text = NULL;
11645  uiBut *but_text = NULL;
11646 
11647  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
11648  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
11649  if (but == actbut && but->type == UI_BTYPE_TEXT) {
11650  block_text = block;
11651  but_text = but;
11652  break;
11653  }
11654  }
11655 
11656  if (but_text) {
11657  break;
11658  }
11659  }
11660 
11661  if (but_text) {
11662  UI_but_active_only(C, region, block_text, but_text);
11663  return true;
11664  }
11665  return false;
11666 }
11667 
11670 /* -------------------------------------------------------------------- */
11675 {
11676  LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
11677  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
11678  if (but->active == NULL) {
11679  continue;
11680  }
11681  ui_but_active_free(C, but);
11682  }
11683  }
11684 }
11685 
11687 {
11688  wmWindow *win = CTX_wm_window(C);
11689 
11690  ED_screen_areas_iter (win, screen, area) {
11691  LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
11692  uiBut *but = ui_region_find_active_but(region);
11693  if (but) {
11694  uiHandleButtonData *data = but->active;
11695 
11696  if (data->menu == NULL && data->searchbox == NULL) {
11697  if (data->state == BUTTON_STATE_HIGHLIGHT) {
11698  ui_but_active_free(C, but);
11699  }
11700  }
11701  }
11702  }
11703  }
11704 }
11705 
11707 {
11708  ARegion *region = CTX_wm_region(C);
11709  uiBut *but = ui_region_find_active_but(region);
11710 
11711  if (but) {
11713  return but;
11714  }
11715  }
11716 
11717  return NULL;
11718 }
11719 
11721 {
11723 }
11724 
11726 {
11727  ARegion *region = CTX_wm_region(C);
11728 
11729  if (region) {
11730  uiBut *but = ui_region_find_active_but(region);
11731 
11732  if (but && but->type == UI_BTYPE_COLOR) {
11733  return true;
11734  }
11735  }
11736 
11737  return false;
11738 }
11739 
11742 /* -------------------------------------------------------------------- */
11747 {
11748  block->custom_interaction_callbacks = *callbacks;
11749 }
11750 
11752  uiBlock *block,
11753  const bool is_click)
11754 {
11756  uiBlockInteraction_Handle *interaction = MEM_callocN(sizeof(*interaction), __func__);
11757 
11758  int unique_retval_ids_len = 0;
11759  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
11760  if (but->active || (but->flag & UI_BUT_DRAG_MULTI)) {
11761  unique_retval_ids_len++;
11762  }
11763  }
11764 
11765  int *unique_retval_ids = MEM_mallocN(sizeof(*unique_retval_ids) * unique_retval_ids_len,
11766  __func__);
11767  unique_retval_ids_len = 0;
11768  LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
11769  if (but->active || (but->flag & UI_BUT_DRAG_MULTI)) {
11770  unique_retval_ids[unique_retval_ids_len++] = but->retval;
11771  }
11772  }
11773 
11774  if (unique_retval_ids_len > 1) {
11775  qsort(unique_retval_ids, unique_retval_ids_len, sizeof(int), BLI_sortutil_cmp_int);
11776  unique_retval_ids_len = BLI_array_deduplicate_ordered(unique_retval_ids,
11777  unique_retval_ids_len);
11778  unique_retval_ids = MEM_reallocN(unique_retval_ids,
11779  sizeof(*unique_retval_ids) * unique_retval_ids_len);
11780  }
11781 
11782  interaction->params.is_click = is_click;
11783  interaction->params.unique_retval_ids = unique_retval_ids;
11784  interaction->params.unique_retval_ids_len = unique_retval_ids_len;
11785 
11786  interaction->user_data = block->custom_interaction_callbacks.begin_fn(
11787  C, &interaction->params, block->custom_interaction_callbacks.arg1);
11788  return interaction;
11789 }
11790 
11793  uiBlockInteraction_Handle *interaction)
11794 {
11795  BLI_assert(callbacks->end_fn != NULL);
11796  callbacks->end_fn(C, &interaction->params, callbacks->arg1, interaction->user_data);
11797  MEM_freeN(interaction->params.unique_retval_ids);
11798  MEM_freeN(interaction);
11799 }
11800 
11803  uiBlockInteraction_Handle *interaction)
11804 {
11805  BLI_assert(callbacks->update_fn != NULL);
11806  callbacks->update_fn(C, &interaction->params, callbacks->arg1, interaction->user_data);
11807 }
11808 
11821  uiBlock *block,
11823  const bool is_click)
11824 {
11825  if (data->custom_interaction_handle) {
11826  return;
11827  }
11828  if (block->custom_interaction_callbacks.begin_fn == NULL) {
11829  return;
11830  }
11831 
11832  uiBlockInteraction_Handle *interaction = ui_block_interaction_begin(C, block, is_click);
11833  interaction->user_count = 1;
11834  data->custom_interaction_handle = interaction;
11835 }
11836 
typedef float(TangentPoint)[2]
AnimationEvalContext BKE_animsys_eval_context_construct(struct Depsgraph *depsgraph, float eval_time)
Definition: anim_sys.c:761
#define BKE_UNDO_STR_MAX
void BKE_brush_color_set(struct Scene *scene, struct Brush *brush, const float color[3])
Definition: brush.cc:2222
struct CBData * BKE_colorband_element_add(struct ColorBand *coba, float position)
Definition: colorband.c:602
void BKE_colorband_update_sort(struct ColorBand *coba)
Definition: colorband.c:580
void BKE_curvemapping_changed(struct CurveMapping *cumap, bool rem_doubles)
Definition: colortools.c:855
void BKE_curvemapping_free_data(struct CurveMapping *cumap)
Definition: colortools.c:83
struct CurveMapPoint * BKE_curvemap_insert(struct CurveMap *cuma, float x, float y)
Definition: colortools.c:222
void BKE_curvemapping_copy_data(struct CurveMapping *target, const struct CurveMapping *cumap)
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
void CTX_store_set(bContext *C, bContextStore *store)
Definition: context.c:188
void CTX_wm_region_set(bContext *C, struct ARegion *region)
Definition: context.c:1009
struct ARegion * CTX_wm_menu(const bContext *C)
Definition: context.c:760
void CTX_wm_menu_set(bContext *C, struct ARegion *menu)
Definition: context.c:1020
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
bContextStore * CTX_store_copy(bContextStore *store)
Definition: context.c:209
void CTX_store_free(bContextStore *store)
Definition: context.c:217
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct MovieClip * CTX_data_edit_movieclip(const bContext *C)
Definition: context.c:1385
struct bScreen * CTX_wm_screen(const bContext *C)
Definition: context.c:733
struct ReportList * CTX_wm_reports(const bContext *C)
Definition: context.c:775
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Definition: context.c:1505
void CTX_wm_area_set(bContext *C, struct ScrArea *area)
Definition: context.c:997
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
@ PROF_UPDATE_CLIP
@ PROF_UPDATE_REMOVE_DOUBLES
@ PROF_UPDATE_NONE
bool BKE_curveprofile_move_point(struct CurveProfile *profile, struct CurveProfilePoint *point, bool snap, const float delta[2])
void BKE_curveprofile_update(struct CurveProfile *profile, int update_flags)
void BKE_curveprofile_copy_data(struct CurveProfile *target, const struct CurveProfile *profile)
bool BKE_curveprofile_move_handle(struct CurveProfilePoint *point, bool handle_1, bool snap, const float delta[2])
struct CurveProfilePoint * BKE_curveprofile_insert(struct CurveProfile *profile, float x, float y)
void BKE_curveprofile_remove_by_flag(struct CurveProfile *profile, short flag)
int BKE_curveprofile_table_size(const struct CurveProfile *profile)
void BKE_curveprofile_free_data(struct CurveProfile *profile)
Definition: curveprofile.cc:43
float BKE_movieclip_remap_scene_to_clip_frame(const struct MovieClip *clip, float framenr)
void BKE_paint_invalidate_cursor_overlay(struct Scene *scene, struct ViewLayer *view_layer, struct CurveMapping *curve)
Definition: paint.c:247
ePaintMode BKE_paintmode_get_active_from_context(const struct bContext *C)
struct Brush * BKE_paint_brush(struct Paint *paint)
Definition: paint.c:607
void BKE_palette_color_remove(struct Palette *palette, struct PaletteColor *color)
Definition: paint.c:742
struct Paint * BKE_paint_get_active_from_context(const struct bContext *C)
@ PAINT_MODE_SCULPT
Definition: BKE_paint.h:68
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
struct MovieTrackingMarker * BKE_tracking_marker_ensure(struct MovieTrackingTrack *track, int framenr)
Definition: tracking.c:1468
bool BKE_unit_is_valid(int system, int type)
Definition: unit.c:1246
@ B_UNIT_AREA
Definition: BKE_unit.h:102
@ B_UNIT_VOLUME
Definition: BKE_unit.h:103
@ B_UNIT_LENGTH
Definition: BKE_unit.h:101
double BKE_unit_base_scalar(int system, int type)
Definition: unit.c:1236
void BLF_boundbox_foreach_glyph(int fontid, const char *str, size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data) ATTR_NONNULL(2)
Definition: blf.c:590
float BLF_width(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: blf.c:688
Generic array manipulation API.
#define BLI_array_deduplicate_ordered(arr, arr_len)
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
#define ATTR_FALLTHROUGH
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:239
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
Definition: BLI_listbase.h:348
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:273
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:344
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:466
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int round_fl_to_int_clamp(float a)
MINLINE float max_fff(float a, float b, float c)
MINLINE int round_fl_to_int(float a)
MINLINE float max_ff(float a, float b)
MINLINE size_t min_zz(size_t a, size_t b)
MINLINE int min_ii(int a, int b)
MINLINE float pow2f(float x)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE float cube_f(float a)
MINLINE float square_f(float a)
#define M_PI
Definition: BLI_math_base.h:20
MINLINE float sqrt3f(float f)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition: math_color.c:49
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
Definition: math_color.c:232
MINLINE void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
void rgb_to_hsv_compat_v(const float rgb[3], float r_hsv[3])
Definition: math_color.c:318
void hsl_to_rgb_v(const float hsl[3], float r_rgb[3])
Definition: math_color.c:54
void hsv_clamp_v(float hsv[3], float v_max)
Definition: math_color.c:323
MINLINE void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
void rgb_to_hsl_v(const float rgb[3], float r_hsl[3])
Definition: math_color.c:292
void rgb_to_hsl_compat_v(const float rgb[3], float r_hsl[3])
Definition: math_color.c:287
int isect_point_tri_v2(const float pt[2], const float v1[2], const float v2[2], const float v3[2])
Definition: math_geom.c:1516
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition: math_geom.c:283
MINLINE void sub_v2_v2v2_int(int r[2], const int a[2], const int b[2])
MINLINE bool compare_v3v3(const float a[3], const float b[3], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float r[3])
MINLINE void add_v2_v2v2_int(int r[2], const int a[2], const int b[2])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
void dist_ensure_v2_v2fl(float v1[2], const float v2[2], float dist)
Definition: math_vector.c:928
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE int len_manhattan_v2v2_int(const int a[2], const int b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3_length(float r[3], float unit_scale)
MINLINE float len_manhattan_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v2_length(float r[2], float unit_scale)
MINLINE float normalize_v2_v2(float r[2], const float a[2])
MINLINE void copy_v2_fl(float r[2], float f)
bool BLI_rctf_clamp_pt_v(const struct rctf *rect, float xy[2])
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition: BLI_rect.h:181
void BLI_rctf_transform_pt_v(const rctf *dst, const rctf *src, float xy_dst[2], const float xy_src[2])
Definition: rct.c:529
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition: BLI_rect.h:177
bool BLI_rctf_isect_rect_y(const struct rctf *src1, const struct rctf *src2, float range_y[2])
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
bool BLI_rctf_isect_rect_x(const struct rctf *src1, const struct rctf *src2, float range_x[2])
void BLI_rctf_recenter(struct rctf *rect, float x, float y)
Definition: rct.c:580
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition: BLI_rect.h:186
bool BLI_rctf_isect_pt(const struct rctf *rect, float x, float y)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:194
void BLI_rcti_rctf_copy(struct rcti *dst, const struct rctf *src)
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition: BLI_rect.h:198
int BLI_sortutil_cmp_int(const void *a_, const void *b_)
Definition: sort_utils.c:52
int BLI_str_rstrip_float_zero(char *str, char pad) ATTR_NONNULL()
Definition: string.c:963
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:42
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:33
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
void BLI_str_cursor_step_utf8(const char *str, size_t maxlen, int *pos, eStrCursorJumpDirection direction, eStrCursorJumpType jump, bool use_init_step)
bool BLI_str_cursor_step_prev_utf8(const char *str, size_t maxlen, int *pos)
eStrCursorJumpDirection
@ STRCUR_DIR_NEXT
@ STRCUR_DIR_PREV
eStrCursorJumpType
@ STRCUR_JUMP_ALL
@ STRCUR_JUMP_NONE
@ STRCUR_JUMP_DELIM
int BLI_str_utf8_size(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: string_utf8.c:452
const char const char * BLI_str_find_next_char_utf8(const char *p, const char *str_end) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1
size_t size_t BLI_strnlen_utf8(const char *strc, size_t maxlen) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
Definition: string_utf8.c:342
size_t BLI_strnlen_utf8_ex(const char *strc, size_t maxlen, size_t *r_len_bytes) ATTR_NONNULL(1
size_t BLI_strlen_utf8(const char *strc) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
Definition: string_utf8.c:317
int BLI_str_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL(1)
Definition: string_utf8.c:181
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(1
#define UNPACK2(a)
#define CLAMP_MAX(a, c)
#define UNUSED_VARS(...)
#define SWAP(type, a, b)
#define UNUSED_VARS_NDEBUG(...)
#define UNUSED(x)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
typedef double(DMatrix)[4][4]
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
@ BRUSH_USE_GRADIENT
@ CUMA_DO_CLIP
@ CUMA_SELECT
#define CM_TABLE
@ HD_FREE
@ HD_ALIGN
@ PROF_H1_SELECT
@ PROF_H2_SELECT
@ PROF_USE_CLIP
#define USER_UNIT_ROT_RADIANS
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ UILST_FLT_ITEM
#define UI_LIST_AUTO_SIZE_THRESHOLD
@ UILST_SCROLL_TO_ACTIVE_ITEM
@ UILST_LAYOUT_BIG_PREVIEW_GRID
@ UILST_LAYOUT_GRID
@ RGN_TYPE_TOOL_HEADER
@ RGN_TYPE_TEMPORARY
@ RGN_TYPE_NAV_BAR
@ RGN_TYPE_FOOTER
@ RGN_TYPE_HEADER
@ RGN_ALIGN_BOTTOM
@ RGN_ALIGN_LEFT
@ RGN_ALIGN_TOP
@ RGN_ALIGN_RIGHT
@ UILST_FLT_SORT_REVERSE
@ UILST_FLT_EXCLUDE
@ MARKER_TRACKED
@ MARKER_DISABLED
@ USER_MENUOPENAUTO
@ USER_CP_CIRCLE_HSV
@ USER_TOOLTIPS
#define ED_screen_areas_iter(win, screen, area_name)
Definition: ED_screen.h:267
void ED_region_tag_refresh_ui(struct ARegion *region)
Definition: area.c:683
void ED_region_tag_redraw_no_rebuild(struct ARegion *region)
Definition: area.c:674
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
bool ED_undo_is_legacy_compatible_for_property(struct bContext *C, struct ID *id)
Definition: ed_undo.c:448
void ED_undo_push(struct bContext *C, const char *str)
Definition: ed_undo.c:100
NSNotificationCenter * center
_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 GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
BLI_INLINE void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], const float srgb[3])
BLI_INLINE void IMB_colormanagement_scene_linear_to_srgb_v3(float srgb[3], const float scene_linear[3])
static bool is_disabled
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
Platform independent time functions.
PropertyScaleType
Definition: RNA_types.h:96
@ PROP_SCALE_LOG
Definition: RNA_types.h:103
@ PROP_SCALE_LINEAR
Definition: RNA_types.h:98
@ PROP_SCALE_CUBIC
Definition: RNA_types.h:108
#define RNA_SUBTYPE_UNIT_VALUE(subtype)
Definition: RNA_types.h:113
@ PROP_FLOAT
Definition: RNA_types.h:61
@ PROP_BOOLEAN
Definition: RNA_types.h:59
@ PROP_ENUM
Definition: RNA_types.h:63
@ PROP_INT
Definition: RNA_types.h:60
@ PROP_STRING
Definition: RNA_types.h:62
@ PROP_POINTER
Definition: RNA_types.h:64
@ PROP_UNIT_ROTATION
Definition: RNA_types.h:75
@ PROP_UNIT_LENGTH
Definition: RNA_types.h:71
@ PROP_PROPORTIONAL
Definition: RNA_types.h:223
@ PROP_NO_DEG_UPDATE
Definition: RNA_types.h:301
PropertySubType
Definition: RNA_types.h:125
@ PROP_LAYER_MEMBER
Definition: RNA_types.h:171
@ PROP_PASSWORD
Definition: RNA_types.h:136
@ PROP_COLOR
Definition: RNA_types.h:153
@ PROP_COLOR_GAMMA
Definition: RNA_types.h:165
@ PROP_LAYER
Definition: RNA_types.h:170
#define C
Definition: RandGen.cpp:25
@ UI_BUT_ACTIVE_RIGHT
Definition: UI_interface.h:291
@ UI_BUT_ACTIVE_LEFT
Definition: UI_interface.h:289
#define UI_UNIT_Y
bool UI_context_copy_to_selected_check(struct PointerRNA *ptr, struct PointerRNA *ptr_link, struct PropertyRNA *prop, const char *path, bool use_path_from_id, struct PointerRNA *r_ptr, struct PropertyRNA **r_prop)
#define AUTOCOMPLETE_FULL_MATCH
@ UI_BUT_DRAG_LOCK
Definition: UI_interface.h:194
@ UI_BUT_REDALERT
Definition: UI_interface.h:201
@ UI_BUT_UNDO
Definition: UI_interface.h:205
@ UI_BUT_ACTIVE_DEFAULT
Definition: UI_interface.h:212
@ UI_BUT_DISABLED
Definition: UI_interface.h:196
@ UI_BUT_DRIVEN
Definition: UI_interface.h:200
@ UI_BUT_DRAG_MULTI
Definition: UI_interface.h:217
@ UI_BUT_UPDATE_DELAY
Definition: UI_interface.h:224
@ UI_BUT_VALUE_CLEAR
Definition: UI_interface.h:228
@ UI_BUT_TEXTEDIT_UPDATE
Definition: UI_interface.h:226
@ UI_BUT_LAST_ACTIVE
Definition: UI_interface.h:204
@ UI_EMBOSS_NONE
Definition: UI_interface.h:109
@ UI_EMBOSS_NONE_OR_STATUS
Definition: UI_interface.h:116
void UI_fontstyle_set(const struct uiFontStyle *fs)
void UI_blocklist_free(const struct bContext *C, struct ARegion *region)
@ UI_RETURN_UPDATE
Definition: UI_interface.h:181
@ UI_RETURN_OUT_PARENT
Definition: UI_interface.h:179
@ UI_RETURN_CANCEL
Definition: UI_interface.h:173
@ UI_RETURN_POPUP_OK
Definition: UI_interface.h:183
@ UI_RETURN_OK
Definition: UI_interface.h:175
@ UI_RETURN_OUT
Definition: UI_interface.h:177
void(* uiButHandleNFunc)(struct bContext *C, void *argN, void *arg2)
Definition: UI_interface.h:507
void(* uiBlockHandleFunc)(struct bContext *C, void *arg, int event)
Definition: UI_interface.h:540
struct PointerRNA * UI_but_operator_ptr_get(uiBut *but)
Definition: interface.cc:5908
int UI_but_unit_type_get(const uiBut *but)
Definition: interface.cc:5939
const struct uiStyle * UI_style_get(void)
struct ARegion * UI_tooltip_create_from_button_or_extra_icon(struct bContext *C, struct ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label)
bool UI_but_is_utf8(const uiBut *but)
#define AUTOCOMPLETE_NO_MATCH
#define UI_TOOLTIP_DELAY_LABEL
void UI_popover_once_clear(uiPopover *pup)
@ UI_DIR_DOWN
Definition: UI_interface.h:124
@ UI_DIR_RIGHT
Definition: UI_interface.h:126
@ UI_DIR_LEFT
Definition: UI_interface.h:125
@ UI_DIR_UP
Definition: UI_interface.h:123
void UI_butstore_free(uiBlock *block, uiButStore *bs)
bool UI_view_item_drag_start(struct bContext *C, const uiViewItemHandle *item_)
#define UI_PRECISION_FLOAT_MAX
#define UI_PRECISION_FLOAT_SCALE
struct PanelType * UI_but_paneltype_get(uiBut *but)
void(* uiFreeArgFunc)(void *arg)
Definition: UI_interface.h:603
#define UI_MAX_DRAW_STR
Definition: UI_interface.h:91
#define UI_DPI_ICON_SIZE
Definition: UI_interface.h:307
void(* uiButHandleRenameFunc)(struct bContext *C, void *arg, char *origstr)
Definition: UI_interface.h:506
bool UI_but_active_only(const struct bContext *C, struct ARegion *region, uiBlock *block, uiBut *but)
#define UI_DPI_FAC
Definition: UI_interface.h:305
void(* uiButHandleFunc)(struct bContext *C, void *arg1, void *arg2)
Definition: UI_interface.h:505
struct MenuType * UI_but_menutype_get(uiBut *but)
uiButStore * UI_butstore_create(uiBlock *block)
void UI_but_execute(const struct bContext *C, struct ARegion *region, uiBut *but)
void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
bool UI_block_layout_needs_resolving(const uiBlock *block)
@ UI_GRAD_L_ALT
Definition: UI_interface.h:408
@ UI_GRAD_SV
Definition: UI_interface.h:400
@ UI_GRAD_V_ALT
Definition: UI_interface.h:407
@ UI_GRAD_S
Definition: UI_interface.h:404
@ UI_GRAD_HV
Definition: UI_interface.h:401
@ UI_GRAD_HS
Definition: UI_interface.h:402
@ UI_GRAD_V
Definition: UI_interface.h:405
@ UI_GRAD_H
Definition: UI_interface.h:403
#define UI_TOOLTIP_DELAY
void(* uiMenuCreateFunc)(struct bContext *C, struct uiLayout *layout, void *arg1)
Definition: UI_interface.h:592
void(* uiMenuHandleFunc)(struct bContext *C, void *arg, int event)
Definition: UI_interface.h:593
void UI_but_ensure_in_view(const struct bContext *C, struct ARegion *region, const uiBut *but)
bool UI_context_copy_to_selected_list(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, struct ListBase *r_lb, bool *r_use_path_from_id, char **r_path)
@ UI_BUT_POIN_CHAR
Definition: UI_interface.h:317
@ UI_BUT_POIN_FLOAT
Definition: UI_interface.h:320
void UI_view_item_begin_rename(uiViewItemHandle *item_handle)
eButType
Definition: UI_interface.h:329
@ UI_BTYPE_BUT
Definition: UI_interface.h:330
@ UI_BTYPE_TOGGLE
Definition: UI_interface.h:340
@ UI_BTYPE_EXTRA
Definition: UI_interface.h:373
@ UI_BTYPE_TAB
Definition: UI_interface.h:350
@ UI_BTYPE_HOTKEY_EVENT
Definition: UI_interface.h:377
@ UI_BTYPE_LISTBOX
Definition: UI_interface.h:366
@ UI_BTYPE_VECTORSCOPE
Definition: UI_interface.h:382
@ UI_BTYPE_SEPR_SPACER
Definition: UI_interface.h:388
@ UI_BTYPE_NODE_SOCKET
Definition: UI_interface.h:384
@ UI_BTYPE_ROUNDBOX
Definition: UI_interface.h:359
@ UI_BTYPE_COLORBAND
Definition: UI_interface.h:360
@ UI_BTYPE_BUT_MENU
Definition: UI_interface.h:335
@ UI_BTYPE_TOGGLE_N
Definition: UI_interface.h:341
@ UI_BTYPE_HISTOGRAM
Definition: UI_interface.h:380
@ UI_BTYPE_WAVEFORM
Definition: UI_interface.h:381
@ UI_BTYPE_BLOCK
Definition: UI_interface.h:353
@ UI_BTYPE_NUM_SLIDER
Definition: UI_interface.h:339
@ UI_BTYPE_HSVCIRCLE
Definition: UI_interface.h:368
@ UI_BTYPE_LISTROW
Definition: UI_interface.h:367
@ UI_BTYPE_TEXT
Definition: UI_interface.h:332
@ UI_BTYPE_BUT_TOGGLE
Definition: UI_interface.h:345
@ UI_BTYPE_VIEW_ITEM
Definition: UI_interface.h:393
@ UI_BTYPE_HSVCUBE
Definition: UI_interface.h:356
@ UI_BTYPE_PREVIEW_TILE
Definition: UI_interface.h:376
@ UI_BTYPE_LABEL
Definition: UI_interface.h:354
@ UI_BTYPE_CURVE
Definition: UI_interface.h:363
@ UI_BTYPE_ICON_TOGGLE_N
Definition: UI_interface.h:343
@ UI_BTYPE_DECORATOR
Definition: UI_interface.h:391
@ UI_BTYPE_ROW
Definition: UI_interface.h:331
@ UI_BTYPE_SEARCH_MENU
Definition: UI_interface.h:372
@ UI_BTYPE_UNITVEC
Definition: UI_interface.h:362
@ UI_BTYPE_SEPR_LINE
Definition: UI_interface.h:386
@ UI_BTYPE_KEY_EVENT
Definition: UI_interface.h:355
@ UI_BTYPE_PROGRESS_BAR
Definition: UI_interface.h:383
@ UI_BTYPE_POPOVER
Definition: UI_interface.h:351
@ UI_BTYPE_CHECKBOX_N
Definition: UI_interface.h:348
@ UI_BTYPE_SEPR
Definition: UI_interface.h:385
@ UI_BTYPE_NUM
Definition: UI_interface.h:337
@ UI_BTYPE_PULLDOWN
Definition: UI_interface.h:358
@ UI_BTYPE_CURVEPROFILE
Definition: UI_interface.h:365
@ UI_BTYPE_TRACK_PREVIEW
Definition: UI_interface.h:369
@ UI_BTYPE_COLOR
Definition: UI_interface.h:349
@ UI_BTYPE_CHECKBOX
Definition: UI_interface.h:347
@ UI_BTYPE_GRIP
Definition: UI_interface.h:390
@ UI_BTYPE_MENU
Definition: UI_interface.h:334
@ UI_BTYPE_ICON_TOGGLE
Definition: UI_interface.h:342
@ UI_BTYPE_IMAGE
Definition: UI_interface.h:379
@ UI_BTYPE_SCROLL
Definition: UI_interface.h:352
uiBlock *(* uiBlockCreateFunc)(struct bContext *C, struct ARegion *region, void *arg1)
Definition: UI_interface.h:714
bool UI_but_has_tooltip_label(const uiBut *but)
#define UI_but_is_decorator(but)
Definition: UI_interface.h:611
@ UI_BLOCK_CLIPBOTTOM
Definition: UI_interface.h:141
@ UI_BLOCK_POPOVER_ONCE
Definition: UI_interface.h:158
@ UI_BLOCK_NUMSELECT
Definition: UI_interface.h:138
@ UI_BLOCK_RADIAL
Definition: UI_interface.h:156
@ UI_BLOCK_LOOP
Definition: UI_interface.h:135
@ UI_BLOCK_POPUP_MEMORY
Definition: UI_interface.h:148
@ UI_BLOCK_MOVEMOUSE_QUIT
Definition: UI_interface.h:143
@ UI_BLOCK_KEEP_OPEN
Definition: UI_interface.h:144
@ UI_BLOCK_IS_FLIP
Definition: UI_interface.h:136
@ UI_BLOCK_CLIPTOP
Definition: UI_interface.h:142
@ UI_BLOCK_POPOVER
Definition: UI_interface.h:157
@ UI_BLOCK_OUT_1
Definition: UI_interface.h:146
@ UI_BLOCK_POPUP_HOLD
Definition: UI_interface.h:154
@ WM_HANDLER_BLOCKING
Definition: WM_api.h:431
@ KM_PRESS
Definition: WM_types.h:267
@ KM_CLICK_DRAG
Definition: WM_types.h:275
@ KM_DBL_CLICK
Definition: WM_types.h:270
@ KM_RELEASE
Definition: WM_types.h:268
@ KM_CLICK
Definition: WM_types.h:269
#define NC_WINDOW
Definition: WM_types.h:325
#define WM_UI_HANDLER_CONTINUE
Definition: WM_types.h:298
@ WM_EVENT_SCROLL_INVERT
Definition: WM_types.h:606
@ WM_EVENT_IS_REPEAT
Definition: WM_types.h:613
@ WM_CURSOR_WRAP_XY
Definition: WM_types.h:192
@ WM_DRAG_FREE_DATA
Definition: WM_types.h:1059
#define NC_MOVIECLIP
Definition: WM_types.h:347
#define NA_EDITED
Definition: WM_types.h:523
#define ND_SPACE_INFO_REPORT
Definition: WM_types.h:463
#define WM_EVENT_CURSOR_MOTION_THRESHOLD
Definition: WM_types.h:757
wmOperatorCallContext
Definition: WM_types.h:199
@ WM_OP_INVOKE_DEFAULT
Definition: WM_types.h:201
@ KM_CTRL
Definition: WM_types.h:239
@ KM_ALT
Definition: WM_types.h:240
@ KM_OSKEY
Definition: WM_types.h:242
@ KM_SHIFT
Definition: WM_types.h:238
#define WM_UI_HANDLER_BREAK
Definition: WM_types.h:299
#define NC_SPACE
Definition: WM_types.h:342
#define WM_DRAG_COLOR
Definition: WM_types.h:1053
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: avxb.h:154
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
void activate(bool forceActivation=false) const
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
unsigned int U
Definition: btGjkEpa3.h:78
void jump(const btVector3 &v=btVector3(0, 0, 0))
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
#define logf(x)
Definition: cuda/compat.h:105
#define sinf(x)
Definition: cuda/compat.h:102
#define cosf(x)
Definition: cuda/compat.h:101
#define expf(x)
Definition: cuda/compat.h:106
#define powf(x, y)
Definition: cuda/compat.h:103
double time
Scene scene
const Depsgraph * depsgraph
void * user_data
SyclQueue void void size_t num_bytes void
int len
Definition: draw_manager.c:108
#define str(s)
uint pos
uint col
static void copy_array(const Node *node, const SocketType &socket, const Node *other, const SocketType &other_socket)
Definition: graph/node.cpp:311
bool ui_but_is_unit(const uiBut *but)
Definition: interface.cc:2420
void ui_but_range_set_hard(uiBut *but)
Definition: interface.cc:3258
void ui_but_extra_operator_icons_free(uiBut *but)
Definition: interface.cc:1674
bool ui_but_is_compatible(const uiBut *but_a, const uiBut *but_b)
Definition: interface.cc:2449
void ui_but_range_set_soft(uiBut *but)
Definition: interface.cc:3280
void ui_but_update(uiBut *but)
Definition: interface.cc:3900
void ui_block_to_window_rctf(const ARegion *region, uiBlock *block, rctf *rct_dst, const rctf *rct_src)
Definition: interface.cc:170
float ui_block_to_window_scale(const ARegion *region, uiBlock *block)
Definition: interface.cc:180
int ui_but_is_pushed(uiBut *but)
Definition: interface.cc:2253
bool ui_but_is_float(const uiBut *but)
Definition: interface.cc:2376
PropertyScaleType ui_but_scale_type(const uiBut *but)
Definition: interface.cc:2389
void ui_but_override_flag(Main *bmain, uiBut *but)
Definition: interface.cc:1613
double ui_but_value_get(uiBut *but)
Definition: interface.cc:2492
void ui_region_to_window(const ARegion *region, int *r_x, int *r_y)
Definition: interface.cc:261
void ui_but_string_get(uiBut *but, char *str, const size_t maxlen)
Definition: interface.cc:2911
bool ui_but_is_rna_valid(uiBut *but)
Definition: interface.cc:2475
void ui_window_to_block_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y)
Definition: interface.cc:191
bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
Definition: interface.cc:3098
bool ui_but_menu_draw_as_popover(const uiBut *but)
Definition: interface.cc:4494
void ui_but_value_set(uiBut *but, double value)
Definition: interface.cc:2557
void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen)
Definition: interface.cc:2690
void ui_but_string_get_ex(uiBut *but, char *str, const size_t maxlen, const int float_precision, const bool use_exp_float, bool *r_use_exp_float)
Definition: interface.cc:2781
void ui_but_update_edited(uiBut *but)
Definition: interface.cc:3905
void ui_but_v3_set(uiBut *but, const float vec[3])
Definition: interface.cc:2338
void ui_but_v3_get(uiBut *but, float vec[3])
Definition: interface.cc:2291
bool ui_but_string_eval_number(bContext *C, const uiBut *but, const char *str, double *r_value)
Definition: interface.cc:3044
void ui_fontscale(float *points, float aspect)
Definition: interface.cc:2014
void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y)
Definition: interface.cc:142
int ui_but_string_get_max_length(uiBut *but)
Definition: interface.cc:2653
void ui_window_to_block(const ARegion *region, uiBlock *block, int *r_x, int *r_y)
Definition: interface.cc:228
char * ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
Definition: interface.cc:2916
bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut *but)
Definition: interface.cc:1898
bool ui_but_supports_cycling(const uiBut *but)
Definition: interface.cc:2484
bool ui_but_is_bool(const uiBut *but)
Definition: interface.cc:2397
void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
bool ui_but_anim_expression_get(uiBut *but, char *str, size_t maxlen)
void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but)
bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *event)
bool ui_but_drag_is_draggable(const uiBut *but)
void ui_but_drag_start(bContext *C, uiBut *but)
void UI_context_update_anim_flag(const bContext *C)
static void ui_color_picker_to_rgb_HSVCUBE_v(const uiButHSVCube *hsv_but, const float hsv[3], float rgb[3])
static int ui_handle_view_item_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *view_but)
static void ui_do_but_extra_operator_icons_mousemove(uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, const bool paste_array)
static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBlock *block)
static void ui_multibut_free(uiHandleButtonData *data, uiBlock *block)
static void ui_list_activate_row_from_index(bContext *C, ARegion *region, uiBut *listbox, uiList *ui_list, int index)
static CurveProfile but_copypaste_profile
struct uiDragToggleHandle uiDragToggleHandle
static float ui_numedit_apply_snapf(uiBut *but, float tempf, float softmin, float softmax, const enum eSnapType snap)
bool UI_textbutton_activate_rna(const bContext *C, ARegion *region, const void *rna_poin_data, const char *rna_prop_id)
void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo)
#define BUTTON_FLASH_DELAY
static int ui_handle_menu_event(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu, int level, const bool is_parent_inside, const bool is_parent_menu, const bool is_floating)
static bool ui_context_rna_button_active_test(const uiBut *but)
static void button_activate_exit(bContext *C, uiBut *but, uiHandleButtonData *data, const bool mousemove, const bool onfree)
uiBlock * UI_region_block_find_mouse_over(const struct ARegion *region, const int xy[2], bool only_clip)
static bool ui_numedit_but_CURVE(uiBlock *block, uiBut *but, uiHandleButtonData *data, int evtx, int evty, bool snap, const bool shift)
uiBut * UI_context_active_but_get_respect_menu(const bContext *C)
static void ui_afterfunc_update_preferences_dirty(uiAfterFunc *after)
static bool ui_multibut_states_tag(uiBut *but_active, uiHandleButtonData *data, const wmEvent *event)
void ui_but_execute_end(struct bContext *C, struct ARegion *UNUSED(region), uiBut *but, void *active_back)
static bool ui_but_is_drag_toggle(const uiBut *but)
static bool but_copypaste_curve_alive
static void ui_but_paste_numeric_array(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste)
static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static char ui_menu_scroll_test(uiBlock *block, int my)
static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
bool ui_but_is_editing(const uiBut *but)
int ui_but_menu_direction(uiBut *but)
bool UI_but_active_drop_name(const bContext *C)
static bool ui_menu_scroll_step(ARegion *region, uiBlock *block, const int scroll_dir)
#define DRAG_MULTINUM_THRESHOLD_DRAG_X
static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
static bool ui_numedit_but_HSVCIRCLE(uiBut *but, uiHandleButtonData *data, float mx, float my, const enum eSnapType snap, const bool shift)
void ui_handle_afterfunc_add_operator(wmOperatorType *ot, wmOperatorCallContext opcontext)
static bool ui_textedit_insert_buf(uiBut *but, uiHandleButtonData *data, const char *buf, int buf_len)
static void ui_but_copy_colorband(uiBut *but)
static bool ui_button_value_default(uiBut *but, double *r_value)
static void ui_but_paste_curvemapping(bContext *C, uiBut *but)
static int ui_list_handle_click_drag(bContext *C, const uiList *ui_list, ARegion *region, const wmEvent *event)
static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *region)
static void ui_rgb_to_color_picker_HSVCUBE_compat_v(const uiButHSVCube *hsv_but, const float rgb[3], float hsv[3])
#define MENU_TOWARDS_WIGGLE_ROOM
#define BUTTON_MOUSE_TOWARDS_THRESH
void UI_block_interaction_set(uiBlock *block, uiBlockInteraction_CallbackData *callbacks)
#define UI_MAX_PASSWORD_STR
uiMenuScrollType
@ MENU_SCROLL_BOTTOM
@ MENU_SCROLL_TOP
@ MENU_SCROLL_DOWN
@ MENU_SCROLL_UP
static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx, int my)
static bool ui_list_invoke_item_operator(bContext *C, const uiBut *context_but, wmOperatorType *ot, PointerRNA **properties)
struct uiAfterFunc uiAfterFunc
static int ui_handle_menu_return_submenu(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu)
static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static CurveMapping but_copypaste_curve
static bool ui_mouse_motion_towards_check(uiBlock *block, uiPopupBlockHandle *menu, const int xy[2], const bool use_wiggle_room)
float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2])
static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop)
static int ui_do_but_CURVEPROFILE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void float_array_to_string(const float *values, const int values_len, char *output, int output_len_max)
static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but)
static bool but_copypaste_profile_alive
static void button_activate_init(bContext *C, ARegion *region, uiBut *but, uiButtonActivateType type)
static bool ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, int mx, int my, const enum eSnapType snap, const bool shift)
uiBut * UI_context_active_but_get(const bContext *C)
static void ui_handle_button_activate(bContext *C, ARegion *region, uiBut *but, uiButtonActivateType type)
void ui_but_activate_over(bContext *C, ARegion *region, uiBut *but)
static void ui_multibut_states_create(uiBut *but_active, uiHandleButtonData *data)
static bool ui_event_is_snap(const wmEvent *event)
static bool ui_do_but_extra_operator_icon(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_but_copy_numeric_value(uiBut *but, char *output, int output_len_max)
static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_textedit_string_ensure_max_length(uiBut *but, uiHandleButtonData *data, int maxlen)
static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(userdata))
static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
bool UI_but_is_userdef(const uiBut *but)
static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event)
static void ui_but_set_float_array(bContext *C, uiBut *but, uiHandleButtonData *data, const float *values, const int values_len)
static int ui_but_pie_menu_apply(bContext *C, uiPopupBlockHandle *menu, uiBut *but, bool force_close)
static void ui_apply_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonData *data)
static int ui_do_but_GRIP(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_apply_but_CURVEPROFILE(bContext *C, uiBut *but, uiHandleButtonData *data)
#define DRAG_MULTINUM_THRESHOLD_DRAG_Y
static bool ui_do_but_ANY_drag_toggle(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event, int *r_retval)
#define UI_PROP_SCALE_LOG_SNAP_OFFSET
static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static bool ui_but_dragedit_update_mval(uiHandleButtonData *data, int mx)
static void popup_check(bContext *C, wmOperator *op)
uiBut * UI_region_active_but_get(const ARegion *region)
static void ui_apply_but_WAVEFORM(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_but_paste_color(bContext *C, uiBut *but, char *buf_paste)
static uiBut * ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, RadialDirection dir)
static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static bool ui_menu_scroll_to_y(ARegion *region, uiBlock *block, int y)
ARegion * UI_region_searchbox_region_get(const ARegion *button_region)
static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data)
static bool ui_but_copy_menu(uiBut *but, char *output, int output_len_max)
static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii)
static bool ui_drag_toggle_but_is_supported(const uiBut *but)
static bool ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, int mx, const bool is_horizontal, const bool is_motion, const bool snap, const bool shift)
static void ui_palette_set_active(uiButColor *color_but)
static bool ui_but_pie_menu_supported_apply(uiBut *but)
static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *event)
static void ui_region_auto_open_clear(ARegion *region)
static void ui_rna_update_preferences_dirty(PointerRNA *ptr, PropertyRNA *prop)
void ui_but_clipboard_free(void)
void UI_but_tooltip_timer_remove(bContext *C, uiBut *but)
void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable)
static int ui_drag_toggle_but_pushed_state(uiBut *but)
static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_selectcontext_apply(bContext *C, uiBut *but, struct uiSelectContextStore *selctx_data, const double value, const double value_orig)
static void ui_apply_but_CURVE(bContext *C, uiBut *but, uiHandleButtonData *data)
static bool button_modal_state(uiHandleButtonState state)
static float ui_mouse_scale_warp_factor(const bool shift)
static void ui_apply_but_IMAGE(bContext *C, uiBut *but, uiHandleButtonData *data)
static ListBase UIAfterFuncs
static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
uiBut * UI_context_active_but_prop_get(const bContext *C, struct PointerRNA *r_ptr, struct PropertyRNA **r_prop, int *r_index)
void ui_but_active_free(const bContext *C, uiBut *but)
static void ui_color_snap_hue(const enum eSnapType snap, float *r_hue)
static void ui_menu_scroll_apply_offset_y(ARegion *region, uiBlock *block, float dy)
static int ui_list_activate_hovered_row(bContext *C, ARegion *region, const uiList *ui_list, const wmEvent *event, bool activate_dragging)
void ui_but_active_string_clear_and_exit(bContext *C, uiBut *but)
static void ui_apply_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str), const size_t str_step_ofs, const rcti *glyph_step_bounds, const int UNUSED(glyph_advance_x), const rcti *glyph_bounds, const int UNUSED(glyph_bearing[2]), void *user_data)
uiHandleButtonState
@ BUTTON_STATE_HIGHLIGHT
@ BUTTON_STATE_INIT
@ BUTTON_STATE_WAIT_FLASH
@ BUTTON_STATE_NUM_EDITING
@ BUTTON_STATE_TEXT_EDITING
@ BUTTON_STATE_TEXT_SELECTING
@ BUTTON_STATE_WAIT_KEY_EVENT
@ BUTTON_STATE_WAIT_DRAG
@ BUTTON_STATE_WAIT_RELEASE
@ BUTTON_STATE_EXIT
@ BUTTON_STATE_MENU_OPEN
static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_but_paste_numeric_value(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste)
static void ui_numedit_end(uiBut *but, uiHandleButtonData *data)
static void ui_apply_but_VIEW_ITEM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtraOpIcon *op_icon)
static void ui_but_paste_normalized_vector(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste)
#define UI_PROP_SCALE_LOG_MIN
static void ui_but_update_preferences_dirty(uiBut *but)
void ui_pan_to_scroll(const wmEvent *event, int *type, int *val)
static int ui_do_but_HISTOGRAM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static struct uiBlockInteraction_Handle * ui_block_interaction_begin(struct bContext *C, uiBlock *block, const bool is_click)
static int ui_list_get_increment(const uiList *ui_list, const int type, const int columns)
static uiBut * ui_context_rna_button_active(const bContext *C)
static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_apply_but_func(bContext *C, uiBut *but)
static void ui_mouse_scale_warp(uiHandleButtonData *data, const float mx, const float my, float *r_mx, float *r_my, const bool shift)
struct uiSelectContextStore uiSelectContextStore
static bool ui_list_is_hovering_draggable_but(bContext *C, const uiList *list, const ARegion *region, const wmEvent *event)
static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, const float x)
static void ui_apply_but_COLORBAND(bContext *C, uiBut *but, uiHandleButtonData *data)
static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, uiBut *but, uiHandleButtonData *data, int evtx, int evty, bool snap, const bool shift)
static bool ui_drag_toggle_set_xy_xy(bContext *C, ARegion *region, const int pushed_state, const int xy_src[2], const int xy_dst[2])
static void ui_apply_but_VEC(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_textedit_move(uiBut *but, uiHandleButtonData *data, eStrCursorJumpDirection direction, const bool select, eStrCursorJumpType jump)
static bool ui_numedit_but_UNITVEC(uiBut *but, uiHandleButtonData *data, int mx, int my, const enum eSnapType snap)
static void ui_apply_but_BUTM(bContext *C, uiBut *but, uiHandleButtonData *data)
static enum eSnapType ui_event_to_snap(const wmEvent *event)
struct uiHandleButtonMulti uiHandleButtonMulti
static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_len_max)
#define MENU_SCROLL_INTERVAL
static void ui_apply_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data)
#define MENU_TOWARDS_MARGIN
static void ui_selectcontext_end(uiBut *but, uiSelectContextStore *selctx_data)
static int ui_do_but_TRACKPREVIEW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
bool UI_textbutton_activate_but(const bContext *C, uiBut *actbut)
void UI_context_active_but_clear(bContext *C, wmWindow *win, ARegion *region)
static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu)
static void ui_block_interaction_end(struct bContext *C, uiBlockInteraction_CallbackData *callbacks, struct uiBlockInteraction_Handle *interaction)
static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const int mode)
static void ui_numedit_set_active(uiBut *but)
struct uiSelectContextElem uiSelectContextElem
static ColorBand but_copypaste_coba
static void ui_block_interaction_update(struct bContext *C, uiBlockInteraction_CallbackData *callbacks, struct uiBlockInteraction_Handle *interaction)
static int ui_do_but_HSVCIRCLE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static int ui_do_but_EXIT(bContext *C, uiBut *but, struct uiHandleButtonData *data, const wmEvent *event)
static bool point_draw_handles(CurveProfilePoint *point)
static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state)
static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event)
static void ui_apply_but_autokey(bContext *C, uiBut *but)
static uiBut * ui_context_button_active(const ARegion *region, bool(*but_check_cb)(const uiBut *))
@ UI_TEXTEDIT_CUT
@ UI_TEXTEDIT_PASTE
@ UI_TEXTEDIT_COPY
static bool ui_but_drag_init(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
uiButtonActivateType
@ BUTTON_ACTIVATE_OVER
@ BUTTON_ACTIVATE_APPLY
@ BUTTON_ACTIVATE_OPEN
@ BUTTON_ACTIVATE
@ BUTTON_ACTIVATE_TEXT_EDITING
static bool ui_numedit_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonData *data, int mx, int my, const bool shift)
static void ui_apply_but_undo(uiBut *but)
static float ui_numedit_apply_snap(int temp, float softmin, float softmax, const enum eSnapType snap)
#define DRAG_MULTINUM_THRESHOLD_VERTICAL
static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_handler_region_drag_toggle_remove(bContext *UNUSED(C), void *userdata)
static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
static uiBut * ui_but_find_open_event(ARegion *region, const wmEvent *event)
#define UI_DRAG_MAP_SOFT_RANGE_PIXEL_MAX
static int ui_do_but_VIEW_ITEM(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
#define BUTTON_DRAGLOCK_THRESH
static void ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot, PointerRNA **properties, wmOperatorCallContext opcontext, const uiBut *context_but)
static void ui_textedit_set_cursor_select(uiBut *but, uiHandleButtonData *data, const float x)
void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but)
uiBut * ui_but_find_select_in_enum(uiBut *but, int direction)
static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void *userdata)
static bool ui_numedit_but_NUM(uiButNumber *number_but, uiHandleButtonData *data, int mx, const bool is_motion, const enum eSnapType snap, float fac)
static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox)
static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but)
static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str)
static void ui_but_copy(bContext *C, uiBut *but, const bool copy_array)
static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
static void ui_multibut_add(uiHandleButtonData *data, uiBut *but)
static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max)
void UI_but_tooltip_refresh(bContext *C, uiBut *but)
#define IS_ALLSELECT_EVENT(event)
static void ui_but_copy_text(uiBut *but, char *output, int output_len_max)
static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static bool ui_handle_button_activate_by_type(bContext *C, ARegion *region, uiBut *but)
static bool parse_float_array(char *text, float *values, int values_len_expected)
bool UI_but_active_drop_color(bContext *C)
static int ui_do_but_TAB(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_block_open_end(bContext *C, uiBut *but, uiHandleButtonData *data)
#define UI_BUT_IS_SELECT_CONTEXT
static void ui_mouse_motion_towards_init(uiPopupBlockHandle *menu, const int xy[2])
static void ui_region_handler_remove(bContext *C, void *UNUSED(userdata))
static uiButExtraOpIcon * ui_but_extra_operator_icon_mouse_over_get(uiBut *but, ARegion *region, const wmEvent *event)
static void ui_mouse_motion_towards_init_ex(uiPopupBlockHandle *menu, const int xy[2], const bool force)
struct uiHandleButtonData uiHandleButtonData
static void ui_apply_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_but_copy_CurveProfile(uiBut *but)
static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const int xy_input[2])
static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const bool interactive)
static void ui_apply_but_HISTOGRAM(bContext *C, uiBut *but, uiHandleButtonData *data)
static bool ui_textedit_delete_selection(uiBut *but, uiHandleButtonData *data)
static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *region)
void UI_screen_free_active_but_highlight(const bContext *C, bScreen *screen)
static void ui_but_paste_CurveProfile(bContext *C, uiBut *but)
static void clamp_axis_max_v3(float v[3], const float max)
static uiAfterFunc * ui_afterfunc_new(void)
#define CASE_NUM_TO_DIR(n, d)
static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static uiButMultiState * ui_multibut_lookup(uiHandleButtonData *data, const uiBut *but)
static void ui_rgb_to_color_picker_HSVCUBE_v(const uiButHSVCube *hsv_but, const float rgb[3], float hsv[3])
static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_but_get_pasted_text_from_clipboard(char **buf_paste, int *buf_len)
void ui_but_update_view_for_active(const bContext *C, const uiBlock *block)
static int ui_text_position_to_hidden(uiBut *but, int pos)
static void ui_apply_but_funcs_after(bContext *C)
void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value)
#define BUTTON_KEYNAV_PX_LIMIT
static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata)
static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static int get_but_property_array_length(uiBut *but)
#define PIE_MENU_INTERVAL
wmOperator * UI_context_active_operator_get(const struct bContext *C)
static int ui_text_position_from_hidden(uiBut *but, int pos)
static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block)
static ARegion * ui_but_tooltip_init(bContext *C, ARegion *region, int *pass, double *r_pass_delay, bool *r_exit_on_event)
static void ui_but_paste_colorband(bContext *C, uiBut *but, uiHandleButtonData *data)
static bool ui_textedit_delete(uiBut *but, uiHandleButtonData *data, int direction, eStrCursorJumpType jump)
static void ui_but_copy_curvemapping(uiBut *but)
static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandle *menu)
void UI_popup_handlers_remove(ListBase *handlers, uiPopupBlockHandle *popup)
static bool ui_but_copy_popover(uiBut *but, char *output, int output_len_max)
static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
static void ui_block_interaction_begin_ensure(bContext *C, uiBlock *block, struct uiHandleButtonData *data, const bool is_click)
static void ui_blocks_set_tooltips(ARegion *region, const bool enable)
static void ui_popup_handler_remove(bContext *C, void *userdata)
static void ui_mouse_motion_towards_reinit(uiPopupBlockHandle *menu, const int xy[2])
static int ui_do_but_UNITVEC(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
void UI_region_handlers_add(ListBase *handlers)
static bool ui_selectcontext_begin(bContext *C, uiBut *but, struct uiSelectContextStore *selctx_data)
static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data)
uiBut * UI_but_active_drop_name_button(const bContext *C)
void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], uiBut *but, const bool restore)
static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b)
static int ui_handle_menus_recursive(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu, int level, const bool is_parent_inside, const bool is_parent_menu, const bool is_floating)
static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int mx)
static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
static void ui_but_copy_color(uiBut *but, char *output, int output_len_max)
struct uiButMultiState uiButMultiState
static void button_tooltip_timer_reset(bContext *C, uiBut *but)
void UI_popup_handlers_add(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, const char flag)
struct uiBlockInteraction_Handle uiBlockInteraction_Handle
@ SNAP_OFF
@ SNAP_ON
@ SNAP_ON_SMALL
static bool ui_menu_scroll_to_but(ARegion *region, uiBlock *block, uiBut *but_target)
void UI_region_free_active_but_all(bContext *C, ARegion *region)
static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx, int my)
#define BUTTON_AUTO_OPEN_THRESH
static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *userdata)
static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu)
static void ui_but_paste_text(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste)
void ui_but_execute_begin(struct bContext *UNUSED(C), struct ARegion *region, uiBut *but, void **active_back)
static uiBut * ui_but_list_row_text_activate(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event, uiButtonActivateType activate_type)
uiBut * UI_region_but_find_rect_over(const ARegion *region, const rcti *rect_px)
static bool ui_menu_pass_event_to_parent_if_nonactive(uiPopupBlockHandle *menu, const uiBut *but, const int level, const int retval)
static void ui_apply_but_LISTROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
void UI_popup_handlers_remove_all(bContext *C, ListBase *handlers)
static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data)
struct uiUndoStack_Text * ui_textedit_undo_stack_create(void)
uiBlock * ui_block_find_mouse_over_ex(const struct ARegion *region, const int xy[2], bool only_clip) ATTR_NONNULL(1
void ui_hsvcircle_vals_from_pos(const rcti *rect, float mx, float my, float *r_val_rad, float *r_val_dist)
bool ui_but_is_cursor_warp(const uiBut *but) ATTR_WARN_UNUSED_RESULT
bool ui_searchbox_apply(uiBut *but, struct ARegion *region)
uiPopupBlockHandle * ui_popup_block_create(struct bContext *C, struct ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg, uiFreeArgFunc arg_free)
uiBut * ui_region_find_active_but(struct ARegion *region) ATTR_WARN_UNUSED_RESULT
uiBut * ui_but_find_mouse_over_ex(const struct ARegion *region, const int xy[2], bool labeledit, bool for_tooltip, const uiButFindPollFn find_poll, const void *find_custom_data) ATTR_NONNULL(1
void ui_perceptual_to_scene_linear_space(uiBut *but, float rgb[3])
size_t size_t ui_but_drawstr_len_without_sep_char(const uiBut *but)
uiBut * ui_list_row_find_mouse_over(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1
void ui_popup_menu_memory_set(uiBlock *block, struct uiBut *but)
RadialDirection
@ UI_RADIAL_W
@ UI_RADIAL_E
@ UI_RADIAL_NONE
@ UI_RADIAL_N
@ UI_RADIAL_SE
@ UI_RADIAL_SW
@ UI_RADIAL_S
@ UI_RADIAL_NE
@ UI_RADIAL_NW
void ui_color_picker_hsv_to_rgb(const float r_cp[3], float rgb[3])
bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
uiBlock *(* uiBlockHandleCreateFunc)(struct bContext *C, struct uiPopupBlockHandle *handle, void *arg1)
int ui_handler_panel_region(struct bContext *C, const struct wmEvent *event, struct ARegion *region, const uiBut *active_but)
bool ui_searchbox_event(struct bContext *C, struct ARegion *region, uiBut *but, struct ARegion *butregion, const struct wmEvent *event)
bool ui_but_has_array_value(const uiBut *but) ATTR_WARN_UNUSED_RESULT
uiBut * ui_view_item_find_mouse_over(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1
#define UI_BITBUT_VALUE_TOGGLED(a, b)
uiBut * ui_but_next(uiBut *but) ATTR_WARN_UNUSED_RESULT
size_t ui_but_tip_len_only_first_line(const uiBut *but)
bool ui_region_contains_point_px(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1
bool ui_block_is_popup_any(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
uiBut * ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) ATTR_WARN_UNUSED_RESULT
const char * ui_textedit_undo(struct uiUndoStack_Text *undo_stack, int direction, int *r_cursor_index)
void ui_scene_linear_to_perceptual_space(uiBut *but, float rgb[3])
#define PIE_CLICK_THRESHOLD_SQ
int ui_but_menu_step(uiBut *but, int direction)
void ui_hsvcube_pos_from_vals(const struct uiButHSVCube *hsv_but, const rcti *rect, const float *hsv, float *xp, float *yp)
void ui_popup_block_free(struct bContext *C, uiPopupBlockHandle *handle)
uiBut * ui_list_row_find_from_index(const struct ARegion *region, int index, uiBut *listbox) ATTR_WARN_UNUSED_RESULT
#define UI_TEXT_MARGIN_X
bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
bool ui_searchbox_inside(struct ARegion *region, const int xy[2]) ATTR_NONNULL(1
#define UI_MENU_SCROLL_PAD
bool ui_but_contains_point_px_icon(const uiBut *but, struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT
struct ARegion struct ARegion * ui_screen_region_find_mouse_over(struct bScreen *screen, const struct wmEvent *event)
void ui_but_hsv_set(uiBut *but)
uiBut * ui_but_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT
@ UI_PIE_DRAG_STYLE
@ UI_PIE_INVALID_DIR
@ UI_PIE_ANIMATION_FINISHED
@ UI_PIE_CLICK_STYLE
@ UI_PIE_INITIAL_DIRECTION
@ UI_PIE_GESTURE_END_WAIT
void ui_hsvcircle_pos_from_vals(const ColorPicker *cpicker, const rcti *rect, const float *hsv, float *xpos, float *ypos)
void ui_textedit_undo_stack_destroy(struct uiUndoStack_Text *undo_stack)
void ui_searchbox_update(struct bContext *C, struct ARegion *region, uiBut *but, bool reset)
#define UI_MENU_SCROLL_ARROW
bool ui_but_contains_pt(const uiBut *but, float mx, float my) ATTR_WARN_UNUSED_RESULT
size_t ui_but_drawstr_without_sep_char(const uiBut *but, char *str, size_t str_maxlen) ATTR_NONNULL(1
uiBut * ui_block_active_but_get(const uiBlock *block)
bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT
bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT
void ui_textedit_undo_push(struct uiUndoStack_Text *undo_stack, const char *text, int cursor_index)
bool ui_but_is_interactive(const uiBut *but, bool labeledit) ATTR_WARN_UNUSED_RESULT
uiBut * ui_but_last(uiBlock *block) ATTR_WARN_UNUSED_RESULT
void ui_color_picker_rgb_to_hsv_compat(const float rgb[3], float r_cp[3])
uiBut * ui_list_find_from_row(const struct ARegion *region, const uiBut *row_but) ATTR_WARN_UNUSED_RESULT
uiPopupBlockHandle * ui_popup_menu_create(struct bContext *C, struct ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
uiBut * ui_region_find_first_but_test_flag(struct ARegion *region, int flag_include, int flag_exclude)
uiBut * ui_but_prev(uiBut *but) ATTR_WARN_UNUSED_RESULT
void ui_but_pie_dir(RadialDirection dir, float vec[2])
bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1
void ui_popup_translate(struct ARegion *region, const int mdiff[2])
bool ui_but_is_popover_once_compat(const uiBut *but) ATTR_WARN_UNUSED_RESULT
bool ui_but_is_editable_as_text(const uiBut *but) ATTR_WARN_UNUSED_RESULT
bool int ui_searchbox_find_index(struct ARegion *region, const char *name)
int ui_searchbox_autocomplete(struct bContext *C, struct ARegion *region, uiBut *but, char *str)
uiBlock * ui_block_func_COLOR(struct bContext *C, uiPopupBlockHandle *handle, void *arg_but)
uiBut * ui_but_first(uiBlock *block) ATTR_WARN_UNUSED_RESULT
void ui_popup_block_scrolltest(struct uiBlock *block)
void ui_searchbox_free(struct bContext *C, struct ARegion *region)
uiBut * ui_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT
@ UI_ACTIVE
@ UI_HIDDEN
@ UI_HAS_ICON
@ UI_SELECT
@ UI_BUT_ACTIVE_OVERRIDE
uiPopupBlockHandle * ui_popover_panel_create(struct bContext *C, struct ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
#define UI_MENU_SCROLL_MOUSE
int count
ccl_global KernelShaderEvalInput ccl_global float * output
const int state
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:28
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static ulong * next
#define atan2f(x, y)
Definition: metal/compat.h:227
#define asinf(x)
Definition: metal/compat.h:221
#define fmodf(x, y)
Definition: metal/compat.h:230
#define fabsf(x)
Definition: metal/compat.h:219
#define sqrtf(x)
Definition: metal/compat.h:243
bool isfinite(uchar)
Definition: scene/image.cpp:31
static unsigned a[3]
Definition: RandGen.cpp:78
static void area(int d1, int d2, int e1, int e2, float weights[2])
T length(const vec_base< T, Size > &a)
T abs(const T &a)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static const pxr::TfToken rgba("rgba", pxr::TfToken::Immortal)
static const pxr::TfToken rgb("rgb", pxr::TfToken::Immortal)
static void update(bNodeTree *ntree)
vector snap(vector a, vector b)
Definition: node_math.h:59
return ret
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2767
void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
Definition: rna_access.c:3095
void RNA_property_boolean_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, bool value)
Definition: rna_access.c:2352
void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
Definition: rna_access.c:2449
bool RNA_property_array_check(PropertyRNA *prop)
Definition: rna_access.c:1080
void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
Definition: rna_access.c:2879
void RNA_id_pointer_create(ID *id, PointerRNA *r_ptr)
Definition: rna_access.c:112
int RNA_property_int_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
Definition: rna_access.c:2581
const char * RNA_property_identifier(const PropertyRNA *prop)
Definition: rna_access.c:1000
void RNA_property_float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, float value)
Definition: rna_access.c:3036
float RNA_property_float_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
Definition: rna_access.c:2954
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value, ReportList *reports)
Definition: rna_access.c:3532
float RNA_property_float_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
Definition: rna_access.c:3125
void RNA_property_float_range(PointerRNA *ptr, PropertyRNA *prop, float *hardmin, float *hardmax)
Definition: rna_access.c:1274
PropertyType RNA_property_type(PropertyRNA *prop)
Definition: rna_access.c:1010
const PointerRNA PointerRNA_NULL
Definition: rna_access.c:61
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
Definition: rna_access.c:3421
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:3493
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:717
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2138
int RNA_property_int_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
Definition: rna_access.c:2743
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2153
void RNA_property_int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, int value)
Definition: rna_access.c:2652
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2429
int RNA_property_flag(PropertyRNA *prop)
Definition: rna_access.c:1055
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
Definition: rna_access.c:2180
int RNA_property_int_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
Definition: rna_access.c:2678
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:1075
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:3402
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
Definition: rna_access.c:2790
PropertySubType RNA_property_subtype(PropertyRNA *prop)
Definition: rna_access.c:1015
void RNA_property_int_range(PointerRNA *ptr, PropertyRNA *prop, int *hardmin, int *hardmax)
Definition: rna_access.c:1190
bool RNA_property_boolean_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
Definition: rna_access.c:2275
float RNA_property_float_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
Definition: rna_access.c:3062
void RNA_property_boolean_set_array(PointerRNA *ptr, PropertyRNA *prop, const bool *values)
Definition: rna_access.c:2304
StructRNA * RNA_struct_base(StructRNA *type)
Definition: rna_access.c:639
#define min(a, b)
Definition: sort.c:35
#define SIZE_MAX
Definition: stdint.h:206
struct ARegion * next
short regiontype
ListBase uiblocks
struct BMLoop * next
Definition: bmesh_class.h:233
struct ColorBand * gradient
CBData data[32]
bool use_luminosity_lock
float hsv_perceptual[3]
float luminosity_lock_value
CurveMap cm[4]
CurveProfilePoint * path
CurveProfilePoint * table
Definition: DNA_ID.h:368
void * link
Definition: BLI_linklist.h:24
struct LinkNode * next
Definition: BLI_linklist.h:23
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
char idname[BKE_ST_MAXNAME]
Definition: BKE_screen.h:361
struct MovieTrackingMarker * marker
struct MovieTrackingTrack * track
int active_color
ListBase colors
char idname[BKE_ST_MAXNAME]
Definition: BKE_screen.h:223
float last_pos[2]
float pie_center_spawned[2]
float pie_center_init[2]
float pie_dir[2]
double duration_gesture
struct StructRNA * type
Definition: RNA_types.h:37
void * data
Definition: RNA_types.h:38
struct ID * owner_id
Definition: RNA_types.h:36
struct RenderData r
float wavefrm_yfac
ListBase regionbase
struct wmTooltipState * tool_tip
float xmax
Definition: DNA_vec_types.h:69
float xmin
Definition: DNA_vec_types.h:69
float ymax
Definition: DNA_vec_types.h:70
float ymin
Definition: DNA_vec_types.h:70
int xmin
Definition: DNA_vec_types.h:63
PropertyRNA * rnaprop
char undostr[BKE_UNDO_STR_MAX]
uiButHandleFunc func
bContextStore * context
uiBlockInteraction_Handle * custom_interaction_handle
PointerRNA rnapoin
uiButHandleNFunc funcN
wmOperatorCallContext opcontext
wmOperatorType * optype
wmOperator * popup_op
struct uiAfterFunc * next
uiBlockInteraction_CallbackData custom_interaction_callbacks
char drawstr[UI_MAX_DRAW_STR]
uiBlockHandleFunc handle_func
uiMenuHandleFunc butm_func
struct uiAfterFunc * prev
uiButHandleRenameFunc rename_func
PointerRNA * opptr
uiFreeArgFunc search_arg_free_fn
uiBlockInteractionBeginFn begin_fn
Definition: UI_interface.h:580
uiBlockInteractionEndFn end_fn
Definition: UI_interface.h:581
uiBlockInteractionUpdateFn update_fn
Definition: UI_interface.h:582
struct uiBlockInteraction_Params params
bool tooltipdisabled
struct UnitSettings * unit
ListBase saferct
uiPopupBlockHandle * handle
struct PieMenuData pie_data
uiBlockHandleFunc handle_func
double auto_open_last
ListBase buttons
void * butm_func_arg
uiBlockInteraction_CallbackData custom_interaction_callbacks
uiMenuHandleFunc butm_func
void * handle_func_arg
int(* block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *)
void * evil_C
struct ColorBand * edit_coba
struct CurveMapping * edit_cumap
struct CurveProfile * edit_profile
struct wmOperatorCallParams * optype_params
eButGradientType gradient_type
uiSelectContextStore select_others
bool results_are_suggestions
uiButSearchCreateFn popup_create_fn
uiFreeArgFunc arg_free_fn
uiViewItemHandle * view_item
short selend
wmOperatorCallContext opcontext
const char * tip
uiButCompleteFunc autocomplete_func
struct bContextStore * context
ListBase extra_op_icons
struct uiBut * prev
void * custom_data
struct uiBut * next
uiButHandleNFunc funcN
void * func_arg2
RadialDirection pie_dir
struct uiHandleButtonData * active
void * rename_orig
float * editvec
char * editstr
eButType type
uchar menu_key
float softmin
double * editval
float hardmax
uiButHandleFunc func
eButPointerType pointype
bool changed
uchar unit_type
uiBlock * block
eUIEmbossType emboss
short bitnr
uiMenuCreateFunc menu_create_func
char * poin
void * func_arg1
short alignnr
short selsta
float hardmin
uiButHandleHoldFunc hold_func
short retval
struct ImBuf * imb
BIFIconID icon
struct PointerRNA * opptr
struct wmOperatorType * optype
uiButHandleRenameFunc rename_func
uiBlockCreateFunc block_create_func
float softmax
short iconadd
char drawstr[UI_MAX_DRAW_STR]
void * autofunc_arg
void * rename_arg1
struct PropertyRNA * rnaprop
void * func_argN
struct PointerRNA rnapoin
struct uiBlockInteraction_Handle * custom_interaction_handle
wmWindowManager * wm
uiHandleButtonState state
struct uiUndoStack_Text * undo_stack_text
struct uiKeyNavLock searchbox_keynav_state
uiSelectContextStore select_others
uiPopupBlockHandle * menu
uiButtonActivateType posttype
uiHandleButtonMulti multi_data
enum uiHandleButtonMulti::@416 init
struct wmOperatorType * custom_activate_optype
struct PointerRNA * custom_drag_opptr
int * items_filter_neworder
struct wmOperatorType * custom_drag_optype
int * items_filter_flags
struct PointerRNA * custom_activate_opptr
int filter_sort_flag
uiListDyn * dyn_data
struct ARegion * region
struct wmTimer * scrolltimer
struct ARegion * ctx_region
struct wmOperator * popup_op
struct uiKeyNavLock keynav_state
struct ScrArea * ctx_area
struct uiPopupBlockCreate popup_create_vars
void(* cancel_func)(struct bContext *C, void *arg)
void(* popup_func)(struct bContext *C, void *arg, int event)
struct uiSafetyRct * next
uiSelectContextElem * elems
uiFontStyle widget
wmUIHandlerRemoveFunc remove_fn
wmUIHandlerFunc handle_fn
wmEventHandler head
struct wmEventHandler * next
enum eWM_EventHandlerType type
short val
Definition: WM_types.h:680
int xy[2]
Definition: WM_types.h:682
char utf8_buf[6]
Definition: WM_types.h:690
int prev_xy[2]
Definition: WM_types.h:728
uint8_t modifier
Definition: WM_types.h:693
eWM_EventFlag flag
Definition: WM_types.h:707
short type
Definition: WM_types.h:678
void * customdata
Definition: WM_types.h:715
struct wmOperatorType * optype
Definition: WM_types.h:1008
struct PointerRNA * opptr
Definition: WM_types.h:1009
wmOperatorCallContext opcontext
Definition: WM_types.h:1010
bool(* check)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:911
struct wmOperatorType * type
double duration
Definition: WM_types.h:872
struct ARegion * region
Definition: WM_types.h:1252
struct wmEvent * eventstate
struct wmIMEData * ime_data
double PIL_check_seconds_timer(void)
Definition: time.c:64
float max
void WM_cursor_modal_set(wmWindow *win, int val)
Definition: wm_cursors.c:191
void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4])
Definition: wm_cursors.c:226
void WM_cursor_modal_restore(wmWindow *win)
Definition: wm_cursors.c:200
void WM_cursor_grab_disable(wmWindow *win, const int mouse_ungrab_xy[2])
Definition: wm_cursors.c:263
@ WM_CURSOR_Y_MOVE
Definition: wm_cursors.h:39
@ WM_CURSOR_TEXT_EDIT
Definition: wm_cursors.h:19
@ WM_CURSOR_X_MOVE
Definition: wm_cursors.h:38
void WM_event_start_drag(bContext *C, int icon, int type, void *poin, double value, unsigned int flags)
Definition: wm_dragdrop.cc:237
int xy[2]
Definition: wm_draw.c:135
int WM_event_drag_threshold(const struct wmEvent *event)
int WM_event_absolute_delta_y(const struct wmEvent *event)
void WM_event_drag_start_xy(const wmEvent *event, int r_xy[2])
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_main_add_notifier(unsigned int type, void *reference)
void WM_report(eReportType type, const char *message)
void WM_report_banner_show(void)
void WM_event_free_ui_handler_all(bContext *C, ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn)
void WM_reportf(eReportType type, const char *format,...)
void WM_operator_name_call_ptr_with_depends_on_cursor(bContext *C, wmOperatorType *ot, wmOperatorCallContext opcontext, PointerRNA *properties, const wmEvent *event, const char *drawstr)
void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn, void *user_data, const bool postpone)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void wm_event_handler_ui_cancel_ex(bContext *C, wmWindow *win, ARegion *region, bool reactivate_button)
void wm_event_init_from_window(wmWindow *win, wmEvent *event)
void WM_event_add_mousemove(wmWindow *win)
@ WM_HANDLER_TYPE_UI
#define WM_HANDLER_CONTINUE
#define ISMOUSE_BUTTON(event_type)
#define ISMOUSE_MOTION(event_type)
@ EVT_PAD8
@ EVT_PAD2
@ MOUSEPAN
@ RIGHTMOUSE
@ EVT_SIXKEY
@ EVT_BUT_CANCEL
@ EVT_PADPERIOD
@ EVT_OKEY
@ TIMER
@ EVT_EKEY
@ EVT_PAD4
@ EVT_JKEY
@ EVT_BUT_OPEN
@ EVT_PAD0
@ EVT_FOURKEY
@ EVT_YKEY
@ EVT_RIGHTCTRLKEY
@ WM_IME_COMPOSITE_EVENT
@ EVT_SKEY
@ EVT_VKEY
@ EVT_IKEY
@ EVT_XKEY
@ EVT_ZEROKEY
@ EVT_FKEY
@ EVT_PAD9
@ EVT_DELKEY
@ EVT_SEVENKEY
@ EVT_CKEY
@ EVT_GKEY
@ EVT_KKEY
@ EVT_TABKEY
@ EVT_DOWNARROWKEY
@ EVT_UKEY
@ EVT_AKEY
@ EVT_PAD3
@ EVT_MINUSKEY
@ WHEELUPMOUSE
@ EVT_PAGEUPKEY
@ EVT_PAGEDOWNKEY
@ EVT_LEFTCTRLKEY
@ EVT_RIGHTARROWKEY
@ EVT_PADENTER
@ EVT_NINEKEY
@ EVT_CAPSLOCKKEY
@ WHEELDOWNMOUSE
@ EVT_PAD6
@ EVT_PAD5
@ EVT_HOMEKEY
@ EVENT_NONE
@ MOUSEMOVE
@ EVT_WKEY
@ EVT_UNKNOWNKEY
@ WM_IME_COMPOSITE_END
@ EVT_ENDKEY
@ EVT_MKEY
@ WM_IME_COMPOSITE_START
@ EVT_TKEY
@ EVT_HKEY
@ EVT_FIVEKEY
@ EVT_ONEKEY
@ EVT_UPARROWKEY
@ LEFTMOUSE
@ EVT_EIGHTKEY
@ EVT_LEFTARROWKEY
@ NDOF_MOTION
@ EVT_NKEY
@ EVT_QKEY
@ MIDDLEMOUSE
@ EVT_ZKEY
@ EVT_ESCKEY
@ EVT_THREEKEY
@ EVT_BACKSPACEKEY
@ EVT_DKEY
@ EVT_DROP
@ EVT_PAD1
@ EVT_TWOKEY
@ EVT_LKEY
@ EVT_RIGHTSHIFTKEY
@ EVT_PAD7
@ EVT_LEFTSHIFTKEY
@ EVT_RKEY
@ WINDEACTIVATE
@ EVT_BKEY
@ EVT_PKEY
@ EVT_RETKEY
#define ISKEYBOARD(event_type)
#define ISHOTKEY(event_type)
PointerRNA * ptr
Definition: wm_files.c:3480
wmOperatorType * ot
Definition: wm_files.c:3479
void WM_gestures_remove(wmWindow *win)
Definition: wm_gesture.c:101
const char * WM_key_event_string(const short type, const bool compact)
Definition: wm_keymap.c:1046
char * WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index)
Definition: wm_operators.c:636
char * WM_operator_pystring_ex(bContext *C, wmOperator *op, const bool all_args, const bool macro_args, wmOperatorType *ot, PointerRNA *opptr)
Definition: wm_operators.c:192
void WM_operator_properties_free(PointerRNA *ptr)
Definition: wm_operators.c:783
bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
Definition: wm_stereo.c:141
void WM_tooltip_timer_clear(bContext *C, wmWindow *win)
Definition: wm_tooltip.c:68
void WM_tooltip_clear(bContext *C, wmWindow *win)
Definition: wm_tooltip.c:80
void WM_tooltip_timer_init_ex(bContext *C, wmWindow *win, ScrArea *area, ARegion *region, wmTooltipInitFn init, double delay)
Definition: wm_tooltip.c:46
double WM_tooltip_time_closed(void)
Definition: wm_tooltip.c:26
void WM_tooltip_immediate_init(bContext *C, wmWindow *win, ScrArea *area, ARegion *region, wmTooltipInitFn init)
Definition: wm_tooltip.c:31
void WM_tooltip_refresh(bContext *C, wmWindow *win)
Definition: wm_tooltip.c:131
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1682
void WM_clipboard_text_set(const char *buf, bool selection)
Definition: wm_window.c:1780
char * WM_clipboard_text_get_firstline(bool selection, int *r_len)
Definition: wm_window.c:1775
bScreen * WM_window_get_active_screen(const wmWindow *win)
Definition: wm_window.c:2300
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1630