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