Blender V4.5
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
8
9#include <algorithm>
10#include <cctype>
11#include <cstdio>
12#include <cstdlib>
13#include <cstring>
14#include <fmt/format.h>
15
16#include "DNA_object_types.h"
17#include "DNA_screen_types.h"
18
19#include "ED_screen.hh"
20
21#include "BLI_listbase.h"
22#include "BLI_string.h"
23#include "BLI_utildefines.h"
24
25#include "BLT_translation.hh"
26
27#include "BKE_context.hh"
28#include "BKE_global.hh"
29#include "BKE_idprop.hh"
30#include "BKE_lib_id.hh"
31#include "BKE_screen.hh"
32
33#include "MEM_guardedalloc.h"
34
35#include "RNA_access.hh"
36#include "RNA_prototypes.hh"
37
38#include "UI_interface.hh"
39#include "UI_interface_icons.hh"
40#include "UI_resources.hh"
41#include "UI_string_search.hh"
42#include "UI_view2d.hh"
43
44#include "WM_api.hh"
45#include "WM_types.hh"
46
47#include "interface_intern.hh"
48
51
52/*************************** RNA Utilities ******************************/
53
56 PropertyRNA *prop,
57 int index,
58 const std::optional<StringRef> name,
59 int icon,
60 int x,
61 int y,
62 int width,
63 int height)
64{
65 uiBut *but = nullptr;
66
67 switch (RNA_property_type(prop)) {
68 case PROP_BOOLEAN: {
69 if (RNA_property_array_check(prop) && index == -1) {
70 return nullptr;
71 }
72
73 if (icon && name && name->is_empty()) {
74 but = uiDefIconButR_prop(block,
76 0,
77 icon,
78 x,
79 y,
80 width,
81 height,
82 ptr,
83 prop,
84 index,
85 0,
86 0,
87 std::nullopt);
88 }
89 else if (icon) {
90 but = uiDefIconTextButR_prop(block,
92 0,
93 icon,
94 name,
95 x,
96 y,
97 width,
98 height,
99 ptr,
100 prop,
101 index,
102 0,
103 0,
104 std::nullopt);
105 }
106 else {
107 but = uiDefButR_prop(block,
109 0,
110 name,
111 x,
112 y,
113 width,
114 height,
115 ptr,
116 prop,
117 index,
118 0,
119 0,
120 std::nullopt);
121 }
122 break;
123 }
124 case PROP_INT:
125 case PROP_FLOAT: {
126 if (RNA_property_array_check(prop) && index == -1) {
128 but = uiDefButR_prop(block,
130 0,
131 name,
132 x,
133 y,
134 width,
135 height,
136 ptr,
137 prop,
138 -1,
139 0,
140 0,
141 std::nullopt);
142 }
143 else {
144 return nullptr;
145 }
146 }
147 else if (RNA_property_subtype(prop) == PROP_PERCENTAGE ||
149 {
150 but = uiDefButR_prop(block,
152 0,
153 name,
154 x,
155 y,
156 width,
157 height,
158 ptr,
159 prop,
160 index,
161 0,
162 0,
163 std::nullopt);
164 }
165 else {
166 but = uiDefButR_prop(block,
168 0,
169 name,
170 x,
171 y,
172 width,
173 height,
174 ptr,
175 prop,
176 index,
177 0,
178 0,
179 std::nullopt);
180 }
181
184 }
185 break;
186 }
187 case PROP_ENUM:
188 if (icon && name && name->is_empty()) {
189 but = uiDefIconButR_prop(block,
191 0,
192 icon,
193 x,
194 y,
195 width,
196 height,
197 ptr,
198 prop,
199 index,
200 0,
201 0,
202 std::nullopt);
203 }
204 else if (icon) {
205 but = uiDefIconTextButR_prop(block,
207 0,
208 icon,
209 std::nullopt,
210 x,
211 y,
212 width,
213 height,
214 ptr,
215 prop,
216 index,
217 0,
218 0,
219 std::nullopt);
220 }
221 else {
222 but = uiDefButR_prop(block,
224 0,
225 name,
226 x,
227 y,
228 width,
229 height,
230 ptr,
231 prop,
232 index,
233 0,
234 0,
235 std::nullopt);
236 }
237 break;
238 case PROP_STRING:
239 if (icon && name && name->is_empty()) {
240 but = uiDefIconButR_prop(block,
242 0,
243 icon,
244 x,
245 y,
246 width,
247 height,
248 ptr,
249 prop,
250 index,
251 0,
252 0,
253 std::nullopt);
254 }
255 else if (icon) {
256 but = uiDefIconTextButR_prop(block,
258 0,
259 icon,
260 name,
261 x,
262 y,
263 width,
264 height,
265 ptr,
266 prop,
267 index,
268 0,
269 0,
270 std::nullopt);
271 }
272 else {
273 but = uiDefButR_prop(block,
275 0,
276 name,
277 x,
278 y,
279 width,
280 height,
281 ptr,
282 prop,
283 index,
284 0,
285 0,
286 std::nullopt);
287 }
288
290 /* TEXTEDIT_UPDATE is usually used for search buttons. For these we also want
291 * the 'x' icon to clear search string, so setting VALUE_CLEAR flag, too. */
293 }
294 break;
295 case PROP_POINTER: {
296 if (icon == 0) {
297 const PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
298 icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop));
299 }
300 if (icon == ICON_DOT) {
301 icon = 0;
302 }
303
304 but = uiDefIconTextButR_prop(block,
306 0,
307 icon,
308 name,
309 x,
310 y,
311 width,
312 height,
313 ptr,
314 prop,
315 index,
316 0,
317 0,
318 std::nullopt);
319 ui_but_add_search(but, ptr, prop, nullptr, nullptr, false);
320 break;
321 }
322 case PROP_COLLECTION: {
323 char text[256];
324 SNPRINTF(text, IFACE_("%d items"), RNA_property_collection_length(ptr, prop));
325 but = uiDefBut(
326 block, UI_BTYPE_LABEL, 0, text, x, y, width, height, nullptr, 0, 0, std::nullopt);
328 break;
329 }
330 default:
331 but = nullptr;
332 break;
333 }
334
335 return but;
336}
337
340 PropertyRNA *prop,
341 const int icon,
342 const int x,
343 const int y,
344 const int tot_width,
345 const int height)
346{
347 const int len = RNA_property_array_length(ptr, prop);
348 if (len == 0) {
349 return;
350 }
351
352 const int item_width = tot_width / len;
353
355 for (int i = 0; i < len; i++) {
356 uiDefAutoButR(block, ptr, prop, i, "", icon, x + i * item_width, y, item_width, height);
357 }
358 UI_block_align_end(block);
359}
360
363 bool (*check_prop)(PointerRNA *ptr,
364 PropertyRNA *prop,
365 void *user_data),
366 void *user_data,
367 PropertyRNA *prop_activate_init,
368 const eButLabelAlign label_align,
369 const bool compact)
370{
372 uiLayout *col;
373 std::optional<StringRefNull> name;
374
375 RNA_STRUCT_BEGIN (ptr, prop) {
376 const int flag = RNA_property_flag(prop);
377
378 if (flag & PROP_HIDDEN) {
379 continue;
380 }
381 if (check_prop && check_prop(ptr, prop, user_data) == 0) {
382 return_info |= UI_PROP_BUTS_ANY_FAILED_CHECK;
383 continue;
384 }
385
386 const PropertyType type = RNA_property_type(prop);
387 switch (label_align) {
390 const bool is_boolean = (type == PROP_BOOLEAN && !RNA_property_array_check(prop));
391
392 name = RNA_property_ui_name(prop);
393
394 if (label_align == UI_BUT_LABEL_ALIGN_COLUMN) {
395 col = &layout->column(true);
396
397 if (!is_boolean) {
398 col->label(*name, ICON_NONE);
399 }
400 }
401 else {
403 col = &layout->column(true);
404 /* Let uiLayout::prop() create the split layout. */
405 uiLayoutSetPropSep(col, true);
406 }
407
408 break;
409 }
411 default:
412 col = layout;
413 name = std::nullopt; /* no smart label alignment, show default name with button */
414 break;
415 }
416
417 /* Only buttons that can be edited as text. */
418 const bool use_activate_init = ((prop == prop_activate_init) &&
420
421 if (use_activate_init) {
423 }
424
425 col->prop(ptr, prop, -1, 0, compact ? UI_ITEM_R_COMPACT : UI_ITEM_NONE, name, ICON_NONE);
426 return_info &= ~UI_PROP_BUTS_NONE_ADDED;
427
428 if (use_activate_init) {
430 }
431 }
433
434 return return_info;
435}
436
441
442/* *** RNA collection search menu *** */
443
445 void *data;
446 std::string name;
447 int index;
450 bool is_id;
452};
453
455 const bool requires_exact_data_name,
456 const bool has_id_icon,
457 uiSearchItems *items)
458{
459
460 /* If no item has its own icon to display, libraries can use the library icons rather than the
461 * name prefix for showing the library status. */
462 int name_prefix_offset = cis.name_prefix_offset;
463 if (!has_id_icon && cis.is_id && !requires_exact_data_name) {
464 cis.iconid = UI_icon_from_library(static_cast<const ID *>(cis.data));
465 char name_buf[UI_MAX_DRAW_STR];
467 name_buf, static_cast<const ID *>(cis.data), false, UI_SEP_CHAR, &name_prefix_offset);
468 cis.name = name_buf;
469 }
470
471 return UI_search_item_add(items,
472 cis.name,
473 cis.data,
474 cis.iconid,
475 cis.has_sep_char ? int(UI_BUT_HAS_SEP_CHAR) : 0,
476 name_prefix_offset);
477}
478
480 const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
481{
482 using namespace blender;
483 uiRNACollectionSearch *data = static_cast<uiRNACollectionSearch *>(arg);
484 const int flag = RNA_property_flag(data->target_prop);
485 const bool is_ptr_target = (RNA_property_type(data->target_prop) == PROP_POINTER);
486 /* For non-pointer properties, UI code acts entirely based on the item's name. So the name has to
487 * match the RNA name exactly. So only for pointer properties, the name can be modified to add
488 * further UI hints. */
489 const bool requires_exact_data_name = !is_ptr_target;
490 const bool skip_filter = is_first;
491 char name_buf[UI_MAX_DRAW_STR];
492 bool has_id_icon = false;
493
494 /* The string search API requires pointer stability. */
496
497 if (data->search_prop != nullptr) {
498 /* build a temporary list of relevant items first */
499 RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
500 if (flag & PROP_ID_SELF_CHECK) {
501 if (itemptr.data == data->target_ptr.owner_id) {
502 continue;
503 }
504 }
505
506 /* use filter */
507 if (is_ptr_target) {
508 if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
509 continue;
510 }
511 }
512
513 int name_prefix_offset = 0;
514 int iconid = ICON_NONE;
515 bool has_sep_char = false;
516 const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
517
518 char *name;
519 if (is_id) {
520 iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false);
521 if (!ELEM(iconid, 0, ICON_BLANK1)) {
522 has_id_icon = true;
523 }
524
525 if (requires_exact_data_name) {
526 name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
527 }
528 else {
529 const ID *id = static_cast<ID *>(itemptr.data);
530 BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset);
531 BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
532 "Name string buffer should be big enough to hold full UI ID name");
533 name = name_buf;
534 has_sep_char = ID_IS_LINKED(id);
535 }
536 }
537 else if (itemptr.type == &RNA_ActionSlot) {
538 PropertyRNA *prop = RNA_struct_find_property(&itemptr, "name_display");
539 name = RNA_property_string_get_alloc(&itemptr, prop, name_buf, sizeof(name_buf), nullptr);
540 }
541 else {
542 name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
543 }
544
545 if (name) {
546 auto cis = std::make_unique<CollItemSearch>();
547 cis->data = itemptr.data;
548 cis->name = name;
549 cis->index = items_list.size();
550 cis->iconid = iconid;
551 cis->is_id = is_id;
552 cis->name_prefix_offset = name_prefix_offset;
553 cis->has_sep_char = has_sep_char;
554 items_list.append(std::move(cis));
555 if (name != name_buf) {
556 MEM_freeN(name);
557 }
558 }
559 }
561 }
562 else {
565 data->target_prop);
567
568 const bool show_extra_info = (G.debug_value == 102);
569
571 &data->target_ptr,
572 data->target_prop,
573 str,
574 [&](StringPropertySearchVisitParams visit_params) {
575 auto cis = std::make_unique<CollItemSearch>();
576
577 cis->data = nullptr;
578 if (visit_params.info && show_extra_info) {
579 cis->name = fmt::format("{}" UI_SEP_CHAR_S "{}",
580 visit_params.text,
581 *visit_params.info);
582 }
583 else {
584 cis->name = std::move(visit_params.text);
585 }
586
587 cis->index = items_list.size();
588 cis->iconid = visit_params.icon_id.value_or(ICON_NONE);
589 cis->is_id = false;
590 cis->name_prefix_offset = 0;
591 cis->has_sep_char = visit_params.info.has_value();
592 items_list.append(std::move(cis));
593 });
594
595 if (search_flag & PROP_STRING_SEARCH_SORT) {
596 std::sort(
597 items_list.begin(),
598 items_list.end(),
599 [](const std::unique_ptr<CollItemSearch> &a, const std::unique_ptr<CollItemSearch> &b) {
600 return BLI_strcasecmp_natural(a->name.c_str(), b->name.c_str()) < 0;
601 });
602 for (const int i : items_list.index_range()) {
603 items_list[i]->index = i;
604 }
605 }
606 }
607
608 if (skip_filter) {
609 for (std::unique_ptr<CollItemSearch> &cis : items_list) {
610 if (!add_collection_search_item(*cis, requires_exact_data_name, has_id_icon, items)) {
611 break;
612 }
613 }
614 }
615 else {
617 for (std::unique_ptr<CollItemSearch> &cis : items_list) {
618 search.add(cis->name, cis.get());
619 }
620
621 const Vector<CollItemSearch *> filtered_items = search.query(str);
622 for (CollItemSearch *cis : filtered_items) {
623 if (!add_collection_search_item(*cis, requires_exact_data_name, has_id_icon, items)) {
624 break;
625 }
626 }
627 }
628}
629
630int UI_icon_from_id(const ID *id)
631{
632 if (id == nullptr) {
633 return ICON_NONE;
634 }
635
636 /* exception for objects */
637 if (GS(id->name) == ID_OB) {
638 Object *ob = (Object *)id;
639
640 if (ob->type == OB_EMPTY) {
641 return ICON_EMPTY_DATA;
642 }
643 return UI_icon_from_id(static_cast<const ID *>(ob->data));
644 }
645
646 /* otherwise get it through RNA, creating the pointer
647 * will set the right type, also with subclassing */
649
650 return (ptr.type) ? RNA_struct_ui_icon(ptr.type) : ICON_NONE;
651}
652
654{
655 if (type & RPT_ERROR_ALL) {
656 return ICON_CANCEL;
657 }
658 if (type & RPT_WARNING_ALL) {
659 return ICON_ERROR;
660 }
661 if (type & RPT_INFO_ALL) {
662 return ICON_INFO;
663 }
664 if (type & RPT_DEBUG_ALL) {
665 return ICON_SYSTEM;
666 }
667 if (type & RPT_PROPERTY) {
668 return ICON_OPTIONS;
669 }
670 if (type & RPT_OPERATOR) {
671 return ICON_CHECKMARK;
672 }
673 return ICON_INFO;
674}
675
677{
678 if (type & RPT_ERROR_ALL) {
679 return TH_ERROR;
680 }
681 if (type & RPT_WARNING_ALL) {
682 return TH_WARNING;
683 }
684 if (type & RPT_INFO_ALL) {
685 return TH_INFO;
686 }
687 if (type & RPT_DEBUG_ALL) {
688 return TH_INFO_DEBUG;
689 }
690 if (type & RPT_PROPERTY) {
691 return TH_INFO_PROPERTY;
692 }
693 if (type & RPT_OPERATOR) {
694 return TH_INFO_OPERATOR;
695 }
696 return TH_WARNING;
697}
698
700{
701 if (type & RPT_ERROR_ALL) {
702 return TH_INFO_ERROR_TEXT;
703 }
704 if (type & RPT_WARNING_ALL) {
706 }
707 if (type & RPT_INFO_ALL) {
708 return TH_INFO_INFO_TEXT;
709 }
710 if (type & RPT_DEBUG_ALL) {
711 return TH_INFO_DEBUG_TEXT;
712 }
713 if (type & RPT_PROPERTY) {
715 }
716 if (type & RPT_OPERATOR) {
718 }
720}
721
722/********************************** Misc **************************************/
723
724int UI_calc_float_precision(int prec, double value)
725{
726 static const double pow10_neg[UI_PRECISION_FLOAT_MAX + 1] = {
727 1e0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6};
728 static const double max_pow = 10000000.0; /* pow(10, UI_PRECISION_FLOAT_MAX) */
729
731 BLI_assert(fabs(pow10_neg[prec] - pow(10, -prec)) < 1e-16);
732
733 /* Check on the number of decimal places need to display the number,
734 * this is so 0.00001 is not displayed as 0.00,
735 * _but_, this is only for small values as 10.0001 will not get the same treatment.
736 */
737 value = fabs(value);
738 if ((value < pow10_neg[prec]) && (value > (1.0 / max_pow))) {
739 int value_i = int(lround(value * max_pow));
740 if (value_i != 0) {
741 const int prec_span = 3; /* show: 0.01001, 5 would allow 0.0100001 for eg. */
742 int test_prec;
743 int prec_min = -1;
744 int dec_flag = 0;
746 while (i && value_i) {
747 if (value_i % 10) {
748 dec_flag |= 1 << i;
749 prec_min = i;
750 }
751 value_i /= 10;
752 i--;
753 }
754
755 /* even though its a small value, if the second last digit is not 0, use it */
756 test_prec = prec_min;
757
758 dec_flag = (dec_flag >> (prec_min + 1)) & ((1 << prec_span) - 1);
759
760 while (dec_flag) {
761 test_prec++;
762 dec_flag = dec_flag >> 1;
763 }
764
765 prec = std::max(test_prec, prec);
766 }
767 }
768
770
771 return prec;
772}
773
774std::optional<std::string> UI_but_online_manual_id(const uiBut *but)
775{
776 if (but->rnapoin.data && but->rnaprop) {
777 return fmt::format(
779 }
780 if (but->optype) {
781 char idname[OP_MAX_TYPENAME];
782 const size_t idname_len = WM_operator_py_idname(idname, but->optype->idname);
783 return std::string(idname, idname_len);
784 }
785
786 return std::nullopt;
787}
788
789std::optional<std::string> UI_but_online_manual_id_from_active(const bContext *C)
790{
791 if (uiBut *but = UI_context_active_but_get(C)) {
792 return UI_but_online_manual_id(but);
793 }
794 return std::nullopt;
795}
796
797/* -------------------------------------------------------------------- */
798
799static rctf ui_but_rect_to_view(const uiBut *but, const ARegion *region, const View2D *v2d)
800{
801 rctf region_rect;
802 ui_block_to_region_rctf(region, but->block, &region_rect, &but->rect);
803
804 rctf view_rect;
805 UI_view2d_region_to_view_rctf(v2d, &region_rect, &view_rect);
806
807 return view_rect;
808}
809
817static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect)
818{
819 const float rect_width = BLI_rctf_size_x(rect);
820 const float rect_height = BLI_rctf_size_y(rect);
821
822 rctf *cur = &v2d->cur;
823 const float cur_width = BLI_rctf_size_x(cur);
824 const float cur_height = BLI_rctf_size_y(cur);
825
826 bool changed = false;
827
828 /* Snap to bottom edge. Also use if rect is higher than view bounds (could be a parameter). */
829 if ((cur->ymin > rect->ymin) || (rect_height > cur_height)) {
830 cur->ymin = rect->ymin;
831 cur->ymax = cur->ymin + cur_height;
832 changed = true;
833 }
834 /* Snap to upper edge. */
835 else if (cur->ymax < rect->ymax) {
836 cur->ymax = rect->ymax;
837 cur->ymin = cur->ymax - cur_height;
838 changed = true;
839 }
840 /* Snap to left edge. Also use if rect is wider than view bounds. */
841 else if ((cur->xmin > rect->xmin) || (rect_width > cur_width)) {
842 cur->xmin = rect->xmin;
843 cur->xmax = cur->xmin + cur_width;
844 changed = true;
845 }
846 /* Snap to right edge. */
847 else if (cur->xmax < rect->xmax) {
848 cur->xmax = rect->xmax;
849 cur->xmin = cur->xmax - cur_width;
850 changed = true;
851 }
852 else {
854 }
855
856 return changed;
857}
858
859void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but)
860{
861 View2D *v2d = &region->v2d;
862 /* Uninitialized view or region that doesn't use View2D. */
863 if ((v2d->flag & V2D_IS_INIT) == 0) {
864 return;
865 }
866
867 rctf rect = ui_but_rect_to_view(but, region, v2d);
868
869 const int margin = UI_UNIT_X * 0.5f;
870 BLI_rctf_pad(&rect, margin, margin);
871
872 const bool changed = ui_view2d_cur_ensure_rect_in_view(v2d, &rect);
873 if (changed) {
876 }
877}
878
879/* -------------------------------------------------------------------- */
890
896
901
903{
904 uiButStore *bs_handle = MEM_callocN<uiButStore>(__func__);
905
906 bs_handle->block = block;
907 BLI_addtail(&block->butstore, bs_handle);
908
909 return bs_handle;
910}
911
912void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
913{
914 /* NOTE(@ideasman42): Workaround for button store being moved into new block,
915 * which then can't use the previous buttons state
916 * (#ui_but_update_from_old_block fails to find a match),
917 * keeping the active button in the old block holding a reference
918 * to the button-state in the new block: see #49034.
919 *
920 * Ideally we would manage moving the 'uiButStore', keeping a correct state.
921 * All things considered this is the most straightforward fix. */
922 if (block != bs_handle->block && bs_handle->block != nullptr) {
923 block = bs_handle->block;
924 }
925
926 BLI_freelistN(&bs_handle->items);
927 BLI_assert(BLI_findindex(&block->butstore, bs_handle) != -1);
928 BLI_remlink(&block->butstore, bs_handle);
929
930 MEM_freeN(bs_handle);
931}
932
934{
935 return (bs_handle->block != nullptr);
936}
937
939{
940 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
941 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
942 if (*bs_elem->but_p == but) {
943 return true;
944 }
945 }
946 }
947
948 return false;
949}
950
951void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
952{
953 uiButStoreElem *bs_elem = MEM_callocN<uiButStoreElem>(__func__);
954 BLI_assert(*but_p);
955 bs_elem->but_p = but_p;
956
957 BLI_addtail(&bs_handle->items, bs_elem);
958}
959
960void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
961{
962 LISTBASE_FOREACH_MUTABLE (uiButStoreElem *, bs_elem, &bs_handle->items) {
963 if (bs_elem->but_p == but_p) {
964 BLI_remlink(&bs_handle->items, bs_elem);
965 MEM_freeN(bs_elem);
966 }
967 }
968
969 BLI_assert(0);
970}
971
972bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src)
973{
974 bool found = false;
975
976 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
977 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
978 if (*bs_elem->but_p == but_src) {
979 *bs_elem->but_p = but_dst;
980 found = true;
981 }
982 }
983 }
984
985 return found;
986}
987
989{
990 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
991 bs_handle->block = nullptr;
992 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
993 *bs_elem->but_p = nullptr;
994 }
995 }
996}
997
999{
1000 /* move this list to the new block */
1001 if (block->oldblock) {
1002 if (block->oldblock->butstore.first) {
1003 BLI_movelisttolist(&block->butstore, &block->oldblock->butstore);
1004 }
1005 }
1006
1007 if (LIKELY(block->butstore.first == nullptr)) {
1008 return;
1009 }
1010
1011 /* warning, loop-in-loop, in practice we only store <10 buttons at a time,
1012 * so this isn't going to be a problem, if that changes old-new mapping can be cached first */
1013 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
1014 BLI_assert(ELEM(bs_handle->block, nullptr, block) ||
1015 (block->oldblock && block->oldblock == bs_handle->block));
1016
1017 if (bs_handle->block == block->oldblock) {
1018 bs_handle->block = block;
1019
1020 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
1021 if (*bs_elem->but_p) {
1022 uiBut *but_new = ui_but_find_new(block, *bs_elem->but_p);
1023
1024 /* can be nullptr if the buttons removed,
1025 * NOTE: we could allow passing in a callback when buttons are removed
1026 * so the caller can cleanup */
1027 *bs_elem->but_p = but_new;
1028 }
1029 }
1030 }
1031 }
1032}
1033
1035
1036/* -------------------------------------------------------------------- */
1039
1044 IDProperty *properties,
1045 const bool is_strict,
1046 wmOperatorType *ui_optype,
1047 PointerRNA *ui_opptr)
1048{
1049 if (ui_optype->idname != opname) {
1050 return false;
1051 }
1052
1053 bool match = false;
1054 if (properties) {
1055 if (ui_opptr &&
1056 IDP_EqualsProperties_ex(properties, static_cast<IDProperty *>(ui_opptr->data), is_strict))
1057 {
1058 match = true;
1059 }
1060 }
1061 else {
1062 match = true;
1063 }
1064 return match;
1065}
1066
1067std::optional<std::string> UI_key_event_operator_string(const bContext *C,
1068 const StringRefNull opname,
1069 IDProperty *properties,
1070 const bool is_strict)
1071{
1072 /* NOTE: currently only actions on UI Lists are supported (for the asset manager).
1073 * Other kinds of events can be supported as needed. */
1074
1075 ARegion *region = CTX_wm_region(C);
1076 if (region == nullptr) {
1077 return std::nullopt;
1078 }
1079
1080 /* Early exit regions which don't have UI-Lists. */
1081 if ((region->runtime->type->keymapflag & ED_KEYMAP_UI) == 0) {
1082 return std::nullopt;
1083 }
1084
1085 uiBut *but = UI_region_active_but_get(region);
1086 if (but == nullptr) {
1087 return std::nullopt;
1088 }
1089
1090 if (but->type != UI_BTYPE_PREVIEW_TILE) {
1091 return std::nullopt;
1092 }
1093
1094 short event_val = KM_NOTHING;
1095 short event_type = KM_NOTHING;
1096
1097 uiBut *listbox = nullptr;
1098 for (int i = but->block->buttons.size() - 1; i >= 0; i--) {
1099 uiBut *but_iter = but->block->buttons[i].get();
1100 if ((but_iter->type == UI_BTYPE_LISTBOX) && ui_but_contains_rect(but_iter, &but->rect)) {
1101 listbox = but_iter;
1102 break;
1103 }
1104 }
1105
1106 if (listbox && listbox->custom_data) {
1107 uiList *list = static_cast<uiList *>(listbox->custom_data);
1108 uiListDyn *dyn_data = list->dyn_data;
1109 if ((dyn_data->custom_activate_optype != nullptr) &&
1111 properties,
1112 is_strict,
1113 dyn_data->custom_activate_optype,
1114 dyn_data->custom_activate_opptr))
1115 {
1116 event_val = KM_CLICK;
1117 event_type = LEFTMOUSE;
1118 }
1119 else if ((dyn_data->custom_activate_optype != nullptr) &&
1121 properties,
1122 is_strict,
1123 dyn_data->custom_drag_optype,
1124 dyn_data->custom_drag_opptr))
1125 {
1126 event_val = KM_CLICK_DRAG;
1127 event_type = LEFTMOUSE;
1128 }
1129 }
1130
1131 if ((event_val != KM_NOTHING) && (event_type != KM_NOTHING)) {
1133 KM_NOTHING,
1134 KM_NOTHING,
1135 KM_NOTHING,
1136 KM_NOTHING,
1137 0,
1138 event_val,
1139 event_type,
1140 false);
1141 }
1142
1143 return std::nullopt;
1144}
1145
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:905
#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:2429
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:83
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
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
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.cc:637
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
bool BLI_rctf_inside_rctf(const rctf *rct_a, const rctf *rct_b)
Definition rct.cc:193
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
#define CLAMP(a, b, c)
#define ELEM(...)
#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
float[3] Vector
@ 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
@ ED_KEYMAP_UI
Definition ED_screen.hh:740
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:659
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:687
@ PROP_STRING_SEARCH_SORT
Definition RNA_types.hh:694
@ PROP_STRING_SEARCH_SUPPORTED
Definition RNA_types.hh:692
PropertyType
Definition RNA_types.hh:149
@ 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_COLLECTION
Definition RNA_types.hh:156
@ PROP_ID_SELF_CHECK
Definition RNA_types.hh:344
@ PROP_TEXTEDIT_UPDATE
Definition RNA_types.hh:312
@ PROP_HIDDEN
Definition RNA_types.hh:324
@ PROP_COLOR
Definition RNA_types.hh:248
@ PROP_PERCENTAGE
Definition RNA_types.hh:238
@ PROP_FACTOR
Definition RNA_types.hh:239
@ PROP_COLOR_GAMMA
Definition RNA_types.hh:260
#define C
Definition RandGen.cpp:29
eAutoPropButsReturn
@ UI_PROP_BUTS_ANY_FAILED_CHECK
@ UI_PROP_BUTS_NONE_ADDED
uiBut * uiDefIconTextButR_prop(uiBlock *block, int type, int retval, int icon, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, std::optional< blender::StringRef > tip)
uiBut * uiDefButR_prop(uiBlock *block, int type, int retval, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, std::optional< blender::StringRef > tip)
#define UI_SEP_CHAR
bool(*)(const uiBut *a, const uiBut *b) uiButIdentityCompareFunc
uiBut * UI_region_active_but_get(const ARegion *region)
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, std::optional< blender::StringRef > tip)
#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)
bool UI_search_item_add(uiSearchItems *items, blender::StringRef name, void *poin, int iconid, int but_flag, uint8_t name_prefix_offset)
void UI_block_align_begin(uiBlock *block)
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, std::optional< blender::StringRef > 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
@ UI_BUT_DISABLED
@ UI_BUT_HAS_SEP_CHAR
@ UI_BUT_VALUE_CLEAR
@ UI_BUT_TEXTEDIT_UPDATE
void UI_but_flag_enable(uiBut *but, int flag)
void UI_block_align_end(uiBlock *block)
int UI_icon_from_library(const ID *id)
@ UI_ITEM_R_COMPACT
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetActivateInit(uiLayout *layout, bool activate_init)
#define UI_MAX_DRAW_STR
@ TH_INFO_PROPERTY_TEXT
@ TH_INFO_WARNING_TEXT
@ TH_INFO_DEBUG
@ TH_WARNING
@ TH_INFO_INFO_TEXT
@ TH_INFO_PROPERTY
@ TH_INFO_DEBUG_TEXT
@ TH_INFO_ERROR_TEXT
@ TH_INFO
@ TH_INFO_OPERATOR
@ TH_ERROR
@ TH_INFO_OPERATOR_TEXT
void UI_view2d_curRect_changed(const bContext *C, View2D *v2d)
Definition view2d.cc:833
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1674
@ KM_NOTHING
Definition WM_types.hh:307
@ KM_CLICK_DRAG
Definition WM_types.hh:316
@ KM_CLICK
Definition WM_types.hh:310
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
int64_t size() const
void append(const T &value)
IndexRange index_range() const
T * end()
T * begin()
void add(const StringRef str, T *user_data, const int weight=0)
Vector< T * > query(const StringRef query) const
#define str(s)
uint col
#define pow
#define ID_IS_LINKED(_id)
#define GS(a)
uiBut * ui_but_find_new(uiBlock *block_new, const uiBut *but_old)
Definition interface.cc:881
void ui_block_to_region_rctf(const ARegion *region, const uiBlock *block, rctf *rct_dst, const rctf *rct_src)
Definition interface.cc:155
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)
uiBut * uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int index, const std::optional< StringRef > name, int icon, int x, int y, int width, int height)
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)
std::optional< std::string > UI_key_event_operator_string(const bContext *C, const StringRefNull opname, IDProperty *properties, const bool is_strict)
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)
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 StringRefNull opname, IDProperty *properties, const bool is_strict, wmOperatorType *ui_optype, PointerRNA *ui_opptr)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
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)
ARegionRuntimeHandle * runtime
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
void * first
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
std::optional< int > icon_id
Definition RNA_types.hh:684
std::optional< std::string > info
Definition RNA_types.hh:682
float xmax
float xmin
float ymax
float ymin
blender::Vector< std::unique_ptr< uiBut > > buttons
uiBlock * oldblock
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
uiLayout & column(bool align)
struct wmOperatorType * custom_activate_optype
struct PointerRNA * custom_drag_opptr
struct wmOperatorType * custom_drag_optype
struct PointerRNA * custom_activate_opptr
uiListDyn * dyn_data
const char * idname
Definition WM_types.hh:1032
i
Definition text_draw.cc:230
uint len
@ LEFTMOUSE
PointerRNA * ptr
Definition wm_files.cc:4227
std::optional< std::string > WM_keymap_item_raw_to_string(const int8_t shift, const int8_t ctrl, const int8_t alt, const int8_t oskey, const int8_t hyper, 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:139