Blender V4.3
interface_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cctype>
10#include <cstdio>
11#include <cstdlib>
12#include <cstring>
13#include <fmt/format.h>
14
15#include "DNA_object_types.h"
16#include "DNA_screen_types.h"
17
18#include "ED_screen.hh"
19
20#include "BLI_listbase.h"
21#include "BLI_string.h"
22#include "BLI_utildefines.h"
23
24#include "BLT_translation.hh"
25
26#include "BKE_context.hh"
27#include "BKE_global.hh"
28#include "BKE_idprop.hh"
29#include "BKE_lib_id.hh"
30#include "BKE_screen.hh"
31
32#include "MEM_guardedalloc.h"
33
34#include "RNA_access.hh"
35#include "RNA_prototypes.hh"
36
37#include "UI_interface.hh"
38#include "UI_interface_icons.hh"
39#include "UI_resources.hh"
40#include "UI_string_search.hh"
41#include "UI_view2d.hh"
42
43#include "WM_api.hh"
44#include "WM_types.hh"
45
46#include "interface_intern.hh"
47
48/*************************** RNA Utilities ******************************/
49
52 PropertyRNA *prop,
53 int index,
54 const char *name,
55 int icon,
56 int x,
57 int y,
58 int width,
59 int height)
60{
61 uiBut *but = nullptr;
62
63 switch (RNA_property_type(prop)) {
64 case PROP_BOOLEAN: {
65 if (RNA_property_array_check(prop) && index == -1) {
66 return nullptr;
67 }
68
69 if (icon && name && name[0] == '\0') {
70 but = uiDefIconButR_prop(block,
72 0,
73 icon,
74 x,
75 y,
76 width,
77 height,
78 ptr,
79 prop,
80 index,
81 0,
82 0,
83 nullptr);
84 }
85 else if (icon) {
86 but = uiDefIconTextButR_prop(block,
88 0,
89 icon,
90 name,
91 x,
92 y,
93 width,
94 height,
95 ptr,
96 prop,
97 index,
98 0,
99 0,
100 nullptr);
101 }
102 else {
103 but = uiDefButR_prop(block,
105 0,
106 name,
107 x,
108 y,
109 width,
110 height,
111 ptr,
112 prop,
113 index,
114 0,
115 0,
116 nullptr);
117 }
118 break;
119 }
120 case PROP_INT:
121 case PROP_FLOAT: {
122 if (RNA_property_array_check(prop) && index == -1) {
124 but = uiDefButR_prop(
125 block, UI_BTYPE_COLOR, 0, name, x, y, width, height, ptr, prop, -1, 0, 0, nullptr);
126 }
127 else {
128 return nullptr;
129 }
130 }
131 else if (RNA_property_subtype(prop) == PROP_PERCENTAGE ||
133 {
134 but = uiDefButR_prop(block,
136 0,
137 name,
138 x,
139 y,
140 width,
141 height,
142 ptr,
143 prop,
144 index,
145 0,
146 0,
147 nullptr);
148 }
149 else {
150 but = uiDefButR_prop(
151 block, UI_BTYPE_NUM, 0, name, x, y, width, height, ptr, prop, index, 0, 0, nullptr);
152 }
153
156 }
157 break;
158 }
159 case PROP_ENUM:
160 if (icon && name && name[0] == '\0') {
161 but = uiDefIconButR_prop(
162 block, UI_BTYPE_MENU, 0, icon, x, y, width, height, ptr, prop, index, 0, 0, nullptr);
163 }
164 else if (icon) {
165 but = uiDefIconTextButR_prop(block,
167 0,
168 icon,
169 nullptr,
170 x,
171 y,
172 width,
173 height,
174 ptr,
175 prop,
176 index,
177 0,
178 0,
179 nullptr);
180 }
181 else {
182 but = uiDefButR_prop(
183 block, UI_BTYPE_MENU, 0, name, x, y, width, height, ptr, prop, index, 0, 0, nullptr);
184 }
185 break;
186 case PROP_STRING:
187 if (icon && name && name[0] == '\0') {
188 but = uiDefIconButR_prop(
189 block, UI_BTYPE_TEXT, 0, icon, x, y, width, height, ptr, prop, index, 0, 0, nullptr);
190 }
191 else if (icon) {
192 but = uiDefIconTextButR_prop(block,
194 0,
195 icon,
196 name,
197 x,
198 y,
199 width,
200 height,
201 ptr,
202 prop,
203 index,
204 0,
205 0,
206 nullptr);
207 }
208 else {
209 but = uiDefButR_prop(
210 block, UI_BTYPE_TEXT, 0, name, x, y, width, height, ptr, prop, index, 0, 0, nullptr);
211 }
212
214 /* TEXTEDIT_UPDATE is usually used for search buttons. For these we also want
215 * the 'x' icon to clear search string, so setting VALUE_CLEAR flag, too. */
217 }
218 break;
219 case PROP_POINTER: {
220 if (icon == 0) {
221 const PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
222 icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop));
223 }
224 if (icon == ICON_DOT) {
225 icon = 0;
226 }
227
228 but = uiDefIconTextButR_prop(block,
230 0,
231 icon,
232 name,
233 x,
234 y,
235 width,
236 height,
237 ptr,
238 prop,
239 index,
240 0,
241 0,
242 nullptr);
243 ui_but_add_search(but, ptr, prop, nullptr, nullptr, false);
244 break;
245 }
246 case PROP_COLLECTION: {
247 char text[256];
248 SNPRINTF(text, IFACE_("%d items"), RNA_property_collection_length(ptr, prop));
249 but = uiDefBut(block, UI_BTYPE_LABEL, 0, text, x, y, width, height, nullptr, 0, 0, nullptr);
251 break;
252 }
253 default:
254 but = nullptr;
255 break;
256 }
257
258 return but;
259}
260
263 PropertyRNA *prop,
264 const int icon,
265 const int x,
266 const int y,
267 const int tot_width,
268 const int height)
269{
270 const int len = RNA_property_array_length(ptr, prop);
271 if (len == 0) {
272 return;
273 }
274
275 const int item_width = tot_width / len;
276
278 for (int i = 0; i < len; i++) {
279 uiDefAutoButR(block, ptr, prop, i, "", icon, x + i * item_width, y, item_width, height);
280 }
281 UI_block_align_end(block);
282}
283
286 bool (*check_prop)(PointerRNA *ptr,
287 PropertyRNA *prop,
288 void *user_data),
289 void *user_data,
290 PropertyRNA *prop_activate_init,
291 const eButLabelAlign label_align,
292 const bool compact)
293{
295 uiLayout *col;
296 const char *name;
297
298 RNA_STRUCT_BEGIN (ptr, prop) {
299 const int flag = RNA_property_flag(prop);
300
301 if (flag & PROP_HIDDEN) {
302 continue;
303 }
304 if (check_prop && check_prop(ptr, prop, user_data) == 0) {
305 return_info |= UI_PROP_BUTS_ANY_FAILED_CHECK;
306 continue;
307 }
308
309 const PropertyType type = RNA_property_type(prop);
310 switch (label_align) {
313 const bool is_boolean = (type == PROP_BOOLEAN && !RNA_property_array_check(prop));
314
315 name = RNA_property_ui_name(prop);
316
317 if (label_align == UI_BUT_LABEL_ALIGN_COLUMN) {
318 col = uiLayoutColumn(layout, true);
319
320 if (!is_boolean) {
321 uiItemL(col, name, ICON_NONE);
322 }
323 }
324 else {
326 col = uiLayoutColumn(layout, true);
327 /* Let uiItemFullR() create the split layout. */
328 uiLayoutSetPropSep(col, true);
329 }
330
331 break;
332 }
334 default:
335 col = layout;
336 name = nullptr; /* no smart label alignment, show default name with button */
337 break;
338 }
339
340 /* Only buttons that can be edited as text. */
341 const bool use_activate_init = ((prop == prop_activate_init) &&
343
344 if (use_activate_init) {
346 }
347
349 col, ptr, prop, -1, 0, compact ? UI_ITEM_R_COMPACT : UI_ITEM_NONE, name, ICON_NONE);
350 return_info &= ~UI_PROP_BUTS_NONE_ADDED;
351
352 if (use_activate_init) {
354 }
355 }
357
358 return return_info;
359}
360
365
366/* *** RNA collection search menu *** */
367
369 void *data;
370 std::string name;
371 int index;
374 bool is_id;
376};
377
379 const bool requires_exact_data_name,
380 const bool has_id_icon,
381 uiSearchItems *items)
382{
383
384 /* If no item has its own icon to display, libraries can use the library icons rather than the
385 * name prefix for showing the library status. */
386 int name_prefix_offset = cis.name_prefix_offset;
387 if (!has_id_icon && cis.is_id && !requires_exact_data_name) {
388 cis.iconid = UI_icon_from_library(static_cast<const ID *>(cis.data));
389 char name_buf[UI_MAX_DRAW_STR];
391 name_buf, static_cast<const ID *>(cis.data), false, UI_SEP_CHAR, &name_prefix_offset);
392 cis.name = name_buf;
393 }
394
395 return UI_search_item_add(items,
396 cis.name.c_str(),
397 cis.data,
398 cis.iconid,
399 cis.has_sep_char ? int(UI_BUT_HAS_SEP_CHAR) : 0,
400 name_prefix_offset);
401}
402
404 const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
405{
406 using namespace blender;
407 uiRNACollectionSearch *data = static_cast<uiRNACollectionSearch *>(arg);
408 const int flag = RNA_property_flag(data->target_prop);
409 const bool is_ptr_target = (RNA_property_type(data->target_prop) == PROP_POINTER);
410 /* For non-pointer properties, UI code acts entirely based on the item's name. So the name has to
411 * match the RNA name exactly. So only for pointer properties, the name can be modified to add
412 * further UI hints. */
413 const bool requires_exact_data_name = !is_ptr_target;
414 const bool skip_filter = is_first;
415 char name_buf[UI_MAX_DRAW_STR];
416 bool has_id_icon = false;
417
418 /* The string search API requires pointer stability. */
419 Vector<std::unique_ptr<CollItemSearch>> items_list;
420
421 if (data->search_prop != nullptr) {
422 /* build a temporary list of relevant items first */
423 RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
424 if (flag & PROP_ID_SELF_CHECK) {
425 if (itemptr.data == data->target_ptr.owner_id) {
426 continue;
427 }
428 }
429
430 /* use filter */
431 if (is_ptr_target) {
432 if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
433 continue;
434 }
435 }
436
437 int name_prefix_offset = 0;
438 int iconid = ICON_NONE;
439 bool has_sep_char = false;
440 const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
441
442 char *name;
443 if (is_id) {
444 iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false);
445 if (!ELEM(iconid, 0, ICON_BLANK1)) {
446 has_id_icon = true;
447 }
448
449 if (requires_exact_data_name) {
450 name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
451 }
452 else {
453 const ID *id = static_cast<ID *>(itemptr.data);
454 BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset);
455 BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
456 "Name string buffer should be big enough to hold full UI ID name");
457 name = name_buf;
458 has_sep_char = ID_IS_LINKED(id);
459 }
460 }
461#ifdef WITH_ANIM_BAKLAVA
462 else if (itemptr.type == &RNA_ActionSlot) {
463 PropertyRNA *prop = RNA_struct_find_property(&itemptr, "name_display");
464 name = RNA_property_string_get_alloc(&itemptr, prop, name_buf, sizeof(name_buf), nullptr);
465 }
466#endif /* WITH_ANIM_BAKLAVA */
467 else {
468 name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
469 }
470
471 if (name) {
472 auto cis = std::make_unique<CollItemSearch>();
473 cis->data = itemptr.data;
474 cis->name = name;
475 cis->index = items_list.size();
476 cis->iconid = iconid;
477 cis->is_id = is_id;
478 cis->name_prefix_offset = name_prefix_offset;
479 cis->has_sep_char = has_sep_char;
480 items_list.append(std::move(cis));
481 if (name != name_buf) {
482 MEM_freeN(name);
483 }
484 }
485 }
487 }
488 else {
489 BLI_assert(RNA_property_type(data->target_prop) == PROP_STRING);
491 data->target_prop);
493
494 const bool show_extra_info = (G.debug_value == 102);
495
497 &data->target_ptr,
498 data->target_prop,
499 str,
500 [&](StringPropertySearchVisitParams visit_params) {
501 auto cis = std::make_unique<CollItemSearch>();
502
503 cis->data = nullptr;
504 if (visit_params.info && show_extra_info) {
505 cis->name = fmt::format("{}" UI_SEP_CHAR_S "{}",
506 visit_params.text,
507 *visit_params.info);
508 }
509 else {
510 cis->name = std::move(visit_params.text);
511 }
512
513 cis->index = items_list.size();
514 cis->iconid = visit_params.icon_id.value_or(ICON_NONE);
515 cis->is_id = false;
516 cis->name_prefix_offset = 0;
517 cis->has_sep_char = visit_params.info.has_value();
518 items_list.append(std::move(cis));
519 });
520
521 if (search_flag & PROP_STRING_SEARCH_SORT) {
522 std::sort(
523 items_list.begin(),
524 items_list.end(),
525 [](const std::unique_ptr<CollItemSearch> &a, const std::unique_ptr<CollItemSearch> &b) {
526 return BLI_strcasecmp_natural(a->name.c_str(), b->name.c_str()) < 0;
527 });
528 for (const int i : items_list.index_range()) {
529 items_list[i]->index = i;
530 }
531 }
532 }
533
534 if (skip_filter) {
535 for (std::unique_ptr<CollItemSearch> &cis : items_list) {
536 if (!add_collection_search_item(*cis, requires_exact_data_name, has_id_icon, items)) {
537 break;
538 }
539 }
540 }
541 else {
543 for (std::unique_ptr<CollItemSearch> &cis : items_list) {
544 search.add(cis->name, cis.get());
545 }
546
547 const Vector<CollItemSearch *> filtered_items = search.query(str);
548 for (CollItemSearch *cis : filtered_items) {
549 if (!add_collection_search_item(*cis, requires_exact_data_name, has_id_icon, items)) {
550 break;
551 }
552 }
553 }
554}
555
556int UI_icon_from_id(const ID *id)
557{
558 if (id == nullptr) {
559 return ICON_NONE;
560 }
561
562 /* exception for objects */
563 if (GS(id->name) == ID_OB) {
564 Object *ob = (Object *)id;
565
566 if (ob->type == OB_EMPTY) {
567 return ICON_EMPTY_DATA;
568 }
569 return UI_icon_from_id(static_cast<const ID *>(ob->data));
570 }
571
572 /* otherwise get it through RNA, creating the pointer
573 * will set the right type, also with subclassing */
575
576 return (ptr.type) ? RNA_struct_ui_icon(ptr.type) : ICON_NONE;
577}
578
580{
581 if (type & RPT_ERROR_ALL) {
582 return ICON_CANCEL;
583 }
584 if (type & RPT_WARNING_ALL) {
585 return ICON_ERROR;
586 }
587 if (type & RPT_INFO_ALL) {
588 return ICON_INFO;
589 }
590 if (type & RPT_DEBUG_ALL) {
591 return ICON_SYSTEM;
592 }
593 if (type & RPT_PROPERTY) {
594 return ICON_OPTIONS;
595 }
596 if (type & RPT_OPERATOR) {
597 return ICON_CHECKMARK;
598 }
599 return ICON_INFO;
600}
601
603{
604 if (type & RPT_ERROR_ALL) {
605 return TH_INFO_ERROR;
606 }
607 if (type & RPT_WARNING_ALL) {
608 return TH_INFO_WARNING;
609 }
610 if (type & RPT_INFO_ALL) {
611 return TH_INFO_INFO;
612 }
613 if (type & RPT_DEBUG_ALL) {
614 return TH_INFO_DEBUG;
615 }
616 if (type & RPT_PROPERTY) {
617 return TH_INFO_PROPERTY;
618 }
619 if (type & RPT_OPERATOR) {
620 return TH_INFO_OPERATOR;
621 }
622 return TH_INFO_WARNING;
623}
624
626{
627 if (type & RPT_ERROR_ALL) {
628 return TH_INFO_ERROR_TEXT;
629 }
630 if (type & RPT_WARNING_ALL) {
632 }
633 if (type & RPT_INFO_ALL) {
634 return TH_INFO_INFO_TEXT;
635 }
636 if (type & RPT_DEBUG_ALL) {
637 return TH_INFO_DEBUG_TEXT;
638 }
639 if (type & RPT_PROPERTY) {
641 }
642 if (type & RPT_OPERATOR) {
644 }
646}
647
648/********************************** Misc **************************************/
649
650int UI_calc_float_precision(int prec, double value)
651{
652 static const double pow10_neg[UI_PRECISION_FLOAT_MAX + 1] = {
653 1e0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6};
654 static const double max_pow = 10000000.0; /* pow(10, UI_PRECISION_FLOAT_MAX) */
655
657 BLI_assert(fabs(pow10_neg[prec] - pow(10, -prec)) < 1e-16);
658
659 /* Check on the number of decimal places need to display the number,
660 * this is so 0.00001 is not displayed as 0.00,
661 * _but_, this is only for small values as 10.0001 will not get the same treatment.
662 */
663 value = fabs(value);
664 if ((value < pow10_neg[prec]) && (value > (1.0 / max_pow))) {
665 int value_i = int(lround(value * max_pow));
666 if (value_i != 0) {
667 const int prec_span = 3; /* show: 0.01001, 5 would allow 0.0100001 for eg. */
668 int test_prec;
669 int prec_min = -1;
670 int dec_flag = 0;
672 while (i && value_i) {
673 if (value_i % 10) {
674 dec_flag |= 1 << i;
675 prec_min = i;
676 }
677 value_i /= 10;
678 i--;
679 }
680
681 /* even though its a small value, if the second last digit is not 0, use it */
682 test_prec = prec_min;
683
684 dec_flag = (dec_flag >> (prec_min + 1)) & ((1 << prec_span) - 1);
685
686 while (dec_flag) {
687 test_prec++;
688 dec_flag = dec_flag >> 1;
689 }
690
691 if (test_prec > prec) {
692 prec = test_prec;
693 }
694 }
695 }
696
698
699 return prec;
700}
701
702std::optional<std::string> UI_but_online_manual_id(const uiBut *but)
703{
704 if (but->rnapoin.data && but->rnaprop) {
705 return fmt::format(
707 }
708 if (but->optype) {
709 char idname[OP_MAX_TYPENAME];
710 const size_t idname_len = WM_operator_py_idname(idname, but->optype->idname);
711 return std::string(idname, idname_len);
712 }
713
714 return std::nullopt;
715}
716
717std::optional<std::string> UI_but_online_manual_id_from_active(const bContext *C)
718{
719 if (uiBut *but = UI_context_active_but_get(C)) {
720 return UI_but_online_manual_id(but);
721 }
722 return std::nullopt;
723}
724
725/* -------------------------------------------------------------------- */
726
727static rctf ui_but_rect_to_view(const uiBut *but, const ARegion *region, const View2D *v2d)
728{
729 rctf region_rect;
730 ui_block_to_region_rctf(region, but->block, &region_rect, &but->rect);
731
732 rctf view_rect;
733 UI_view2d_region_to_view_rctf(v2d, &region_rect, &view_rect);
734
735 return view_rect;
736}
737
745static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect)
746{
747 const float rect_width = BLI_rctf_size_x(rect);
748 const float rect_height = BLI_rctf_size_y(rect);
749
750 rctf *cur = &v2d->cur;
751 const float cur_width = BLI_rctf_size_x(cur);
752 const float cur_height = BLI_rctf_size_y(cur);
753
754 bool changed = false;
755
756 /* Snap to bottom edge. Also use if rect is higher than view bounds (could be a parameter). */
757 if ((cur->ymin > rect->ymin) || (rect_height > cur_height)) {
758 cur->ymin = rect->ymin;
759 cur->ymax = cur->ymin + cur_height;
760 changed = true;
761 }
762 /* Snap to upper edge. */
763 else if (cur->ymax < rect->ymax) {
764 cur->ymax = rect->ymax;
765 cur->ymin = cur->ymax - cur_height;
766 changed = true;
767 }
768 /* Snap to left edge. Also use if rect is wider than view bounds. */
769 else if ((cur->xmin > rect->xmin) || (rect_width > cur_width)) {
770 cur->xmin = rect->xmin;
771 cur->xmax = cur->xmin + cur_width;
772 changed = true;
773 }
774 /* Snap to right edge. */
775 else if (cur->xmax < rect->xmax) {
776 cur->xmax = rect->xmax;
777 cur->xmin = cur->xmax - cur_width;
778 changed = true;
779 }
780 else {
782 }
783
784 return changed;
785}
786
787void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but)
788{
789 View2D *v2d = &region->v2d;
790 /* Uninitialized view or region that doesn't use View2D. */
791 if ((v2d->flag & V2D_IS_INIT) == 0) {
792 return;
793 }
794
795 rctf rect = ui_but_rect_to_view(but, region, v2d);
796
797 const int margin = UI_UNIT_X * 0.5f;
798 BLI_rctf_pad(&rect, margin, margin);
799
800 const bool changed = ui_view2d_cur_ensure_rect_in_view(v2d, &rect);
801 if (changed) {
804 }
805}
806
807/* -------------------------------------------------------------------- */
824
829
831{
832 uiButStore *bs_handle = MEM_cnew<uiButStore>(__func__);
833
834 bs_handle->block = block;
835 BLI_addtail(&block->butstore, bs_handle);
836
837 return bs_handle;
838}
839
840void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
841{
842 /* NOTE(@ideasman42): Workaround for button store being moved into new block,
843 * which then can't use the previous buttons state
844 * (#ui_but_update_from_old_block fails to find a match),
845 * keeping the active button in the old block holding a reference
846 * to the button-state in the new block: see #49034.
847 *
848 * Ideally we would manage moving the 'uiButStore', keeping a correct state.
849 * All things considered this is the most straightforward fix. */
850 if (block != bs_handle->block && bs_handle->block != nullptr) {
851 block = bs_handle->block;
852 }
853
854 BLI_freelistN(&bs_handle->items);
855 BLI_assert(BLI_findindex(&block->butstore, bs_handle) != -1);
856 BLI_remlink(&block->butstore, bs_handle);
857
858 MEM_freeN(bs_handle);
859}
860
862{
863 return (bs_handle->block != nullptr);
864}
865
867{
868 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
869 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
870 if (*bs_elem->but_p == but) {
871 return true;
872 }
873 }
874 }
875
876 return false;
877}
878
879void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
880{
881 uiButStoreElem *bs_elem = MEM_cnew<uiButStoreElem>(__func__);
882 BLI_assert(*but_p);
883 bs_elem->but_p = but_p;
884
885 BLI_addtail(&bs_handle->items, bs_elem);
886}
887
888void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
889{
890 LISTBASE_FOREACH_MUTABLE (uiButStoreElem *, bs_elem, &bs_handle->items) {
891 if (bs_elem->but_p == but_p) {
892 BLI_remlink(&bs_handle->items, bs_elem);
893 MEM_freeN(bs_elem);
894 }
895 }
896
897 BLI_assert(0);
898}
899
900bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src)
901{
902 bool found = false;
903
904 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
905 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
906 if (*bs_elem->but_p == but_src) {
907 *bs_elem->but_p = but_dst;
908 found = true;
909 }
910 }
911 }
912
913 return found;
914}
915
917{
918 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
919 bs_handle->block = nullptr;
920 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
921 *bs_elem->but_p = nullptr;
922 }
923 }
924}
925
927{
928 /* move this list to the new block */
929 if (block->oldblock) {
930 if (block->oldblock->butstore.first) {
931 BLI_movelisttolist(&block->butstore, &block->oldblock->butstore);
932 }
933 }
934
935 if (LIKELY(block->butstore.first == nullptr)) {
936 return;
937 }
938
939 /* warning, loop-in-loop, in practice we only store <10 buttons at a time,
940 * so this isn't going to be a problem, if that changes old-new mapping can be cached first */
941 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
942 BLI_assert(ELEM(bs_handle->block, nullptr, block) ||
943 (block->oldblock && block->oldblock == bs_handle->block));
944
945 if (bs_handle->block == block->oldblock) {
946 bs_handle->block = block;
947
948 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
949 if (*bs_elem->but_p) {
950 uiBut *but_new = ui_but_find_new(block, *bs_elem->but_p);
951
952 /* can be nullptr if the buttons removed,
953 * NOTE: we could allow passing in a callback when buttons are removed
954 * so the caller can cleanup */
955 *bs_elem->but_p = but_new;
956 }
957 }
958 }
959 }
960}
961
964/* -------------------------------------------------------------------- */
971static bool ui_key_event_property_match(const char *opname,
972 IDProperty *properties,
973 const bool is_strict,
974 wmOperatorType *ui_optype,
975 PointerRNA *ui_opptr)
976{
977 if (!STREQ(ui_optype->idname, opname)) {
978 return false;
979 }
980
981 bool match = false;
982 if (properties) {
983 if (ui_opptr &&
984 IDP_EqualsProperties_ex(properties, static_cast<IDProperty *>(ui_opptr->data), is_strict))
985 {
986 match = true;
987 }
988 }
989 else {
990 match = true;
991 }
992 return match;
993}
994
995std::optional<std::string> UI_key_event_operator_string(const bContext *C,
996 const char *opname,
997 IDProperty *properties,
998 const bool is_strict)
999{
1000 /* NOTE: currently only actions on UI Lists are supported (for the asset manager).
1001 * Other kinds of events can be supported as needed. */
1002
1003 ARegion *region = CTX_wm_region(C);
1004 if (region == nullptr) {
1005 return std::nullopt;
1006 }
1007
1008 /* Early exit regions which don't have UI-Lists. */
1009 if ((region->type->keymapflag & ED_KEYMAP_UI) == 0) {
1010 return std::nullopt;
1011 }
1012
1013 uiBut *but = UI_region_active_but_get(region);
1014 if (but == nullptr) {
1015 return std::nullopt;
1016 }
1017
1018 if (but->type != UI_BTYPE_PREVIEW_TILE) {
1019 return std::nullopt;
1020 }
1021
1022 short event_val = KM_NOTHING;
1023 short event_type = KM_NOTHING;
1024
1025 uiBut *listbox = nullptr;
1026 LISTBASE_FOREACH_BACKWARD (uiBut *, but_iter, &but->block->buttons) {
1027 if ((but_iter->type == UI_BTYPE_LISTBOX) && ui_but_contains_rect(but_iter, &but->rect)) {
1028 listbox = but_iter;
1029 break;
1030 }
1031 }
1032
1033 if (listbox && listbox->custom_data) {
1034 uiList *list = static_cast<uiList *>(listbox->custom_data);
1035 uiListDyn *dyn_data = list->dyn_data;
1036 if ((dyn_data->custom_activate_optype != nullptr) &&
1038 properties,
1039 is_strict,
1040 dyn_data->custom_activate_optype,
1041 dyn_data->custom_activate_opptr))
1042 {
1043 event_val = KM_CLICK;
1044 event_type = LEFTMOUSE;
1045 }
1046 else if ((dyn_data->custom_activate_optype != nullptr) &&
1048 properties,
1049 is_strict,
1050 dyn_data->custom_drag_optype,
1051 dyn_data->custom_drag_opptr))
1052 {
1053 event_val = KM_CLICK_DRAG;
1054 event_type = LEFTMOUSE;
1055 }
1056 }
1057
1058 if ((event_val != KM_NOTHING) && (event_type != KM_NOTHING)) {
1060 false, false, false, false, 0, event_val, event_type, false);
1061 }
1062
1063 return std::nullopt;
1064}
1065
ARegion * CTX_wm_region(const bContext *C)
bool IDP_EqualsProperties_ex(const IDProperty *prop1, const IDProperty *prop2, bool is_strict) ATTR_WARN_UNUSED_RESULT
Definition idprop.cc:893
#define MAX_ID_FULL_NAME_UI
void BKE_id_full_name_ui_prefix_get(char name[MAX_ID_FULL_NAME_UI], const ID *id, bool add_lib_hint, char separator_char, int *r_prefix_len)
Definition lib_id.cc:2382
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:87
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.c:631
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
bool BLI_rctf_inside_rctf(const rctf *rct_a, const rctf *rct_b)
Definition rct.c:193
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#define CLAMP(a, b, c)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
#define IFACE_(msgid)
int rect_width(int rect[2][2])
Definition Basic.c:43
int rect_height(int rect[2][2])
Definition Basic.c:47
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:654
@ ID_OB
Object is a sort of wrapper for general info.
@ OB_EMPTY
@ V2D_IS_INIT
#define OP_MAX_TYPENAME
#define RPT_ERROR_ALL
#define RPT_INFO_ALL
#define RPT_WARNING_ALL
#define RPT_DEBUG_ALL
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:653
@ ED_KEYMAP_UI
Definition ED_screen.hh:725
Read Guarded memory(de)allocation.
#define RNA_PROP_END
#define RNA_STRUCT_BEGIN(sptr, prop)
#define RNA_STRUCT_END
#define RNA_PROP_BEGIN(sptr, itemptr, prop)
eStringPropertySearchFlag
Definition RNA_types.hh:570
@ PROP_STRING_SEARCH_SORT
Definition RNA_types.hh:577
@ PROP_STRING_SEARCH_SUPPORTED
Definition RNA_types.hh:575
PropertyType
Definition RNA_types.hh:64
@ PROP_FLOAT
Definition RNA_types.hh:67
@ PROP_BOOLEAN
Definition RNA_types.hh:65
@ PROP_ENUM
Definition RNA_types.hh:69
@ PROP_INT
Definition RNA_types.hh:66
@ PROP_STRING
Definition RNA_types.hh:68
@ PROP_POINTER
Definition RNA_types.hh:70
@ PROP_COLLECTION
Definition RNA_types.hh:71
@ PROP_ID_SELF_CHECK
Definition RNA_types.hh:259
@ PROP_TEXTEDIT_UPDATE
Definition RNA_types.hh:227
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ PROP_COLOR
Definition RNA_types.hh:163
@ PROP_PERCENTAGE
Definition RNA_types.hh:153
@ PROP_FACTOR
Definition RNA_types.hh:154
@ PROP_COLOR_GAMMA
Definition RNA_types.hh:175
eAutoPropButsReturn
@ UI_PROP_BUTS_ANY_FAILED_CHECK
@ UI_PROP_BUTS_NONE_ADDED
#define UI_SEP_CHAR
bool(*)(const uiBut *a, const uiBut *b) uiButIdentityCompareFunc
uiBut * uiDefBut(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
void uiItemL(uiLayout *layout, const char *name, int icon)
uiBut * UI_region_active_but_get(const ARegion *region)
uiBut * uiDefIconButR_prop(uiBlock *block, int type, int retval, int icon, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, const char *tip)
bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, int but_flag, uint8_t name_prefix_offset)
void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, const char *name, int icon, const char *placeholder=nullptr)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_PRECISION_FLOAT_MAX
eButLabelAlign
@ UI_BUT_LABEL_ALIGN_SPLIT_COLUMN
@ UI_BUT_LABEL_ALIGN_NONE
@ UI_BUT_LABEL_ALIGN_COLUMN
uiBut * UI_context_active_but_get(const bContext *C)
#define UI_ITEM_NONE
#define UI_MAX_DRAW_STR
void UI_block_align_begin(uiBlock *block)
uiBut * uiDefIconTextButR_prop(uiBlock *block, int type, int retval, int icon, const char *str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, const char *tip)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiLayoutSetActivateInit(uiLayout *layout, bool activate_init)
uiBut * uiDefButR_prop(uiBlock *block, int type, int retval, const char *str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, const char *tip)
#define UI_UNIT_X
@ UI_BTYPE_LISTBOX
@ UI_BTYPE_NUM_SLIDER
@ UI_BTYPE_TEXT
@ UI_BTYPE_PREVIEW_TILE
@ UI_BTYPE_LABEL
@ UI_BTYPE_SEARCH_MENU
@ UI_BTYPE_NUM
@ UI_BTYPE_COLOR
@ UI_BTYPE_CHECKBOX
@ UI_BTYPE_MENU
@ UI_BTYPE_ICON_TOGGLE
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_ITEM_R_COMPACT
@ UI_BUT_DISABLED
@ UI_BUT_HAS_SEP_CHAR
@ UI_BUT_VALUE_CLEAR
@ UI_BUT_TEXTEDIT_UPDATE
void UI_block_align_end(uiBlock *block)
int UI_icon_from_library(const ID *id)
@ TH_INFO_PROPERTY_TEXT
@ TH_INFO_WARNING_TEXT
@ TH_INFO_DEBUG
@ TH_INFO_INFO
@ TH_INFO_INFO_TEXT
@ TH_INFO_PROPERTY
@ TH_INFO_DEBUG_TEXT
@ TH_INFO_ERROR
@ TH_INFO_ERROR_TEXT
@ TH_INFO_OPERATOR
@ TH_INFO_WARNING
@ TH_INFO_OPERATOR_TEXT
void UI_view2d_curRect_changed(const bContext *C, View2D *v2d)
Definition view2d.cc:829
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1670
@ KM_NOTHING
Definition WM_types.hh:283
@ KM_CLICK_DRAG
Definition WM_types.hh:292
@ KM_CLICK
Definition WM_types.hh:286
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
void add(const StringRef str, T *user_data, const int weight=0)
Vector< T * > query(const StringRef query) const
local_group_size(16, 16) .push_constant(Type b
pow(value.r - subtrahend, 2.0)") .do_static_compilation(true)
int len
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
uint col
uiBut * ui_but_find_new(uiBlock *block_new, const uiBut *but_old)
Definition interface.cc:804
void ui_block_to_region_rctf(const ARegion *region, const uiBlock *block, rctf *rct_dst, const rctf *rct_src)
Definition interface.cc:153
int ui_id_icon_get(const bContext *C, ID *id, const bool big)
bool ui_but_contains_rect(const uiBut *but, const rctf *rect)
uiBut * ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, bool results_are_suggestions)
int UI_text_colorid_from_report_type(int type)
int UI_icon_from_report_type(int type)
eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout, PointerRNA *ptr, bool(*check_prop)(PointerRNA *ptr, PropertyRNA *prop, void *user_data), void *user_data, PropertyRNA *prop_activate_init, const eButLabelAlign label_align, const bool compact)
int UI_icon_from_id(const ID *id)
int UI_icon_colorid_from_report_type(int type)
void UI_butstore_update(uiBlock *block)
void uiDefAutoButsArrayR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, const int icon, const int x, const int y, const int tot_width, const int height)
static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect)
void UI_but_func_identity_compare_set(uiBut *but, uiButIdentityCompareFunc cmp_fn)
void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
std::optional< std::string > UI_but_online_manual_id(const uiBut *but)
bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src)
void ui_rna_collection_search_update_fn(const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
bool UI_butstore_is_registered(uiBlock *block, uiBut *but)
std::optional< std::string > UI_key_event_operator_string(const bContext *C, const char *opname, IDProperty *properties, const bool is_strict)
bool UI_butstore_is_valid(uiButStore *bs_handle)
void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
std::optional< std::string > UI_but_online_manual_id_from_active(const bContext *C)
void UI_butstore_clear(uiBlock *block)
uiButStore * UI_butstore_create(uiBlock *block)
static rctf ui_but_rect_to_view(const uiBut *but, const ARegion *region, const View2D *v2d)
static bool add_collection_search_item(CollItemSearch &cis, const bool requires_exact_data_name, const bool has_id_icon, uiSearchItems *items)
void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but)
int UI_calc_float_precision(int prec, double value)
static bool ui_key_event_property_match(const char *opname, IDProperty *properties, const bool is_strict, wmOperatorType *ui_optype, PointerRNA *ui_opptr)
uiBut * uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int index, const char *name, int icon, int x, int y, int width, int height)
#define GS(x)
Definition iris.cc:202
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float2 fabs(const float2 a)
#define G(x, y, z)
bool RNA_property_array_check(PropertyRNA *prop)
bool RNA_struct_is_ID(const StructRNA *type)
eStringPropertySearchFlag RNA_property_string_search_flag(PropertyRNA *prop)
StructRNA * RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_property_string_search(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, const char *edit_text, blender::FunctionRef< void(StringPropertySearchVisitParams)> visit_fn)
char * RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
PropertyType RNA_property_type(PropertyRNA *prop)
char * RNA_property_string_get_alloc(PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len)
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
const char * RNA_struct_identifier(const StructRNA *type)
bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *value)
int RNA_property_flag(PropertyRNA *prop)
int RNA_struct_ui_icon(const StructRNA *type)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
const char * RNA_property_ui_name(const PropertyRNA *prop)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
const char * RNA_property_identifier(const PropertyRNA *prop)
int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
Definition DNA_ID.h:413
void * first
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
std::optional< int > icon_id
Definition RNA_types.hh:567
std::optional< std::string > info
Definition RNA_types.hh:565
float xmax
float xmin
float ymax
float ymin
uiBlock * oldblock
ListBase buttons
ListBase butstore
uiButStoreElem * next
uiButStoreElem * prev
uiBlock * block
uiButStore * next
uiButStore * prev
void * custom_data
uiButIdentityCompareFunc identity_cmp_func
PropertyRNA * rnaprop
wmOperatorType * optype
eButType type
uiBlock * block
PointerRNA rnapoin
struct wmOperatorType * custom_activate_optype
struct PointerRNA * custom_drag_opptr
struct wmOperatorType * custom_drag_optype
struct PointerRNA * custom_activate_opptr
const char * idname
Definition WM_types.hh:992
static int cmp_fn(const void *a, const void *b)
@ LEFTMOUSE
PointerRNA * ptr
Definition wm_files.cc:4126
std::optional< std::string > WM_keymap_item_raw_to_string(const short shift, const short ctrl, const short alt, const short oskey, const short keymodifier, const short val, const short type, const bool compact)
size_t WM_operator_py_idname(char *dst, const char *src)
uint8_t flag
Definition wm_window.cc:138