Blender V5.0
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_string_utf8.h"
24#include "BLI_utildefines.h"
25
26#include "BLT_translation.hh"
27
28#include "BKE_context.hh"
29#include "BKE_global.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 "ANIM_action.hh"
39
40#include "UI_interface_icons.hh"
42#include "UI_resources.hh"
43#include "UI_string_search.hh"
44#include "UI_view2d.hh"
45
46#include "WM_api.hh"
47#include "WM_types.hh"
48
49#include "interface_intern.hh"
50
53
54/*************************** RNA Utilities ******************************/
55
58 PropertyRNA *prop,
59 int index,
60 const std::optional<StringRef> name,
61 int icon,
62 int x,
63 int y,
64 int width,
65 int height)
66{
67 uiBut *but = nullptr;
68
69 switch (RNA_property_type(prop)) {
70 case PROP_BOOLEAN: {
71 if (RNA_property_array_check(prop) && index == -1) {
72 return nullptr;
73 }
74
75 if (icon && name && name->is_empty()) {
76 but = uiDefIconButR_prop(block,
78 0,
79 icon,
80 x,
81 y,
82 width,
83 height,
84 ptr,
85 prop,
86 index,
87 0,
88 0,
89 std::nullopt);
90 }
91 else if (icon) {
92 but = uiDefIconTextButR_prop(block,
94 0,
95 icon,
96 name,
97 x,
98 y,
99 width,
100 height,
101 ptr,
102 prop,
103 index,
104 0,
105 0,
106 std::nullopt);
107 }
108 else {
109 but = uiDefButR_prop(block,
111 0,
112 name,
113 x,
114 y,
115 width,
116 height,
117 ptr,
118 prop,
119 index,
120 0,
121 0,
122 std::nullopt);
123 }
124 break;
125 }
126 case PROP_INT:
127 case PROP_FLOAT: {
128 if (RNA_property_array_check(prop) && index == -1) {
130 but = uiDefButR_prop(block,
132 0,
133 name,
134 x,
135 y,
136 width,
137 height,
138 ptr,
139 prop,
140 -1,
141 0,
142 0,
143 std::nullopt);
144 }
145 else {
146 return nullptr;
147 }
148 }
149 else if (RNA_property_subtype(prop) == PROP_PERCENTAGE ||
151 {
152 but = uiDefButR_prop(block,
154 0,
155 name,
156 x,
157 y,
158 width,
159 height,
160 ptr,
161 prop,
162 index,
163 0,
164 0,
165 std::nullopt);
166 }
167 else {
168 but = uiDefButR_prop(block,
170 0,
171 name,
172 x,
173 y,
174 width,
175 height,
176 ptr,
177 prop,
178 index,
179 0,
180 0,
181 std::nullopt);
182 }
183
186 }
187 break;
188 }
189 case PROP_ENUM:
190 if (icon && name && name->is_empty()) {
191 but = uiDefIconButR_prop(block,
193 0,
194 icon,
195 x,
196 y,
197 width,
198 height,
199 ptr,
200 prop,
201 index,
202 0,
203 0,
204 std::nullopt);
205 }
206 else if (icon) {
207 but = uiDefIconTextButR_prop(block,
209 0,
210 icon,
211 std::nullopt,
212 x,
213 y,
214 width,
215 height,
216 ptr,
217 prop,
218 index,
219 0,
220 0,
221 std::nullopt);
222 }
223 else {
224 but = uiDefButR_prop(block,
226 0,
227 name,
228 x,
229 y,
230 width,
231 height,
232 ptr,
233 prop,
234 index,
235 0,
236 0,
237 std::nullopt);
238 }
239 break;
240 case PROP_STRING:
241 if (icon && name && name->is_empty()) {
242 but = uiDefIconButR_prop(block,
244 0,
245 icon,
246 x,
247 y,
248 width,
249 height,
250 ptr,
251 prop,
252 index,
253 0,
254 0,
255 std::nullopt);
256 }
257 else if (icon) {
258 but = uiDefIconTextButR_prop(block,
260 0,
261 icon,
262 name,
263 x,
264 y,
265 width,
266 height,
267 ptr,
268 prop,
269 index,
270 0,
271 0,
272 std::nullopt);
273 }
274 else {
275 but = uiDefButR_prop(block,
277 0,
278 name,
279 x,
280 y,
281 width,
282 height,
283 ptr,
284 prop,
285 index,
286 0,
287 0,
288 std::nullopt);
289 }
290
292 /* TEXTEDIT_UPDATE is usually used for search buttons. For these we also want
293 * the 'x' icon to clear search string, so setting VALUE_CLEAR flag, too. */
295 }
296 break;
297 case PROP_POINTER: {
298 if (icon == 0) {
299 const PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
300 icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop));
301 }
302 if (icon == ICON_DOT) {
303 icon = 0;
304 }
305
306 but = uiDefIconTextButR_prop(block,
308 0,
309 icon,
310 name,
311 x,
312 y,
313 width,
314 height,
315 ptr,
316 prop,
317 index,
318 0,
319 0,
320 std::nullopt);
321 ui_but_add_search(but, ptr, prop, nullptr, nullptr, nullptr, false);
322 break;
323 }
324 case PROP_COLLECTION: {
325 char text[256];
326 SNPRINTF_UTF8(text, IFACE_("%d items"), RNA_property_collection_length(ptr, prop));
327 but = uiDefBut(
328 block, ButType::Label, 0, text, x, y, width, height, nullptr, 0, 0, std::nullopt);
330 break;
331 }
332 default:
333 but = nullptr;
334 break;
335 }
336
337 return but;
338}
339
342 PropertyRNA *prop,
343 const int icon,
344 const int x,
345 const int y,
346 const int tot_width,
347 const int height)
348{
349 const int len = RNA_property_array_length(ptr, prop);
350 if (len == 0) {
351 return;
352 }
353
354 const int item_width = tot_width / len;
355
357 for (int i = 0; i < len; i++) {
358 uiDefAutoButR(block, ptr, prop, i, "", icon, x + i * item_width, y, item_width, height);
359 }
360 UI_block_align_end(block);
361}
362
365 bool (*check_prop)(PointerRNA *ptr,
366 PropertyRNA *prop,
367 void *user_data),
368 void *user_data,
369 PropertyRNA *prop_activate_init,
370 const eButLabelAlign label_align,
371 const bool compact)
372{
374 uiLayout *col;
375 std::optional<StringRefNull> name;
376
377 RNA_STRUCT_BEGIN (ptr, prop) {
378 const int flag = RNA_property_flag(prop);
379
380 if (flag & PROP_HIDDEN) {
381 continue;
382 }
383 if (check_prop && check_prop(ptr, prop, user_data) == 0) {
384 return_info |= UI_PROP_BUTS_ANY_FAILED_CHECK;
385 continue;
386 }
387
388 const PropertyType type = RNA_property_type(prop);
389 switch (label_align) {
392 const bool is_boolean = (type == PROP_BOOLEAN && !RNA_property_array_check(prop));
393
395
396 if (label_align == UI_BUT_LABEL_ALIGN_COLUMN) {
397 col = &layout->column(true);
398
399 if (!is_boolean) {
400 col->label(*name, ICON_NONE);
401 }
402 }
403 else {
405 col = &layout->column(true);
406 /* Let uiLayout::prop() create the split layout. */
407 col->use_property_split_set(true);
408 }
409
410 break;
411 }
413 default:
414 col = layout;
415 name = std::nullopt; /* no smart label alignment, show default name with button */
416 break;
417 }
418
419 /* Only buttons that can be edited as text. */
420 const bool use_activate_init = ((prop == prop_activate_init) &&
422
423 if (use_activate_init) {
424 col->activate_init_set(true);
425 }
426
427 col->prop(ptr, prop, -1, 0, compact ? UI_ITEM_R_COMPACT : UI_ITEM_NONE, name, ICON_NONE);
428 return_info &= ~UI_PROP_BUTS_NONE_ADDED;
429
430 if (use_activate_init) {
431 col->activate_init_set(false);
432 }
433 }
435
436 return return_info;
437}
438
443
444/* *** RNA collection search menu *** */
445
447 void *data;
448 std::string name;
449 int index;
452 bool is_id;
454};
455
457 const bool requires_exact_data_name,
458 const bool has_id_icon,
459 uiSearchItems *items)
460{
461
462 /* If no item has its own icon to display, libraries can use the library icons rather than the
463 * name prefix for showing the library status. */
464 int name_prefix_offset = cis.name_prefix_offset;
465 if (!has_id_icon && cis.is_id && !requires_exact_data_name) {
466 cis.iconid = UI_icon_from_library(static_cast<const ID *>(cis.data));
467 char name_buf[UI_MAX_DRAW_STR];
469 name_buf, static_cast<const ID *>(cis.data), false, UI_SEP_CHAR, &name_prefix_offset);
470 cis.name = name_buf;
471 }
472
473 return UI_search_item_add(items,
474 cis.name,
475 cis.data,
476 cis.iconid,
477 cis.has_sep_char ? int(UI_BUT_HAS_SEP_CHAR) : 0,
478 name_prefix_offset);
479}
480
482 const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
483{
484 using namespace blender;
485 uiRNACollectionSearch *data = static_cast<uiRNACollectionSearch *>(arg);
486 const int flag = RNA_property_flag(data->target_prop);
487 const bool is_ptr_target = (RNA_property_type(data->target_prop) == PROP_POINTER);
488 /* For non-pointer properties, UI code acts entirely based on the item's name. So the name has to
489 * match the RNA name exactly. So only for pointer properties, the name can be modified to add
490 * further UI hints. */
491 const bool requires_exact_data_name = !is_ptr_target;
492 const bool skip_filter = is_first;
493 char name_buf[UI_MAX_DRAW_STR];
494 bool has_id_icon = false;
495
496 /* The string search API requires pointer stability. */
498
499 if (data->search_prop != nullptr) {
500 /* build a temporary list of relevant items first */
501 RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
502 if (flag & PROP_ID_SELF_CHECK) {
503 if (itemptr.data == data->target_ptr.owner_id) {
504 continue;
505 }
506 }
507
508 /* use filter */
509 if (is_ptr_target) {
510 if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
511 continue;
512 }
513 }
514
515 int name_prefix_offset = 0;
516 int iconid = ICON_NONE;
517 bool has_sep_char = false;
518 const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
519
520 char *name;
521 if (is_id) {
522 iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false);
523 if (!ELEM(iconid, 0, ICON_BLANK1)) {
524 has_id_icon = true;
525 }
526
527 if (requires_exact_data_name) {
528 name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
529 }
530 else {
531 const ID *id = static_cast<ID *>(itemptr.data);
532 BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset);
533 BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
534 "Name string buffer should be big enough to hold full UI ID name");
535 name = name_buf;
536 has_sep_char = ID_IS_LINKED(id);
537 }
538 }
539 else if (data->item_search_prop) {
541 &itemptr, data->item_search_prop, name_buf, sizeof(name_buf), nullptr);
542 }
543 else if (itemptr.type == &RNA_ActionSlot) {
544 /* FIXME: This special case is fairly annoying.
545 *
546 * `item_search_prop` now allows to specify another string property than the default RNA
547 * struct name one as source, but icons are still an issue. RNA access API for icons likely
548 * needs some love, to allow callbacks, data-based icons retrieval, in addition to the
549 * purely static options currently available (see #RNA_struct_ui_icon and
550 * #RNA_property_ui_icon).
551 */
552 PropertyRNA *prop = RNA_struct_find_property(&itemptr, "name_display");
553 name = RNA_property_string_get_alloc(&itemptr, prop, name_buf, sizeof(name_buf), nullptr);
554 /* Also show an icon for the data-block type that each slot is intended for. */
555 animrig::Slot &slot = reinterpret_cast<ActionSlot *>(itemptr.data)->wrap();
556 iconid = UI_icon_from_idcode(slot.idtype);
557 /* So indentation is kept when no icon is present. */
558 if (iconid == ICON_NONE) {
559 iconid = ICON_BLANK1;
560 }
561 }
562 else {
563 name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
564 }
565
566 if (name) {
567 auto cis = std::make_unique<CollItemSearch>();
568 cis->data = itemptr.data;
569 cis->name = name;
570 cis->index = items_list.size();
571 cis->iconid = iconid;
572 cis->is_id = is_id;
573 cis->name_prefix_offset = name_prefix_offset;
574 cis->has_sep_char = has_sep_char;
575 items_list.append(std::move(cis));
576 if (name != name_buf) {
578 }
579 }
580 }
582 }
583 else {
586 data->target_prop);
588
589 const bool show_extra_info = (G.debug_value == 102);
590
592 &data->target_ptr,
593 data->target_prop,
594 str,
595 [&](StringPropertySearchVisitParams visit_params) {
596 auto cis = std::make_unique<CollItemSearch>();
597
598 cis->data = nullptr;
599 if (visit_params.info && show_extra_info) {
600 cis->name = fmt::format("{}" UI_SEP_CHAR_S "{}",
601 visit_params.text,
602 *visit_params.info);
603 }
604 else {
605 cis->name = std::move(visit_params.text);
606 }
607
608 cis->index = items_list.size();
609 cis->iconid = visit_params.icon_id.value_or(ICON_NONE);
610 cis->is_id = false;
611 cis->name_prefix_offset = 0;
612 cis->has_sep_char = visit_params.info.has_value();
613 items_list.append(std::move(cis));
614 });
615
616 if (search_flag & PROP_STRING_SEARCH_SORT) {
617 std::sort(
618 items_list.begin(),
619 items_list.end(),
620 [](const std::unique_ptr<CollItemSearch> &a, const std::unique_ptr<CollItemSearch> &b) {
621 return BLI_strcasecmp_natural(a->name.c_str(), b->name.c_str()) < 0;
622 });
623 for (const int i : items_list.index_range()) {
624 items_list[i]->index = i;
625 }
626 }
627 }
628
629 if (skip_filter) {
630 for (std::unique_ptr<CollItemSearch> &cis : items_list) {
631 if (!add_collection_search_item(*cis, requires_exact_data_name, has_id_icon, items)) {
632 break;
633 }
634 }
635 }
636 else {
638 for (std::unique_ptr<CollItemSearch> &cis : items_list) {
639 search.add(cis->name, cis.get());
640 }
641
642 const Vector<CollItemSearch *> filtered_items = search.query(str);
643 for (CollItemSearch *cis : filtered_items) {
644 if (!add_collection_search_item(*cis, requires_exact_data_name, has_id_icon, items)) {
645 break;
646 }
647 }
648 }
649}
650
651int UI_icon_from_id(const ID *id)
652{
653 if (id == nullptr) {
654 return ICON_NONE;
655 }
656
657 /* exception for objects */
658 if (GS(id->name) == ID_OB) {
659 Object *ob = (Object *)id;
660
661 if (ob->type == OB_EMPTY) {
662 return ICON_EMPTY_DATA;
663 }
664 return UI_icon_from_id(static_cast<const ID *>(ob->data));
665 }
666
667 /* otherwise get it through RNA, creating the pointer
668 * will set the right type, also with subclassing */
670
671 return (ptr.type) ? RNA_struct_ui_icon(ptr.type) : ICON_NONE;
672}
673
675{
676 if (type & RPT_ERROR_ALL) {
677 return ICON_CANCEL;
678 }
679 if (type & RPT_WARNING_ALL) {
680 return ICON_ERROR;
681 }
682 if (type & RPT_INFO_ALL) {
683 return ICON_INFO;
684 }
685 if (type & RPT_DEBUG_ALL) {
686 return ICON_SYSTEM;
687 }
688 if (type & RPT_PROPERTY) {
689 return ICON_OPTIONS;
690 }
691 if (type & RPT_OPERATOR) {
692 return ICON_CHECKMARK;
693 }
694 return ICON_INFO;
695}
696
698{
699 if (type & RPT_ERROR_ALL) {
700 return TH_ERROR;
701 }
702 if (type & RPT_WARNING_ALL) {
703 return TH_WARNING;
704 }
705 if (type & RPT_INFO_ALL) {
706 return TH_INFO;
707 }
708 if (type & RPT_DEBUG_ALL) {
709 return TH_INFO_DEBUG;
710 }
711 if (type & RPT_PROPERTY) {
712 return TH_INFO_PROPERTY;
713 }
714 if (type & RPT_OPERATOR) {
715 return TH_INFO_OPERATOR;
716 }
717 return TH_WARNING;
718}
719
721{
722 if (type & RPT_ERROR_ALL) {
723 return TH_INFO_ERROR_TEXT;
724 }
725 if (type & RPT_WARNING_ALL) {
727 }
728 if (type & RPT_INFO_ALL) {
729 return TH_INFO_INFO_TEXT;
730 }
731 if (type & RPT_DEBUG_ALL) {
732 return TH_INFO_DEBUG_TEXT;
733 }
734 if (type & RPT_PROPERTY) {
736 }
737 if (type & RPT_OPERATOR) {
739 }
741}
742
743/********************************** Misc **************************************/
744
745int UI_calc_float_precision(int prec, double value)
746{
747 static const double pow10_neg[UI_PRECISION_FLOAT_MAX + 1] = {
748 1e0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6};
749 static const double max_pow = 10000000.0; /* pow(10, UI_PRECISION_FLOAT_MAX) */
750
752 BLI_assert(fabs(pow10_neg[prec] - pow(10, -prec)) < 1e-16);
753
754 /* Check on the number of decimal places need to display the number,
755 * this is so 0.00001 is not displayed as 0.00,
756 * _but_, this is only for small values as 10.0001 will not get the same treatment.
757 */
758 value = fabs(value);
759 if ((value < pow10_neg[prec]) && (value > (1.0 / max_pow))) {
760 int value_i = int(lround(value * max_pow));
761 if (value_i != 0) {
762 const int prec_span = 3; /* show: 0.01001, 5 would allow 0.0100001 for eg. */
763 int test_prec;
764 int prec_min = -1;
765 int dec_flag = 0;
767 while (i && value_i) {
768 if (value_i % 10) {
769 dec_flag |= 1 << i;
770 prec_min = i;
771 }
772 value_i /= 10;
773 i--;
774 }
775
776 /* even though its a small value, if the second last digit is not 0, use it */
777 test_prec = prec_min;
778
779 dec_flag = (dec_flag >> (prec_min + 1)) & ((1 << prec_span) - 1);
780
781 while (dec_flag) {
782 test_prec++;
783 dec_flag = dec_flag >> 1;
784 }
785
786 prec = std::max(test_prec, prec);
787 }
788 }
789
791
792 return prec;
793}
794
795std::optional<std::string> UI_but_online_manual_id(const uiBut *but)
796{
797 if (but->rnapoin.data && but->rnaprop) {
798 return fmt::format(
800 }
801 if (but->optype) {
802 char idname[OP_MAX_TYPENAME];
803 const size_t idname_len = WM_operator_py_idname(idname, but->optype->idname);
804 return std::string(idname, idname_len);
805 }
806
807 return std::nullopt;
808}
809
810std::optional<std::string> UI_but_online_manual_id_from_active(const bContext *C)
811{
812 if (uiBut *but = UI_context_active_but_get(C)) {
813 return UI_but_online_manual_id(but);
814 }
815 return std::nullopt;
816}
817
818/* -------------------------------------------------------------------- */
819
820static rctf ui_but_rect_to_view(const uiBut *but, const ARegion *region, const View2D *v2d)
821{
822 rctf region_rect;
823 ui_block_to_region_rctf(region, but->block, &region_rect, &but->rect);
824
825 rctf view_rect;
826 UI_view2d_region_to_view_rctf(v2d, &region_rect, &view_rect);
827
828 return view_rect;
829}
830
838static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect)
839{
840 const float rect_width = BLI_rctf_size_x(rect);
841 const float rect_height = BLI_rctf_size_y(rect);
842
843 rctf *cur = &v2d->cur;
844 const float cur_width = BLI_rctf_size_x(cur);
845 const float cur_height = BLI_rctf_size_y(cur);
846
847 bool changed = false;
848
849 /* Snap to bottom edge. Also use if rect is higher than view bounds (could be a parameter). */
850 if ((cur->ymin > rect->ymin) || (rect_height > cur_height)) {
851 cur->ymin = rect->ymin;
852 cur->ymax = cur->ymin + cur_height;
853 changed = true;
854 }
855 /* Snap to upper edge. */
856 else if (cur->ymax < rect->ymax) {
857 cur->ymax = rect->ymax;
858 cur->ymin = cur->ymax - cur_height;
859 changed = true;
860 }
861 /* Snap to left edge. Also use if rect is wider than view bounds. */
862 else if ((cur->xmin > rect->xmin) || (rect_width > cur_width)) {
863 cur->xmin = rect->xmin;
864 cur->xmax = cur->xmin + cur_width;
865 changed = true;
866 }
867 /* Snap to right edge. */
868 else if (cur->xmax < rect->xmax) {
869 cur->xmax = rect->xmax;
870 cur->xmin = cur->xmax - cur_width;
871 changed = true;
872 }
873 else {
875 }
876
877 return changed;
878}
879
880void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but)
881{
882 View2D *v2d = &region->v2d;
883 /* Uninitialized view or region that doesn't use View2D. */
884 if ((v2d->flag & V2D_IS_INIT) == 0) {
885 return;
886 }
887
888 rctf rect = ui_but_rect_to_view(but, region, v2d);
889
890 const int margin = UI_UNIT_X * 0.5f;
891 BLI_rctf_pad(&rect, margin, margin);
892
893 const bool changed = ui_view2d_cur_ensure_rect_in_view(v2d, &rect);
894 if (changed) {
897 }
898}
899
900/* -------------------------------------------------------------------- */
911
917
922
924{
925 uiButStore *bs_handle = MEM_callocN<uiButStore>(__func__);
926
927 bs_handle->block = block;
928 BLI_addtail(&block->butstore, bs_handle);
929
930 return bs_handle;
931}
932
933void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
934{
935 /* NOTE(@ideasman42): Workaround for button store being moved into new block,
936 * which then can't use the previous buttons state
937 * (#ui_but_update_from_old_block fails to find a match),
938 * keeping the active button in the old block holding a reference
939 * to the button-state in the new block: see #49034.
940 *
941 * Ideally we would manage moving the 'uiButStore', keeping a correct state.
942 * All things considered this is the most straightforward fix. */
943 if (block != bs_handle->block && bs_handle->block != nullptr) {
944 block = bs_handle->block;
945 }
946
947 BLI_freelistN(&bs_handle->items);
948 BLI_assert(BLI_findindex(&block->butstore, bs_handle) != -1);
949 BLI_remlink(&block->butstore, bs_handle);
950
951 MEM_freeN(bs_handle);
952}
953
955{
956 return (bs_handle->block != nullptr);
957}
958
960{
961 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
962 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
963 if (*bs_elem->but_p == but) {
964 return true;
965 }
966 }
967 }
968
969 return false;
970}
971
972void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
973{
974 uiButStoreElem *bs_elem = MEM_callocN<uiButStoreElem>(__func__);
975 BLI_assert(*but_p);
976 bs_elem->but_p = but_p;
977
978 BLI_addtail(&bs_handle->items, bs_elem);
979}
980
981void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
982{
983 LISTBASE_FOREACH_MUTABLE (uiButStoreElem *, bs_elem, &bs_handle->items) {
984 if (bs_elem->but_p == but_p) {
985 BLI_remlink(&bs_handle->items, bs_elem);
986 MEM_freeN(bs_elem);
987 }
988 }
989
990 BLI_assert(0);
991}
992
993bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src)
994{
995 bool found = false;
996
997 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
998 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
999 if (*bs_elem->but_p == but_src) {
1000 *bs_elem->but_p = but_dst;
1001 found = true;
1002 }
1003 }
1004 }
1005
1006 return found;
1007}
1008
1010{
1011 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
1012 bs_handle->block = nullptr;
1013 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
1014 *bs_elem->but_p = nullptr;
1015 }
1016 }
1017}
1018
1020{
1021 /* move this list to the new block */
1022 if (block->oldblock) {
1023 if (block->oldblock->butstore.first) {
1024 BLI_movelisttolist(&block->butstore, &block->oldblock->butstore);
1025 }
1026 }
1027
1028 if (LIKELY(block->butstore.first == nullptr)) {
1029 return;
1030 }
1031
1032 /* warning, loop-in-loop, in practice we only store <10 buttons at a time,
1033 * so this isn't going to be a problem, if that changes old-new mapping can be cached first */
1034 LISTBASE_FOREACH (uiButStore *, bs_handle, &block->butstore) {
1035 BLI_assert(ELEM(bs_handle->block, nullptr, block) ||
1036 (block->oldblock && block->oldblock == bs_handle->block));
1037
1038 if (bs_handle->block == block->oldblock) {
1039 bs_handle->block = block;
1040
1041 LISTBASE_FOREACH (uiButStoreElem *, bs_elem, &bs_handle->items) {
1042 if (*bs_elem->but_p) {
1043 uiBut *but_new = ui_but_find_new(block, *bs_elem->but_p);
1044
1045 /* can be nullptr if the buttons removed,
1046 * NOTE: we could allow passing in a callback when buttons are removed
1047 * so the caller can cleanup */
1048 *bs_elem->but_p = but_new;
1049 }
1050 }
1051 }
1052 }
1053}
1054
Functions and classes to work with Actions.
#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:2449
@ RPT_OPERATOR
Definition BKE_report.hh:36
@ RPT_PROPERTY
Definition BKE_report.hh:37
#define RPT_ERROR_ALL
Definition BKE_report.hh:51
#define RPT_INFO_ALL
Definition BKE_report.hh:47
#define RPT_WARNING_ALL
Definition BKE_report.hh:50
#define RPT_DEBUG_ALL
Definition BKE_report.hh:46
#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_UTF8(dst, format,...)
#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
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
@ ID_OB
Object is a sort of wrapper for general info.
@ OB_EMPTY
@ V2D_IS_INIT
#define OP_MAX_TYPENAME
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:638
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:778
@ PROP_STRING_SEARCH_SORT
Definition RNA_types.hh:785
@ PROP_STRING_SEARCH_SUPPORTED
Definition RNA_types.hh:783
PropertyType
Definition RNA_types.hh:161
@ PROP_FLOAT
Definition RNA_types.hh:164
@ PROP_BOOLEAN
Definition RNA_types.hh:162
@ PROP_ENUM
Definition RNA_types.hh:166
@ PROP_INT
Definition RNA_types.hh:163
@ PROP_STRING
Definition RNA_types.hh:165
@ PROP_POINTER
Definition RNA_types.hh:167
@ PROP_COLLECTION
Definition RNA_types.hh:168
@ PROP_ID_SELF_CHECK
Definition RNA_types.hh:370
@ PROP_TEXTEDIT_UPDATE
Definition RNA_types.hh:326
@ PROP_HIDDEN
Definition RNA_types.hh:338
@ PROP_COLOR
Definition RNA_types.hh:260
@ PROP_PERCENTAGE
Definition RNA_types.hh:250
@ PROP_FACTOR
Definition RNA_types.hh:251
@ PROP_COLOR_GAMMA
Definition RNA_types.hh:272
#define C
Definition RandGen.cpp:29
eAutoPropButsReturn
@ UI_PROP_BUTS_ANY_FAILED_CHECK
@ UI_PROP_BUTS_NONE_ADDED
#define UI_SEP_CHAR
bool(*)(const uiBut *a, const uiBut *b) uiButIdentityCompareFunc
@ UI_BUT_DISABLED
@ UI_BUT_HAS_SEP_CHAR
@ UI_BUT_VALUE_CLEAR
@ UI_BUT_TEXTEDIT_UPDATE
#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)
uiBut * uiDefButR_prop(uiBlock *block, ButType 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)
uiBut * uiDefIconTextButR_prop(uiBlock *block, ButType 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)
bool UI_search_item_add(uiSearchItems *items, blender::StringRef name, void *poin, int iconid, int but_flag, uint8_t name_prefix_offset)
uiBut * uiDefIconButR_prop(uiBlock *block, ButType 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)
void UI_block_align_begin(uiBlock *block)
#define UI_UNIT_X
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_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)
void UI_but_flag_enable(uiBut *but, int flag)
void UI_block_align_end(uiBlock *block)
int UI_icon_from_idcode(int idcode)
int UI_icon_from_library(const ID *id)
@ UI_ITEM_R_COMPACT
#define UI_ITEM_NONE
#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:1675
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)
#define GS(x)
uint col
#define pow
uiBut * ui_but_find_new(uiBlock *block_new, const uiBut *but_old)
Definition interface.cc:893
void ui_block_to_region_rctf(const ARegion *region, const uiBlock *block, rctf *rct_dst, const rctf *rct_src)
Definition interface.cc:160
int ui_id_icon_get(const bContext *C, ID *id, const bool big)
uiBut * ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, PropertyRNA *item_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)
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)
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)
float wrap(float value, float max, float min)
Definition node_math.h:103
const char * name
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)
const char * RNA_property_ui_name(const PropertyRNA *prop, const PointerRNA *ptr)
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)
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:414
char name[258]
Definition DNA_ID.h:432
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:775
std::optional< std::string > info
Definition RNA_types.hh:773
float xmax
float xmin
float ymax
float ymin
uiBlock * oldblock
ListBase butstore
uiButStoreElem * next
uiButStoreElem * prev
uiBlock * block
uiButStore * next
uiButStore * prev
uiButIdentityCompareFunc identity_cmp_func
PropertyRNA * rnaprop
wmOperatorType * optype
uiBlock * block
PointerRNA rnapoin
uiLayout & column(bool align)
const char * idname
Definition WM_types.hh:1035
i
Definition text_draw.cc:230
uint len
PointerRNA * ptr
Definition wm_files.cc:4238
size_t WM_operator_py_idname(char *dst, const char *src)
uint8_t flag
Definition wm_window.cc:145