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