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