Blender V4.3
interface_layout.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10#include <climits>
11#include <cmath>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_armature_types.h"
18#include "DNA_screen_types.h"
19#include "DNA_userdef_types.h"
20
21#include "BLI_array.hh"
22#include "BLI_dynstr.h"
23#include "BLI_listbase.h"
24#include "BLI_math_base.h"
25#include "BLI_memory_utils.hh"
26#include "BLI_rect.h"
27#include "BLI_string.h"
28#include "BLI_string_ref.hh"
29#include "BLI_utildefines.h"
30
31#include "BLT_translation.hh"
32
33#include "BKE_context.hh"
34#include "BKE_global.hh"
35#include "BKE_idprop.hh"
36#include "BKE_lib_id.hh"
37#include "BKE_screen.hh"
38
39#include "RNA_access.hh"
40#include "RNA_prototypes.hh"
41
42#include "UI_interface.hh"
43
44#include "ED_id_management.hh"
45
46#include "WM_api.hh"
47#include "WM_types.hh"
48
49#include "interface_intern.hh"
50
51/* Show an icon button after each RNA button to use to quickly set keyframes,
52 * this is a way to display animation/driven/override status, see #54951. */
53#define UI_PROP_DECORATE
54/* Alternate draw mode where some buttons can use single icon width,
55 * giving more room for the text at the expense of nicely aligned text. */
56#define UI_PROP_SEP_ICON_WIDTH_EXCEPTION
57
58/* -------------------------------------------------------------------- */
62#define UI_OPERATOR_ERROR_RET(_ot, _opname, return_statement) \
63 if (ot == nullptr) { \
64 ui_item_disabled(layout, _opname); \
65 RNA_warning("'%s' unknown operator", _opname); \
66 return_statement; \
67 } \
68 (void)0
69
70#define UI_ITEM_PROP_SEP_DIVIDE 0.4f
71
72/* uiLayoutRoot */
73
90
91/* Item */
92
119
123
124 UI_ITEM_BOX_ITEM = 1 << 2, /* The item is "inside" a box item */
127 /* Show an icon button next to each property (to set keyframes, show status).
128 * Enabled by default, depends on 'UI_ITEM_PROP_SEP'. */
131};
133
134struct uiItem {
137
138 uiItem() = default;
139 uiItem(const uiItem &) = default;
140 virtual ~uiItem() = default;
141};
142
145};
146
177
182
184 /* Extra parameters */
185 bool row_major; /* Fill first row first, instead of filling first column first. */
186 bool even_columns; /* Same width for all columns. */
187 bool even_rows; /* Same height for all rows. */
195
196 /* Pure internal runtime storage. */
198};
199
203
208
210
214
216
219/* -------------------------------------------------------------------- */
223static const char *ui_item_name_add_colon(const char *name, char namestr[UI_MAX_NAME_STR])
224{
225 const int len = strlen(name);
226
227 if (len != 0 && len + 1 < UI_MAX_NAME_STR) {
228 memcpy(namestr, name, len);
229 namestr[len] = ':';
230 namestr[len + 1] = '\0';
231 return namestr;
232 }
233
234 return name;
235}
236
237static int ui_item_fit(const int item,
238 const int pos,
239 const int all,
240 const int available,
241 const bool is_last,
242 const int alignment,
243 float *extra_pixel)
244{
245 /* available == 0 is unlimited */
246 if (ELEM(0, available, all)) {
247 return item;
248 }
249
250 if (all > available) {
251 /* contents is bigger than available space */
252 if (is_last) {
253 return available - pos;
254 }
255
256 const float width = *extra_pixel + (item * available) / float(all);
257 *extra_pixel = width - int(width);
258 return int(width);
259 }
260
261 /* contents is smaller or equal to available space */
262 if (alignment == UI_LAYOUT_ALIGN_EXPAND) {
263 if (is_last) {
264 return available - pos;
265 }
266
267 const float width = *extra_pixel + (item * available) / float(all);
268 *extra_pixel = width - int(width);
269 return int(width);
270 }
271 return item;
272}
273
274/* variable button size in which direction? */
275#define UI_ITEM_VARY_X 1
276#define UI_ITEM_VARY_Y 2
277
279{
280 return ((ELEM(layout->root->type, UI_LAYOUT_HEADER, UI_LAYOUT_PIEMENU) ||
281 (layout->alignment != UI_LAYOUT_ALIGN_EXPAND)) ?
284}
285
287{
288 /* Note that this code is probably a bit flaky, we'd probably want to know whether it's
289 * variable in X and/or Y, etc. But for now it mimics previous one,
290 * with addition of variable flag set for children of grid-flow layouts. */
291 return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size;
292}
293
299 float text;
300 float icon;
302};
303
312constexpr uiTextIconPadFactor ui_text_pad_default = {1.50f, 0.25f, 0.0f};
313
315constexpr uiTextIconPadFactor ui_text_pad_compact = {1.25f, 0.35f, 0.0f};
316
318constexpr uiTextIconPadFactor ui_text_pad_none = {0.25f, 1.50f, 0.0f};
319
324 const char *name,
325 int icon,
326 const uiTextIconPadFactor &pad_factor,
327 const uiFontStyle *fstyle)
328{
329 const int unit_x = UI_UNIT_X * (layout->scale[0] ? layout->scale[0] : 1.0f);
330
331 /* When there is no text, always behave as if this is an icon-only button
332 * since it's not useful to return empty space. */
333 if (icon && !name[0]) {
334 return unit_x * (1.0f + pad_factor.icon_only);
335 }
336
337 if (ui_layout_variable_size(layout)) {
338 if (!icon && !name[0]) {
339 return unit_x * (1.0f + pad_factor.icon_only);
340 }
341
342 if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) {
343 layout->flag |= UI_ITEM_FIXED_SIZE;
344 }
345
346 float margin = pad_factor.text;
347 if (icon) {
348 margin += pad_factor.icon;
349 }
350
351 const float aspect = layout->root->block->aspect;
352 return UI_fontstyle_string_width_with_block_aspect(fstyle, name, aspect) +
353 int(ceilf(unit_x * margin));
354 }
355 return unit_x * 10;
356}
357
358static int ui_text_icon_width(uiLayout *layout,
359 const char *name,
360 const int icon,
361 const bool compact)
362{
364 layout, name, icon, compact ? ui_text_pad_compact : ui_text_pad_default, UI_FSTYLE_WIDGET);
365}
366
367static void ui_item_size(const uiItem *item, int *r_w, int *r_h)
368{
369 if (item->type == ITEM_BUTTON) {
370 const uiButtonItem *bitem = static_cast<const uiButtonItem *>(item);
371
372 if (r_w) {
373 *r_w = BLI_rctf_size_x(&bitem->but->rect);
374 }
375 if (r_h) {
376 *r_h = BLI_rctf_size_y(&bitem->but->rect);
377 }
378 }
379 else {
380 const uiLayout *litem = static_cast<const uiLayout *>(item);
381
382 if (r_w) {
383 *r_w = litem->w;
384 }
385 if (r_h) {
386 *r_h = litem->h;
387 }
388 }
389}
390
391static void ui_item_offset(const uiItem *item, int *r_x, int *r_y)
392{
393 if (item->type == ITEM_BUTTON) {
394 const uiButtonItem *bitem = static_cast<const uiButtonItem *>(item);
395
396 if (r_x) {
397 *r_x = bitem->but->rect.xmin;
398 }
399 if (r_y) {
400 *r_y = bitem->but->rect.ymin;
401 }
402 }
403 else {
404 if (r_x) {
405 *r_x = 0;
406 }
407 if (r_y) {
408 *r_y = 0;
409 }
410 }
411}
412
413static void ui_item_position(uiItem *item, const int x, const int y, const int w, const int h)
414{
415 if (item->type == ITEM_BUTTON) {
416 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
417
418 bitem->but->rect.xmin = x;
419 bitem->but->rect.ymin = y;
420 bitem->but->rect.xmax = x + w;
421 bitem->but->rect.ymax = y + h;
422
423 ui_but_update(bitem->but); /* for strlen */
424 }
425 else {
426 uiLayout *litem = static_cast<uiLayout *>(item);
427
428 litem->x = x;
429 litem->y = y + h;
430 litem->w = w;
431 litem->h = h;
432 }
433}
434
435static void ui_item_move(uiItem *item, const int delta_xmin, const int delta_xmax)
436{
437 if (item->type == ITEM_BUTTON) {
438 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
439
440 bitem->but->rect.xmin += delta_xmin;
441 bitem->but->rect.xmax += delta_xmax;
442
443 ui_but_update(bitem->but); /* for strlen */
444 }
445 else {
446 uiLayout *litem = static_cast<uiLayout *>(item);
447
448 if (delta_xmin > 0) {
449 litem->x += delta_xmin;
450 }
451 else {
452 litem->w += delta_xmax;
453 }
454 }
455}
456
459/* -------------------------------------------------------------------- */
464{
465 switch (layout->type) {
466 case ITEM_LAYOUT_ROW:
467 case ITEM_LAYOUT_ROOT:
476 case ITEM_LAYOUT_BOX:
478 default:
479 return UI_LAYOUT_VERTICAL;
480 }
481}
482
483static uiLayout *ui_item_local_sublayout(uiLayout *test, uiLayout *layout, bool align)
484{
485 uiLayout *sub;
487 sub = uiLayoutRow(layout, align);
488 }
489 else {
490 sub = uiLayoutColumn(layout, align);
491 }
492
493 sub->space = 0;
494 return sub;
495}
496
497static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
498{
499 wmWindow *win = CTX_wm_window(C);
500 uiBut *but = static_cast<uiBut *>(arg_but);
501 PointerRNA *ptr = &but->rnapoin;
502 PropertyRNA *prop = but->rnaprop;
503 const int index = POINTER_AS_INT(arg_index);
504 const bool shift = win->eventstate->modifier & KM_SHIFT;
505 const int len = RNA_property_array_length(ptr, prop);
506
507 if (!shift) {
508 RNA_property_boolean_set_index(ptr, prop, index, true);
509
510 for (int i = 0; i < len; i++) {
511 if (i != index) {
512 RNA_property_boolean_set_index(ptr, prop, i, false);
513 }
514 }
515
516 RNA_property_update(C, ptr, prop);
517
518 LISTBASE_FOREACH (uiBut *, cbut, &but->block->buttons) {
519 ui_but_update(cbut);
520 }
521 }
522}
523
524/* create buttons for an item with an RNA array */
525static void ui_item_array(uiLayout *layout,
526 uiBlock *block,
527 const char *name,
528 int icon,
530 PropertyRNA *prop,
531 const int len,
532 int x,
533 const int y,
534 int w,
535 const int /*h*/,
536 const bool expand,
537 const bool slider,
538 const int toggle,
539 const bool icon_only,
540 const bool compact,
541 const bool show_text)
542{
543 const uiStyle *style = layout->root->style;
544
545 /* retrieve type and subtype */
546 const PropertyType type = RNA_property_type(prop);
547 const PropertySubType subtype = RNA_property_subtype(prop);
548
549 uiLayout *sub = ui_item_local_sublayout(layout, layout, true);
550 UI_block_layout_set_current(block, sub);
551
552 /* create label */
553 if (name[0] && show_text) {
554 uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
555 }
556
557 /* create buttons */
558 if (type == PROP_BOOLEAN && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) {
559 /* special check for layer layout */
560 const int cols = (len >= 20) ? 2 : 1;
561 const int colbuts = len / (2 * cols);
562 uint layer_used = 0;
563 uint layer_active = 0;
564
565 UI_block_layout_set_current(block, uiLayoutAbsolute(layout, false));
566
567 const int butw = UI_UNIT_X * 0.75;
568 const int buth = UI_UNIT_X * 0.75;
569
570 for (int b = 0; b < cols; b++) {
572
573 for (int a = 0; a < colbuts; a++) {
574 const int layer_num = a + b * colbuts;
575 const uint layer_flag = (1u << layer_num);
576
577 if (layer_used & layer_flag) {
578 if (layer_active & layer_flag) {
579 icon = ICON_LAYER_ACTIVE;
580 }
581 else {
582 icon = ICON_LAYER_USED;
583 }
584 }
585 else {
586 icon = ICON_BLANK1;
587 }
588
589 uiBut *but = uiDefAutoButR(
590 block, ptr, prop, layer_num, "", icon, x + butw * a, y + buth, butw, buth);
591 if (subtype == PROP_LAYER_MEMBER) {
592 UI_but_func_set(but, ui_layer_but_cb, but, POINTER_FROM_INT(layer_num));
593 }
594 }
595 for (int a = 0; a < colbuts; a++) {
596 const int layer_num = a + len / 2 + b * colbuts;
597 const uint layer_flag = (1u << layer_num);
598
599 if (layer_used & layer_flag) {
600 if (layer_active & layer_flag) {
601 icon = ICON_LAYER_ACTIVE;
602 }
603 else {
604 icon = ICON_LAYER_USED;
605 }
606 }
607 else {
608 icon = ICON_BLANK1;
609 }
610
611 uiBut *but = uiDefAutoButR(
612 block, ptr, prop, layer_num, "", icon, x + butw * a, y, butw, buth);
613 if (subtype == PROP_LAYER_MEMBER) {
614 UI_but_func_set(but, ui_layer_but_cb, but, POINTER_FROM_INT(layer_num));
615 }
616 }
617 UI_block_align_end(block);
618
619 x += colbuts * butw + style->buttonspacex;
620 }
621 }
622 else if (subtype == PROP_MATRIX) {
623 int totdim, dim_size[3]; /* 3 == RNA_MAX_ARRAY_DIMENSION */
624 int row, col;
625
626 UI_block_layout_set_current(block, uiLayoutAbsolute(layout, true));
627
628 totdim = RNA_property_array_dimension(ptr, prop, dim_size);
629 if (totdim != 2) {
630 /* Only 2D matrices supported in UI so far. */
631 return;
632 }
633
634 w /= dim_size[0];
635 // h /= dim_size[1]; /* UNUSED */
636
637 for (int a = 0; a < len; a++) {
638 col = a % dim_size[0];
639 row = a / dim_size[0];
640
641 uiBut *but = uiDefAutoButR(block,
642 ptr,
643 prop,
644 a,
645 "",
646 ICON_NONE,
647 x + w * col,
648 y + (dim_size[1] * UI_UNIT_Y) - (row * UI_UNIT_Y),
649 w,
650 UI_UNIT_Y);
651 if (slider && but->type == UI_BTYPE_NUM) {
652 uiButNumber *number_but = (uiButNumber *)but;
653 const float step_size = number_but->step_size;
654 const float precision = number_but->precision;
656 uiButNumberSlider *slider_but = reinterpret_cast<uiButNumberSlider *>(but);
657 slider_but->step_size = step_size;
658 slider_but->precision = precision;
659 }
660 }
661 }
662 else if (subtype == PROP_DIRECTION && !expand) {
663 uiDefButR_prop(block,
665 0,
666 name,
667 x,
668 y,
669 UI_UNIT_X * 3,
670 UI_UNIT_Y * 3,
671 ptr,
672 prop,
673 -1,
674 0,
675 0,
676 nullptr);
677 }
678 else {
679 /* NOTE: this block of code is a bit arbitrary and has just been made
680 * to work with common cases, but may need to be re-worked */
681
682 /* special case, boolean array in a menu, this could be used in a more generic way too */
683 if (ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA) && !expand && ELEM(len, 3, 4)) {
684 uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, 0, 0, w, UI_UNIT_Y);
685 }
686 else {
687 /* Even if 'expand' is false, we expand anyway. */
688
689 /* Layout for known array sub-types. */
690 char str[3] = {'\0'};
691
692 if (!icon_only && show_text) {
693 if (type != PROP_BOOLEAN) {
694 str[1] = ':';
695 }
696 }
697
698 /* Show check-boxes for rna on a non-emboss block (menu for eg). */
699 bool *boolarr = nullptr;
700 if (type == PROP_BOOLEAN &&
702 {
703 boolarr = static_cast<bool *>(MEM_callocN(sizeof(bool) * len, __func__));
704 RNA_property_boolean_get_array(ptr, prop, boolarr);
705 }
706
707 const char *str_buf = show_text ? str : "";
708 for (int a = 0; a < len; a++) {
709 if (!icon_only && show_text) {
710 str[0] = RNA_property_array_item_char(prop, a);
711 }
712 if (boolarr) {
713 icon = boolarr[a] ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
714 }
715
716 const int width_item = ((compact && type == PROP_BOOLEAN) ?
717 min_ii(w, ui_text_icon_width(layout, str_buf, icon, false)) :
718 w);
719
720 uiBut *but = uiDefAutoButR(
721 block, ptr, prop, a, str_buf, icon, 0, 0, width_item, UI_UNIT_Y);
722 if (slider && but->type == UI_BTYPE_NUM) {
723 uiButNumber *number_but = (uiButNumber *)but;
724 const float step_size = number_but->step_size;
725 const float precision = number_but->precision;
727 uiButNumberSlider *slider_but = reinterpret_cast<uiButNumberSlider *>(but);
728 slider_but->step_size = step_size;
729 slider_but->precision = precision;
730 }
731 if ((toggle == 1) && but->type == UI_BTYPE_CHECKBOX) {
732 but->type = UI_BTYPE_TOGGLE;
733 }
734 if ((a == 0) && (subtype == PROP_AXISANGLE)) {
736 }
737 }
738
739 if (boolarr) {
740 MEM_freeN(boolarr);
741 }
742 }
743 }
744
745 UI_block_layout_set_current(block, layout);
746}
747
748static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2)
749{
750 wmWindow *win = CTX_wm_window(C);
751
752 if ((win->eventstate->modifier & KM_SHIFT) == 0) {
753 uiBut *but = (uiBut *)arg1;
754 const int enum_value = POINTER_AS_INT(arg2);
755
756 int current_value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
757 if (!(current_value & enum_value)) {
758 current_value = enum_value;
759 }
760 else {
761 current_value &= enum_value;
762 }
763 RNA_property_enum_set(&but->rnapoin, but->rnaprop, current_value);
764 }
765}
766
771 uiBlock *block,
773 PropertyRNA *prop,
774 const char *uiname,
775 const int h,
776 const eButType but_type,
777 const bool icon_only,
778 const EnumPropertyItem *item,
779 const bool is_first)
780{
781 const char *name = (!uiname || uiname[0]) ? item->name : "";
782 const int icon = item->icon;
783 const int value = item->value;
784 const int itemw = ui_text_icon_width(block->curlayout, icon_only ? "" : name, icon, false);
785
786 uiBut *but;
787 if (icon && name[0] && !icon_only) {
789 block, but_type, 0, icon, name, 0, 0, itemw, h, ptr, prop, -1, 0, value, nullptr);
790 }
791 else if (icon) {
792 const int w = (is_first) ? itemw : ceilf(itemw - U.pixelsize);
793 but = uiDefIconButR_prop(
794 block, but_type, 0, icon, 0, 0, w, h, ptr, prop, -1, 0, value, nullptr);
795 }
796 else {
797 but = uiDefButR_prop(
798 block, but_type, 0, name, 0, 0, itemw, h, ptr, prop, -1, 0, value, nullptr);
799 }
800
801 if (RNA_property_flag(prop) & PROP_ENUM_FLAG) {
802 /* If this is set, assert since we're clobbering someone else's callback. */
803 /* Buttons get their block's func by default, so we cannot assert in that case either. */
804 BLI_assert(ELEM(but->func, nullptr, block->func));
806 }
807
809 but->drawflag |= UI_BUT_TEXT_LEFT;
810 }
811
812 /* Allow quick, inaccurate swipe motions to switch tabs
813 * (no need to keep cursor over them). */
814 if (but_type == UI_BTYPE_TAB) {
815 but->flag |= UI_BUT_DRAG_LOCK;
816 }
817}
818
820 uiBlock *block,
822 PropertyRNA *prop,
823 const char *uiname,
824 const int h,
825 const eButType but_type,
826 const bool icon_only)
827{
828 /* XXX: The way this function currently handles uiname parameter
829 * is insane and inconsistent with general UI API:
830 *
831 * - uiname is the *enum property* label.
832 * - when it is nullptr or empty, we do not draw *enum items* labels,
833 * this doubles the icon_only parameter.
834 * - we *never* draw (i.e. really use) the enum label uiname, it is just used as a mere flag!
835 *
836 * Unfortunately, fixing this implies an API "soft break", so better to defer it for later... :/
837 * - mont29
838 */
839
841
842 const bool radial = (layout->root->type == UI_LAYOUT_PIEMENU);
843
844 bool free;
845 const EnumPropertyItem *item_array;
846 if (radial) {
848 static_cast<bContext *>(block->evil_C), ptr, prop, &item_array, nullptr, &free);
849 }
850 else {
852 static_cast<bContext *>(block->evil_C), ptr, prop, &item_array, nullptr, &free);
853 }
854
855 /* We don't want nested rows, cols in menus. */
856 uiLayout *layout_radial = nullptr;
857 if (radial) {
858 if (layout->root->layout == layout) {
859 layout_radial = uiLayoutRadial(layout);
860 UI_block_layout_set_current(block, layout_radial);
861 }
862 else {
863 if (layout->type == ITEM_LAYOUT_RADIAL) {
864 layout_radial = layout;
865 }
866 UI_block_layout_set_current(block, layout);
867 }
868 }
870 layout->root->type == UI_LAYOUT_MENU)
871 {
872 UI_block_layout_set_current(block, layout);
873 }
874 else {
875 UI_block_layout_set_current(block, ui_item_local_sublayout(layout, layout, true));
876 }
877
878 for (const EnumPropertyItem *item = item_array; item->identifier; item++) {
879 const bool is_first = item == item_array;
880
881 if (!item->identifier[0]) {
882 const EnumPropertyItem *next_item = item + 1;
883
884 /* Separate items, potentially with a label. */
885 if (next_item->identifier) {
886 /* Item without identifier but with name:
887 * Add group label for the following items. */
888 if (item->name) {
889 if (!is_first) {
890 uiItemS(block->curlayout);
891 }
892 uiItemL(block->curlayout, item->name, item->icon);
893 }
894 else if (radial && layout_radial) {
895 uiItemS(layout_radial);
896 }
897 else {
898 uiItemS(block->curlayout);
899 }
900 }
901 continue;
902 }
903
905 layout, block, ptr, prop, uiname, h, but_type, icon_only, item, is_first);
906 }
907
908 UI_block_layout_set_current(block, layout);
909
910 if (free) {
911 MEM_freeN((void *)item_array);
912 }
913}
914static void ui_item_enum_expand(uiLayout *layout,
915 uiBlock *block,
917 PropertyRNA *prop,
918 const char *uiname,
919 const int h,
920 const bool icon_only)
921{
922 ui_item_enum_expand_exec(layout, block, ptr, prop, uiname, h, UI_BTYPE_ROW, icon_only);
923}
925 bContext *C,
926 uiBlock *block,
928 PropertyRNA *prop,
929 PointerRNA *ptr_highlight,
930 PropertyRNA *prop_highlight,
931 const char *uiname,
932 const int h,
933 const bool icon_only)
934{
935 uiBut *last = static_cast<uiBut *>(block->buttons.last);
936
937 ui_item_enum_expand_exec(layout, block, ptr, prop, uiname, h, UI_BTYPE_TAB, icon_only);
938 BLI_assert(last != block->buttons.last);
939
940 for (uiBut *tab = last ? last->next : static_cast<uiBut *>(block->buttons.first); tab;
941 tab = tab->next)
942 {
944 if (icon_only) {
946 }
947 }
948
949 const bool use_custom_highlight = (prop_highlight != nullptr);
950
951 if (use_custom_highlight) {
952 const int highlight_array_len = RNA_property_array_length(ptr_highlight, prop_highlight);
953 blender::Array<bool, 64> highlight_array(highlight_array_len);
954 RNA_property_boolean_get_array(ptr_highlight, prop_highlight, highlight_array.data());
955 int i = 0;
956 for (uiBut *tab_but = last ? last->next : static_cast<uiBut *>(block->buttons.first);
957 (tab_but != nullptr) && (i < highlight_array_len);
958 tab_but = tab_but->next, i++)
959 {
960 SET_FLAG_FROM_TEST(tab_but->flag, !highlight_array[i], UI_BUT_INACTIVE);
961 }
962 }
963}
964
965/* callback for keymap item change button */
966static void ui_keymap_but_cb(bContext * /*C*/, void *but_v, void * /*key_v*/)
967{
968 uiBut *but = static_cast<uiBut *>(but_v);
970 const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but;
971
973 &but->rnapoin, "shift", (hotkey_but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING);
975 &but->rnapoin, "ctrl", (hotkey_but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING);
977 &but->rnapoin, "alt", (hotkey_but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING);
979 &but->rnapoin, "oskey", (hotkey_but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING);
980}
981
989 uiBlock *block,
990 const char *name,
991 const int icon,
993 PropertyRNA *prop,
994 const int index,
995 const int x,
996 const int y,
997 const int w_hint,
998 const int h,
999 const int flag)
1000{
1001 uiLayout *sub = layout;
1002 int prop_but_width = w_hint;
1003#ifdef UI_PROP_DECORATE
1004 uiLayout *layout_prop_decorate = nullptr;
1005 const bool use_prop_sep = ((layout->flag & UI_ITEM_PROP_SEP) != 0);
1006 const bool use_prop_decorate = use_prop_sep && (layout->flag & UI_ITEM_PROP_DECORATE) &&
1007 (layout->flag & UI_ITEM_PROP_DECORATE_NO_PAD) == 0;
1008#endif
1009
1010 const bool is_keymapitem_ptr = RNA_struct_is_a(ptr->type, &RNA_KeyMapItem);
1011 if ((flag & UI_ITEM_R_FULL_EVENT) && !is_keymapitem_ptr) {
1012 RNA_warning("Data is not a keymap item struct: %s. Ignoring 'full_event' option.",
1014 }
1015
1016 UI_block_layout_set_current(block, layout);
1017
1018 /* Only add new row if more than 1 item will be added. */
1019 if (name[0]
1020#ifdef UI_PROP_DECORATE
1021 || use_prop_decorate
1022#endif
1023 )
1024 {
1025 /* Also avoid setting 'align' if possible. Set the space to zero instead as aligning a large
1026 * number of labels can end up aligning thousands of buttons when displaying key-map search (a
1027 * heavy operation), see: #78636. */
1028 sub = uiLayoutRow(layout, layout->align);
1029 sub->space = 0;
1030 }
1031
1032 if (name[0]) {
1033#ifdef UI_PROP_DECORATE
1034 if (use_prop_sep) {
1035 layout_prop_decorate = uiItemL_respect_property_split(layout, name, ICON_NONE);
1036 }
1037 else
1038#endif
1039 {
1040 int w_label;
1041 if (ui_layout_variable_size(layout)) {
1042 /* In this case, a pure label without additional padding.
1043 * Use a default width for property button(s). */
1044 prop_but_width = UI_UNIT_X * 5;
1045 w_label = ui_text_icon_width_ex(
1046 layout, name, ICON_NONE, ui_text_pad_none, UI_FSTYLE_WIDGET);
1047 }
1048 else {
1049 w_label = w_hint / 3;
1050 }
1051 uiDefBut(block, UI_BTYPE_LABEL, 0, name, x, y, w_label, h, nullptr, 0.0, 0.0, "");
1052 }
1053 }
1054
1055 const PropertyType type = RNA_property_type(prop);
1056 const PropertySubType subtype = RNA_property_subtype(prop);
1057
1058 uiBut *but;
1059 if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH)) {
1060 UI_block_layout_set_current(block, uiLayoutRow(sub, true));
1061 but = uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, prop_but_width - UI_UNIT_X, h);
1062
1063 /* BUTTONS_OT_file_browse calls UI_context_active_but_prop_get_filebrowser */
1064 uiDefIconButO(block,
1066 subtype == PROP_DIRPATH ? "BUTTONS_OT_directory_browse" :
1067 "BUTTONS_OT_file_browse",
1069 ICON_FILEBROWSER,
1070 x,
1071 y,
1072 UI_UNIT_X,
1073 h,
1074 nullptr);
1075 }
1076 else if (flag & UI_ITEM_R_EVENT) {
1077 but = uiDefButR_prop(block,
1079 0,
1080 name,
1081 x,
1082 y,
1083 prop_but_width,
1084 h,
1085 ptr,
1086 prop,
1087 index,
1088 0,
1089 0,
1090 nullptr);
1091 }
1092 else if ((flag & UI_ITEM_R_FULL_EVENT) && is_keymapitem_ptr) {
1093 std::string kmi_str =
1094 WM_keymap_item_to_string(static_cast<const wmKeyMapItem *>(ptr->data), false).value_or("");
1095
1096 but = uiDefButR_prop(block,
1098 0,
1099 kmi_str.c_str(),
1100 x,
1101 y,
1102 prop_but_width,
1103 h,
1104 ptr,
1105 prop,
1106 0,
1107 0,
1108 0,
1109 nullptr);
1110 UI_but_func_set(but, ui_keymap_but_cb, but, nullptr);
1111 }
1112 else {
1113 const char *str = (type == PROP_ENUM && !(flag & UI_ITEM_R_ICON_ONLY)) ? nullptr : "";
1114 but = uiDefAutoButR(block, ptr, prop, index, str, icon, x, y, prop_but_width, h);
1115 }
1116
1117 if (flag & UI_ITEM_R_IMMEDIATE) {
1119 }
1120
1121#ifdef UI_PROP_DECORATE
1122 /* Only for alignment. */
1123 if (use_prop_decorate) { /* Note that sep flag may have been unset meanwhile. */
1124 uiItemL(layout_prop_decorate ? layout_prop_decorate : sub, nullptr, ICON_BLANK1);
1125 }
1126#endif /* UI_PROP_DECORATE */
1127
1128 UI_block_layout_set_current(block, layout);
1129 return but;
1130}
1131
1133 PointerRNA *r_ptr,
1134 PropertyRNA **r_prop,
1135 bool *r_is_undo,
1136 bool *r_is_userdef)
1137{
1139 uiBut *prevbut = nullptr;
1140
1141 *r_ptr = {};
1142 *r_prop = nullptr;
1143 *r_is_undo = false;
1144 *r_is_userdef = false;
1145
1146 if (!region) {
1147 return;
1148 }
1149
1150 LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
1151 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
1152 if (but && but->rnapoin.data) {
1153 if (RNA_property_type(but->rnaprop) == PROP_STRING) {
1154 prevbut = but;
1155 }
1156 }
1157
1158 /* find the button before the active one */
1159 if ((but->flag & UI_BUT_LAST_ACTIVE) && prevbut) {
1160 *r_ptr = prevbut->rnapoin;
1161 *r_prop = prevbut->rnaprop;
1162 *r_is_undo = (prevbut->flag & UI_BUT_UNDO) != 0;
1163 *r_is_userdef = UI_but_is_userdef(prevbut);
1164 return;
1165 }
1166 }
1167 }
1168}
1169
1172/* -------------------------------------------------------------------- */
1180{
1181 if (but->tip == nullptr || but->tip[0] == '\0') {
1182 if (item->description && item->description[0] &&
1183 !(but->optype && but->optype->get_description))
1184 {
1185 but->tip = item->description;
1186 }
1187 }
1188}
1189
1190/* disabled item */
1191static void ui_item_disabled(uiLayout *layout, const char *name)
1192{
1193 uiBlock *block = layout->root->block;
1194
1195 UI_block_layout_set_current(block, layout);
1196
1197 if (!name) {
1198 name = "";
1199 }
1200
1201 const int w = ui_text_icon_width(layout, name, 0, false);
1202
1203 uiBut *but = uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
1204 UI_but_disable(but, "");
1205}
1206
1214 const char *name,
1215 int icon,
1216 IDProperty *properties,
1217 const wmOperatorCallContext context,
1218 const eUI_Item_Flag flag,
1219 PointerRNA *r_opptr)
1220{
1221 /* Take care to fill 'r_opptr' whatever happens. */
1222 uiBlock *block = layout->root->block;
1223
1224 std::string operator_name;
1225 if (!name) {
1226 if (ot && ot->srna && (flag & UI_ITEM_R_ICON_ONLY) == 0) {
1227 operator_name = WM_operatortype_name(ot, nullptr);
1228 name = operator_name.c_str();
1229 }
1230 else {
1231 name = "";
1232 }
1233 }
1234
1235 if (layout->root->type == UI_LAYOUT_MENU && !icon) {
1236 icon = ICON_BLANK1;
1237 }
1238
1239 UI_block_layout_set_current(block, layout);
1241
1242 const int w = ui_text_icon_width(layout, name, icon, false);
1243
1244 const eUIEmbossType prev_emboss = layout->emboss;
1245 if (flag & UI_ITEM_R_NO_BG) {
1247 }
1248
1249 /* create the button */
1250 uiBut *but;
1251 if (icon) {
1252 if (name[0]) {
1254 block, UI_BTYPE_BUT, ot, context, icon, name, 0, 0, w, UI_UNIT_Y, nullptr);
1255 }
1256 else {
1257 but = uiDefIconButO_ptr(block, UI_BTYPE_BUT, ot, context, icon, 0, 0, w, UI_UNIT_Y, nullptr);
1258 }
1259 }
1260 else {
1261 but = uiDefButO_ptr(block, UI_BTYPE_BUT, ot, context, name, 0, 0, w, UI_UNIT_Y, nullptr);
1262 }
1263
1264 BLI_assert(but->optype != nullptr);
1265
1266 if (flag & UI_ITEM_R_NO_BG) {
1267 layout->emboss = prev_emboss;
1268 }
1269
1270 if (flag & UI_ITEM_O_DEPRESS) {
1271 but->flag |= UI_SELECT_DRAW;
1272 }
1273
1274 if (flag & UI_ITEM_R_ICON_ONLY) {
1276 }
1277
1278 if (layout->redalert) {
1280 }
1281
1282 if (layout->active_default) {
1284 }
1285
1286 /* assign properties */
1287 if (properties || r_opptr) {
1289 if (properties) {
1290 opptr->data = properties;
1291 }
1292 else {
1293 opptr->data = blender::bke::idprop::create_group("wmOperatorProperties").release();
1294 }
1295 if (r_opptr) {
1296 *r_opptr = *opptr;
1297 }
1298 }
1299
1300 return but;
1301}
1302
1303static void ui_item_menu_hold(bContext *C, ARegion *butregion, uiBut *but)
1304{
1305 uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE);
1306 uiLayout *layout = UI_popup_menu_layout(pup);
1307 uiBlock *block = layout->root->block;
1308 UI_popup_menu_but_set(pup, butregion, but);
1309
1310 block->flag |= UI_BLOCK_POPUP_HOLD;
1311
1312 char direction = UI_DIR_DOWN;
1313 if (but->drawstr.empty()) {
1314 switch (RGN_ALIGN_ENUM_FROM_MASK(butregion->alignment)) {
1315 case RGN_ALIGN_LEFT:
1316 direction = UI_DIR_RIGHT;
1317 break;
1318 case RGN_ALIGN_RIGHT:
1319 direction = UI_DIR_LEFT;
1320 break;
1321 case RGN_ALIGN_BOTTOM:
1322 direction = UI_DIR_UP;
1323 break;
1324 default:
1325 direction = UI_DIR_DOWN;
1326 break;
1327 }
1328 }
1329 UI_block_direction_set(block, direction);
1330
1331 const char *menu_id = static_cast<const char *>(but->hold_argN);
1332 MenuType *mt = WM_menutype_find(menu_id, true);
1333 if (mt) {
1334 uiLayoutSetContextFromBut(layout, but);
1335 UI_menutype_draw(C, mt, layout);
1336 }
1337 else {
1338 uiItemL(layout, RPT_("Menu Missing:"), ICON_NONE);
1339 uiItemL(layout, menu_id, ICON_NONE);
1340 }
1341 UI_popup_menu_end(C, pup);
1342}
1343
1346 const char *name,
1347 const int icon,
1348 IDProperty *properties,
1349 const wmOperatorCallContext context,
1350 const eUI_Item_Flag flag,
1351 PointerRNA *r_opptr)
1352{
1353 uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr);
1354}
1355
1358 const char *name,
1359 int icon,
1360 IDProperty *properties,
1361 const wmOperatorCallContext context,
1362 const eUI_Item_Flag flag,
1363 const char *menu_id,
1364 PointerRNA *r_opptr)
1365{
1366 uiBut *but = uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr);
1368}
1369
1371 const char *opname,
1372 const char *name,
1373 int icon,
1374 IDProperty *properties,
1375 wmOperatorCallContext context,
1376 const eUI_Item_Flag flag,
1377 PointerRNA *r_opptr)
1378{
1379 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1380
1381 UI_OPERATOR_ERROR_RET(ot, opname, {
1382 if (r_opptr) {
1383 *r_opptr = PointerRNA_NULL;
1384 }
1385 return;
1386 });
1387
1388 uiItemFullO_ptr(layout, ot, name, icon, properties, context, flag, r_opptr);
1389}
1390
1391static const char *ui_menu_enumpropname(uiLayout *layout,
1392 PointerRNA *ptr,
1393 PropertyRNA *prop,
1394 int retval)
1395{
1396 bool free;
1397 const EnumPropertyItem *item;
1399 static_cast<bContext *>(layout->root->block->evil_C), ptr, prop, &item, nullptr, &free);
1400
1401 const char *name;
1402 if (RNA_enum_name(item, retval, &name)) {
1403 name = CTX_IFACE_(RNA_property_translation_context(prop), name);
1404 }
1405 else {
1406 name = "";
1407 }
1408
1409 if (free) {
1410 MEM_freeN((void *)item);
1411 }
1412
1413 return name;
1414}
1415
1418 const char *name,
1419 int icon,
1420 const char *propname,
1421 int value)
1422{
1425
1426 PropertyRNA *prop = RNA_struct_find_property(&ptr, propname);
1427 if (prop == nullptr) {
1428 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname);
1429 return;
1430 }
1431
1432 RNA_property_enum_set(&ptr, prop, value);
1433
1434 if (!name) {
1435 name = ui_menu_enumpropname(layout, &ptr, prop, value);
1436 }
1437
1438 uiItemFullO_ptr(layout,
1439 ot,
1440 name,
1441 icon,
1442 static_cast<IDProperty *>(ptr.data),
1443 layout->root->opcontext,
1445 nullptr);
1446}
1448 const char *opname,
1449 const char *name,
1450 int icon,
1451 const char *propname,
1452 int value)
1453{
1454 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1455
1456 if (ot) {
1457 uiItemEnumO_ptr(layout, ot, name, icon, propname, value);
1458 }
1459 else {
1460 ui_item_disabled(layout, opname);
1461 RNA_warning("unknown operator '%s'", opname);
1462 }
1463}
1464
1466{
1467 return (layout->type == ITEM_LAYOUT_RADIAL) ||
1468 ((layout->type == ITEM_LAYOUT_ROOT) && (layout->root->type == UI_LAYOUT_PIEMENU));
1469}
1470
1473 const PointerRNA &ptr,
1474 PropertyRNA *prop,
1475 IDProperty *properties,
1476 wmOperatorCallContext context,
1478 const EnumPropertyItem *item_array,
1479 int totitem,
1480 int active)
1481{
1482 const char *propname = RNA_property_identifier(prop);
1483 if (RNA_property_type(prop) != PROP_ENUM) {
1484 RNA_warning("%s.%s, not an enum type", RNA_struct_identifier(ptr.type), propname);
1485 return;
1486 }
1487
1488 uiLayout *target, *split = nullptr;
1489 uiBlock *block = layout->root->block;
1490 const bool radial = ui_layout_is_radial(layout);
1491
1492 if (radial) {
1493 target = uiLayoutRadial(layout);
1494 }
1496 target = layout;
1497 UI_block_layout_set_current(block, target);
1498
1499 /* Add a blank button to the beginning of the row. */
1500 uiDefIconBut(block,
1502 0,
1503 ICON_BLANK1,
1504 0,
1505 0,
1506 1.25f * UI_UNIT_X,
1507 UI_UNIT_Y,
1508 nullptr,
1509 0,
1510 0,
1511 nullptr);
1512 }
1513 else {
1514 split = uiLayoutSplit(layout, 0.0f, false);
1515 target = uiLayoutColumn(split, layout->align);
1516 }
1517
1518 bool last_iter = false;
1519 const EnumPropertyItem *item = item_array;
1520 for (int i = 1; item->identifier && !last_iter; i++, item++) {
1521 /* Handle over-sized pies. */
1522 if (radial && (totitem > PIE_MAX_ITEMS) && (i >= PIE_MAX_ITEMS)) {
1523 if (item->name) { /* only visible items */
1524 const EnumPropertyItem *tmp;
1525
1526 /* Check if there are more visible items for the next level. If not, we don't
1527 * add a new level and add the remaining item instead of the 'more' button. */
1528 for (tmp = item + 1; tmp->identifier; tmp++) {
1529 if (tmp->name) {
1530 break;
1531 }
1532 }
1533
1534 if (tmp->identifier) { /* only true if loop above found item and did early-exit */
1536 block, ot, propname, properties, item_array, totitem, context, flag);
1537 /* break since rest of items is handled in new pie level */
1538 break;
1539 }
1540 last_iter = true;
1541 }
1542 else {
1543 continue;
1544 }
1545 }
1546
1547 if (item->identifier[0]) {
1548 PointerRNA tptr;
1550 if (properties) {
1551 if (tptr.data) {
1552 IDP_FreeProperty(static_cast<IDProperty *>(tptr.data));
1553 }
1554 tptr.data = IDP_CopyProperty(properties);
1555 }
1556 RNA_property_enum_set(&tptr, prop, item->value);
1557
1558 uiItemFullO_ptr(target,
1559 ot,
1560 (flag & UI_ITEM_R_ICON_ONLY) ? nullptr : item->name,
1561 item->icon,
1562 static_cast<IDProperty *>(tptr.data),
1563 context,
1564 flag,
1565 nullptr);
1566
1567 uiBut *but = static_cast<uiBut *>(block->buttons.last);
1568
1569 if (active == (i - 1)) {
1570 but->flag |= UI_SELECT_DRAW;
1571 }
1572
1573 ui_but_tip_from_enum_item(but, item);
1574 }
1575 else {
1576 if (item->name) {
1577 if (item != item_array && !radial && split != nullptr) {
1578 target = uiLayoutColumn(split, layout->align);
1579 }
1580
1581 uiBut *but;
1582 if (item->icon || radial) {
1583 uiItemL(target, item->name, item->icon);
1584
1585 but = static_cast<uiBut *>(block->buttons.last);
1586 }
1587 else {
1588 /* Do not use uiItemL here, as our root layout is a menu one,
1589 * it will add a fake blank icon! */
1590 but = uiDefBut(block,
1592 0,
1593 item->name,
1594 0,
1595 0,
1596 UI_UNIT_X * 5,
1597 UI_UNIT_Y,
1598 nullptr,
1599 0.0,
1600 0.0,
1601 "");
1602 uiItemS(target);
1603 }
1604 ui_but_tip_from_enum_item(but, item);
1605 }
1606 else {
1607 if (radial) {
1608 /* invisible dummy button to ensure all items are
1609 * always at the same position */
1610 uiItemS(target);
1611 }
1612 else {
1613 /* XXX bug here, columns draw bottom item badly */
1614 uiItemS(target);
1615 }
1616 }
1617 }
1618 }
1619}
1620
1622 const char *opname,
1623 const char *propname,
1624 IDProperty *properties,
1625 wmOperatorCallContext context,
1627 const int active)
1628{
1629 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1630
1631 if (!ot || !ot->srna) {
1632 ui_item_disabled(layout, opname);
1633 RNA_warning("%s '%s'", ot ? "operator missing srna" : "unknown operator", opname);
1634 return;
1635 }
1636
1639 /* so the context is passed to itemf functions (some need it) */
1641 PropertyRNA *prop = RNA_struct_find_property(&ptr, propname);
1642
1643 /* don't let bad properties slip through */
1644 BLI_assert((prop == nullptr) || (RNA_property_type(prop) == PROP_ENUM));
1645
1646 uiBlock *block = layout->root->block;
1647 if (prop && RNA_property_type(prop) == PROP_ENUM) {
1648 const EnumPropertyItem *item_array = nullptr;
1649 int totitem;
1650 bool free;
1651
1652 if (ui_layout_is_radial(layout)) {
1653 /* XXX: While "_all()" guarantees spatial stability,
1654 * it's bad when an enum has > 8 items total,
1655 * but only a small subset will ever be shown at once
1656 * (e.g. Mode Switch menu, after the introduction of GP editing modes).
1657 */
1658#if 0
1660 static_cast<bContext *>(block->evil_C), &ptr, prop, &item_array, &totitem, &free);
1661#else
1663 static_cast<bContext *>(block->evil_C), &ptr, prop, &item_array, &totitem, &free);
1664#endif
1665 }
1666 else {
1667 bContext *C = static_cast<bContext *>(block->evil_C);
1668 const bContextStore *previous_ctx = CTX_store_get(C);
1669 CTX_store_set(C, layout->context);
1670 RNA_property_enum_items_gettexted(C, &ptr, prop, &item_array, &totitem, &free);
1671 CTX_store_set(C, previous_ctx);
1672 }
1673
1674 /* add items */
1676 layout, ot, ptr, prop, properties, context, flag, item_array, totitem, active);
1677
1678 if (free) {
1679 MEM_freeN((void *)item_array);
1680 }
1681 }
1682 else if (prop && RNA_property_type(prop) != PROP_ENUM) {
1683 RNA_warning("%s.%s, not an enum type", RNA_struct_identifier(ptr.type), propname);
1684 return;
1685 }
1686 else {
1687 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname);
1688 return;
1689 }
1690}
1691
1692void uiItemsEnumO(uiLayout *layout, const char *opname, const char *propname)
1693{
1694 uiItemsFullEnumO(layout, opname, propname, nullptr, layout->root->opcontext, UI_ITEM_NONE);
1695}
1696
1698 const char *name,
1699 int icon,
1700 const char *opname,
1701 const char *propname,
1702 int value)
1703{
1704 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1705 UI_OPERATOR_ERROR_RET(ot, opname, return);
1706
1709
1710 /* enum lookup */
1711 PropertyRNA *prop = RNA_struct_find_property(&ptr, propname);
1712 if (prop == nullptr) {
1713 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname);
1714 return;
1715 }
1716
1717 RNA_property_enum_set(&ptr, prop, value);
1718
1719 /* same as uiItemEnumO */
1720 if (!name) {
1721 name = ui_menu_enumpropname(layout, &ptr, prop, value);
1722 }
1723
1724 uiItemFullO_ptr(layout,
1725 ot,
1726 name,
1727 icon,
1728 static_cast<IDProperty *>(ptr.data),
1729 layout->root->opcontext,
1731 nullptr);
1732}
1733
1735 const char *name,
1736 int icon,
1737 const char *opname,
1738 const char *propname,
1739 const char *value_str)
1740{
1741 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1742 UI_OPERATOR_ERROR_RET(ot, opname, return);
1743
1746
1747 PropertyRNA *prop = RNA_struct_find_property(&ptr, propname);
1748 if (prop == nullptr) {
1749 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname);
1750 return;
1751 }
1752
1753 /* enum lookup */
1754 /* no need for translations here */
1755 const EnumPropertyItem *item;
1756 bool free;
1758 static_cast<bContext *>(layout->root->block->evil_C), &ptr, prop, &item, nullptr, &free);
1759
1760 int value;
1761 if (item == nullptr || RNA_enum_value_from_id(item, value_str, &value) == 0) {
1762 if (free) {
1763 MEM_freeN((void *)item);
1764 }
1765 RNA_warning("%s.%s, enum %s not found", RNA_struct_identifier(ptr.type), propname, value_str);
1766 return;
1767 }
1768
1769 if (free) {
1770 MEM_freeN((void *)item);
1771 }
1772
1773 RNA_property_enum_set(&ptr, prop, value);
1774
1775 /* same as uiItemEnumO */
1776 if (!name) {
1777 name = ui_menu_enumpropname(layout, &ptr, prop, value);
1778 }
1779
1780 uiItemFullO_ptr(layout,
1781 ot,
1782 name,
1783 icon,
1784 static_cast<IDProperty *>(ptr.data),
1785 layout->root->opcontext,
1787 nullptr);
1788}
1789
1791 const char *name,
1792 int icon,
1793 const char *opname,
1794 const char *propname,
1795 int value)
1796{
1797 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1798 UI_OPERATOR_ERROR_RET(ot, opname, return);
1799
1802 RNA_boolean_set(&ptr, propname, value);
1803
1804 uiItemFullO_ptr(layout,
1805 ot,
1806 name,
1807 icon,
1808 static_cast<IDProperty *>(ptr.data),
1809 layout->root->opcontext,
1811 nullptr);
1812}
1813
1815 const char *name,
1816 int icon,
1817 const char *opname,
1818 const char *propname,
1819 int value)
1820{
1821 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1822 UI_OPERATOR_ERROR_RET(ot, opname, return);
1823
1826 RNA_int_set(&ptr, propname, value);
1827
1828 uiItemFullO_ptr(layout,
1829 ot,
1830 name,
1831 icon,
1832 static_cast<IDProperty *>(ptr.data),
1833 layout->root->opcontext,
1835 nullptr);
1836}
1837
1839 const char *name,
1840 int icon,
1841 const char *opname,
1842 const char *propname,
1843 float value)
1844{
1845 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1846
1847 UI_OPERATOR_ERROR_RET(ot, opname, return);
1848
1851 RNA_float_set(&ptr, propname, value);
1852
1853 uiItemFullO_ptr(layout,
1854 ot,
1855 name,
1856 icon,
1857 static_cast<IDProperty *>(ptr.data),
1858 layout->root->opcontext,
1860 nullptr);
1861}
1862
1864 const char *name,
1865 int icon,
1866 const char *opname,
1867 const char *propname,
1868 const char *value)
1869{
1870 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
1871
1872 UI_OPERATOR_ERROR_RET(ot, opname, return);
1873
1876 RNA_string_set(&ptr, propname, value);
1877
1878 uiItemFullO_ptr(layout,
1879 ot,
1880 name,
1881 icon,
1882 static_cast<IDProperty *>(ptr.data),
1883 layout->root->opcontext,
1885 nullptr);
1886}
1887
1888void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname)
1889{
1890 uiItemFullO(layout, opname, name, icon, nullptr, layout->root->opcontext, UI_ITEM_NONE, nullptr);
1891}
1892
1893/* RNA property items */
1894
1895static void ui_item_rna_size(uiLayout *layout,
1896 const char *name,
1897 int icon,
1898 PointerRNA *ptr,
1899 PropertyRNA *prop,
1900 int index,
1901 bool icon_only,
1902 bool compact,
1903 int *r_w,
1904 int *r_h)
1905{
1906 int w = 0, h;
1907
1908 /* arbitrary extended width by type */
1909 const PropertyType type = RNA_property_type(prop);
1910 const PropertySubType subtype = RNA_property_subtype(prop);
1911 const int len = RNA_property_array_length(ptr, prop);
1912
1913 bool is_checkbox_only = false;
1914 if (!name[0] && !icon_only) {
1915 if (ELEM(type, PROP_STRING, PROP_POINTER)) {
1916 name = "non-empty text";
1917 }
1918 else if (type == PROP_BOOLEAN) {
1919 if (icon == ICON_NONE) {
1920 /* Exception for check-boxes, they need a little less space to align nicely. */
1921 is_checkbox_only = true;
1922 }
1923 icon = ICON_DOT;
1924 }
1925 else if (type == PROP_ENUM) {
1926 /* Find the longest enum item name, instead of using a dummy text! */
1927 const EnumPropertyItem *item_array;
1928 bool free;
1930 ptr,
1931 prop,
1932 &item_array,
1933 nullptr,
1934 &free);
1935
1936 for (const EnumPropertyItem *item = item_array; item->identifier; item++) {
1937 if (item->identifier[0]) {
1938 w = max_ii(w, ui_text_icon_width(layout, item->name, item->icon, compact));
1939 }
1940 }
1941 if (free) {
1942 MEM_freeN((void *)item_array);
1943 }
1944 }
1945 }
1946
1947 if (!w) {
1948 if (type == PROP_ENUM && icon_only) {
1949 w = ui_text_icon_width(layout, "", ICON_BLANK1, compact);
1950 if (index != RNA_ENUM_VALUE) {
1951 w += 0.6f * UI_UNIT_X;
1952 }
1953 }
1954 else {
1955 /* not compact for float/int buttons, looks too squashed */
1957 layout, name, icon, ELEM(type, PROP_FLOAT, PROP_INT) ? false : compact);
1958 }
1959 }
1960 h = UI_UNIT_Y;
1961
1962 /* increase height for arrays */
1963 if (index == RNA_NO_INDEX && len > 0) {
1964 if (!name[0] && icon == ICON_NONE) {
1965 h = 0;
1966 }
1967 if (layout->flag & UI_ITEM_PROP_SEP) {
1968 h = 0;
1969 }
1970 if (ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) {
1971 h += 2 * UI_UNIT_Y;
1972 }
1973 else if (subtype == PROP_MATRIX) {
1974 h += ceilf(sqrtf(len)) * UI_UNIT_Y;
1975 }
1976 else {
1977 h += len * UI_UNIT_Y;
1978 }
1979 }
1980
1981 /* Increase width requirement if in a variable size layout. */
1982 if (ui_layout_variable_size(layout)) {
1983 if (type == PROP_BOOLEAN && name[0]) {
1984 w += UI_UNIT_X / 5;
1985 }
1986 else if (is_checkbox_only) {
1987 w -= UI_UNIT_X / 4;
1988 }
1989 else if (type == PROP_ENUM && !icon_only) {
1990 w += UI_UNIT_X / 4;
1991 }
1992 else if (ELEM(type, PROP_FLOAT, PROP_INT)) {
1993 w += UI_UNIT_X * 3;
1994 }
1995 }
1996
1997 *r_w = w;
1998 *r_h = h;
1999}
2000
2001static bool ui_item_rna_is_expand(PropertyRNA *prop, int index, const eUI_Item_Flag item_flag)
2002{
2003 const bool is_array = RNA_property_array_check(prop);
2004 const int subtype = RNA_property_subtype(prop);
2005 return is_array && (index == RNA_NO_INDEX) &&
2006 ((item_flag & UI_ITEM_R_EXPAND) ||
2008}
2009
2018{
2019 for (uiLayout *parent = cur_layout; parent; parent = parent->parent) {
2020 if (parent->heading[0]) {
2021 return parent;
2022 }
2023 }
2024
2025 return nullptr;
2026}
2027
2029 uiLayout *heading_layout,
2030 bool right_align,
2031 bool respect_prop_split)
2032{
2033 const int prev_alignment = layout->alignment;
2034
2035 if (right_align) {
2037 }
2038
2039 if (respect_prop_split) {
2040 uiItemL_respect_property_split(layout, heading_layout->heading, ICON_NONE);
2041 }
2042 else {
2043 uiItemL(layout, heading_layout->heading, ICON_NONE);
2044 }
2045 /* After adding the heading label, we have to mark it somehow as added, so it's not added again
2046 * for other items in this layout. For now just clear it. */
2047 heading_layout->heading[0] = '\0';
2048
2049 layout->alignment = prev_alignment;
2050}
2051
2057static uiLayout *ui_item_prop_split_layout_hack(uiLayout *layout_parent, uiLayout *layout_split)
2058{
2059 /* Tag item as using property split layout, this is inherited to children so they can get special
2060 * treatment if needed. */
2061 layout_parent->flag |= UI_ITEM_INSIDE_PROP_SEP;
2062
2063 if (layout_parent->type == ITEM_LAYOUT_ROW) {
2064 /* Prevent further splits within the row. */
2065 uiLayoutSetPropSep(layout_parent, false);
2066
2067 layout_parent->child_items_layout = uiLayoutRow(layout_split, true);
2068 return layout_parent->child_items_layout;
2069 }
2070 return layout_split;
2071}
2072
2074 PointerRNA *ptr,
2075 PropertyRNA *prop,
2076 int index,
2077 int value,
2079 const char *name,
2080 int icon,
2081 const char *placeholder)
2082{
2083 uiBlock *block = layout->root->block;
2084 char namestr[UI_MAX_NAME_STR];
2085 const bool use_prop_sep = ((layout->flag & UI_ITEM_PROP_SEP) != 0);
2086 const bool inside_prop_sep = ((layout->flag & UI_ITEM_INSIDE_PROP_SEP) != 0);
2087 /* Columns can define a heading to insert. If the first item added to a split layout doesn't have
2088 * a label to display in the first column, the heading is inserted there. Otherwise it's inserted
2089 * as a new row before the first item. */
2090 uiLayout *heading_layout = ui_layout_heading_find(layout);
2091 /* Although check-boxes use the split layout, they are an exception and should only place their
2092 * label in the second column, to not make that almost empty.
2093 *
2094 * Keep using 'use_prop_sep' instead of disabling it entirely because
2095 * we need the ability to have decorators still. */
2096 bool use_prop_sep_split_label = use_prop_sep;
2097 bool use_split_empty_name = (flag & UI_ITEM_R_SPLIT_EMPTY_NAME);
2098
2099#ifdef UI_PROP_DECORATE
2100 struct DecorateInfo {
2101 bool use_prop_decorate;
2102 int len;
2103 uiLayout *layout;
2104 uiBut *but;
2105 };
2106 DecorateInfo ui_decorate{};
2107 ui_decorate.use_prop_decorate = (((layout->flag & UI_ITEM_PROP_DECORATE) != 0) && use_prop_sep);
2108
2109#endif /* UI_PROP_DECORATE */
2110
2111 UI_block_layout_set_current(block, layout);
2113
2114 /* retrieve info */
2115 const PropertyType type = RNA_property_type(prop);
2116 const bool is_array = RNA_property_array_check(prop);
2117 const int len = (is_array) ? RNA_property_array_length(ptr, prop) : 0;
2118 const bool is_id_name_prop = (ptr->owner_id == ptr->data && type == PROP_STRING &&
2120
2121 const bool icon_only = (flag & UI_ITEM_R_ICON_ONLY) != 0;
2122
2123 /* Boolean with -1 to signify that the value depends on the presence of an icon. */
2124 const int toggle = ((flag & UI_ITEM_R_TOGGLE) ? 1 : ((flag & UI_ITEM_R_ICON_NEVER) ? 0 : -1));
2125 const bool no_icon = (toggle == 0);
2126
2127 /* set name and icon */
2128 if (!name) {
2129 if (!icon_only) {
2130 name = RNA_property_ui_name(prop);
2131 }
2132 else {
2133 name = "";
2134 }
2135 }
2136
2137 if (type != PROP_BOOLEAN) {
2138 flag &= ~UI_ITEM_R_CHECKBOX_INVERT;
2139 }
2140
2141 if (flag & UI_ITEM_R_ICON_ONLY) {
2142 /* pass */
2143 }
2144 else if (ELEM(type, PROP_INT, PROP_FLOAT, PROP_STRING, PROP_POINTER)) {
2145 if (use_prop_sep == false) {
2146 name = ui_item_name_add_colon(name, namestr);
2147 }
2148 }
2149 else if (type == PROP_BOOLEAN && is_array && index == RNA_NO_INDEX) {
2150 if (use_prop_sep == false) {
2151 name = ui_item_name_add_colon(name, namestr);
2152 }
2153 }
2154 else if (type == PROP_ENUM && index != RNA_ENUM_VALUE) {
2155 if (flag & UI_ITEM_R_COMPACT) {
2156 name = "";
2157 }
2158 else {
2159 if (use_prop_sep == false) {
2160 name = ui_item_name_add_colon(name, namestr);
2161 }
2162 }
2163 }
2164
2165 if (no_icon == false) {
2166 if (icon == ICON_NONE) {
2167 icon = RNA_property_ui_icon(prop);
2168 }
2169
2170 /* Menus and pie-menus don't show checkbox without this. */
2171 if ((layout->root->type == UI_LAYOUT_MENU) ||
2172 /* Use check-boxes only as a fallback in pie-menu's, when no icon is defined. */
2173 ((layout->root->type == UI_LAYOUT_PIEMENU) && (icon == ICON_NONE)))
2174 {
2175 const int prop_flag = RNA_property_flag(prop);
2176 if (type == PROP_BOOLEAN) {
2177 if ((is_array == false) || (index != RNA_NO_INDEX)) {
2178 if (prop_flag & PROP_ICONS_CONSECUTIVE) {
2179 icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
2180 }
2181 else if (is_array) {
2182 icon = RNA_property_boolean_get_index(ptr, prop, index) ? ICON_CHECKBOX_HLT :
2183 ICON_CHECKBOX_DEHLT;
2184 }
2185 else {
2186 icon = RNA_property_boolean_get(ptr, prop) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
2187 }
2188 }
2189 }
2190 else if (type == PROP_ENUM) {
2191 if (index == RNA_ENUM_VALUE) {
2192 const int enum_value = RNA_property_enum_get(ptr, prop);
2193 if (prop_flag & PROP_ICONS_CONSECUTIVE) {
2194 icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
2195 }
2196 else if (prop_flag & PROP_ENUM_FLAG) {
2197 icon = (enum_value & value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
2198 }
2199 else {
2200 /* Only a single value can be chosen, so display as radio buttons. */
2201 icon = (enum_value == value) ? ICON_RADIOBUT_ON : ICON_RADIOBUT_OFF;
2202 }
2203 }
2204 }
2205 }
2206 }
2207
2208#ifdef UI_PROP_SEP_ICON_WIDTH_EXCEPTION
2209 if (use_prop_sep) {
2210 if (type == PROP_BOOLEAN && (icon == ICON_NONE) && !icon_only) {
2211 use_prop_sep_split_label = false;
2212 /* For check-boxes we make an exception: We allow showing them in a split row even without
2213 * label. It typically relates to its neighbor items, so no need for an extra label. */
2214 use_split_empty_name = true;
2215 }
2216 }
2217#endif
2218
2219 if ((type == PROP_ENUM) && (RNA_property_flag(prop) & PROP_ENUM_FLAG)) {
2221 }
2222
2223 const bool slider = (flag & UI_ITEM_R_SLIDER) != 0;
2224 const bool expand = (flag & UI_ITEM_R_EXPAND) != 0;
2225 const bool no_bg = (flag & UI_ITEM_R_NO_BG) != 0;
2226 const bool compact = (flag & UI_ITEM_R_COMPACT) != 0;
2227
2228 /* get size */
2229 int w, h;
2230 ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, compact, &w, &h);
2231
2232 const eUIEmbossType prev_emboss = layout->emboss;
2233 if (no_bg) {
2235 }
2236
2237 uiBut *but = nullptr;
2238
2239 /* Split the label / property. */
2240 uiLayout *layout_parent = layout;
2241
2242 if (use_prop_sep) {
2243 uiLayout *layout_row = nullptr;
2244#ifdef UI_PROP_DECORATE
2245 if (ui_decorate.use_prop_decorate) {
2246 layout_row = uiLayoutRow(layout, true);
2247 layout_row->space = 0;
2248 ui_decorate.len = max_ii(1, len);
2249 }
2250#endif /* UI_PROP_DECORATE */
2251
2252 if ((name[0] == '\0') && !use_split_empty_name) {
2253 /* Ensure we get a column when text is not set. */
2254 layout = uiLayoutColumn(layout_row ? layout_row : layout, true);
2255 layout->space = 0;
2256 if (heading_layout) {
2257 ui_layout_heading_label_add(layout, heading_layout, false, false);
2258 }
2259 }
2260 else {
2261 uiLayout *layout_split = uiLayoutSplit(
2262 layout_row ? layout_row : layout, UI_ITEM_PROP_SEP_DIVIDE, true);
2263 bool label_added = false;
2264 uiLayout *layout_sub = uiLayoutColumn(layout_split, true);
2265 layout_sub->space = 0;
2266
2267 if (!use_prop_sep_split_label) {
2268 /* Pass */
2269 }
2270 else if (ui_item_rna_is_expand(prop, index, flag)) {
2271 char name_with_suffix[UI_MAX_DRAW_STR + 2];
2272 char str[2] = {'\0'};
2273 for (int a = 0; a < len; a++) {
2274 str[0] = RNA_property_array_item_char(prop, a);
2275 const bool use_prefix = (a == 0 && name[0]);
2276 if (use_prefix) {
2277 char *s = name_with_suffix;
2278 s += STRNCPY_RLEN(name_with_suffix, name);
2279 *s++ = ' ';
2280 *s++ = str[0];
2281 *s++ = '\0';
2282 }
2283 but = uiDefBut(block,
2285 0,
2286 use_prefix ? name_with_suffix : str,
2287 0,
2288 0,
2289 w,
2290 UI_UNIT_Y,
2291 nullptr,
2292 0.0,
2293 0.0,
2294 "");
2296 but->drawflag &= ~UI_BUT_TEXT_LEFT;
2297
2298 label_added = true;
2299 }
2300 }
2301 else {
2302 but = uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
2304 but->drawflag &= ~UI_BUT_TEXT_LEFT;
2305
2306 label_added = true;
2307 }
2308
2309 if (!label_added && heading_layout) {
2310 ui_layout_heading_label_add(layout_sub, heading_layout, true, false);
2311 }
2312
2313 layout_split = ui_item_prop_split_layout_hack(layout_parent, layout_split);
2314
2315 /* Watch out! We can only write into the new layout now. */
2316 if ((type == PROP_ENUM) && (flag & UI_ITEM_R_EXPAND)) {
2317 /* Expanded enums each have their own name. */
2318
2319 /* Often expanded enum's are better arranged into a row,
2320 * so check the existing layout. */
2322 layout = uiLayoutRow(layout_split, true);
2323 }
2324 else {
2325 layout = uiLayoutColumn(layout_split, true);
2326 }
2327 }
2328 else {
2329 if (use_prop_sep_split_label) {
2330 name = "";
2331 }
2332 layout = uiLayoutColumn(layout_split, true);
2333 }
2334 layout->space = 0;
2335 }
2336
2337#ifdef UI_PROP_DECORATE
2338 if (ui_decorate.use_prop_decorate) {
2339 ui_decorate.layout = uiLayoutColumn(layout_row, true);
2340 ui_decorate.layout->space = 0;
2341 UI_block_layout_set_current(block, layout);
2342 ui_decorate.but = static_cast<uiBut *>(block->buttons.last);
2343
2344 /* Clear after. */
2346 }
2347#endif /* UI_PROP_DECORATE */
2348 }
2349 /* End split. */
2350 else if (heading_layout) {
2351 /* Could not add heading to split layout, fallback to inserting it to the layout with the
2352 * heading itself. */
2353 ui_layout_heading_label_add(heading_layout, heading_layout, false, false);
2354 }
2355
2356 /* array property */
2357 if (index == RNA_NO_INDEX && is_array) {
2358 if (inside_prop_sep) {
2359 /* Within a split row, add array items to a column so they match the column layout of
2360 * previous items (e.g. transform vector with lock icon for each item). */
2361 layout = uiLayoutColumn(layout, true);
2362 }
2363
2364 ui_item_array(layout,
2365 block,
2366 name,
2367 icon,
2368 ptr,
2369 prop,
2370 len,
2371 0,
2372 0,
2373 w,
2374 h,
2375 expand,
2376 slider,
2377 toggle,
2378 icon_only,
2379 compact,
2380 !use_prop_sep_split_label);
2381 }
2382 /* enum item */
2383 else if (type == PROP_ENUM && index == RNA_ENUM_VALUE) {
2384 if (icon && name[0] && !icon_only) {
2386 block, UI_BTYPE_ROW, 0, icon, name, 0, 0, w, h, ptr, prop, -1, 0, value, nullptr);
2387 }
2388 else if (icon) {
2390 block, UI_BTYPE_ROW, 0, icon, 0, 0, w, h, ptr, prop, -1, 0, value, nullptr);
2391 }
2392 else {
2393 uiDefButR_prop(block, UI_BTYPE_ROW, 0, name, 0, 0, w, h, ptr, prop, -1, 0, value, nullptr);
2394 }
2395 }
2396 /* expanded enum */
2397 else if (type == PROP_ENUM && expand) {
2398 ui_item_enum_expand(layout, block, ptr, prop, name, h, icon_only);
2399 }
2400 /* property with separate label */
2401 else if (ELEM(type, PROP_ENUM, PROP_STRING, PROP_POINTER)) {
2402 but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag);
2403
2404 if (is_id_name_prop) {
2405 Main *bmain = CTX_data_main(static_cast<bContext *>(block->evil_C));
2406 ID *id = ptr->owner_id;
2407 UI_but_func_rename_full_set(but, [bmain, id](const std::string &new_name) {
2408 ED_id_rename(*bmain, *id, new_name);
2410 });
2411 }
2412
2413 bool results_are_suggestions = false;
2414 if (type == PROP_STRING) {
2416 if (search_flag & PROP_STRING_SEARCH_SUGGESTION) {
2417 results_are_suggestions = true;
2418 }
2419 }
2420 but = ui_but_add_search(but, ptr, prop, nullptr, nullptr, results_are_suggestions);
2421
2422 if (layout->redalert) {
2424 }
2425
2426 if (layout->activate_init) {
2428 }
2429 }
2430 /* single button */
2431 else {
2432 but = uiDefAutoButR(block, ptr, prop, index, name, icon, 0, 0, w, h);
2433
2434 if (slider && but->type == UI_BTYPE_NUM) {
2435 uiButNumber *number_but = (uiButNumber *)but;
2436 const float step_size = number_but->step_size;
2437 const float precision = number_but->precision;
2439 uiButNumberSlider *slider_but = reinterpret_cast<uiButNumberSlider *>(but);
2440 slider_but->step_size = step_size;
2441 slider_but->precision = precision;
2442 }
2443
2445 if (ELEM(but->type,
2450 {
2452 }
2453 }
2454
2455 if ((toggle == 1) && but->type == UI_BTYPE_CHECKBOX) {
2456 but->type = UI_BTYPE_TOGGLE;
2457 }
2458
2459 if (layout->redalert) {
2461 }
2462
2463 if (layout->activate_init) {
2465 }
2466 }
2467
2468 /* The resulting button may have the icon set since boolean button drawing
2469 * is being 'helpful' and adding an icon for us.
2470 * In this case we want the ability not to have an icon.
2471 *
2472 * We could pass an argument not to set the icon to begin with however this is the one case
2473 * the functionality is needed. */
2474 if (but && no_icon) {
2475 if ((icon == ICON_NONE) && (but->icon != ICON_NONE)) {
2477 }
2478 }
2479
2480 /* Mark non-embossed text-fields inside a list-box. */
2481 if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == UI_BTYPE_TEXT) &&
2483 {
2485 }
2486
2487 if (but) {
2488 if (placeholder) {
2489 UI_but_placeholder_set(but, placeholder);
2490 }
2493 }
2494 }
2495
2496#ifdef UI_PROP_DECORATE
2497 if (ui_decorate.use_prop_decorate) {
2498 uiBut *but_decorate = ui_decorate.but ? ui_decorate.but->next :
2499 static_cast<uiBut *>(block->buttons.first);
2500 const bool use_blank_decorator = (flag & UI_ITEM_R_FORCE_BLANK_DECORATE);
2501 uiLayout *layout_col = uiLayoutColumn(ui_decorate.layout, false);
2502 layout_col->space = 0;
2503 layout_col->emboss = UI_EMBOSS_NONE;
2504
2505 int i;
2506 for (i = 0; i < ui_decorate.len && but_decorate; i++) {
2507 PointerRNA *ptr_dec = use_blank_decorator ? nullptr : &but_decorate->rnapoin;
2508 PropertyRNA *prop_dec = use_blank_decorator ? nullptr : but_decorate->rnaprop;
2509
2510 /* The icons are set in 'ui_but_anim_flag' */
2511 uiItemDecoratorR_prop(layout_col, ptr_dec, prop_dec, but_decorate->rnaindex);
2512 but = static_cast<uiBut *>(block->buttons.last);
2513
2514 /* Order the decorator after the button we decorate, this is used so we can always
2515 * do a quick lookup. */
2516 BLI_remlink(&block->buttons, but);
2517 BLI_insertlinkafter(&block->buttons, but_decorate, but);
2518 but_decorate = but->next;
2519 }
2520 BLI_assert(ELEM(i, 1, ui_decorate.len));
2521
2522 layout->flag &= ~UI_ITEM_PROP_DECORATE_NO_PAD;
2523 }
2524#endif /* UI_PROP_DECORATE */
2525
2526 if (no_bg) {
2527 layout->emboss = prev_emboss;
2528 }
2529
2530 /* ensure text isn't added to icon_only buttons */
2531 if (but && icon_only) {
2532 BLI_assert(but->str.empty());
2533 }
2534}
2535
2536void uiItemR(uiLayout *layout,
2537 PointerRNA *ptr,
2538 const char *propname,
2539 const eUI_Item_Flag flag,
2540 const char *name,
2541 int icon)
2542{
2543 PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
2544
2545 if (!prop) {
2546 ui_item_disabled(layout, propname);
2547 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
2548 return;
2549 }
2550
2551 uiItemFullR(layout, ptr, prop, RNA_NO_INDEX, 0, flag, name, icon);
2552}
2553
2555 PointerRNA *ptr,
2556 PropertyRNA *prop,
2557 int index,
2558 int value,
2559 const eUI_Item_Flag flag,
2560 const char *name,
2561 int icon,
2562 const char *panel_type)
2563{
2564 uiBlock *block = layout->root->block;
2565 uiBut *but = static_cast<uiBut *>(block->buttons.last);
2566 uiItemFullR(layout, ptr, prop, index, value, flag, name, icon);
2567 but = but->next;
2568 while (but) {
2569 if (but->rnaprop == prop && ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_COLOR)) {
2571 break;
2572 }
2573 but = but->next;
2574 }
2575 if (but == nullptr) {
2576 const char *propname = RNA_property_identifier(prop);
2577 ui_item_disabled(layout, panel_type);
2578 RNA_warning("property could not use a popover: %s.%s (%s)",
2580 propname,
2581 panel_type);
2582 }
2583}
2584
2586 PointerRNA *ptr,
2587 PropertyRNA *prop,
2588 int index,
2589 int value,
2590 const eUI_Item_Flag flag,
2591 const char *name,
2592 int icon,
2593 const char *menu_type)
2594{
2595 uiBlock *block = layout->root->block;
2596 uiBut *but = static_cast<uiBut *>(block->buttons.last);
2597 uiItemFullR(layout, ptr, prop, index, value, flag, name, icon);
2598 but = but->next;
2599 while (but) {
2600 if (but->rnaprop == prop && but->type == UI_BTYPE_MENU) {
2602 break;
2603 }
2604 but = but->next;
2605 }
2606 if (but == nullptr) {
2607 const char *propname = RNA_property_identifier(prop);
2608 ui_item_disabled(layout, menu_type);
2609 RNA_warning("property could not use a menu: %s.%s (%s)",
2611 propname,
2612 menu_type);
2613 }
2614}
2615
2617 uiLayout *layout, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int value)
2618{
2619 if (RNA_property_type(prop) != PROP_ENUM) {
2620 const char *propname = RNA_property_identifier(prop);
2621 ui_item_disabled(layout, propname);
2622 RNA_warning("property not an enum: %s.%s", RNA_struct_identifier(ptr->type), propname);
2623 return;
2624 }
2625
2626 uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, value, UI_ITEM_NONE, name, icon);
2627}
2628
2630 uiLayout *layout, const char *name, int icon, PointerRNA *ptr, const char *propname, int value)
2631{
2632 PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
2633
2634 if (prop == nullptr) {
2635 ui_item_disabled(layout, propname);
2636 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
2637 return;
2638 }
2639
2640 uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, value, UI_ITEM_NONE, name, icon);
2641}
2642
2644 PointerRNA *ptr,
2645 PropertyRNA *prop,
2646 const char *value,
2647 const char *name,
2648 int icon)
2649{
2650 if (UNLIKELY(RNA_property_type(prop) != PROP_ENUM)) {
2651 const char *propname = RNA_property_identifier(prop);
2652 ui_item_disabled(layout, propname);
2653 RNA_warning("not an enum property: %s.%s", RNA_struct_identifier(ptr->type), propname);
2654 return;
2655 }
2656
2657 const EnumPropertyItem *item;
2658 bool free;
2660 static_cast<bContext *>(layout->root->block->evil_C), ptr, prop, &item, nullptr, &free);
2661
2662 int ivalue;
2663 if (!RNA_enum_value_from_id(item, value, &ivalue)) {
2664 const char *propname = RNA_property_identifier(prop);
2665 if (free) {
2666 MEM_freeN((void *)item);
2667 }
2668 ui_item_disabled(layout, propname);
2669 RNA_warning("enum property value not found: %s", value);
2670 return;
2671 }
2672
2673 for (int a = 0; item[a].identifier; a++) {
2674 if (item[a].identifier[0] == '\0') {
2675 /* Skip enum item separators. */
2676 continue;
2677 }
2678 if (item[a].value == ivalue) {
2679 const char *item_name = name ?
2680 name :
2681 CTX_IFACE_(RNA_property_translation_context(prop), item[a].name);
2682 const eUI_Item_Flag flag = item_name[0] ? UI_ITEM_NONE : UI_ITEM_R_ICON_ONLY;
2683
2685 layout, ptr, prop, RNA_ENUM_VALUE, ivalue, flag, item_name, icon ? icon : item[a].icon);
2686 break;
2687 }
2688 }
2689
2690 if (free) {
2691 MEM_freeN((void *)item);
2692 }
2693}
2694
2696 PointerRNA *ptr,
2697 const char *propname,
2698 const char *value,
2699 const char *name,
2700 int icon)
2701{
2702 PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
2703 if (UNLIKELY(prop == nullptr)) {
2704 ui_item_disabled(layout, propname);
2705 RNA_warning("enum property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
2706 return;
2707 }
2708 uiItemEnumR_string_prop(layout, ptr, prop, value, name, icon);
2709}
2710
2711void uiItemsEnumR(uiLayout *layout, PointerRNA *ptr, const char *propname)
2712{
2713 uiBlock *block = layout->root->block;
2714
2715 PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
2716
2717 if (!prop) {
2718 ui_item_disabled(layout, propname);
2719 RNA_warning("enum property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
2720 return;
2721 }
2722
2723 if (RNA_property_type(prop) != PROP_ENUM) {
2724 RNA_warning("not an enum property: %s.%s", RNA_struct_identifier(ptr->type), propname);
2725 return;
2726 }
2727
2728 uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
2729 uiLayout *column = uiLayoutColumn(split, false);
2730
2731 int totitem;
2732 const EnumPropertyItem *item;
2733 bool free;
2735 static_cast<bContext *>(block->evil_C), ptr, prop, &item, &totitem, &free);
2736
2737 for (int i = 0; i < totitem; i++) {
2738 if (item[i].identifier[0]) {
2739 uiItemEnumR_prop(column, item[i].name, item[i].icon, ptr, prop, item[i].value);
2740 ui_but_tip_from_enum_item(static_cast<uiBut *>(block->buttons.last), &item[i]);
2741 }
2742 else {
2743 if (item[i].name) {
2744 if (i != 0) {
2745 column = uiLayoutColumn(split, false);
2746 }
2747
2748 uiItemL(column, item[i].name, ICON_NONE);
2749 uiBut *bt = static_cast<uiBut *>(block->buttons.last);
2751
2752 ui_but_tip_from_enum_item(bt, &item[i]);
2753 }
2754 else {
2755 uiItemS(column);
2756 }
2757 }
2758 }
2759
2760 if (free) {
2761 MEM_freeN((void *)item);
2762 }
2763}
2764
2765/* Pointer RNA button with search */
2766
2767static void search_id_collection(StructRNA *ptype, PointerRNA *r_ptr, PropertyRNA **r_prop)
2768{
2769 /* look for collection property in Main */
2770 /* NOTE: using global Main is OK-ish here, UI shall not access other Mains anyway. */
2772
2773 *r_prop = nullptr;
2774
2775 RNA_STRUCT_BEGIN (r_ptr, iprop) {
2776 /* if it's a collection and has same pointer type, we've got it */
2777 if (RNA_property_type(iprop) == PROP_COLLECTION) {
2778 StructRNA *srna = RNA_property_pointer_type(r_ptr, iprop);
2779
2780 if (ptype == srna) {
2781 *r_prop = iprop;
2782 break;
2783 }
2784 }
2785 }
2787}
2788
2790{
2791 uiRNACollectionSearch *coll_search = static_cast<uiRNACollectionSearch *>(ptr);
2792 UI_butstore_free(coll_search->butstore_block, coll_search->butstore);
2793 MEM_freeN(ptr);
2794}
2795
2797 PointerRNA *ptr,
2798 PropertyRNA *prop,
2799 PointerRNA *searchptr,
2800 PropertyRNA *searchprop,
2801 const bool results_are_suggestions)
2802{
2803 /* for ID's we do automatic lookup */
2804 bool has_search_fn = false;
2805
2806 PointerRNA sptr;
2807 if (!searchprop) {
2808 if (RNA_property_type(prop) == PROP_STRING) {
2809 has_search_fn = (RNA_property_string_search_flag(prop) != 0);
2810 }
2811 if (RNA_property_type(prop) == PROP_POINTER) {
2812 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
2813 search_id_collection(ptype, &sptr, &searchprop);
2814 searchptr = &sptr;
2815 }
2816 }
2817
2818 /* turn button into search button */
2819 if (has_search_fn || searchprop) {
2820 uiRNACollectionSearch *coll_search = static_cast<uiRNACollectionSearch *>(
2821 MEM_mallocN(sizeof(*coll_search), __func__));
2822 uiButSearch *search_but;
2823
2825 search_but = (uiButSearch *)but;
2826
2827 if (searchptr) {
2828 search_but->rnasearchpoin = *searchptr;
2829 search_but->rnasearchprop = searchprop;
2830 }
2831
2832 but->hardmax = std::max(but->hardmax, 256.0f);
2834 if (RNA_property_is_unlink(prop)) {
2835 but->flag |= UI_BUT_VALUE_CLEAR;
2836 }
2837
2838 coll_search->target_ptr = *ptr;
2839 coll_search->target_prop = prop;
2840
2841 if (searchptr) {
2842 coll_search->search_ptr = *searchptr;
2843 coll_search->search_prop = searchprop;
2844 }
2845 else {
2846 /* Rely on `has_search_fn`. */
2847 coll_search->search_ptr = PointerRNA_NULL;
2848 coll_search->search_prop = nullptr;
2849 }
2850
2851 coll_search->search_but = but;
2852 coll_search->butstore_block = but->block;
2853 coll_search->butstore = UI_butstore_create(coll_search->butstore_block);
2854 UI_butstore_register(coll_search->butstore, &coll_search->search_but);
2855
2856 if (RNA_property_type(prop) == PROP_ENUM) {
2857 /* XXX, this will have a menu string,
2858 * but in this case we just want the text */
2859 but->str.clear();
2860 }
2861
2862 UI_but_func_search_set_results_are_suggestions(but, results_are_suggestions);
2863
2867 coll_search,
2868 false,
2870 nullptr,
2871 nullptr);
2872 /* If this is called multiple times for the same button, an earlier call may have taken the
2873 * else branch below so the button was disabled. Now we have a searchprop, so it can be enabled
2874 * again. */
2875 but->flag &= ~UI_BUT_DISABLED;
2876 }
2877 else if (but->type == UI_BTYPE_SEARCH_MENU) {
2878 /* In case we fail to find proper searchprop,
2879 * so other code might have already set but->type to search menu... */
2880 but->flag |= UI_BUT_DISABLED;
2881 }
2882
2883 return but;
2884}
2885
2887 PointerRNA *ptr,
2888 PropertyRNA *prop,
2889 PointerRNA *searchptr,
2890 PropertyRNA *searchprop,
2891 const char *name,
2892 int icon,
2893 bool results_are_suggestions)
2894{
2895 const bool use_prop_sep = ((layout->flag & UI_ITEM_PROP_SEP) != 0);
2896
2898
2899 const PropertyType type = RNA_property_type(prop);
2900 if (!ELEM(type, PROP_POINTER, PROP_STRING, PROP_ENUM)) {
2901 RNA_warning("Property %s.%s must be a pointer, string or enum",
2904 return;
2905 }
2906 if (RNA_property_type(searchprop) != PROP_COLLECTION) {
2907 RNA_warning("search collection property is not a collection type: %s.%s",
2908 RNA_struct_identifier(searchptr->type),
2909 RNA_property_identifier(searchprop));
2910 return;
2911 }
2912
2913 /* get icon & name */
2914 if (icon == ICON_NONE) {
2915 const StructRNA *icontype;
2916 if (type == PROP_POINTER) {
2917 icontype = RNA_property_pointer_type(ptr, prop);
2918 }
2919 else {
2920 icontype = RNA_property_pointer_type(searchptr, searchprop);
2921 }
2922
2923 icon = RNA_struct_ui_icon(icontype);
2924 }
2925 if (!name) {
2926 name = RNA_property_ui_name(prop);
2927 }
2928
2929 char namestr[UI_MAX_NAME_STR];
2930 if (use_prop_sep == false) {
2931 name = ui_item_name_add_colon(name, namestr);
2932 }
2933
2934 /* create button */
2935 uiBlock *block = uiLayoutGetBlock(layout);
2936
2937 int w, h;
2938 ui_item_rna_size(layout, name, icon, ptr, prop, 0, false, false, &w, &h);
2939 w += UI_UNIT_X; /* X icon needs more space */
2940 uiBut *but = ui_item_with_label(layout, block, name, icon, ptr, prop, 0, 0, 0, w, h, 0);
2941
2942 but = ui_but_add_search(but, ptr, prop, searchptr, searchprop, results_are_suggestions);
2943}
2944
2946 PointerRNA *ptr,
2947 const char *propname,
2948 PointerRNA *searchptr,
2949 const char *searchpropname,
2950 const char *name,
2951 int icon)
2952{
2953 /* validate arguments */
2954 PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
2955 if (!prop) {
2956 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
2957 return;
2958 }
2959 PropertyRNA *searchprop = RNA_struct_find_property(searchptr, searchpropname);
2960 if (!searchprop) {
2961 RNA_warning("search collection property not found: %s.%s",
2962 RNA_struct_identifier(searchptr->type),
2963 searchpropname);
2964 return;
2965 }
2966
2967 uiItemPointerR_prop(layout, ptr, prop, searchptr, searchprop, name, icon, false);
2968}
2969
2970void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt)
2971{
2972 MenuType *mt = (MenuType *)arg_mt;
2973 UI_menutype_draw(C, mt, layout);
2974}
2975
2976void ui_item_paneltype_func(bContext *C, uiLayout *layout, void *arg_pt)
2977{
2978 PanelType *pt = (PanelType *)arg_pt;
2979 UI_paneltype_draw(C, pt, layout);
2980}
2981
2983 const char *name,
2984 int icon,
2985 uiMenuCreateFunc func,
2986 void *arg,
2987 void *argN,
2988 const char *tip,
2989 bool force_menu,
2990 uiButArgNFree func_argN_free_fn = MEM_freeN,
2991 uiButArgNCopy func_argN_copy_fn = MEM_dupallocN)
2992{
2993 uiBlock *block = layout->root->block;
2994 uiLayout *heading_layout = ui_layout_heading_find(layout);
2995
2996 UI_block_layout_set_current(block, layout);
2998
2999 if (!name) {
3000 name = "";
3001 }
3002 if (layout->root->type == UI_LAYOUT_MENU && !icon) {
3003 icon = ICON_BLANK1;
3004 }
3005
3007 if (layout->root->type == UI_LAYOUT_HEADER) { /* Ugly! */
3008 if (icon == ICON_NONE && force_menu) {
3009 /* pass */
3010 }
3011 else if (force_menu) {
3012 pad_factor.text = 1.85;
3013 pad_factor.icon_only = 0.6f;
3014 }
3015 else {
3016 pad_factor.text = 0.75f;
3017 }
3018 }
3019
3020 const int w = ui_text_icon_width_ex(layout, name, icon, pad_factor, UI_FSTYLE_WIDGET);
3021 const int h = UI_UNIT_Y;
3022
3023 if (heading_layout) {
3024 ui_layout_heading_label_add(layout, heading_layout, true, true);
3025 }
3026
3027 uiBut *but;
3028 if (name[0] && icon) {
3029 but = uiDefIconTextMenuBut(block, func, arg, icon, name, 0, 0, w, h, tip);
3030 }
3031 else if (icon) {
3032 but = uiDefIconMenuBut(block, func, arg, icon, 0, 0, w, h, tip);
3033 if (force_menu && name[0]) {
3035 }
3036 }
3037 else {
3038 but = uiDefMenuBut(block, func, arg, name, 0, 0, w, h, tip);
3039 }
3040
3041 if (argN) {
3042 /* ugly! */
3043 if (arg != argN) {
3044 but->poin = (char *)but;
3045 }
3046 but->func_argN = argN;
3047 but->func_argN_free_fn = func_argN_free_fn;
3048 but->func_argN_copy_fn = func_argN_copy_fn;
3049 }
3050
3052 /* We never want a drop-down in menu! */
3053 (force_menu && layout->root->type != UI_LAYOUT_MENU))
3054 {
3056 }
3057
3058 return but;
3059}
3060
3061void uiItemM_ptr(uiLayout *layout, MenuType *mt, const char *name, int icon)
3062{
3063 uiBlock *block = layout->root->block;
3064 bContext *C = static_cast<bContext *>(block->evil_C);
3065 if (WM_menutype_poll(C, mt) == false) {
3066 return;
3067 }
3068
3069 if (!name) {
3070 name = CTX_IFACE_(mt->translation_context, mt->label);
3071 }
3072
3073 if (layout->root->type == UI_LAYOUT_MENU && !icon) {
3074 icon = ICON_BLANK1;
3075 }
3076
3077 ui_item_menu(layout,
3078 name,
3079 icon,
3081 mt,
3082 nullptr,
3083 mt->description ? TIP_(mt->description) : "",
3084 false);
3085}
3086
3087void uiItemM(uiLayout *layout, const char *menuname, const char *name, int icon)
3088{
3089 MenuType *mt = WM_menutype_find(menuname, false);
3090 if (mt == nullptr) {
3091 RNA_warning("not found %s", menuname);
3092 return;
3093 }
3094 uiItemM_ptr(layout, mt, name, icon);
3095}
3096
3097void uiItemMContents(uiLayout *layout, const char *menuname)
3098{
3099 MenuType *mt = WM_menutype_find(menuname, false);
3100 if (mt == nullptr) {
3101 RNA_warning("not found %s", menuname);
3102 return;
3103 }
3104
3105 uiBlock *block = layout->root->block;
3106 bContext *C = static_cast<bContext *>(block->evil_C);
3107 if (WM_menutype_poll(C, mt) == false) {
3108 return;
3109 }
3110
3111 const bContextStore *previous_ctx = CTX_store_get(C);
3112 UI_menutype_draw(C, mt, layout);
3113
3114 /* Restore context that was cleared by `UI_menutype_draw`. */
3115 if (layout->context) {
3116 CTX_store_set(C, previous_ctx);
3117 }
3118}
3119
3121{
3122 uiBlock *block = layout->root->block;
3123
3124 UI_block_layout_set_current(block, layout);
3125 uiLayout *col = uiLayoutColumn(layout, false);
3126 col->space = 0;
3127 col->emboss = UI_EMBOSS_NONE;
3128
3129 if (ELEM(nullptr, ptr, prop) || !RNA_property_animateable(ptr, prop)) {
3130 uiBut *but = uiDefIconBut(block,
3132 0,
3133 ICON_BLANK1,
3134 0,
3135 0,
3136 UI_UNIT_X,
3137 UI_UNIT_Y,
3138 nullptr,
3139 0.0,
3140 0.0,
3141 "");
3142 but->flag |= UI_BUT_DISABLED;
3143 return;
3144 }
3145
3146 const bool is_expand = ui_item_rna_is_expand(prop, index, UI_ITEM_NONE);
3147 const bool is_array = RNA_property_array_check(prop);
3148
3149 /* Loop for the array-case, but only do in case of an expanded array. */
3150 for (int i = 0; i < (is_expand ? RNA_property_array_length(ptr, prop) : 1); i++) {
3153 0,
3154 ICON_DOT,
3155 0,
3156 0,
3157 UI_UNIT_X,
3158 UI_UNIT_Y,
3159 nullptr,
3160 0.0,
3161 0.0,
3162 TIP_("Animate property"));
3163
3164 UI_but_func_set(but, ui_but_anim_decorate_cb, but, nullptr);
3166 /* Decorators have their own RNA data, using the normal #uiBut RNA members has many
3167 * side-effects. */
3168 but->decorated_rnapoin = *ptr;
3169 but->decorated_rnaprop = prop;
3170 /* ui_def_but_rna() sets non-array buttons to have a RNA index of 0. */
3171 but->decorated_rnaindex = (!is_array || is_expand) ? i : index;
3172 }
3173}
3174
3175void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, int index)
3176{
3177 PropertyRNA *prop = nullptr;
3178
3179 if (ptr && propname) {
3180 /* validate arguments */
3181 prop = RNA_struct_find_property(ptr, propname);
3182 if (!prop) {
3183 ui_item_disabled(layout, propname);
3184 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
3185 return;
3186 }
3187 }
3188
3189 /* ptr and prop are allowed to be nullptr here. */
3190 uiItemDecoratorR_prop(layout, ptr, prop, index);
3191}
3192
3194 uiLayout *layout, const bContext *C, PanelType *pt, const char *name, int icon)
3195{
3196 if (!name) {
3197 name = CTX_IFACE_(pt->translation_context, pt->label);
3198 }
3199
3200 if (layout->root->type == UI_LAYOUT_MENU && !icon) {
3201 icon = ICON_BLANK1;
3202 }
3203
3204 const bContextStore *previous_ctx = CTX_store_get(C);
3205 /* Set context for polling (and panel header drawing). */
3206 CTX_store_set(const_cast<bContext *>(C), layout->context);
3207
3208 const bool ok = (pt->poll == nullptr) || pt->poll(C, pt);
3209 if (ok && (pt->draw_header != nullptr)) {
3210 layout = uiLayoutRow(layout, true);
3211 Panel panel{};
3212 Panel_Runtime panel_runtime{};
3213 panel.runtime = &panel_runtime;
3214 panel.type = pt;
3215 panel.layout = layout;
3216 panel.flag = PNL_POPOVER;
3217 pt->draw_header(C, &panel);
3218 }
3219
3220 CTX_store_set(const_cast<bContext *>(C), previous_ctx);
3221
3222 uiBut *but = ui_item_menu(
3223 layout, name, icon, ui_item_paneltype_func, pt, nullptr, TIP_(pt->description), true);
3224 but->type = UI_BTYPE_POPOVER;
3225 if (!ok) {
3226 but->flag |= UI_BUT_DISABLED;
3227 }
3228}
3229
3231 uiLayout *layout, const bContext *C, const char *panel_type, const char *name, int icon)
3232{
3233 PanelType *pt = WM_paneltype_find(panel_type, true);
3234 if (pt == nullptr) {
3235 RNA_warning("Panel type not found '%s'", panel_type);
3236 return;
3237 }
3238 uiItemPopoverPanel_ptr(layout, C, pt, name, icon);
3239}
3240
3242 bContext *C,
3243 int space_id,
3244 int region_id,
3245 const char *context,
3246 const char *category)
3247{
3248 SpaceType *st = BKE_spacetype_from_id(space_id);
3249 if (st == nullptr) {
3250 RNA_warning("space type not found %d", space_id);
3251 return;
3252 }
3253 ARegionType *art = BKE_regiontype_from_id(st, region_id);
3254 if (art == nullptr) {
3255 RNA_warning("region type not found %d", region_id);
3256 return;
3257 }
3258
3259 LISTBASE_FOREACH (PanelType *, pt, &art->paneltypes) {
3260 /* Causes too many panels, check context. */
3261 if (pt->parent_id[0] == '\0') {
3262 if (/* (*context == '\0') || */ STREQ(pt->context, context)) {
3263 if ((*category == '\0') || STREQ(pt->category, category)) {
3264 if (pt->poll == nullptr || pt->poll(C, pt)) {
3265 uiItemPopoverPanel_ptr(layout, C, pt, nullptr, ICON_NONE);
3266 }
3267 }
3268 }
3269 }
3270 }
3271}
3272
3273/* label item */
3274static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon)
3275{
3276 uiBlock *block = layout->root->block;
3277
3278 UI_block_layout_set_current(block, layout);
3280
3281 if (!name) {
3282 name = "";
3283 }
3284 if (layout->root->type == UI_LAYOUT_MENU && !icon) {
3285 icon = ICON_BLANK1;
3286 }
3287
3288 const int w = ui_text_icon_width_ex(layout, name, icon, ui_text_pad_none, UI_FSTYLE_WIDGET);
3289 uiBut *but;
3290 if (icon && name[0]) {
3291 but = uiDefIconTextBut(
3292 block, UI_BTYPE_LABEL, 0, icon, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, nullptr);
3293 }
3294 else if (icon) {
3295 but = uiDefIconBut(
3296 block, UI_BTYPE_LABEL, 0, icon, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, nullptr);
3297 }
3298 else {
3299 but = uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, nullptr);
3300 }
3301
3302 /* to compensate for string size padding in ui_text_icon_width,
3303 * make text aligned right if the layout is aligned right.
3304 */
3306 but->drawflag &= ~UI_BUT_TEXT_LEFT; /* default, needs to be unset */
3308 }
3309
3310 /* Mark as a label inside a list-box. */
3311 if (block->flag & UI_BLOCK_LIST_ITEM) {
3312 but->flag |= UI_BUT_LIST_ITEM;
3313 }
3314
3315 if (layout->redalert) {
3317 }
3318
3319 return but;
3320}
3321
3323 uiLayout *layout, const char *name, int icon, const bool highlight, const bool redalert)
3324{
3325 uiBut *but = uiItemL_(layout, name, icon);
3326
3327 if (highlight) {
3328 /* TODO: add another flag for this. */
3330 }
3331
3332 if (redalert) {
3334 }
3335
3336 return but;
3337}
3338
3339void uiItemL(uiLayout *layout, const char *name, int icon)
3340{
3341 uiItemL_(layout, name, icon);
3342}
3343
3345{
3346 uiPropertySplitWrapper split_wrapper = {nullptr};
3347
3348 uiLayout *layout_row = uiLayoutRow(parent_layout, true);
3349 uiLayout *layout_split = uiLayoutSplit(layout_row, UI_ITEM_PROP_SEP_DIVIDE, true);
3350
3351 split_wrapper.label_column = uiLayoutColumn(layout_split, true);
3353 split_wrapper.property_row = ui_item_prop_split_layout_hack(parent_layout, layout_split);
3354 split_wrapper.decorate_column = uiLayoutGetPropDecorate(parent_layout) ?
3355 uiLayoutColumn(layout_row, true) :
3356 nullptr;
3357
3358 return split_wrapper;
3359}
3360
3361uiLayout *uiItemL_respect_property_split(uiLayout *layout, const char *text, int icon)
3362{
3363 if (layout->flag & UI_ITEM_PROP_SEP) {
3364 uiBlock *block = uiLayoutGetBlock(layout);
3365 const uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout);
3366 /* Further items added to 'layout' will automatically be added to split_wrapper.property_row */
3367
3368 uiItemL_(split_wrapper.label_column, text, icon);
3369 UI_block_layout_set_current(block, split_wrapper.property_row);
3370
3371 return split_wrapper.decorate_column;
3372 }
3373
3374 char namestr[UI_MAX_NAME_STR];
3375 if (text) {
3376 text = ui_item_name_add_colon(text, namestr);
3377 }
3378 uiItemL_(layout, text, icon);
3379
3380 return nullptr;
3381}
3382
3383void uiItemLDrag(uiLayout *layout, PointerRNA *ptr, const char *name, int icon)
3384{
3385 uiBut *but = uiItemL_(layout, name, icon);
3386
3387 if (ptr && ptr->type) {
3388 if (RNA_struct_is_ID(ptr->type)) {
3390 }
3391 }
3392}
3393
3394void uiItemS_ex(uiLayout *layout, float factor, const LayoutSeparatorType type)
3395{
3396 uiBlock *block = layout->root->block;
3397 const bool is_menu = ui_block_is_menu(block);
3398 const bool is_pie = ui_block_is_pie_menu(block);
3399 if (is_menu && !UI_block_can_add_separator(block)) {
3400 return;
3401 }
3402
3403 int space = (is_menu) ? int(0.35f * UI_UNIT_X) : int(0.3f * UI_UNIT_X);
3404 space *= factor;
3405
3406 eButType but_type;
3407
3408 switch (type) {
3410 but_type = UI_BTYPE_SEPR_LINE;
3411 break;
3413 but_type = (is_menu && !is_pie) ? UI_BTYPE_SEPR_LINE : UI_BTYPE_SEPR;
3414 break;
3415 default:
3416 but_type = UI_BTYPE_SEPR;
3417 }
3418
3419 bool is_vertical_bar = (layout->w == 0) && but_type == UI_BTYPE_SEPR_LINE;
3420
3421 UI_block_layout_set_current(block, layout);
3422 uiBut *but = uiDefBut(block,
3423 but_type,
3424 0,
3425 "",
3426 0,
3427 0,
3428 space,
3429 is_vertical_bar ? UI_UNIT_Y : space,
3430 nullptr,
3431 0.0,
3432 0.0,
3433 "");
3434
3435 if (but_type == UI_BTYPE_SEPR_LINE) {
3436 uiButSeparatorLine *but_line = static_cast<uiButSeparatorLine *>(but);
3437 but_line->is_vertical = is_vertical_bar;
3438 }
3439}
3440
3441void uiItemS(uiLayout *layout)
3442{
3443 uiItemS_ex(layout, 1.0f);
3444}
3445
3447 const char *text,
3448 const float factor,
3449 const eButProgressType progress_type)
3450{
3451 const bool has_text = text && text[0];
3452 uiBlock *block = layout->root->block;
3453 short width;
3454
3455 if (progress_type == UI_BUT_PROGRESS_TYPE_BAR) {
3456 width = UI_UNIT_X * 5;
3457 }
3458 else if (has_text) {
3459 width = UI_UNIT_X * 8;
3460 }
3461 else {
3462 width = UI_UNIT_X;
3463 }
3464
3465 UI_block_layout_set_current(block, layout);
3466 uiBut *but = uiDefBut(block,
3468 0,
3469 (text) ? text : "",
3470 0,
3471 0,
3472 width,
3473 short(UI_UNIT_Y),
3474 nullptr,
3475 0.0,
3476 0.0,
3477 "");
3478
3479 if (has_text && (progress_type == UI_BUT_PROGRESS_TYPE_RING)) {
3480 /* For progress bar, centered is okay, left aligned for ring/pie. */
3481 but->drawflag |= UI_BUT_TEXT_LEFT;
3482 }
3483
3484 uiButProgress *progress_bar = static_cast<uiButProgress *>(but);
3486 progress_bar->progress_factor = factor;
3487}
3488
3490{
3491 uiBlock *block = layout->root->block;
3492 const bool is_popup = ui_block_is_popup_any(block);
3493
3494 if (is_popup) {
3495 printf("Error: separator_spacer() not supported in popups.\n");
3496 return;
3497 }
3498
3499 if (block->direction & UI_DIR_RIGHT) {
3500 printf("Error: separator_spacer() only supported in horizontal blocks.\n");
3501 return;
3502 }
3503
3504 UI_block_layout_set_current(block, layout);
3505 uiDefBut(block,
3507 0,
3508 "",
3509 0,
3510 0,
3511 0.3f * UI_UNIT_X,
3512 UI_UNIT_Y,
3513 nullptr,
3514 0.0,
3515 0.0,
3516 "");
3517}
3518
3519void uiItemMenuF(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg)
3520{
3521 if (!func) {
3522 return;
3523 }
3524
3525 ui_item_menu(layout, name, icon, func, arg, nullptr, "", false);
3526}
3527
3528void uiItemMenuFN(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *argN)
3529{
3530 if (!func) {
3531 return;
3532 }
3533
3534 /* Second 'argN' only ensures it gets freed. */
3535 ui_item_menu(layout, name, icon, func, argN, argN, "", false);
3536}
3537
3540 /* don't use pointers to the strings because python can dynamically
3541 * allocate strings and free before the menu draws, see #27304. */
3545};
3546
3547/* Obtain the active menu item based on the calling button's text. */
3549{
3551
3552 if (!ot) {
3553 return -1;
3554 }
3555
3557 const EnumPropertyItem *item_array = nullptr;
3558 bool free;
3559 int totitem;
3561 /* so the context is passed to itemf functions (some need it) */
3564 if (!prop) {
3565 return -1;
3566 }
3567 RNA_property_enum_items_gettexted(C, &ptr, prop, &item_array, &totitem, &free);
3568 int active = RNA_enum_from_name(item_array, but->str.c_str());
3569 if (free) {
3570 MEM_freeN((void *)item_array);
3571 }
3572
3573 return active;
3574}
3575
3576static void menu_item_enum_opname_menu(bContext *C, uiLayout *layout, void *arg)
3577{
3578 uiBut *but = static_cast<uiBut *>(arg);
3579 MenuItemLevel *lvl = static_cast<MenuItemLevel *>(but->func_argN);
3580 /* Use the operator properties from the button owning the menu. */
3581 IDProperty *op_props = but->opptr ? static_cast<IDProperty *>(but->opptr->data) : nullptr;
3582
3583 /* The calling but's str _probably_ contains the active
3584 * menu item name, set in uiItemMenuEnumFullO_ptr. */
3585 const int active = menu_item_enum_opname_menu_active(C, but, lvl);
3586
3589 layout, lvl->opname, lvl->propname, op_props, lvl->opcontext, UI_ITEM_NONE, active);
3590
3591 /* override default, needed since this was assumed pre 2.70 */
3593}
3594
3596 const bContext *C,
3598 const char *propname,
3599 const char *name,
3600 int icon,
3601 PointerRNA *r_opptr)
3602{
3603 /* Caller must check */
3604 BLI_assert(ot->srna != nullptr);
3605
3606 std::string operator_name;
3607 if (name == nullptr) {
3608 operator_name = WM_operatortype_name(ot, nullptr);
3609 name = operator_name.c_str();
3610 }
3611
3612 if (layout->root->type == UI_LAYOUT_MENU && !icon) {
3613 icon = ICON_BLANK1;
3614 }
3615
3616 MenuItemLevel *lvl = MEM_new<MenuItemLevel>("MenuItemLevel");
3617 STRNCPY(lvl->opname, ot->idname);
3618 STRNCPY(lvl->propname, propname);
3619 lvl->opcontext = layout->root->opcontext;
3620
3621 uiBut *but = ui_item_menu(layout,
3622 name,
3623 icon,
3625 nullptr,
3626 lvl,
3627 nullptr,
3628 true,
3631 /* Use the menu button as owner for the operator properties, which will then be passed to the
3632 * individual menu items. */
3633 if (r_opptr) {
3634 but->opptr = MEM_new<PointerRNA>("uiButOpPtr");
3636 BLI_assert(but->opptr->data == nullptr);
3638 *r_opptr = *but->opptr;
3639 }
3640
3641 /* add hotkey here, lower UI code can't detect it */
3642 if ((layout->root->block->flag & UI_BLOCK_LOOP) && (ot->prop && ot->invoke)) {
3643 if (std::optional<std::string> shortcut_str = WM_key_event_operator_string(
3644 C, ot->idname, layout->root->opcontext, nullptr, false))
3645 {
3646 ui_but_add_shortcut(but, shortcut_str->c_str(), false);
3647 }
3648 }
3649}
3650
3652 const bContext *C,
3653 const char *opname,
3654 const char *propname,
3655 const char *name,
3656 int icon,
3657 PointerRNA *r_opptr)
3658{
3659 wmOperatorType *ot = WM_operatortype_find(opname, false); /* print error next */
3660
3661 UI_OPERATOR_ERROR_RET(ot, opname, return);
3662
3663 if (!ot->srna) {
3664 ui_item_disabled(layout, opname);
3665 RNA_warning("operator missing srna '%s'", opname);
3666 return;
3667 }
3668
3669 uiItemMenuEnumFullO_ptr(layout, C, ot, propname, name, icon, r_opptr);
3670}
3671
3673 const bContext *C,
3674 const char *opname,
3675 const char *propname,
3676 const char *name,
3677 int icon)
3678{
3679 uiItemMenuEnumFullO(layout, C, opname, propname, name, icon, nullptr);
3680}
3681
3682static void menu_item_enum_rna_menu(bContext * /*C*/, uiLayout *layout, void *arg)
3683{
3684 MenuItemLevel *lvl = (MenuItemLevel *)(((uiBut *)arg)->func_argN);
3685
3687 uiItemsEnumR(layout, &lvl->rnapoin, lvl->propname);
3688}
3689
3691 uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, const char *name, int icon)
3692{
3693 if (!name) {
3694 name = RNA_property_ui_name(prop);
3695 }
3696 if (layout->root->type == UI_LAYOUT_MENU && !icon) {
3697 icon = ICON_BLANK1;
3698 }
3699
3700 MenuItemLevel *lvl = MEM_new<MenuItemLevel>("MenuItemLevel");
3701 lvl->rnapoin = *ptr;
3703 lvl->opcontext = layout->root->opcontext;
3704
3705 ui_item_menu(layout,
3706 name,
3707 icon,
3709 nullptr,
3710 lvl,
3712 false,
3715}
3716
3718 uiLayout *layout, PointerRNA *ptr, const char *propname, const char *name, int icon)
3719{
3720 PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
3721 if (!prop) {
3722 ui_item_disabled(layout, propname);
3723 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
3724 return;
3725 }
3726
3727 uiItemMenuEnumR_prop(layout, ptr, prop, name, icon);
3728}
3729
3731 bContext *C,
3732 PointerRNA *ptr,
3733 PropertyRNA *prop,
3734 PointerRNA *ptr_highlight,
3735 PropertyRNA *prop_highlight,
3736 bool icon_only)
3737{
3738 uiBlock *block = layout->root->block;
3739
3740 UI_block_layout_set_current(block, layout);
3742 layout, C, block, ptr, prop, ptr_highlight, prop_highlight, nullptr, UI_UNIT_Y, icon_only);
3743}
3744
3747/* -------------------------------------------------------------------- */
3751/* single-row layout */
3753{
3754 int itemw, itemh;
3755 bool min_size_flag = true;
3756
3757 litem->w = 0;
3758 litem->h = 0;
3759
3760 if (litem->items.is_empty()) {
3761 return;
3762 }
3763
3764 const uiItem *item_last = litem->items.last();
3765 for (uiItem *item : litem->items) {
3766 const bool is_item_last = (item == item_last);
3767 ui_item_size(item, &itemw, &itemh);
3768
3769 min_size_flag = min_size_flag && (item->flag & UI_ITEM_FIXED_SIZE);
3770
3771 litem->w += itemw;
3772 litem->h = std::max(itemh, litem->h);
3773
3774 if (!is_item_last) {
3775 litem->w += litem->space;
3776 }
3777 }
3778
3779 if (min_size_flag) {
3780 litem->flag |= UI_ITEM_FIXED_SIZE;
3781 }
3782}
3783
3784static int ui_litem_min_width(int itemw)
3785{
3786 return std::min(2 * UI_UNIT_X, itemw);
3787}
3788
3790{
3791 if (litem->items.is_empty()) {
3792 return;
3793 }
3794
3795 int last_free_item_idx = -1;
3796 int x, neww, newtotw, itemw, minw, itemh, offset;
3797 int freew, fixedx, freex, flag = 0, lastw = 0;
3798 float extra_pixel;
3799
3800 // x = litem->x; /* UNUSED */
3801 const int y = litem->y;
3802 int w = litem->w;
3803 int totw = 0;
3804 int tot = 0;
3805
3806 for (uiItem *item : litem->items) {
3807 ui_item_size(item, &itemw, &itemh);
3808 totw += itemw;
3809 tot++;
3810 }
3811
3812 if (totw == 0) {
3813 return;
3814 }
3815
3816 if (w != 0) {
3817 w -= (tot - 1) * litem->space;
3818 }
3819 int fixedw = 0;
3820
3821 const uiItem *item_last = litem->items.last();
3822
3823 /* keep clamping items to fixed minimum size until all are done */
3824 do {
3825 freew = 0;
3826 x = 0;
3827 flag = 0;
3828 newtotw = totw;
3829 extra_pixel = 0.0f;
3830
3831 for (uiItem *item : litem->items) {
3832 if (item->flag & UI_ITEM_AUTO_FIXED_SIZE) {
3833 continue;
3834 }
3835 const bool is_item_last = (item == item_last);
3836
3837 ui_item_size(item, &itemw, &itemh);
3838 minw = ui_litem_min_width(itemw);
3839
3840 if (w - lastw > 0) {
3841 neww = ui_item_fit(
3842 itemw, x, totw, w - lastw, is_item_last, litem->alignment, &extra_pixel);
3843 }
3844 else {
3845 neww = 0; /* no space left, all will need clamping to minimum size */
3846 }
3847
3848 x += neww;
3849
3850 bool min_flag = item->flag & UI_ITEM_FIXED_SIZE;
3851 /* ignore min flag for rows with right or center alignment */
3852 if (item->type != ITEM_BUTTON &&
3853 ELEM((static_cast<uiLayout *>(item))->alignment,
3857 {
3858 min_flag = false;
3859 }
3860
3861 if ((neww < minw || min_flag) && w != 0) {
3862 /* fixed size */
3863 item->flag |= UI_ITEM_AUTO_FIXED_SIZE;
3864 if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_FIXED_SIZE) {
3865 minw = itemw;
3866 }
3867 fixedw += minw;
3868 flag = 1;
3869 newtotw -= itemw;
3870 }
3871 else {
3872 /* keep free size */
3873 item->flag &= ~UI_ITEM_AUTO_FIXED_SIZE;
3874 freew += itemw;
3875 }
3876 }
3877
3878 totw = newtotw;
3879 lastw = fixedw;
3880 } while (flag);
3881
3882 freex = 0;
3883 fixedx = 0;
3884 extra_pixel = 0.0f;
3885 x = litem->x;
3886
3887 int item_idx = -1;
3888 for (uiItem *item : litem->items) {
3889 item_idx++;
3890 const bool is_item_last = (item == item_last);
3891 ui_item_size(item, &itemw, &itemh);
3892 minw = ui_litem_min_width(itemw);
3893
3894 if (item->flag & UI_ITEM_AUTO_FIXED_SIZE) {
3895 /* fixed minimum size items */
3896 if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_FIXED_SIZE) {
3897 minw = itemw;
3898 }
3899 itemw = ui_item_fit(
3900 minw, fixedx, fixedw, min_ii(w, fixedw), is_item_last, litem->alignment, &extra_pixel);
3901 fixedx += itemw;
3902 }
3903 else {
3904 /* free size item */
3905 itemw = ui_item_fit(
3906 itemw, freex, freew, w - fixedw, is_item_last, litem->alignment, &extra_pixel);
3907 freex += itemw;
3908 last_free_item_idx = item_idx;
3909 }
3910
3911 /* align right/center */
3912 offset = 0;
3913 if (litem->alignment == UI_LAYOUT_ALIGN_RIGHT) {
3914 if (freew + fixedw > 0 && freew + fixedw < w) {
3915 offset = w - (fixedw + freew);
3916 }
3917 }
3918 else if (litem->alignment == UI_LAYOUT_ALIGN_CENTER) {
3919 if (freew + fixedw > 0 && freew + fixedw < w) {
3920 offset = (w - (fixedw + freew)) / 2;
3921 }
3922 }
3923
3924 /* position item */
3925 ui_item_position(item, x + offset, y - itemh, itemw, itemh);
3926
3927 x += itemw;
3928 if (!is_item_last) {
3929 x += litem->space;
3930 }
3931 }
3932
3933 /* add extra pixel */
3934 int extra_pixel_move = litem->w - (x - litem->x);
3935 if (extra_pixel_move > 0 && litem->alignment == UI_LAYOUT_ALIGN_EXPAND &&
3936 last_free_item_idx >= 0 && item_last && item_last->flag & UI_ITEM_AUTO_FIXED_SIZE)
3937 {
3938 ui_item_move(litem->items[last_free_item_idx], 0, extra_pixel_move);
3939 blender::MutableSpan<uiItem *> items_after_last_free =
3940 litem->items.as_mutable_span().drop_front(last_free_item_idx + 1);
3941 for (uiItem *item : items_after_last_free) {
3942 ui_item_move(item, extra_pixel_move, extra_pixel_move);
3943 }
3944 }
3945
3946 litem->w = x - litem->x;
3947 litem->h = litem->y - y;
3948 litem->x = x;
3949 litem->y = y;
3950}
3951
3952static int spaces_after_column_item(const uiLayout *litem,
3953 const uiItem *item,
3954 const uiItem *next_item,
3955 const bool is_box)
3956{
3957 if (next_item == nullptr) {
3958 return 0;
3959 }
3960 if (item->type == ITEM_LAYOUT_PANEL_HEADER && next_item->type == ITEM_LAYOUT_PANEL_HEADER) {
3961 /* No extra space between layout panel headers. */
3962 return 0;
3963 }
3964 if (item->type == ITEM_LAYOUT_PANEL_BODY &&
3966 {
3967 /* One for the end of the panel and one at the start of the parent panel. */
3968 return 2;
3969 }
3970 if (!is_box) {
3971 return 1;
3972 }
3973 if (item != litem->items.first()) {
3974 return 1;
3975 }
3976 return 0;
3977}
3978
3979/* single-column layout */
3980static void ui_litem_estimate_column(uiLayout *litem, bool is_box)
3981{
3982 int itemw, itemh;
3983 bool min_size_flag = true;
3984
3985 litem->w = 0;
3986 litem->h = 0;
3987
3988 for (auto iter = litem->items.begin(); iter != litem->items.end(); iter++) {
3989 uiItem *item = *iter;
3990 ui_item_size(item, &itemw, &itemh);
3991
3992 min_size_flag = min_size_flag && (item->flag & UI_ITEM_FIXED_SIZE);
3993
3994 litem->w = std::max(litem->w, itemw);
3995 litem->h += itemh;
3996
3997 const uiItem *next_item = (item == litem->items.last()) ? nullptr : *(iter + 1);
3998 const int spaces_num = spaces_after_column_item(litem, item, next_item, is_box);
3999 litem->h += spaces_num * litem->space;
4000 }
4001
4002 if (min_size_flag) {
4003 litem->flag |= UI_ITEM_FIXED_SIZE;
4004 }
4005}
4006
4007static void ui_litem_layout_column(uiLayout *litem, bool is_box, bool is_menu)
4008{
4009 const int x = litem->x;
4010 int y = litem->y;
4011
4012 for (auto iter = litem->items.begin(); iter != litem->items.end(); iter++) {
4013 uiItem *item = *iter;
4014 int itemw, itemh;
4015 ui_item_size(item, &itemw, &itemh);
4016
4017 y -= itemh;
4018 ui_item_position(item, x, y, is_menu ? itemw : litem->w, itemh);
4019
4020 const uiItem *next_item = (item == litem->items.last()) ? nullptr : *(iter + 1);
4021 const int spaces_num = spaces_after_column_item(litem, item, next_item, is_box);
4022 y -= spaces_num * litem->space;
4023
4024 if (is_box) {
4025 item->flag |= UI_ITEM_BOX_ITEM;
4026 }
4027 }
4028
4029 litem->h = litem->y - y;
4030 litem->x = x;
4031 litem->y = y;
4032}
4033
4034/* calculates the angle of a specified button in a radial menu,
4035 * stores a float vector in unit circle */
4036static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum)
4037{
4038 if (itemnum >= PIE_MAX_ITEMS) {
4039 itemnum %= PIE_MAX_ITEMS;
4040 printf("Warning: Pie menus with more than %i items are currently unsupported\n",
4042 }
4043
4045 ui_but_pie_dir(dir, vec);
4046
4047 return dir;
4048}
4049
4051{
4052
4053 if ((item->type == ITEM_BUTTON) &&
4054 ((static_cast<uiButtonItem *>(item))->but->type == UI_BTYPE_LABEL))
4055 {
4056 return false;
4057 }
4058
4059 return true;
4060}
4061
4063{
4064
4066 return false;
4067 }
4068
4069 return true;
4070}
4071
4073{
4074 int itemh, itemw;
4075 int itemnum = 0;
4076
4077 /* For the radial layout we will use Matt Ebb's design
4078 * for radiation, see http://mattebb.com/weblog/radiation/
4079 * also the old code at #5103. */
4080
4081 const int pie_radius = U.pie_menu_radius * UI_SCALE_FAC;
4082
4083 const int x = litem->x;
4084 const int y = litem->y;
4085
4086 int minx = x, miny = y, maxx = x, maxy = y;
4087
4088 litem->root->block->pie_data.pie_dir_mask = 0;
4089
4090 for (uiItem *item : litem->items) {
4091 /* Not all button types are drawn in a radial menu, do filtering here. */
4092 if (!ui_item_is_radial_displayable(item)) {
4093 continue;
4094 }
4095
4096 float vec[2];
4097 const RadialDirection dir = ui_get_radialbut_vec(vec, itemnum);
4098 const float factor[2] = {
4099 (vec[0] > 0.01f) ? 0.0f : ((vec[0] < -0.01f) ? -1.0f : -0.5f),
4100 (vec[1] > 0.99f) ? 0.0f : ((vec[1] < -0.99f) ? -1.0f : -0.5f),
4101 };
4102 itemnum++;
4103
4104 /* Enable for non-buttons because a direction may reference a layout, see: #112610. */
4105 bool use_dir = true;
4106
4107 if (item->type == ITEM_BUTTON) {
4108 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
4109
4110 bitem->but->pie_dir = dir;
4111 /* Scale the buttons. */
4112 bitem->but->rect.ymax *= 1.5f;
4113 /* Add a little bit more here to include number. */
4114 bitem->but->rect.xmax += 1.5f * UI_UNIT_X;
4115 /* Enable drawing as pie item if supported by widget. */
4116 if (ui_item_is_radial_drawable(bitem)) {
4117 bitem->but->emboss = UI_EMBOSS_PIE_MENU;
4118 bitem->but->drawflag |= UI_BUT_ICON_LEFT;
4119 }
4120
4121 if (ELEM(bitem->but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
4122 use_dir = false;
4123 }
4124 }
4125
4126 if (use_dir) {
4127 litem->root->block->pie_data.pie_dir_mask |= 1 << int(dir);
4128 }
4129
4130 ui_item_size(item, &itemw, &itemh);
4131
4132 ui_item_position(item,
4133 x + (vec[0] * pie_radius) + (factor[0] * itemw),
4134 y + (vec[1] * pie_radius) + (factor[1] * itemh),
4135 itemw,
4136 itemh);
4137
4138 minx = min_ii(minx, x + (vec[0] * pie_radius) - (itemw / 2));
4139 maxx = max_ii(maxx, x + (vec[0] * pie_radius) + (itemw / 2));
4140 miny = min_ii(miny, y + (vec[1] * pie_radius) - (itemh / 2));
4141 maxy = max_ii(maxy, y + (vec[1] * pie_radius) + (itemh / 2));
4142 }
4143
4144 litem->x = minx;
4145 litem->y = miny;
4146 litem->w = maxx - minx;
4147 litem->h = maxy - miny;
4148}
4149
4150/* root layout */
4151static void ui_litem_estimate_root(uiLayout * /*litem*/)
4152{
4153 /* nothing to do */
4154}
4155
4157{
4158 /* first item is pie menu title, align on center of menu */
4159 uiItem *item = litem->items.first();
4160
4161 if (item->type == ITEM_BUTTON) {
4162 int itemh, itemw, x, y;
4163 x = litem->x;
4164 y = litem->y;
4165
4166 ui_item_size(item, &itemw, &itemh);
4167
4169 item, x - itemw / 2, y + UI_SCALE_FAC * (U.pie_menu_threshold + 9.0f), itemw, itemh);
4170 }
4171}
4172
4174{
4175 if (litem->root->type == UI_LAYOUT_HEADER) {
4176 ui_litem_layout_row(litem);
4177 }
4178 else if (litem->root->type == UI_LAYOUT_PIEMENU) {
4180 }
4181 else if (litem->root->type == UI_LAYOUT_MENU) {
4182 ui_litem_layout_column(litem, false, true);
4183 }
4184 else {
4185 ui_litem_layout_column(litem, false, false);
4186 }
4187}
4188
4189/* panel header layout */
4191{
4192 BLI_assert(litem->items.size() == 1);
4193 uiItem *item = litem->items.first();
4194
4195 int w, h;
4196 ui_item_size(item, &w, &h);
4197 litem->w = w;
4198 litem->h = h;
4199}
4200
4202{
4203 uiLayoutItemPanelHeader *header_litem = static_cast<uiLayoutItemPanelHeader *>(litem);
4204 Panel *panel = litem->root->block->panel;
4205
4206 BLI_assert(litem->items.size() == 1);
4207 uiItem *item = litem->items.first();
4208
4209 int w, h;
4210 ui_item_size(item, &w, &h);
4211 litem->y -= h;
4212 ui_item_position(item, litem->x, litem->y, litem->w, h);
4213 const float offset = UI_style_get_dpi()->panelspace;
4214 panel->runtime->layout_panels.headers.append({float(litem->y) - offset,
4215 float(litem->y + litem->h) - offset,
4216 header_litem->open_prop_owner,
4217 header_litem->open_prop_name});
4218}
4219
4220/* panel body layout */
4222{
4223 ui_litem_estimate_column(litem, false);
4224}
4225
4227{
4228 Panel *panel = litem->root->block->panel;
4229 ui_litem_layout_column(litem, false, false);
4230 const float offset = UI_style_get_dpi()->panelspace;
4232 float(litem->y - litem->space) - offset,
4233 float(litem->y + litem->h + litem->space) - offset,
4234 });
4235}
4236
4237/* box layout */
4239{
4240 const uiStyle *style = litem->root->style;
4241
4242 ui_litem_estimate_column(litem, true);
4243
4244 int boxspace = style->boxspace;
4245 if (litem->root->type == UI_LAYOUT_HEADER) {
4246 boxspace = 0;
4247 }
4248 litem->w += 2 * boxspace;
4249 litem->h += 2 * boxspace;
4250}
4251
4253{
4254 uiLayoutItemBx *box = static_cast<uiLayoutItemBx *>(litem);
4255 const uiStyle *style = litem->root->style;
4256
4257 int boxspace = style->boxspace;
4258 if (litem->root->type == UI_LAYOUT_HEADER) {
4259 boxspace = 0;
4260 }
4261
4262 const int w = litem->w;
4263 const int h = litem->h;
4264
4265 litem->x += boxspace;
4266 litem->y -= boxspace;
4267
4268 if (w != 0) {
4269 litem->w -= 2 * boxspace;
4270 }
4271 if (h != 0) {
4272 litem->h -= 2 * boxspace;
4273 }
4274
4275 ui_litem_layout_column(litem, true, false);
4276
4277 litem->x -= boxspace;
4278 litem->y -= boxspace;
4279
4280 if (w != 0) {
4281 litem->w += 2 * boxspace;
4282 }
4283 if (h != 0) {
4284 litem->h += 2 * boxspace;
4285 }
4286
4287 /* roundbox around the sublayout */
4288 uiBut *but = box->roundbox;
4289 but->rect.xmin = litem->x;
4290 but->rect.ymin = litem->y;
4291 but->rect.xmax = litem->x + litem->w;
4292 but->rect.ymax = litem->y + litem->h;
4293}
4294
4295/* multi-column layout, automatically flowing to the next */
4297{
4298 const uiStyle *style = litem->root->style;
4299 uiLayoutItemFlow *flow = static_cast<uiLayoutItemFlow *>(litem);
4300 int itemw, itemh, maxw = 0;
4301
4302 /* compute max needed width and total height */
4303 int toth = 0;
4304 int totitem = 0;
4305 for (uiItem *item : litem->items) {
4306 ui_item_size(item, &itemw, &itemh);
4307 maxw = std::max(maxw, itemw);
4308 toth += itemh;
4309 totitem++;
4310 }
4311
4312 if (flow->number <= 0) {
4313 /* auto compute number of columns, not very good */
4314 if (maxw == 0) {
4315 flow->totcol = 1;
4316 return;
4317 }
4318
4319 flow->totcol = max_ii(litem->root->emw / maxw, 1);
4320 flow->totcol = min_ii(flow->totcol, totitem);
4321 }
4322 else {
4323 flow->totcol = flow->number;
4324 }
4325
4326 /* compute sizes */
4327 int x = 0;
4328 int y = 0;
4329 int emy = 0;
4330 int miny = 0;
4331
4332 maxw = 0;
4333 const int emh = toth / flow->totcol;
4334
4335 /* create column per column */
4336 int col = 0;
4337 for (uiItem *item : litem->items) {
4338 ui_item_size(item, &itemw, &itemh);
4339
4340 y -= itemh + style->buttonspacey;
4341 miny = min_ii(miny, y);
4342 emy -= itemh;
4343 maxw = max_ii(itemw, maxw);
4344
4345 /* decide to go to next one */
4346 if (col < flow->totcol - 1 && emy <= -emh) {
4347 x += maxw + litem->space;
4348 maxw = 0;
4349 y = 0;
4350 emy = 0; /* need to reset height again for next column */
4351 col++;
4352 }
4353 }
4354
4355 litem->w = x;
4356 litem->h = litem->y - miny;
4357}
4358
4360{
4361 const uiStyle *style = litem->root->style;
4362 uiLayoutItemFlow *flow = static_cast<uiLayoutItemFlow *>(litem);
4363 int col, emh, itemw, itemh;
4364
4365 /* compute max needed width and total height */
4366 int toth = 0;
4367 for (uiItem *item : litem->items) {
4368 ui_item_size(item, &itemw, &itemh);
4369 toth += itemh;
4370 }
4371
4372 /* compute sizes */
4373 int x = litem->x;
4374 int y = litem->y;
4375 int emy = 0;
4376 int miny = 0;
4377
4378 emh = toth / flow->totcol;
4379
4380 /* create column per column */
4381 col = 0;
4382 int w = (litem->w - (flow->totcol - 1) * style->columnspace) / flow->totcol;
4383 for (uiItem *item : litem->items) {
4384 ui_item_size(item, &itemw, &itemh);
4385
4386 itemw = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? w : min_ii(w, itemw);
4387
4388 y -= itemh;
4389 emy -= itemh;
4390 ui_item_position(item, x, y, itemw, itemh);
4391 y -= style->buttonspacey;
4392 miny = min_ii(miny, y);
4393
4394 /* decide to go to next one */
4395 if (col < flow->totcol - 1 && emy <= -emh) {
4396 x += w + style->columnspace;
4397 y = litem->y;
4398 emy = 0; /* need to reset height again for next column */
4399 col++;
4400
4401 const int remaining_width = litem->w - (x - litem->x);
4402 const int remaining_width_between_columns = (flow->totcol - col - 1) * style->columnspace;
4403 const int remaining_columns = flow->totcol - col;
4404 w = (remaining_width - remaining_width_between_columns) / remaining_columns;
4405 }
4406 }
4407
4408 litem->h = litem->y - miny;
4409 litem->x = x;
4410 litem->y = miny;
4411}
4412
4413/* multi-column and multi-row layout. */
4415 /* General layout control settings. */
4416 bool row_major : 1; /* Fill rows before columns */
4417 bool even_columns : 1; /* All columns will have same width. */
4418 bool even_rows : 1; /* All rows will have same height. */
4419 int space_x; /* Space between columns. */
4420 int space_y; /* Space between rows. */
4421 /* Real data about current position and size of this layout item
4422 * (either estimated, or final values). */
4423 int litem_w; /* Layout item width. */
4424 int litem_x; /* Layout item X position. */
4425 int litem_y; /* Layout item Y position. */
4426 /* Actual number of columns and rows to generate (computed from first pass usually). */
4427 int tot_columns; /* Number of columns. */
4428 int tot_rows; /* Number of rows. */
4429};
4430
4432 int *tot_items; /* Total number of items in this grid layout. */
4433 /* Width / X pos data. */
4434 float *global_avg_w; /* Computed average width of the columns. */
4435 int *cos_x_array; /* Computed X coordinate of each column. */
4436 int *widths_array; /* Computed width of each column. */
4437 int *tot_w; /* Computed total width. */
4438 /* Height / Y pos data. */
4439 int *global_max_h; /* Computed height of the tallest item in the grid. */
4440 int *cos_y_array; /* Computed Y coordinate of each column. */
4441 int *heights_array; /* Computed height of each column. */
4442 int *tot_h; /* Computed total height. */
4443};
4444
4447 UILayoutGridFlowOutput *results)
4448{
4449 float tot_w = 0.0f, tot_h = 0.0f;
4450 float global_avg_w = 0.0f, global_totweight_w = 0.0f;
4451 int global_max_h = 0;
4452
4453 BLI_assert(parameters->tot_columns != 0 ||
4454 (results->cos_x_array == nullptr && results->widths_array == nullptr &&
4455 results->tot_w == nullptr));
4456 BLI_assert(parameters->tot_rows != 0 ||
4457 (results->cos_y_array == nullptr && results->heights_array == nullptr &&
4458 results->tot_h == nullptr));
4459
4460 if (results->tot_items) {
4461 *results->tot_items = 0;
4462 }
4463
4464 if (items.is_empty()) {
4465 if (results->global_avg_w) {
4466 *results->global_avg_w = 0.0f;
4467 }
4468 if (results->global_max_h) {
4469 *results->global_max_h = 0;
4470 }
4471 return;
4472 }
4473
4474 blender::Array<float, 64> avg_w(parameters->tot_columns, 0.0f);
4475 blender::Array<float, 64> totweight_w(parameters->tot_columns, 0.0f);
4476 blender::Array<int, 64> max_h(parameters->tot_rows, 0);
4477
4478 int i = 0;
4479 for (const uiItem *item : items) {
4480 int item_w, item_h;
4481 ui_item_size(item, &item_w, &item_h);
4482
4483 global_avg_w += float(item_w * item_w);
4484 global_totweight_w += float(item_w);
4485 global_max_h = max_ii(global_max_h, item_h);
4486
4487 if (parameters->tot_rows != 0 && parameters->tot_columns != 0) {
4488 const int index_col = parameters->row_major ? i % parameters->tot_columns :
4489 i / parameters->tot_rows;
4490 const int index_row = parameters->row_major ? i / parameters->tot_columns :
4491 i % parameters->tot_rows;
4492
4493 avg_w[index_col] += float(item_w * item_w);
4494 totweight_w[index_col] += float(item_w);
4495
4496 max_h[index_row] = max_ii(max_h[index_row], item_h);
4497 }
4498
4499 if (results->tot_items) {
4500 (*results->tot_items)++;
4501 }
4502 i++;
4503 }
4504
4505 /* Finalize computing of column average sizes */
4506 global_avg_w /= global_totweight_w;
4507 if (parameters->tot_columns != 0) {
4508 for (i = 0; i < parameters->tot_columns; i++) {
4509 avg_w[i] /= totweight_w[i];
4510 tot_w += avg_w[i];
4511 }
4512 if (parameters->even_columns) {
4513 tot_w = ceilf(global_avg_w) * parameters->tot_columns;
4514 }
4515 }
4516 /* Finalize computing of rows max sizes */
4517 if (parameters->tot_rows != 0) {
4518 for (i = 0; i < parameters->tot_rows; i++) {
4519 tot_h += max_h[i];
4520 }
4521 if (parameters->even_rows) {
4522 tot_h = global_max_h * parameters->tot_columns;
4523 }
4524 }
4525
4526 /* Compute positions and sizes of all cells. */
4527 if (results->cos_x_array != nullptr && results->widths_array != nullptr) {
4528 /* We enlarge/narrow columns evenly to match available width. */
4529 const float wfac = float(parameters->litem_w -
4530 (parameters->tot_columns - 1) * parameters->space_x) /
4531 tot_w;
4532
4533 for (int col = 0; col < parameters->tot_columns; col++) {
4534 results->cos_x_array[col] = (col ? results->cos_x_array[col - 1] +
4535 results->widths_array[col - 1] + parameters->space_x :
4536 parameters->litem_x);
4537 if (parameters->even_columns) {
4538 /* (< remaining width > - < space between remaining columns >) / < remaining columns > */
4539 results->widths_array[col] = (((parameters->litem_w -
4540 (results->cos_x_array[col] - parameters->litem_x)) -
4541 (parameters->tot_columns - col - 1) * parameters->space_x) /
4542 (parameters->tot_columns - col));
4543 }
4544 else if (col == parameters->tot_columns - 1) {
4545 /* Last column copes width rounding errors... */
4546 results->widths_array[col] = parameters->litem_w -
4547 (results->cos_x_array[col] - parameters->litem_x);
4548 }
4549 else {
4550 results->widths_array[col] = int(avg_w[col] * wfac);
4551 }
4552 }
4553 }
4554 if (results->cos_y_array != nullptr && results->heights_array != nullptr) {
4555 for (int row = 0; row < parameters->tot_rows; row++) {
4556 if (parameters->even_rows) {
4557 results->heights_array[row] = global_max_h;
4558 }
4559 else {
4560 results->heights_array[row] = max_h[row];
4561 }
4562 results->cos_y_array[row] = (row ? results->cos_y_array[row - 1] - parameters->space_y -
4563 results->heights_array[row] :
4564 parameters->litem_y - results->heights_array[row]);
4565 }
4566 }
4567
4568 if (results->global_avg_w) {
4569 *results->global_avg_w = global_avg_w;
4570 }
4571 if (results->global_max_h) {
4572 *results->global_max_h = global_max_h;
4573 }
4574 if (results->tot_w) {
4575 *results->tot_w = int(tot_w) + parameters->space_x * (parameters->tot_columns - 1);
4576 }
4577 if (results->tot_h) {
4578 *results->tot_h = tot_h + parameters->space_y * (parameters->tot_rows - 1);
4579 }
4580}
4581
4583{
4584 const uiStyle *style = litem->root->style;
4585 uiLayoutItemGridFlow *gflow = static_cast<uiLayoutItemGridFlow *>(litem);
4586
4587 const int space_x = style->columnspace;
4588 const int space_y = style->buttonspacey;
4589
4590 /* Estimate average needed width and height per item. */
4591 {
4592 float avg_w;
4593 int max_h;
4594
4595 UILayoutGridFlowInput input{};
4596 input.row_major = gflow->row_major;
4597 input.even_columns = gflow->even_columns;
4598 input.even_rows = gflow->even_rows;
4599 input.litem_w = litem->w;
4600 input.litem_x = litem->x;
4601 input.litem_y = litem->y;
4602 input.space_x = space_x;
4603 input.space_y = space_y;
4604 UILayoutGridFlowOutput output{};
4605 output.tot_items = &gflow->tot_items;
4606 output.global_avg_w = &avg_w;
4607 output.global_max_h = &max_h;
4608 ui_litem_grid_flow_compute(litem->items, &input, &output);
4609
4610 if (gflow->tot_items == 0) {
4611 litem->w = litem->h = 0;
4612 gflow->tot_columns = gflow->tot_rows = 0;
4613 return;
4614 }
4615
4616 /* Even in varying column width case,
4617 * we fix our columns number from weighted average width of items,
4618 * a proper solving of required width would be too costly,
4619 * and this should give reasonably good results in all reasonable cases. */
4620 if (gflow->columns_len > 0) {
4621 gflow->tot_columns = gflow->columns_len;
4622 }
4623 else {
4624 if (avg_w == 0.0f) {
4625 gflow->tot_columns = 1;
4626 }
4627 else {
4628 gflow->tot_columns = min_ii(max_ii(int(litem->w / avg_w), 1), gflow->tot_items);
4629 }
4630 }
4631 gflow->tot_rows = int(ceilf(float(gflow->tot_items) / gflow->tot_columns));
4632
4633 /* Try to tweak number of columns and rows to get better filling of last column or row,
4634 * and apply 'modulo' value to number of columns or rows.
4635 * Note that modulo does not prevent ending with fewer columns/rows than modulo, if mandatory
4636 * to avoid empty column/row. */
4637 {
4638 const int modulo = (gflow->columns_len < -1) ? -gflow->columns_len : 0;
4639 const int step = modulo ? modulo : 1;
4640
4641 if (gflow->row_major) {
4642 /* Adjust number of columns to be multiple of given modulo. */
4643 if (modulo && gflow->tot_columns % modulo != 0 && gflow->tot_columns > modulo) {
4644 gflow->tot_columns = gflow->tot_columns - (gflow->tot_columns % modulo);
4645 }
4646 /* Find smallest number of columns conserving computed optimal number of rows. */
4647 for (gflow->tot_rows = int(ceilf(float(gflow->tot_items) / gflow->tot_columns));
4648 (gflow->tot_columns - step) > 0 &&
4649 int(ceilf(float(gflow->tot_items) / (gflow->tot_columns - step))) <= gflow->tot_rows;
4650 gflow->tot_columns -= step)
4651 {
4652 /* pass */
4653 }
4654 }
4655 else {
4656 /* Adjust number of rows to be multiple of given modulo. */
4657 if (modulo && gflow->tot_rows % modulo != 0) {
4658 gflow->tot_rows = min_ii(gflow->tot_rows + modulo - (gflow->tot_rows % modulo),
4659 gflow->tot_items);
4660 }
4661 /* Find smallest number of rows conserving computed optimal number of columns. */
4662 for (gflow->tot_columns = int(ceilf(float(gflow->tot_items) / gflow->tot_rows));
4663 (gflow->tot_rows - step) > 0 &&
4664 int(ceilf(float(gflow->tot_items) / (gflow->tot_rows - step))) <= gflow->tot_columns;
4665 gflow->tot_rows -= step)
4666 {
4667 /* pass */
4668 }
4669 }
4670 }
4671
4672 /* Set evenly-spaced axes size
4673 * (quick optimization in case we have even columns and rows). */
4674 if (gflow->even_columns && gflow->even_rows) {
4675 litem->w = int(gflow->tot_columns * avg_w) + space_x * (gflow->tot_columns - 1);
4676 litem->h = int(gflow->tot_rows * max_h) + space_y * (gflow->tot_rows - 1);
4677 return;
4678 }
4679 }
4680
4681 /* Now that we have our final number of columns and rows,
4682 * we can compute actual needed space for non-evenly sized axes. */
4683 {
4684 int tot_w, tot_h;
4685 UILayoutGridFlowInput input{};
4686 input.row_major = gflow->row_major;
4687 input.even_columns = gflow->even_columns;
4688 input.even_rows = gflow->even_rows;
4689 input.litem_w = litem->w;
4690 input.litem_x = litem->x;
4691 input.litem_y = litem->y;
4692 input.space_x = space_x;
4693 input.space_y = space_y;
4694 input.tot_columns = gflow->tot_columns;
4695 input.tot_rows = gflow->tot_rows;
4696 UILayoutGridFlowOutput output{};
4697 output.tot_w = &tot_w;
4698 output.tot_h = &tot_h;
4699 ui_litem_grid_flow_compute(litem->items, &input, &output);
4700
4701 litem->w = tot_w;
4702 litem->h = tot_h;
4703 }
4704}
4705
4707{
4708 const uiStyle *style = litem->root->style;
4709 uiLayoutItemGridFlow *gflow = static_cast<uiLayoutItemGridFlow *>(litem);
4710
4711 if (gflow->tot_items == 0) {
4712 litem->w = litem->h = 0;
4713 return;
4714 }
4715
4716 BLI_assert(gflow->tot_columns > 0);
4717 BLI_assert(gflow->tot_rows > 0);
4718
4719 const int space_x = style->columnspace;
4720 const int space_y = style->buttonspacey;
4721
4722 blender::Array<int, 64> widths(gflow->tot_columns);
4723 blender::Array<int, 64> heights(gflow->tot_rows);
4725 blender::Array<int, 64> cos_y(gflow->tot_rows);
4726
4727 /* This time we directly compute coordinates and sizes of all cells. */
4728 UILayoutGridFlowInput input{};
4729 input.row_major = gflow->row_major;
4730 input.even_columns = gflow->even_columns;
4731 input.even_rows = gflow->even_rows;
4732 input.litem_w = litem->w;
4733 input.litem_x = litem->x;
4734 input.litem_y = litem->y;
4735 input.space_x = space_x;
4736 input.space_y = space_y;
4737 input.tot_columns = gflow->tot_columns;
4738 input.tot_rows = gflow->tot_rows;
4739 UILayoutGridFlowOutput output{};
4740 output.cos_x_array = cos_x.data();
4741 output.cos_y_array = cos_y.data();
4742 output.widths_array = widths.data();
4743 output.heights_array = heights.data();
4744 ui_litem_grid_flow_compute(litem->items, &input, &output);
4745
4746 int i = 0;
4747 for (uiItem *item : litem->items) {
4748 const int col = gflow->row_major ? i % gflow->tot_columns : i / gflow->tot_rows;
4749 const int row = gflow->row_major ? i / gflow->tot_columns : i % gflow->tot_rows;
4750 int item_w, item_h;
4751 ui_item_size(item, &item_w, &item_h);
4752
4753 const int w = widths[col];
4754 const int h = heights[row];
4755
4756 item_w = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? w : min_ii(w, item_w);
4757 item_h = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? h : min_ii(h, item_h);
4758
4759 ui_item_position(item, cos_x[col], cos_y[row], item_w, item_h);
4760 i++;
4761 }
4762
4763 litem->h = litem->y - cos_y[gflow->tot_rows - 1];
4764 litem->x = (cos_x[gflow->tot_columns - 1] - litem->x) + widths[gflow->tot_columns - 1];
4765 litem->y = litem->y - litem->h;
4766}
4767
4768/* free layout */
4770{
4771 int minx = 1e6;
4772 int miny = 1e6;
4773 litem->w = 0;
4774 litem->h = 0;
4775
4776 for (uiItem *item : litem->items) {
4777 int itemx, itemy, itemw, itemh;
4778 ui_item_offset(item, &itemx, &itemy);
4779 ui_item_size(item, &itemw, &itemh);
4780
4781 minx = min_ii(minx, itemx);
4782 miny = min_ii(miny, itemy);
4783
4784 litem->w = std::max(litem->w, itemx + itemw);
4785 litem->h = std::max(litem->h, itemy + itemh);
4786 }
4787
4788 litem->w -= minx;
4789 litem->h -= miny;
4790}
4791
4793{
4794 float scalex = 1.0f, scaley = 1.0f;
4795 int x, y, newx, newy, itemx, itemy, itemh, itemw;
4796
4797 int minx = 1e6;
4798 int miny = 1e6;
4799 int totw = 0;
4800 int toth = 0;
4801
4802 for (uiItem *item : litem->items) {
4803 ui_item_offset(item, &itemx, &itemy);
4804 ui_item_size(item, &itemw, &itemh);
4805
4806 minx = min_ii(minx, itemx);
4807 miny = min_ii(miny, itemy);
4808
4809 totw = max_ii(totw, itemx + itemw);
4810 toth = max_ii(toth, itemy + itemh);
4811 }
4812
4813 totw -= minx;
4814 toth -= miny;
4815
4816 if (litem->w && totw > 0) {
4817 scalex = float(litem->w) / float(totw);
4818 }
4819 if (litem->h && toth > 0) {
4820 scaley = float(litem->h) / float(toth);
4821 }
4822
4823 x = litem->x;
4824 y = litem->y - scaley * toth;
4825
4826 for (uiItem *item : litem->items) {
4827 ui_item_offset(item, &itemx, &itemy);
4828 ui_item_size(item, &itemw, &itemh);
4829
4830 if (scalex != 1.0f) {
4831 newx = (itemx - minx) * scalex;
4832 itemw = (itemx - minx + itemw) * scalex - newx;
4833 itemx = minx + newx;
4834 }
4835
4836 if (scaley != 1.0f) {
4837 newy = (itemy - miny) * scaley;
4838 itemh = (itemy - miny + itemh) * scaley - newy;
4839 itemy = miny + newy;
4840 }
4841
4842 ui_item_position(item, x + itemx - minx, y + itemy - miny, itemw, itemh);
4843 }
4844
4845 litem->w = scalex * totw;
4846 litem->h = litem->y - y;
4847 litem->x = x + litem->w;
4848 litem->y = y;
4849}
4850
4851/* split layout */
4853{
4854 ui_litem_estimate_row(litem);
4855 litem->flag &= ~UI_ITEM_FIXED_SIZE;
4856}
4857
4859{
4860 uiLayoutItemSplit *split = static_cast<uiLayoutItemSplit *>(litem);
4861 float extra_pixel = 0.0f;
4862 const int tot = int(litem->items.size());
4863
4864 if (tot == 0) {
4865 return;
4866 }
4867
4868 int x = litem->x;
4869 const int y = litem->y;
4870
4871 const float percentage = (split->percentage == 0.0f) ? 1.0f / float(tot) : split->percentage;
4872
4873 const int w = (litem->w - (tot - 1) * litem->space);
4874 int colw = w * percentage;
4875 colw = std::max(colw, 0);
4876
4877 const uiItem *item_last = litem->items.last();
4878 for (uiItem *item : litem->items) {
4879 const bool is_item_last = (item == item_last);
4880 int itemh;
4881 ui_item_size(item, nullptr, &itemh);
4882
4883 ui_item_position(item, x, y - itemh, colw, itemh);
4884 x += colw;
4885
4886 if (!is_item_last) {
4887 const float width = extra_pixel + (w - int(w * percentage)) / (float(tot) - 1);
4888 extra_pixel = width - int(width);
4889 colw = int(width);
4890 colw = std::max(colw, 0);
4891
4892 x += litem->space;
4893 }
4894 }
4895
4896 litem->w = x - litem->x;
4897 litem->h = litem->y - y;
4898 litem->x = x;
4899 litem->y = y;
4900}
4901
4902/* overlap layout */
4904{
4905 litem->w = 0;
4906 litem->h = 0;
4907
4908 for (uiItem *item : litem->items) {
4909 int itemw, itemh;
4910 ui_item_size(item, &itemw, &itemh);
4911
4912 litem->w = std::max(itemw, litem->w);
4913 litem->h = std::max(itemh, litem->h);
4914 }
4915}
4916
4918{
4919
4920 const int x = litem->x;
4921 const int y = litem->y;
4922
4923 for (uiItem *item : litem->items) {
4924 int itemw, itemh;
4925 ui_item_size(item, &itemw, &itemh);
4926 ui_item_position(item, x, y - itemh, litem->w, itemh);
4927
4928 litem->h = std::max(litem->h, itemh);
4929 }
4930
4931 litem->x = x;
4932 litem->y = y - litem->h;
4933}
4934
4935static void ui_litem_init_from_parent(uiLayout *litem, uiLayout *layout, int align)
4936{
4937 litem->root = layout->root;
4938 litem->align = align;
4939 /* Children of grid-flow layout shall never have "ideal big size" returned as estimated size. */
4940 litem->variable_size = layout->variable_size || layout->type == ITEM_LAYOUT_GRID_FLOW;
4941 litem->active = true;
4942 litem->enabled = true;
4943 litem->context = layout->context;
4944 litem->redalert = layout->redalert;
4945 litem->w = layout->w;
4946 litem->emboss = layout->emboss;
4947 litem->flag = (layout->flag &
4949
4950 if (layout->child_items_layout) {
4951 layout->child_items_layout->items.append(litem);
4952 litem->parent = layout->child_items_layout;
4953 }
4954 else {
4955 layout->items.append(litem);
4956 litem->parent = layout;
4957 }
4958}
4959
4960static void ui_layout_heading_set(uiLayout *layout, const char *heading)
4961{
4962 BLI_assert(layout->heading[0] == '\0');
4963 if (heading) {
4964 STRNCPY(layout->heading, heading);
4965 }
4966}
4967
4968uiLayout *uiLayoutRow(uiLayout *layout, bool align)
4969{
4970 uiLayout *litem = MEM_new<uiLayout>(__func__);
4971 ui_litem_init_from_parent(litem, layout, align);
4972
4973 litem->type = ITEM_LAYOUT_ROW;
4974 litem->space = (align) ? 0 : layout->root->style->buttonspacex;
4975
4976 UI_block_layout_set_current(layout->root->block, litem);
4977
4978 return litem;
4979}
4980
4982 uiLayout *layout,
4983 PointerRNA *open_prop_owner,
4984 const char *open_prop_name)
4985{
4986 const ARegion *region = CTX_wm_region(C);
4987
4988 const bool is_real_open = RNA_boolean_get(open_prop_owner, open_prop_name);
4989 const bool search_filter_active = region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE;
4990 const bool is_open = is_real_open || search_filter_active;
4991
4992 PanelLayout panel_layout{};
4993 {
4994 uiLayoutItemPanelHeader *header_litem = MEM_new<uiLayoutItemPanelHeader>(__func__);
4995 ui_litem_init_from_parent(header_litem, layout, false);
4996 header_litem->type = ITEM_LAYOUT_PANEL_HEADER;
4997
4998 header_litem->open_prop_owner = *open_prop_owner;
4999 STRNCPY(header_litem->open_prop_name, open_prop_name);
5000
5001 uiLayout *row = uiLayoutRow(header_litem, true);
5002 uiLayoutSetUnitsY(row, 1.2f);
5003
5004 uiBlock *block = uiLayoutGetBlock(row);
5005 const int icon = is_open ? ICON_DOWNARROW_HLT : ICON_RIGHTARROW;
5006 const int width = ui_text_icon_width(layout, "", icon, false);
5008 block, UI_BTYPE_LABEL, 0, icon, "", 0, 0, width, UI_UNIT_Y, nullptr, 0.0f, 0.0f, "");
5009
5010 panel_layout.header = row;
5011 }
5012
5013 if (!is_open) {
5014 return panel_layout;
5015 }
5016
5017 uiLayoutItemPanelBody *body_litem = MEM_new<uiLayoutItemPanelBody>(__func__);
5018 body_litem->type = ITEM_LAYOUT_PANEL_BODY;
5019 body_litem->space = layout->root->style->templatespace;
5020 ui_litem_init_from_parent(body_litem, layout, false);
5021 UI_block_layout_set_current(layout->root->block, body_litem);
5022 panel_layout.body = body_litem;
5023
5024 return panel_layout;
5025}
5026
5028 uiLayout *layout,
5029 PointerRNA *open_prop_owner,
5030 const char *open_prop_name,
5031 const char *label)
5032{
5033 PanelLayout panel = uiLayoutPanelProp(C, layout, open_prop_owner, open_prop_name);
5034 uiItemL(panel.header, label, ICON_NONE);
5035
5036 return panel.body;
5037}
5038
5040 uiLayout *layout,
5041 const char *idname,
5042 const bool default_closed)
5043{
5044 Panel *panel = uiLayoutGetRootPanel(layout);
5045 BLI_assert(panel != nullptr);
5046
5047 LayoutPanelState *state = BKE_panel_layout_panel_state_ensure(panel, idname, default_closed);
5048 PointerRNA state_ptr = RNA_pointer_create(nullptr, &RNA_LayoutPanelState, state);
5049
5050 return uiLayoutPanelProp(C, layout, &state_ptr, "is_open");
5051}
5052
5054 uiLayout *layout,
5055 const char *idname,
5056 const bool default_closed,
5057 const char *label)
5058{
5059 PanelLayout panel = uiLayoutPanel(C, layout, idname, default_closed);
5060 uiItemL(panel.header, label, ICON_NONE);
5061
5062 return panel.body;
5063}
5064
5066{
5067 if (layout.items.is_empty()) {
5068 return false;
5069 }
5070 const uiItem *item = layout.items.last();
5071 return item->type == ITEM_LAYOUT_PANEL_HEADER;
5072}
5073
5074uiLayout *uiLayoutRowWithHeading(uiLayout *layout, bool align, const char *heading)
5075{
5076 uiLayout *litem = uiLayoutRow(layout, align);
5077 ui_layout_heading_set(litem, heading);
5078 return litem;
5079}
5080
5081uiLayout *uiLayoutColumn(uiLayout *layout, bool align)
5082{
5083 uiLayout *litem = MEM_new<uiLayout>(__func__);
5084 ui_litem_init_from_parent(litem, layout, align);
5085
5086 litem->type = ITEM_LAYOUT_COLUMN;
5087 litem->space = (align) ? 0 : layout->root->style->buttonspacey;
5088
5089 UI_block_layout_set_current(layout->root->block, litem);
5090
5091 return litem;
5092}
5093
5094uiLayout *uiLayoutColumnWithHeading(uiLayout *layout, bool align, const char *heading)
5095{
5096 uiLayout *litem = uiLayoutColumn(layout, align);
5097 ui_layout_heading_set(litem, heading);
5098 return litem;
5099}
5100
5101uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, bool align)
5102{
5103 uiLayoutItemFlow *flow = MEM_new<uiLayoutItemFlow>(__func__);
5104 ui_litem_init_from_parent(flow, layout, align);
5105
5107 flow->space = (flow->align) ? 0 : layout->root->style->columnspace;
5108 flow->number = number;
5109
5110 UI_block_layout_set_current(layout->root->block, flow);
5111
5112 return flow;
5113}
5114
5116 bool row_major,
5117 int columns_len,
5118 bool even_columns,
5119 bool even_rows,
5120 bool align)
5121{
5122 uiLayoutItemGridFlow *flow = MEM_new<uiLayoutItemGridFlow>(__func__);
5124 ui_litem_init_from_parent(flow, layout, align);
5125
5126 flow->space = (flow->align) ? 0 : layout->root->style->columnspace;
5127 flow->row_major = row_major;
5128 flow->columns_len = columns_len;
5129 flow->even_columns = even_columns;
5130 flow->even_rows = even_rows;
5131
5132 UI_block_layout_set_current(layout->root->block, flow);
5133
5134 return flow;
5135}
5136
5137static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type)
5138{
5139 uiLayoutItemBx *box = MEM_new<uiLayoutItemBx>(__func__);
5140 ui_litem_init_from_parent(box, layout, false);
5141
5142 box->type = ITEM_LAYOUT_BOX;
5143 box->space = layout->root->style->columnspace;
5144
5145 UI_block_layout_set_current(layout->root->block, box);
5146
5147 box->roundbox = uiDefBut(layout->root->block, type, 0, "", 0, 0, 0, 0, nullptr, 0.0, 0.0, "");
5148
5149 return box;
5150}
5151
5153{
5154 /* radial layouts are only valid for radial menus */
5155 if (layout->root->type != UI_LAYOUT_PIEMENU) {
5156 return ui_item_local_sublayout(layout, layout, false);
5157 }
5158
5159 /* only one radial wheel per root layout is allowed, so check and return that, if it exists */
5160 for (uiItem *item : layout->root->layout->items) {
5161 if (item->type == ITEM_LAYOUT_RADIAL) {
5162 uiLayout *litem = static_cast<uiLayout *>(item);
5163 UI_block_layout_set_current(layout->root->block, litem);
5164 return litem;
5165 }
5166 }
5167
5168 uiLayout *litem = MEM_new<uiLayout>(__func__);
5169 ui_litem_init_from_parent(litem, layout, false);
5170
5171 litem->type = ITEM_LAYOUT_RADIAL;
5172
5173 UI_block_layout_set_current(layout->root->block, litem);
5174
5175 return litem;
5176}
5177
5179{
5180 return (uiLayout *)ui_layout_box(layout, UI_BTYPE_ROUNDBOX);
5181}
5182
5184{
5185 for (uiItem *item : layout->items) {
5186 if (item->type != ITEM_BUTTON) {
5187 ui_layout_list_set_labels_active(static_cast<uiLayout *>(item));
5188 }
5189 else {
5190 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5191 if (bitem->but->flag & UI_BUT_LIST_ITEM) {
5193 }
5194 }
5195 }
5196}
5197
5199 uiList *ui_list,
5200 PointerRNA *actptr,
5201 PropertyRNA *actprop)
5202{
5204 uiBut *but = box->roundbox;
5205
5206 but->custom_data = ui_list;
5207
5208 but->rnapoin = *actptr;
5209 but->rnaprop = actprop;
5210
5211 /* only for the undo string */
5212 if (but->flag & UI_BUT_UNDO) {
5213 but->tip = RNA_property_description(actprop);
5214 }
5215
5216 return (uiLayout *)box;
5217}
5218
5220{
5221 uiLayout *litem = MEM_new<uiLayout>(__func__);
5222 ui_litem_init_from_parent(litem, layout, align);
5223
5224 litem->type = ITEM_LAYOUT_ABSOLUTE;
5225
5226 UI_block_layout_set_current(layout->root->block, litem);
5227
5228 return litem;
5229}
5230
5232{
5233 uiBlock *block = uiLayoutGetBlock(layout);
5234 uiLayoutAbsolute(layout, false);
5235
5236 return block;
5237}
5238
5240{
5241 uiLayout *litem = MEM_new<uiLayout>(__func__);
5242 ui_litem_init_from_parent(litem, layout, false);
5243
5244 litem->type = ITEM_LAYOUT_OVERLAP;
5245
5246 UI_block_layout_set_current(layout->root->block, litem);
5247
5248 return litem;
5249}
5250
5251uiLayout *uiLayoutSplit(uiLayout *layout, float percentage, bool align)
5252{
5253 uiLayoutItemSplit *split = MEM_new<uiLayoutItemSplit>(__func__);
5254 ui_litem_init_from_parent(split, layout, align);
5255
5256 split->type = ITEM_LAYOUT_SPLIT;
5257 split->space = layout->root->style->columnspace;
5258 split->percentage = percentage;
5259
5260 UI_block_layout_set_current(layout->root->block, split);
5261
5262 return split;
5263}
5264
5265void uiLayoutSetActive(uiLayout *layout, bool active)
5266{
5267 layout->active = active;
5268}
5269
5270void uiLayoutSetActiveDefault(uiLayout *layout, bool active_default)
5271{
5272 layout->active_default = active_default;
5273}
5274
5275void uiLayoutSetActivateInit(uiLayout *layout, bool activate_init)
5276{
5277 layout->activate_init = activate_init;
5278}
5279
5280void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
5281{
5282 layout->enabled = enabled;
5283}
5284
5285void uiLayoutSetRedAlert(uiLayout *layout, bool redalert)
5286{
5287 layout->redalert = redalert;
5288}
5289
5290void uiLayoutSetKeepAspect(uiLayout *layout, bool keepaspect)
5291{
5292 layout->keepaspect = keepaspect;
5293}
5294
5295void uiLayoutSetAlignment(uiLayout *layout, char alignment)
5296{
5297 layout->alignment = alignment;
5298}
5299
5300void uiLayoutSetScaleX(uiLayout *layout, float scale)
5301{
5302 layout->scale[0] = scale;
5303}
5304
5305void uiLayoutSetScaleY(uiLayout *layout, float scale)
5306{
5307 layout->scale[1] = scale;
5308}
5309
5310void uiLayoutSetUnitsX(uiLayout *layout, float unit)
5311{
5312 layout->units[0] = unit;
5313}
5314
5315void uiLayoutSetUnitsY(uiLayout *layout, float unit)
5316{
5317 layout->units[1] = unit;
5318}
5319
5321{
5322 layout->emboss = emboss;
5323}
5324
5326{
5327 return (layout->flag & UI_ITEM_PROP_SEP) != 0;
5328}
5329
5330void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
5331{
5332 SET_FLAG_FROM_TEST(layout->flag, is_sep, UI_ITEM_PROP_SEP);
5333}
5334
5336{
5337 return (layout->flag & UI_ITEM_PROP_DECORATE) != 0;
5338}
5339
5340void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
5341{
5343}
5344
5345void uiLayoutSetSearchWeight(uiLayout *layout, const float weight)
5346{
5347 layout->search_weight = weight;
5348}
5349
5351{
5352 return layout->search_weight;
5353}
5354
5356{
5357 return layout->root->block->panel;
5358}
5359
5361{
5362 return layout->active;
5363}
5364
5366{
5367 return layout->active_default;
5368}
5369
5371{
5372 return layout->activate_init;
5373}
5374
5376{
5377 return layout->enabled;
5378}
5379
5381{
5382 return layout->redalert;
5383}
5384
5386{
5387 return layout->keepaspect;
5388}
5389
5391{
5392 return layout->alignment;
5393}
5394
5396{
5397 return layout->w;
5398}
5399
5401{
5402 return layout->scale[0];
5403}
5404
5406{
5407 return layout->scale[1];
5408}
5409
5411{
5412 return layout->units[0];
5413}
5414
5416{
5417 return layout->units[1];
5418}
5419
5421{
5422 if (layout->emboss == UI_EMBOSS_UNDEFINED) {
5423 return layout->root->block->emboss;
5424 }
5425 return layout->emboss;
5426}
5427
5429{
5430 return 5 * UI_SCALE_FAC;
5431}
5432
5434{
5435 uiBlock *block = uiLayoutGetBlock(layout);
5436 uiLayout *row = uiLayoutRow(layout, true);
5437 uiLayoutSetFixedSize(row, true);
5438
5439 uiDefBut(
5440 block, UI_BTYPE_SEPR, 0, "", 0, 0, uiLayoutListItemPaddingWidth(), 0, nullptr, 0.0, 0.0, "");
5441
5442 /* Restore. */
5443 UI_block_layout_set_current(block, layout);
5444}
5445
5448/* -------------------------------------------------------------------- */
5452/* Disabled for performance reasons, but this could be turned on in the future. */
5453// #define PROPERTY_SEARCH_USE_TOOLTIPS
5454
5455static bool block_search_panel_label_matches(const uiBlock *block, const char *search_string)
5456{
5457 if ((block->panel != nullptr) && (block->panel->type != nullptr)) {
5458 if (BLI_strcasestr(block->panel->type->label, search_string)) {
5459 return true;
5460 }
5461 }
5462 return false;
5463}
5464
5468static bool button_matches_search_filter(uiBut *but, const char *search_filter)
5469{
5470 /* Do the shorter checks first for better performance in case there is a match. */
5471 if (BLI_strcasestr(but->str.c_str(), search_filter)) {
5472 return true;
5473 }
5474
5475 if (but->optype != nullptr) {
5476 if (BLI_strcasestr(but->optype->name, search_filter)) {
5477 return true;
5478 }
5479 }
5480
5481 if (but->rnaprop != nullptr) {
5482 if (BLI_strcasestr(RNA_property_ui_name(but->rnaprop), search_filter)) {
5483 return true;
5484 }
5485#ifdef PROPERTY_SEARCH_USE_TOOLTIPS
5486 if (BLI_strcasestr(RNA_property_description(but->rnaprop), search_filter)) {
5487 return true;
5488 }
5489#endif
5490
5491 /* Search through labels of enum property items if they are in a drop-down menu.
5492 * Unfortunately we have no #bContext here so we cannot search through RNA enums
5493 * with dynamic entries (or "itemf" functions) which require context. */
5494 if (but->type == UI_BTYPE_MENU) {
5495 PointerRNA *ptr = &but->rnapoin;
5496 PropertyRNA *enum_prop = but->rnaprop;
5497 int items_len;
5498 const EnumPropertyItem *items_array = nullptr;
5499 bool free;
5500 RNA_property_enum_items_gettexted(nullptr, ptr, enum_prop, &items_array, &items_len, &free);
5501 if (items_array == nullptr) {
5502 return false;
5503 }
5504
5505 bool found = false;
5506 for (int i = 0; i < items_len; i++) {
5507 /* Check for nullptr name field which enums use for separators. */
5508 if (items_array[i].name == nullptr) {
5509 continue;
5510 }
5511 if (BLI_strcasestr(items_array[i].name, search_filter)) {
5512 found = true;
5513 break;
5514 }
5515 }
5516 if (free) {
5517 MEM_freeN((EnumPropertyItem *)items_array);
5518 }
5519 if (found) {
5520 return true;
5521 }
5522 }
5523 }
5524
5525 return false;
5526}
5527
5531static bool button_group_has_search_match(const uiButtonGroup &group, const char *search_filter)
5532{
5533 for (uiBut *but : group.buttons) {
5534 if (button_matches_search_filter(but, search_filter)) {
5535 return true;
5536 }
5537 }
5538
5539 return false;
5540}
5541
5551static bool block_search_filter_tag_buttons(uiBlock *block, const char *search_filter)
5552{
5553 bool has_result = false;
5554 for (const uiButtonGroup &group : block->button_groups) {
5555 if (button_group_has_search_match(group, search_filter)) {
5556 has_result = true;
5557 }
5558 else {
5559 for (uiBut *but : group.buttons) {
5560 but->flag |= UI_SEARCH_FILTER_NO_MATCH;
5561 }
5562 }
5563 }
5564 return has_result;
5565}
5566
5567bool UI_block_apply_search_filter(uiBlock *block, const char *search_filter)
5568{
5569 if (search_filter == nullptr || search_filter[0] == '\0') {
5570 return false;
5571 }
5572
5573 Panel *panel = block->panel;
5574
5575 if (panel != nullptr) {
5576 /* Panels for active blocks should always have a valid `panel->type`,
5577 * otherwise they wouldn't be created. */
5578 if (panel->type->flag & PANEL_TYPE_NO_SEARCH) {
5579 return false;
5580 }
5581 }
5582
5583 const bool panel_label_matches = block_search_panel_label_matches(block, search_filter);
5584
5585 const bool has_result = (panel_label_matches) ?
5586 true :
5587 block_search_filter_tag_buttons(block, search_filter);
5588
5589 if (panel != nullptr) {
5590 if (has_result) {
5592 }
5593 }
5594
5595 return has_result;
5596}
5597
5600/* -------------------------------------------------------------------- */
5604static void ui_item_scale(uiLayout *litem, const float scale[2])
5605{
5606 int x, y, w, h;
5607
5608 for (auto riter = litem->items.rbegin(); riter != litem->items.rend(); riter++) {
5609 uiItem *item = *riter;
5610 if (item->type != ITEM_BUTTON) {
5611 uiLayout *subitem = static_cast<uiLayout *>(item);
5612 ui_item_scale(subitem, scale);
5613 }
5614
5615 ui_item_size(item, &w, &h);
5616 ui_item_offset(item, &x, &y);
5617
5618 if (scale[0] != 0.0f) {
5619 x *= scale[0];
5620 w *= scale[0];
5621 }
5622
5623 if (scale[1] != 0.0f) {
5624 y *= scale[1];
5625 h *= scale[1];
5626 }
5627
5628 ui_item_position(item, x, y, w, h);
5629 }
5630}
5631
5632static void ui_item_estimate(uiItem *item)
5633{
5634 if (item->type != ITEM_BUTTON) {
5635 uiLayout *litem = static_cast<uiLayout *>(item);
5636
5637 if (litem->items.is_empty()) {
5638 litem->w = 0;
5639 litem->h = 0;
5640 return;
5641 }
5642
5643 for (uiItem *subitem : litem->items) {
5644 ui_item_estimate(subitem);
5645 }
5646
5647 if (litem->scale[0] != 0.0f || litem->scale[1] != 0.0f) {
5648 ui_item_scale(litem, litem->scale);
5649 }
5650
5651 switch (litem->type) {
5652 case ITEM_LAYOUT_COLUMN:
5653 ui_litem_estimate_column(litem, false);
5654 break;
5657 break;
5660 break;
5661 case ITEM_LAYOUT_ROW:
5662 ui_litem_estimate_row(litem);
5663 break;
5666 break;
5669 break;
5670 case ITEM_LAYOUT_BOX:
5671 ui_litem_estimate_box(litem);
5672 break;
5673 case ITEM_LAYOUT_ROOT:
5675 break;
5678 break;
5679 case ITEM_LAYOUT_SPLIT:
5681 break;
5684 break;
5685 default:
5686 break;
5687 }
5688
5689 /* Force fixed size. */
5690 if (litem->units[0] > 0) {
5691 litem->w = UI_UNIT_X * litem->units[0];
5692 }
5693 if (litem->units[1] > 0) {
5694 litem->h = UI_UNIT_Y * litem->units[1];
5695 }
5696 }
5697}
5698
5699static void ui_item_align(uiLayout *litem, short nr)
5700{
5701 for (auto riter = litem->items.rbegin(); riter != litem->items.rend(); riter++) {
5702 uiItem *item = *riter;
5703 if (item->type == ITEM_BUTTON) {
5704 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5705#ifndef USE_UIBUT_SPATIAL_ALIGN
5706 if (ui_but_can_align(bitem->but))
5707#endif
5708 {
5709 if (!bitem->but->alignnr) {
5710 bitem->but->alignnr = nr;
5711 }
5712 }
5713 }
5714 else if (item->type == ITEM_LAYOUT_ABSOLUTE) {
5715 /* pass */
5716 }
5717 else if (item->type == ITEM_LAYOUT_OVERLAP) {
5718 /* pass */
5719 }
5720 else if (item->type == ITEM_LAYOUT_BOX) {
5721 uiLayoutItemBx *box = static_cast<uiLayoutItemBx *>(item);
5722 if (!box->roundbox->alignnr) {
5723 box->roundbox->alignnr = nr;
5724 }
5725 }
5726 else {
5727 uiLayout *litem = static_cast<uiLayout *>(item);
5728 if (litem->align) {
5729 ui_item_align(litem, nr);
5730 }
5731 }
5732 }
5733}
5734
5735static void ui_item_flag(uiLayout *litem, int flag)
5736{
5737 for (auto riter = litem->items.rbegin(); riter != litem->items.rend(); riter++) {
5738 uiItem *item = *riter;
5739 if (item->type == ITEM_BUTTON) {
5740 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5741 bitem->but->flag |= flag;
5742 }
5743 else {
5744 ui_item_flag(static_cast<uiLayout *>(item), flag);
5745 }
5746 }
5747}
5748
5749static void ui_item_layout(uiItem *item)
5750{
5751 if (item->type != ITEM_BUTTON) {
5752 uiLayout *litem = static_cast<uiLayout *>(item);
5753
5754 if (litem->items.is_empty()) {
5755 return;
5756 }
5757
5758 if (litem->align) {
5759 ui_item_align(litem, ++litem->root->block->alignnr);
5760 }
5761 if (!litem->active) {
5763 }
5764 if (!litem->enabled) {
5766 }
5767
5768 switch (litem->type) {
5769 case ITEM_LAYOUT_COLUMN:
5770 ui_litem_layout_column(litem, false, false);
5771 break;
5774 break;
5777 break;
5778 case ITEM_LAYOUT_ROW:
5779 ui_litem_layout_row(litem);
5780 break;
5783 break;
5786 break;
5787 case ITEM_LAYOUT_BOX:
5788 ui_litem_layout_box(litem);
5789 break;
5790 case ITEM_LAYOUT_ROOT:
5791 ui_litem_layout_root(litem);
5792 break;
5795 break;
5796 case ITEM_LAYOUT_SPLIT:
5797 ui_litem_layout_split(litem);
5798 break;
5801 break;
5802 case ITEM_LAYOUT_RADIAL:
5804 break;
5805 default:
5806 break;
5807 }
5808
5809 for (uiItem *subitem : litem->items) {
5810 if (item->flag & UI_ITEM_BOX_ITEM) {
5811 subitem->flag |= UI_ITEM_BOX_ITEM;
5812 }
5813 ui_item_layout(subitem);
5814 }
5815 }
5816 else {
5817 if (item->flag & UI_ITEM_BOX_ITEM) {
5818 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5819 bitem->but->drawflag |= UI_BUT_BOX_ITEM;
5820 }
5821 }
5822}
5823
5824static void ui_layout_end(uiBlock *block, uiLayout *layout, int *r_x, int *r_y)
5825{
5826 if (layout->root->handlefunc) {
5827 UI_block_func_handle_set(block, layout->root->handlefunc, layout->root->argv);
5828 }
5829
5830 ui_item_estimate(layout);
5831 ui_item_layout(layout);
5832
5833 if (r_x) {
5834 *r_x = layout->x;
5835 }
5836 if (r_y) {
5837 *r_y = layout->y;
5838 }
5839}
5840
5841static void ui_layout_free(uiLayout *layout)
5842{
5843 for (uiItem *item : layout->items) {
5844 if (item->type == ITEM_BUTTON) {
5845 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5846
5847 bitem->but->layout = nullptr;
5848 MEM_delete(item);
5849 }
5850 else {
5851 uiLayout *litem = static_cast<uiLayout *>(item);
5852 ui_layout_free(litem);
5853 }
5854 }
5855
5856 MEM_delete(layout);
5857}
5858
5860{
5861 if (root->padding) {
5862 /* add an invisible button for padding */
5863 uiBlock *block = root->block;
5864 uiLayout *prev_layout = block->curlayout;
5865
5866 block->curlayout = root->layout;
5867 uiDefBut(
5868 block, UI_BTYPE_SEPR, 0, "", 0, 0, root->padding, root->padding, nullptr, 0.0, 0.0, "");
5869 block->curlayout = prev_layout;
5870 }
5871}
5872
5874 int dir,
5875 int type,
5876 int x,
5877 int y,
5878 int size,
5879 int em,
5880 int padding,
5881 const uiStyle *style)
5882{
5883 uiLayoutRoot *root = MEM_cnew<uiLayoutRoot>(__func__);
5884 root->type = type;
5885 root->style = style;
5886 root->block = block;
5887 root->padding = padding;
5889
5890 uiLayout *layout = MEM_new<uiLayout>(__func__);
5892
5893 /* Only used when 'UI_ITEM_PROP_SEP' is set. */
5894 layout->flag = UI_ITEM_PROP_DECORATE;
5895
5896 layout->x = x;
5897 layout->y = y;
5898 layout->root = root;
5899 layout->space = style->templatespace;
5900 layout->active = true;
5901 layout->enabled = true;
5902 layout->context = nullptr;
5903 layout->emboss = UI_EMBOSS_UNDEFINED;
5904
5906 layout->space = 0;
5907 }
5908
5909 if (dir == UI_LAYOUT_HORIZONTAL) {
5910 layout->h = size;
5911 layout->root->emh = em * UI_UNIT_Y;
5912 }
5913 else {
5914 layout->w = size;
5915 layout->root->emw = em * UI_UNIT_X;
5916 }
5917
5918 block->curlayout = layout;
5919 root->layout = layout;
5920 BLI_addtail(&block->layouts, root);
5921
5923
5924 return layout;
5925}
5926
5928{
5929 return layout->root->block;
5930}
5931
5936
5938{
5939 block->curlayout = layout;
5940}
5941
5943{
5944 uiButtonItem *bitem = MEM_new<uiButtonItem>(__func__);
5945 bitem->type = ITEM_BUTTON;
5946 bitem->but = but;
5947
5948 int w, h;
5949 ui_item_size((uiItem *)bitem, &w, &h);
5950 /* XXX uiBut hasn't scaled yet
5951 * we can flag the button as not expandable, depending on its size */
5952 if (w <= 2 * UI_UNIT_X && but->str.empty()) {
5953 bitem->flag |= UI_ITEM_FIXED_SIZE;
5954 }
5955
5956 if (layout->child_items_layout) {
5957 layout->child_items_layout->items.append(bitem);
5958 }
5959 else {
5960 layout->items.append(bitem);
5961 }
5962 but->layout = layout;
5963 but->search_weight = layout->search_weight;
5964
5965 if (layout->context) {
5966 but->context = layout->context;
5967 layout->context->used = true;
5968 }
5969
5970 if (layout->emboss != UI_EMBOSS_UNDEFINED) {
5971 but->emboss = layout->emboss;
5972 }
5973
5975}
5976
5977static uiButtonItem *ui_layout_find_button_item(const uiLayout *layout, const uiBut *but)
5978{
5979 const blender::Vector<uiItem *> &child_list = layout->child_items_layout ?
5980 layout->child_items_layout->items :
5981 layout->items;
5982
5983 for (uiItem *item : child_list) {
5984 if (item->type == ITEM_BUTTON) {
5985 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5986
5987 if (bitem->but == but) {
5988 return bitem;
5989 }
5990 }
5991 else {
5992 uiButtonItem *nested_item = ui_layout_find_button_item(static_cast<uiLayout *>(item), but);
5993 if (nested_item) {
5994 return nested_item;
5995 }
5996 }
5997 }
5998
5999 return nullptr;
6000}
6001
6002void ui_layout_remove_but(uiLayout *layout, const uiBut *but)
6003{
6004 blender::Vector<uiItem *> &child_list = layout->child_items_layout ?
6005 layout->child_items_layout->items :
6006 layout->items;
6007 const int64_t removed_num = child_list.remove_if([but](auto item) {
6008 if (item->type == ITEM_BUTTON) {
6009 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
6010 return (bitem->but == but);
6011 }
6012 return false;
6013 });
6014
6015 BLI_assert(removed_num <= 1);
6016 UNUSED_VARS_NDEBUG(removed_num);
6017}
6018
6019bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but)
6020{
6022 static_cast<const uiBut *>(old_but_ptr));
6023 if (!bitem) {
6024 return false;
6025 }
6026
6027 bitem->but = new_but;
6028 return true;
6029}
6030
6031void uiLayoutSetFixedSize(uiLayout *layout, bool fixed_size)
6032{
6033 if (fixed_size) {
6034 layout->flag |= UI_ITEM_FIXED_SIZE;
6035 }
6036 else {
6037 layout->flag &= ~UI_ITEM_FIXED_SIZE;
6038 }
6039}
6040
6042{
6043 return (layout->flag & UI_ITEM_FIXED_SIZE) != 0;
6044}
6045
6047{
6048 layout->root->opcontext = opcontext;
6049}
6050
6051void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv)
6052{
6053 layout->root->handlefunc = handlefunc;
6054 layout->root->argv = argv;
6055}
6056
6058{
6059 LISTBASE_FOREACH_MUTABLE (uiLayoutRoot *, root, &block->layouts) {
6060 ui_layout_free(root->layout);
6061 MEM_freeN(root);
6062 }
6063}
6064
6065void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
6066{
6067 BLI_assert(block->active);
6068
6069 if (r_x) {
6070 *r_x = 0;
6071 }
6072 if (r_y) {
6073 *r_y = 0;
6074 }
6075
6076 block->curlayout = nullptr;
6077
6078 LISTBASE_FOREACH_MUTABLE (uiLayoutRoot *, root, &block->layouts) {
6080
6081 /* nullptr in advance so we don't interfere when adding button */
6082 ui_layout_end(block, root->layout, r_x, r_y);
6083 ui_layout_free(root->layout);
6084 MEM_freeN(root);
6085 }
6086
6087 BLI_listbase_clear(&block->layouts);
6088}
6089
6091{
6092 return !BLI_listbase_is_empty(&block->layouts);
6093}
6094
6095void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *ptr)
6096{
6097 uiBlock *block = layout->root->block;
6098 layout->context = CTX_store_add(block->contexts, name, ptr);
6099}
6100
6101void uiLayoutSetContextString(uiLayout *layout, const char *name, blender::StringRef value)
6102{
6103 uiBlock *block = layout->root->block;
6104 layout->context = CTX_store_add(block->contexts, name, value);
6105}
6106
6108{
6109 return layout->context;
6110}
6111
6112void uiLayoutContextCopy(uiLayout *layout, const bContextStore *context)
6113{
6114 uiBlock *block = layout->root->block;
6115 layout->context = CTX_store_add_all(block->contexts, context);
6116}
6117
6119 uiButToolTipFunc func,
6120 void *arg,
6121 uiCopyArgFunc copy_arg,
6122 uiFreeArgFunc free_arg)
6123{
6124 bool arg_used = false;
6125
6126 for (uiItem *item : layout->items) {
6127 /* Each button will call free_arg for "its" argument, so we need to
6128 * duplicate the allocation for each button after the first. */
6129 if (copy_arg != nullptr && arg_used) {
6130 arg = copy_arg(arg);
6131 }
6132
6133 if (item->type == ITEM_BUTTON) {
6134 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
6135 if (bitem->but->type == UI_BTYPE_DECORATOR) {
6136 continue;
6137 }
6138 UI_but_func_tooltip_set(bitem->but, func, arg, free_arg);
6139 arg_used = true;
6140 }
6141 else {
6142 uiLayoutSetTooltipFunc(static_cast<uiLayout *>(item), func, arg, copy_arg, free_arg);
6143 arg_used = true;
6144 }
6145 }
6146
6147 if (free_arg != nullptr && !arg_used) {
6148 /* Free the original copy of arg in case the layout is empty. */
6149 free_arg(arg);
6150 }
6151}
6152
6154{
6155 if (but->opptr) {
6156 uiLayoutSetContextPointer(layout, "button_operator", but->opptr);
6157 }
6158
6159 if (but->rnapoin.data && but->rnaprop) {
6160 /* TODO: index could be supported as well */
6161 PointerRNA ptr_prop = RNA_pointer_create(nullptr, &RNA_Property, but->rnaprop);
6162 uiLayoutSetContextPointer(layout, "button_prop", &ptr_prop);
6163 uiLayoutSetContextPointer(layout, "button_pointer", &but->rnapoin);
6164 }
6165}
6166
6168{
6169 if (r_prop != nullptr) {
6170 *r_prop = nullptr;
6171 }
6172
6174 MenuItemLevel *lvl = static_cast<MenuItemLevel *>(but->func_argN);
6176 if ((ot != nullptr) && (r_prop != nullptr)) {
6178 }
6179 return ot;
6180 }
6181 return nullptr;
6182}
6183
6185{
6187 return (MenuType *)but->poin;
6188 }
6189 return nullptr;
6190}
6191
6193{
6195 return (PanelType *)but->poin;
6196 }
6197 return nullptr;
6198}
6199
6200std::optional<blender::StringRefNull> UI_but_asset_shelf_type_idname_get(const uiBut *but)
6201{
6203}
6204
6206{
6207 Menu menu{};
6208 menu.layout = layout;
6209 menu.type = mt;
6210
6211 if (G.debug & G_DEBUG_WM) {
6212 printf("%s: opening menu \"%s\"\n", __func__, mt->idname);
6213 }
6214
6215 uiBlock *block = uiLayoutGetBlock(layout);
6216 if (bool(mt->flag & MenuTypeFlag::SearchOnKeyPress)) {
6218 }
6219 if (mt->listener) {
6220 /* Forward the menu type listener to the block we're drawing in. */
6222 }
6223
6224 if (layout->context) {
6225 CTX_store_set(C, layout->context);
6226 }
6227
6228 mt->draw(C, &menu);
6229
6230 if (layout->context) {
6231 CTX_store_set(C, nullptr);
6232 }
6233}
6234
6235static bool ui_layout_has_panel_label(const uiLayout *layout, const PanelType *pt)
6236{
6237 for (uiItem *subitem : layout->items) {
6238 if (subitem->type == ITEM_BUTTON) {
6239 uiButtonItem *bitem = static_cast<uiButtonItem *>(subitem);
6240 if (!(bitem->but->flag & UI_HIDDEN) &&
6241 bitem->but->str == CTX_IFACE_(pt->translation_context, pt->label))
6242 {
6243 return true;
6244 }
6245 }
6246 else {
6247 uiLayout *litem = static_cast<uiLayout *>(subitem);
6248 if (ui_layout_has_panel_label(litem, pt)) {
6249 return true;
6250 }
6251 }
6252 }
6253
6254 return false;
6255}
6256
6257static void ui_paneltype_draw_impl(bContext *C, PanelType *pt, uiLayout *layout, bool show_header)
6258{
6259 uiBlock *block = uiLayoutGetBlock(layout);
6260 Panel *panel = BKE_panel_new(pt);
6261 panel->flag = PNL_POPOVER;
6262
6263 if (pt->listener) {
6265 }
6266
6267 /* This check may be paranoid, this function might run outside the context of a popup or can run
6268 * in popovers that are not supposed to support refreshing, see #ui_popover_create_block. */
6269 if (block->handle && block->handle->region) {
6270 /* Allow popovers to contain collapsible sections, see #uiItemPopoverPanel. */
6271 UI_popup_dummy_panel_set(block->handle->region, block);
6272 }
6273
6274 uiItem *item_last = layout->items.is_empty() ? nullptr : layout->items.last();
6275
6276 /* Draw main panel. */
6277 if (show_header) {
6278 uiLayout *row = uiLayoutRow(layout, false);
6279 if (pt->draw_header) {
6280 panel->layout = row;
6281 pt->draw_header(C, panel);
6282 panel->layout = nullptr;
6283 }
6284
6285 /* draw_header() is often used to add a checkbox to the header. If we add the label like below
6286 * the label is disconnected from the checkbox, adding a weird looking gap. As workaround, let
6287 * the checkbox add the label instead. */
6288 if (!ui_layout_has_panel_label(row, pt)) {
6289 uiItemL(row, CTX_IFACE_(pt->translation_context, pt->label), ICON_NONE);
6290 }
6291 }
6292
6293 panel->layout = layout;
6294 pt->draw(C, panel);
6295 panel->layout = nullptr;
6296 BLI_assert(panel->runtime->custom_data_ptr == nullptr);
6297
6298 BKE_panel_free(panel);
6299
6300 /* Draw child panels. */
6301 LISTBASE_FOREACH (LinkData *, link, &pt->children) {
6302 PanelType *child_pt = static_cast<PanelType *>(link->data);
6303
6304 if (child_pt->poll == nullptr || child_pt->poll(C, child_pt)) {
6305 /* Add space if something was added to the layout. */
6306 if (!layout->items.is_empty() && item_last != layout->items.last()) {
6307 uiItemS(layout);
6308 item_last = layout->items.last();
6309 }
6310
6311 uiLayout *col = uiLayoutColumn(layout, false);
6312 ui_paneltype_draw_impl(C, child_pt, col, true);
6313 }
6314 }
6315}
6316
6318{
6319 if (layout->context) {
6320 CTX_store_set(C, layout->context);
6321 }
6322
6323 ui_paneltype_draw_impl(C, pt, layout, false);
6324
6325 if (layout->context) {
6326 CTX_store_set(C, nullptr);
6327 }
6328}
6329
6332/* -------------------------------------------------------------------- */
6344{
6345 uiBut *but = bitem->but;
6346 BLI_dynstr_appendf(ds, "'type':%d, ", int(but->type));
6347 BLI_dynstr_appendf(ds, "'draw_string':'''%s''', ", but->drawstr.c_str());
6348 /* Not exactly needed, rna has this. */
6349 BLI_dynstr_appendf(ds, "'tip':'''%s''', ", but->tip ? but->tip : "");
6350
6351 if (but->optype) {
6352 std::string opstr = WM_operator_pystring_ex(static_cast<bContext *>(but->block->evil_C),
6353 nullptr,
6354 false,
6355 true,
6356 but->optype,
6357 but->opptr);
6358 BLI_dynstr_appendf(ds, "'operator':'''%s''', ", opstr.c_str());
6359 }
6360
6361 {
6362 PropertyRNA *prop = nullptr;
6364 if (ot) {
6365 std::string opstr = WM_operator_pystring_ex(
6366 static_cast<bContext *>(but->block->evil_C), nullptr, false, true, ot, nullptr);
6367 BLI_dynstr_appendf(ds, "'operator':'''%s''', ", opstr.c_str());
6368 BLI_dynstr_appendf(ds, "'property':'''%s''', ", prop ? RNA_property_identifier(prop) : "");
6369 }
6370 }
6371
6372 if (but->rnaprop) {
6374 "'rna':'%s.%s[%d]', ",
6377 but->rnaindex);
6378 }
6379}
6380
6382{
6383 BLI_dynstr_append(ds, "[");
6384
6385 for (const uiItem *item : items) {
6386
6387 BLI_dynstr_append(ds, "{");
6388
6389#define CASE_ITEM(id) \
6390 case id: { \
6391 const char *id_str = STRINGIFY(id); \
6392 BLI_dynstr_append(ds, "'type': '"); \
6393 /* Skip 'ITEM_'. */ \
6394 BLI_dynstr_append(ds, id_str + 5); \
6395 BLI_dynstr_append(ds, "', "); \
6396 break; \
6397 } \
6398 ((void)0)
6399
6400 switch (item->type) {
6415 }
6416
6417#undef CASE_ITEM
6418
6419 switch (item->type) {
6420 case ITEM_BUTTON:
6421 ui_layout_introspect_button(ds, static_cast<const uiButtonItem *>(item));
6422 break;
6423 default:
6424 BLI_dynstr_append(ds, "'items':");
6425 ui_layout_introspect_items(ds, (static_cast<const uiLayout *>(item))->items);
6426 break;
6427 }
6428
6429 BLI_dynstr_append(ds, "}");
6430
6431 if (item != items.last()) {
6432 BLI_dynstr_append(ds, ", ");
6433 }
6434 }
6435 /* Don't use a comma here as it's not needed and
6436 * causes the result to evaluate to a tuple of 1. */
6437 BLI_dynstr_append(ds, "]");
6438}
6439
6440const char *UI_layout_introspect(uiLayout *layout)
6441{
6442 DynStr *ds = BLI_dynstr_new();
6443 uiLayout layout_copy(*layout);
6444 blender::Vector<uiItem *> layout_dummy_list(1, static_cast<uiItem *>(&layout_copy));
6445 ui_layout_introspect_items(ds, layout_dummy_list);
6446 const char *result = BLI_dynstr_get_cstring(ds);
6447 BLI_dynstr_free(ds);
6448 return result;
6449}
6450
6453/* -------------------------------------------------------------------- */
6458 const uiStyle *style,
6459 const int dialog_width,
6460 const eAlertIcon icon,
6461 const int icon_size)
6462{
6463 /* By default, the space between icon and text/buttons will be equal to the 'columnspace',
6464 * this extra padding will add some space by increasing the left column width,
6465 * making the icon placement more symmetrical, between the block edge and the text. */
6466 const float icon_padding = 5.0f * UI_SCALE_FAC;
6467 /* Calculate the factor of the fixed icon column depending on the block width. */
6468 const float split_factor = (float(icon_size) + icon_padding) /
6469 float(dialog_width - style->columnspace);
6470
6471 uiLayout *block_layout = UI_block_layout(
6472 block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, dialog_width, 0, 0, style);
6473
6474 /* Split layout to put alert icon on left side. */
6475 uiLayout *split_block = uiLayoutSplit(block_layout, split_factor, false);
6476
6477 /* Alert icon on the left. */
6478 uiLayout *layout = uiLayoutRow(split_block, false);
6479 /* Using 'align_left' with 'row' avoids stretching the icon along the width of column. */
6481 uiDefButAlert(block, icon, 0, 0, icon_size, icon_size);
6482
6483 /* The rest of the content on the right. */
6484 layout = uiLayoutColumn(split_block, false);
6485
6486 return layout;
6487}
6488
6489uiLayout *uiItemsAlertBox(uiBlock *block, const int size, const eAlertIcon icon)
6490{
6491 const uiStyle *style = UI_style_get_dpi();
6492 const short icon_size = 64 * UI_SCALE_FAC;
6493 const int dialog_width = icon_size + (style->widget.points * size * UI_SCALE_FAC);
6494 return uiItemsAlertBox(block, style, dialog_width, icon, icon_size);
6495}
6496
bContextStore * CTX_store_add_all(blender::Vector< std::unique_ptr< bContextStore > > &contexts, const bContextStore *context)
bContextStore * CTX_store_add(blender::Vector< std::unique_ptr< bContextStore > > &contexts, blender::StringRefNull name, const PointerRNA *ptr)
ARegion * CTX_wm_region_popup(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Main * CTX_data_main(const bContext *C)
const bContextStore * CTX_store_get(const bContext *C)
void CTX_store_set(bContext *C, const bContextStore *store)
ARegion * CTX_wm_region(const bContext *C)
#define G_MAIN
@ G_DEBUG_WM
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1227
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:861
LayoutPanelState * BKE_panel_layout_panel_state_ensure(Panel *panel, const char *idname, bool default_closed)
Definition screen.cc:502
SpaceType * BKE_spacetype_from_id(int spaceid)
Definition screen.cc:243
ARegionType * BKE_regiontype_from_id(const SpaceType *st, int regionid)
Definition screen.cc:253
void BKE_panel_free(Panel *panel)
Definition screen.cc:529
Panel * BKE_panel_new(PanelType *panel_type)
Definition screen.cc:518
@ PANEL_TYPE_NO_SEARCH
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition BLI_dynstr.c:149
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.c:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
Definition BLI_dynstr.c:174
void BLI_dynstr_appendf(DynStr *__restrict ds, const char *__restrict format,...) ATTR_PRINTF_FORMAT(2
void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr) ATTR_NONNULL()
Definition BLI_dynstr.c:62
void BLI_kdtree_nd_ free(KDTree *tree)
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:331
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
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define STRNCPY_RLEN(dst, src)
Definition BLI_string.h:596
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
unsigned int uint
#define ENUM_OPERATORS(_type, _max)
#define POINTER_FROM_INT(i)
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define POINTER_AS_INT(i)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define MAX_IDPROP_NAME
Definition DNA_ID.h:185
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ RGN_ALIGN_BOTTOM
@ RGN_ALIGN_LEFT
@ RGN_ALIGN_RIGHT
@ RGN_FLAG_SEARCH_FILTER_ACTIVE
@ PNL_POPOVER
#define UI_SCALE_FAC
#define OP_MAX_TYPENAME
static void split(const char *text, const char *seps, char ***str, int *count)
Read Guarded memory(de)allocation.
#define RNA_STRUCT_BEGIN(sptr, prop)
#define RNA_STRUCT_END
#define RNA_warning(format,...)
eStringPropertySearchFlag
Definition RNA_types.hh:570
@ PROP_STRING_SEARCH_SUGGESTION
Definition RNA_types.hh:584
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_UNIT_ROTATION
Definition RNA_types.hh:81
@ PROP_ENUM_FLAG
Definition RNA_types.hh:293
@ PROP_ICONS_CONSECUTIVE
Definition RNA_types.hh:230
PropertySubType
Definition RNA_types.hh:135
@ PROP_MATRIX
Definition RNA_types.hh:168
@ PROP_DIRECTION
Definition RNA_types.hh:165
@ PROP_LAYER_MEMBER
Definition RNA_types.hh:181
@ PROP_COLOR
Definition RNA_types.hh:163
@ PROP_AXISANGLE
Definition RNA_types.hh:171
@ PROP_DIRPATH
Definition RNA_types.hh:140
@ PROP_COLOR_GAMMA
Definition RNA_types.hh:175
@ PROP_LAYER
Definition RNA_types.hh:180
@ PROP_FILEPATH
Definition RNA_types.hh:139
constexpr PointerRNA PointerRNA_NULL
Definition RNA_types.hh:45
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
void * but_func_argN_copy(const void *argN)
void but_func_argN_free(void *argN)
void UI_but_disable(uiBut *but, const char *disabled_hint)
void(*)(void *arg) uiFreeArgFunc
#define UI_UNIT_Y
uiBut * uiDefIconTextMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, blender::StringRef str, int x, int y, short width, short height, const char *tip)
eUIEmbossType
@ UI_EMBOSS_UNDEFINED
@ UI_EMBOSS_NONE
@ UI_EMBOSS_PIE_MENU
@ UI_EMBOSS_PULLDOWN
@ UI_EMBOSS_NONE_OR_STATUS
std::string(*)(bContext *C, void *argN, const char *tip) uiButToolTipFunc
@ UI_LAYOUT_ALIGN_LEFT
@ UI_LAYOUT_ALIGN_CENTER
@ UI_LAYOUT_ALIGN_RIGHT
@ UI_LAYOUT_ALIGN_EXPAND
void UI_popup_menu_but_set(uiPopupMenu *pup, ARegion *butregion, uiBut *but)
@ UI_BLOCK_LOOP
@ UI_BLOCK_NO_ACCELERATOR_KEYS
@ UI_BLOCK_LIST_ITEM
@ UI_BLOCK_POPUP_HOLD
bool UI_block_can_add_separator(const uiBlock *block)
void UI_but_func_rename_full_set(uiBut *but, std::function< void(std::string &new_name)> rename_full_func)
int int UI_fontstyle_string_width_with_block_aspect(const uiFontStyle *fs, const char *str, float aspect) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
void(*)(bContext *C, uiLayout *layout, void *arg1) uiMenuCreateFunc
void UI_but_placeholder_set(uiBut *but, const char *placeholder_text) ATTR_NONNULL(1)
void UI_but_func_search_set_results_are_suggestions(uiBut *but, bool value)
void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg)
@ UI_BUT2_FORCE_SEMI_MODAL_ACTIVE
void UI_popup_dummy_panel_set(ARegion *region, uiBlock *block)
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 UI_but_func_search_set(uiBut *but, uiButSearchCreateFn search_create_fn, uiButSearchUpdateFn search_update_fn, void *arg, bool free_arg, uiFreeArgFunc search_arg_free_fn, uiButHandleFunc search_exec_fn, void *active)
bool UI_but_is_userdef(const uiBut *but)
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)
const uiStyle * UI_style_get_dpi()
eButProgressType
@ UI_BUT_PROGRESS_TYPE_BAR
@ UI_BUT_PROGRESS_TYPE_RING
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
void UI_but_drawflag_enable(uiBut *but, int flag)
uiBut * uiDefIconTextButO_ptr(uiBlock *block, int type, wmOperatorType *ot, wmOperatorCallContext opcontext, int icon, blender::StringRef str, int x, int y, short width, short height, const char *tip)
uiBut * uiDefIconBut(uiBlock *block, int type, int retval, int icon, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
void UI_but_flag2_enable(uiBut *but, int flag)
void UI_block_func_handle_set(uiBlock *block, uiBlockHandleFunc func, void *arg)
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
uiBut * uiDefButO_ptr(uiBlock *block, int type, wmOperatorType *ot, wmOperatorCallContext opcontext, blender::StringRef str, int x, int y, short width, short height, const char *tip)
void *(*)(const void *argN) uiButArgNCopy
void(*)(void *argN) uiButArgNFree
#define UI_ITEM_NONE
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
void UI_but_type_set_menu_from_pulldown(uiBut *but)
uiBut * uiDefIconButO(uiBlock *block, int type, const char *opname, wmOperatorCallContext opcontext, int icon, int x, int y, short width, short height, const char *tip)
LayoutSeparatorType
#define UI_MAX_DRAW_STR
uiBut * uiDefButAlert(uiBlock *block, int icon, int x, int y, short width, short height)
uiBut * uiDefIconButO_ptr(uiBlock *block, int type, wmOperatorType *ot, wmOperatorCallContext opcontext, int icon, int x, int y, short width, short height, const char *tip)
@ UI_DIR_DOWN
@ UI_DIR_RIGHT
@ UI_DIR_LEFT
@ UI_DIR_UP
PointerRNA * UI_but_operator_ptr_ensure(uiBut *but)
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void UI_but_drawflag_disable(uiBut *but, int flag)
@ UI_LAYOUT_VERTICAL
@ UI_LAYOUT_HORIZONTAL
void UI_block_align_begin(uiBlock *block)
@ UI_LAYOUT_PIEMENU
@ UI_LAYOUT_MENU
@ UI_LAYOUT_PANEL
@ UI_LAYOUT_VERT_BAR
@ UI_LAYOUT_TOOLBAR
@ UI_LAYOUT_HEADER
uiBut * uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, blender::StringRef str, int x, int y, short width, short height, const char *tip)
void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
void(*)(bContext *C, void *arg, int event) uiMenuHandleFunc
uiButStore * UI_butstore_create(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)
void UI_block_direction_set(uiBlock *block, char direction)
void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN)
void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
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_FSTYLE_WIDGET
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BTYPE_BUT
@ UI_BTYPE_TOGGLE
@ UI_BTYPE_PROGRESS
@ UI_BTYPE_TAB
@ UI_BTYPE_HOTKEY_EVENT
@ UI_BTYPE_LISTBOX
@ UI_BTYPE_SEPR_SPACER
@ UI_BTYPE_ROUNDBOX
@ UI_BTYPE_NUM_SLIDER
@ UI_BTYPE_TEXT
@ UI_BTYPE_LABEL
@ UI_BTYPE_ICON_TOGGLE_N
@ UI_BTYPE_DECORATOR
@ UI_BTYPE_ROW
@ UI_BTYPE_SEARCH_MENU
@ UI_BTYPE_UNITVEC
@ UI_BTYPE_SEPR_LINE
@ UI_BTYPE_KEY_EVENT
@ UI_BTYPE_POPOVER
@ UI_BTYPE_CHECKBOX_N
@ UI_BTYPE_SEPR
@ UI_BTYPE_NUM
@ UI_BTYPE_COLOR
@ UI_BTYPE_CHECKBOX
@ UI_BTYPE_MENU
@ UI_BTYPE_ICON_TOGGLE
#define UI_MAX_NAME_STR
void UI_but_drag_set_id(uiBut *but, ID *id)
void *(*)(const void *arg) uiCopyArgFunc
uiBut * uiDefIconMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, int x, int y, short width, short height, const char *tip)
void UI_but_unit_type_set(uiBut *but, int unit_type)
void UI_but_flag_enable(uiBut *but, int flag)
uiBut * uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int index, const char *name, int icon, int x, int y, int width, int height)
@ UI_BUT_TEXT_RIGHT
@ UI_BUT_ICON_LEFT
@ UI_BUT_HAS_TOOLTIP_LABEL
@ UI_BUT_CHECKBOX_INVERT
@ UI_BUT_TEXT_LEFT
@ UI_BUT_BOX_ITEM
eUI_Item_Flag
@ UI_ITEM_R_EVENT
@ UI_ITEM_R_TOGGLE
@ UI_ITEM_O_DEPRESS
@ UI_ITEM_R_SPLIT_EMPTY_NAME
@ UI_ITEM_R_ICON_NEVER
@ UI_ITEM_R_TEXT_BUT_FORCE_SEMI_MODAL_ACTIVE
@ UI_ITEM_R_IMMEDIATE
@ UI_ITEM_R_FORCE_BLANK_DECORATE
@ UI_ITEM_R_COMPACT
@ UI_ITEM_R_EXPAND
@ UI_ITEM_R_NO_BG
@ UI_ITEM_R_CHECKBOX_INVERT
@ UI_ITEM_R_ICON_ONLY
@ UI_ITEM_R_FULL_EVENT
@ UI_ITEM_R_SLIDER
@ UI_BUT_DRAG_LOCK
@ UI_BUT_LIST_ITEM
@ UI_BUT_REDALERT
@ UI_BUT_UNDO
@ UI_BUT_ACTIVE_DEFAULT
@ UI_BUT_ACTIVATE_ON_INIT
@ UI_BUT_DISABLED
@ UI_BUT_INACTIVE
@ UI_BUT_VALUE_CLEAR
@ UI_BUT_LAST_ACTIVE
void UI_block_align_end(uiBlock *block)
#define NC_ID
Definition WM_types.hh:362
@ KM_NOTHING
Definition WM_types.hh:283
wmOperatorCallContext
Definition WM_types.hh:216
@ WM_OP_INVOKE_REGION_WIN
Definition WM_types.hh:219
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
#define NA_RENAME
Definition WM_types.hh:554
@ KM_CTRL
Definition WM_types.hh:256
@ KM_ALT
Definition WM_types.hh:257
@ KM_OSKEY
Definition WM_types.hh:259
@ KM_SHIFT
Definition WM_types.hh:255
#define KM_MOD_HELD
Definition WM_types.hh:270
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
unsigned int U
Definition btGjkEpa3.h:78
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
const T * data() const
Definition BLI_array.hh:301
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
std::reverse_iterator< T * > rbegin()
int64_t remove_if(Predicate &&predicate)
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
MutableSpan< T > as_mutable_span()
std::reverse_iterator< T * > rend()
const T & first() const
local_group_size(16, 16) .push_constant(Type b
#define printf
const char * label
#define ceilf(x)
#define sqrtf(x)
int len
bool ED_id_rename(Main &bmain, ID &id, blender::StringRefNull name)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
uint col
uint padding(uint offset, uint alignment)
void ui_but_update(uiBut *but)
void ui_def_but_icon_clear(uiBut *but)
void ui_block_add_dynamic_listener(uiBlock *block, void(*listener_func)(const wmRegionListenerParams *params))
void ui_but_rna_menu_convert_to_panel_type(uiBut *but, const char *panel_type)
uiBut * ui_but_change_type(uiBut *but, eButType new_type)
void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_strip)
void ui_but_rna_menu_convert_to_menu_type(uiBut *but, const char *menu_type)
const char ui_radial_dir_order[8]
bool ui_but_can_align(const uiBut *but)
int ui_but_align_opposite_to_area_align_get(const ARegion *region)
void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *)
void ui_block_new_button_group(uiBlock *block, uiButtonGroupFlag flag)
void ui_button_group_add_but(uiBlock *block, uiBut *but)
#define RNA_NO_INDEX
uiButtonGroupFlag
RadialDirection
bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
bool ui_block_is_popup_any(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
ARegion * ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearch *search_but)
#define PIE_MAX_ITEMS
void ui_panel_tag_search_filter_match(Panel *panel)
bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
std::optional< blender::StringRefNull > UI_asset_shelf_idname_from_button_context(const uiBut *but)
void ui_pie_menu_level_create(uiBlock *block, wmOperatorType *ot, const char *propname, IDProperty *properties, const EnumPropertyItem *items, int totitem, wmOperatorCallContext context, eUI_Item_Flag flag)
#define RNA_ENUM_VALUE
@ UI_SELECT_DRAW
@ UI_HIDDEN
@ UI_SELECT
@ UI_SEARCH_FILTER_NO_MATCH
void ui_but_pie_dir(RadialDirection dir, float vec[2])
void ui_rna_collection_search_update_fn(const bContext *C, void *arg, const char *str, uiSearchItems *items, bool is_first)
static void ui_litem_estimate_column(uiLayout *litem, bool is_box)
void uiItemBooleanO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
static uiBut * uiItemFullO_ptr_ex(uiLayout *layout, wmOperatorType *ot, const char *name, int icon, IDProperty *properties, const wmOperatorCallContext context, const eUI_Item_Flag flag, PointerRNA *r_opptr)
static int ui_litem_min_width(int itemw)
void uiItemEnumR(uiLayout *layout, const char *name, int icon, PointerRNA *ptr, const char *propname, int value)
static void ui_litem_layout_column_flow(uiLayout *litem)
bool uiLayoutGetActivateInit(uiLayout *layout)
uiLayout * uiLayoutColumnFlow(uiLayout *layout, int number, bool align)
Panel * uiLayoutGetRootPanel(uiLayout *layout)
void uiLayoutSetActive(uiLayout *layout, bool active)
static void ui_litem_init_from_parent(uiLayout *litem, uiLayout *layout, int align)
static void ui_item_flag(uiLayout *litem, int flag)
void uiItemFullO_ptr(uiLayout *layout, wmOperatorType *ot, const char *name, const int icon, IDProperty *properties, const wmOperatorCallContext context, const eUI_Item_Flag flag, PointerRNA *r_opptr)
bool uiLayoutGetPropDecorate(uiLayout *layout)
int uiLayoutGetAlignment(uiLayout *layout)
void uiLayoutSetUnitsY(uiLayout *layout, float unit)
uiLayout * uiLayoutBox(uiLayout *layout)
void uiItemsEnumR(uiLayout *layout, PointerRNA *ptr, const char *propname)
static void ui_litem_layout_column(uiLayout *litem, bool is_box, bool is_menu)
void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, const eUI_Item_Flag flag, const char *name, int icon)
static void ui_item_estimate(uiItem *item)
static void ui_litem_layout_overlap(uiLayout *litem)
static bool button_matches_search_filter(uiBut *but, const char *search_filter)
bool UI_block_apply_search_filter(uiBlock *block, const char *search_filter)
uiLayout * uiItemsAlertBox(uiBlock *block, const uiStyle *style, const int dialog_width, const eAlertIcon icon, const int icon_size)
static bool button_group_has_search_match(const uiButtonGroup &group, const char *search_filter)
static void ui_item_rna_size(uiLayout *layout, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, bool icon_only, bool compact, int *r_w, int *r_h)
void uiLayoutSetFixedSize(uiLayout *layout, bool fixed_size)
static void ui_litem_estimate_column_flow(uiLayout *litem)
#define CASE_ITEM(id)
void uiItemMenuEnumO(uiLayout *layout, const bContext *C, const char *opname, const char *propname, const char *name, int icon)
float uiLayoutGetUnitsY(uiLayout *layout)
uiPropertySplitWrapper uiItemPropertySplitWrapperCreate(uiLayout *parent_layout)
void uiLayoutSetScaleY(uiLayout *layout, float scale)
void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt)
static void ui_layout_heading_set(uiLayout *layout, const char *heading)
void uiItemEnumO_string(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, const char *value_str)
static void ui_litem_estimate_split(uiLayout *litem)
static uiBut * uiItemL_(uiLayout *layout, const char *name, int icon)
static void ui_litem_estimate_overlap(uiLayout *litem)
static void ui_item_disabled(uiLayout *layout, const char *name)
static int spaces_after_column_item(const uiLayout *litem, const uiItem *item, const uiItem *next_item, const bool is_box)
void uiItemIntO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
void uiItemL(uiLayout *layout, const char *name, int icon)
void uiLayoutSetActiveDefault(uiLayout *layout, bool active_default)
uiItemInternalFlag
@ UI_ITEM_INSIDE_PROP_SEP
@ UI_ITEM_PROP_DECORATE_NO_PAD
@ UI_ITEM_PROP_DECORATE
@ UI_ITEM_PROP_SEP
@ UI_ITEM_FIXED_SIZE
@ UI_ITEM_BOX_ITEM
@ UI_ITEM_AUTO_FIXED_SIZE
static const char * ui_menu_enumpropname(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int retval)
static int menu_item_enum_opname_menu_active(bContext *C, uiBut *but, MenuItemLevel *lvl)
void uiLayoutSetRedAlert(uiLayout *layout, bool redalert)
static void ui_item_offset(const uiItem *item, int *r_x, int *r_y)
static bool ui_item_rna_is_expand(PropertyRNA *prop, int index, const eUI_Item_Flag item_flag)
MenuType * UI_but_menutype_get(const uiBut *but)
#define UI_ITEM_PROP_SEP_DIVIDE
uiBlock * uiLayoutGetBlock(uiLayout *layout)
uiBut * ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, const bool results_are_suggestions)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
uiLayout * UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int size, int em, int padding, const uiStyle *style)
void uiLayoutSetScaleX(uiLayout *layout, float scale)
static void ui_item_layout(uiItem *item)
static void ui_litem_layout_absolute(uiLayout *litem)
static uiLayout * ui_item_prop_split_layout_hack(uiLayout *layout_parent, uiLayout *layout_split)
static bool ui_item_is_radial_displayable(uiItem *item)
uiBut * uiItemL_ex(uiLayout *layout, const char *name, int icon, const bool highlight, const bool redalert)
float uiLayoutGetUnitsX(uiLayout *layout)
bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but)
void uiLayoutSetAlignment(uiLayout *layout, char alignment)
bool uiLayoutGetEnabled(uiLayout *layout)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
static uiLayoutItemBx * ui_layout_box(uiLayout *layout, int type)
static void ui_litem_layout_panel_header(uiLayout *litem)
static void ui_item_enum_expand_exec(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, const char *uiname, const int h, const eButType but_type, const bool icon_only)
void uiItemS(uiLayout *layout)
int uiLayoutListItemPaddingWidth()
#define UI_ITEM_VARY_X
uiLayout * uiLayoutAbsolute(uiLayout *layout, bool align)
static uiBut * ui_item_with_label(uiLayout *layout, uiBlock *block, const char *name, const int icon, PointerRNA *ptr, PropertyRNA *prop, const int index, const int x, const int y, const int w_hint, const int h, const int flag)
static void ui_litem_layout_panel_body(uiLayout *litem)
float uiLayoutGetScaleY(uiLayout *layout)
wmOperatorCallContext uiLayoutGetOperatorContext(uiLayout *layout)
void uiItemFullO(uiLayout *layout, const char *opname, const char *name, int icon, IDProperty *properties, wmOperatorCallContext context, const eUI_Item_Flag flag, PointerRNA *r_opptr)
void uiItemDecoratorR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index)
uiLayout * uiLayoutListBox(uiLayout *layout, uiList *ui_list, PointerRNA *actptr, PropertyRNA *actprop)
static bool block_search_filter_tag_buttons(uiBlock *block, const char *search_filter)
void uiItemPopoverPanelFromGroup(uiLayout *layout, bContext *C, int space_id, int region_id, const char *context, const char *category)
void uiItemEnumO(uiLayout *layout, const char *opname, const char *name, int icon, const char *propname, int value)
void uiItemEnumR_string(uiLayout *layout, PointerRNA *ptr, const char *propname, const char *value, const char *name, int icon)
bool uiLayoutGetActive(uiLayout *layout)
static void ui_layout_heading_label_add(uiLayout *layout, uiLayout *heading_layout, bool right_align, bool respect_prop_split)
static void ui_litem_layout_row(uiLayout *litem)
uiLayout * uiLayoutRowWithHeading(uiLayout *layout, bool align, const char *heading)
static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum)
static void ui_item_enum_expand_tabs(uiLayout *layout, bContext *C, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *ptr_highlight, PropertyRNA *prop_highlight, const char *uiname, const int h, const bool icon_only)
BLI_INLINE bool ui_layout_is_radial(const uiLayout *layout)
float uiLayoutGetSearchWeight(uiLayout *layout)
bool uiLayoutGetKeepAspect(uiLayout *layout)
static void ui_layout_add_padding_button(uiLayoutRoot *root)
void uiItemMenuEnumR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, const char *name, int icon)
bool uiLayoutGetPropSep(uiLayout *layout)
static void ui_layout_introspect_button(DynStr *ds, const uiButtonItem *bitem)
static void ui_item_array(uiLayout *layout, uiBlock *block, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, const int len, int x, const int y, int w, const int, const bool expand, const bool slider, const int toggle, const bool icon_only, const bool compact, const bool show_text)
void ui_item_paneltype_func(bContext *C, uiLayout *layout, void *arg_pt)
static int ui_text_icon_width_ex(uiLayout *layout, const char *name, int icon, const uiTextIconPadFactor &pad_factor, const uiFontStyle *fstyle)
static int ui_layout_vary_direction(uiLayout *layout)
void uiItemMenuEnumFullO(uiLayout *layout, const bContext *C, const char *opname, const char *propname, const char *name, int icon, PointerRNA *r_opptr)
static void menu_item_enum_rna_menu(bContext *, uiLayout *layout, void *arg)
void UI_paneltype_draw(bContext *C, PanelType *pt, uiLayout *layout)
void uiItemFullR_with_popover(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index, int value, const eUI_Item_Flag flag, const char *name, int icon, const char *panel_type)
void uiItemLDrag(uiLayout *layout, PointerRNA *ptr, const char *name, int icon)
void uiLayoutSetUnitsX(uiLayout *layout, float unit)
void uiItemFloatO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, float value)
static void ui_litem_estimate_row(uiLayout *litem)
static void ui_layout_end(uiBlock *block, uiLayout *layout, int *r_x, int *r_y)
eUIEmbossType uiLayoutGetEmboss(uiLayout *layout)
void uiItemPointerR(uiLayout *layout, PointerRNA *ptr, const char *propname, PointerRNA *searchptr, const char *searchpropname, const char *name, int icon)
constexpr uiTextIconPadFactor ui_text_pad_default
void uiLayoutSetTooltipFunc(uiLayout *layout, uiButToolTipFunc func, void *arg, uiCopyArgFunc copy_arg, uiFreeArgFunc free_arg)
static bool block_search_panel_label_matches(const uiBlock *block, const char *search_string)
int uiLayoutGetLocalDir(const uiLayout *layout)
void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, const char *name, int icon, const char *placeholder)
PanelLayout uiLayoutPanel(const bContext *C, uiLayout *layout, const char *idname, const bool default_closed)
void uiItemSpacer(uiLayout *layout)
void uiItemM_ptr(uiLayout *layout, MenuType *mt, const char *name, int icon)
static void ui_litem_grid_flow_compute(blender::Span< uiItem * > items, const UILayoutGridFlowInput *parameters, UILayoutGridFlowOutput *results)
void ui_layout_list_set_labels_active(uiLayout *layout)
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
void uiLayoutSetKeepAspect(uiLayout *layout, bool keepaspect)
bool uiLayoutGetFixedSize(uiLayout *layout)
#define UI_ITEM_VARY_Y
static void ui_litem_estimate_box(uiLayout *litem)
static void ui_rna_collection_search_arg_free_fn(void *ptr)
void UI_context_active_but_prop_get_filebrowser(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop, bool *r_is_undo, bool *r_is_userdef)
void uiItemFullR_with_menu(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index, int value, const eUI_Item_Flag flag, const char *name, int icon, const char *menu_type)
static bool ui_layout_has_panel_label(const uiLayout *layout, const PanelType *pt)
#define UI_PROP_DECORATE
static void ui_layout_introspect_items(DynStr *ds, blender::Span< uiItem * > items)
void uiItemMContents(uiLayout *layout, const char *menuname)
static void ui_litem_estimate_panel_header(uiLayout *litem)
static void ui_keymap_but_cb(bContext *, void *but_v, void *)
bool uiLayoutGetActiveDefault(uiLayout *layout)
static const char * ui_item_name_add_colon(const char *name, char namestr[UI_MAX_NAME_STR])
static uiLayout * ui_layout_heading_find(uiLayout *cur_layout)
static void ui_litem_estimate_grid_flow(uiLayout *litem)
void uiItemsEnumO(uiLayout *layout, const char *opname, const char *propname)
constexpr uiTextIconPadFactor ui_text_pad_compact
PanelType * UI_but_paneltype_get(const uiBut *but)
void uiItemEnumR_string_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, const char *value, const char *name, int icon)
static uiButtonItem * ui_layout_find_button_item(const uiLayout *layout, const uiBut *but)
static void ui_litem_estimate_root(uiLayout *)
static void menu_item_enum_opname_menu(bContext *C, uiLayout *layout, void *arg)
void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, int index)
void uiLayoutSetEmboss(uiLayout *layout, eUIEmbossType emboss)
void uiLayoutSetSearchWeight(uiLayout *layout, const float weight)
static void ui_item_scale(uiLayout *litem, const float scale[2])
int uiLayoutGetWidth(uiLayout *layout)
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname)
static void search_id_collection(StructRNA *ptype, PointerRNA *r_ptr, PropertyRNA **r_prop)
uiLayout * uiLayoutColumnWithHeading(uiLayout *layout, bool align, const char *heading)
static void ui_litem_layout_split(uiLayout *litem)
uiLayout * uiLayoutGridFlow(uiLayout *layout, bool row_major, int columns_len, bool even_columns, bool even_rows, bool align)
const char * UI_layout_introspect(uiLayout *layout)
void uiItemEnumO_value(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
float uiLayoutGetScaleX(uiLayout *layout)
bool UI_block_layout_needs_resolving(const uiBlock *block)
PanelLayout uiLayoutPanelProp(const bContext *C, uiLayout *layout, PointerRNA *open_prop_owner, const char *open_prop_name)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiItemS_ex(uiLayout *layout, float factor, const LayoutSeparatorType type)
void uiLayoutListItemAddPadding(uiLayout *layout)
void uiLayoutSetContextFromBut(uiLayout *layout, uiBut *but)
static void ui_but_tip_from_enum_item(uiBut *but, const EnumPropertyItem *item)
uiLayout * uiLayoutRadial(uiLayout *layout)
void UI_block_layout_free(uiBlock *block)
uiLayout * uiItemL_respect_property_split(uiLayout *layout, const char *text, int icon)
static void ui_litem_layout_radial(uiLayout *litem)
#define UI_OPERATOR_ERROR_RET(_ot, _opname, return_statement)
void uiItemFullOMenuHold_ptr(uiLayout *layout, wmOperatorType *ot, const char *name, int icon, IDProperty *properties, const wmOperatorCallContext context, const eUI_Item_Flag flag, const char *menu_id, PointerRNA *r_opptr)
static void ui_litem_estimate_absolute(uiLayout *litem)
static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2)
static int ui_item_fit(const int item, const int pos, const int all, const int available, const bool is_last, const int alignment, float *extra_pixel)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemPointerR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, const char *name, int icon, bool results_are_suggestions)
static void ui_item_menu_hold(bContext *C, ARegion *butregion, uiBut *but)
uiLayout * uiLayoutSplit(uiLayout *layout, float percentage, bool align)
void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
void uiItemEnumO_ptr(uiLayout *layout, wmOperatorType *ot, const char *name, int icon, const char *propname, int value)
static uiBut * ui_item_menu(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg, void *argN, const char *tip, bool force_menu, uiButArgNFree func_argN_free_fn=MEM_freeN, uiButArgNCopy func_argN_copy_fn=MEM_dupallocN)
void uiItemEnumR_prop(uiLayout *layout, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int value)
static bool ui_layout_variable_size(uiLayout *layout)
uiBlock * uiLayoutAbsoluteBlock(uiLayout *layout)
void uiLayoutSetActivateInit(uiLayout *layout, bool activate_init)
static void ui_layout_free(uiLayout *layout)
static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, const char *uiname, const int h, const bool icon_only)
void uiItemStringO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, const char *value)
static void ui_litem_layout_box(uiLayout *litem)
void uiItemProgressIndicator(uiLayout *layout, const char *text, const float factor, const eButProgressType progress_type)
static void ui_paneltype_draw_impl(bContext *C, PanelType *pt, uiLayout *layout, bool show_header)
@ ITEM_LAYOUT_COLUMN_FLOW
@ ITEM_LAYOUT_SPLIT
@ ITEM_LAYOUT_GRID_FLOW
@ ITEM_LAYOUT_ROOT
@ ITEM_LAYOUT_OVERLAP
@ ITEM_LAYOUT_PANEL_HEADER
@ ITEM_LAYOUT_RADIAL
@ ITEM_LAYOUT_ROW
@ ITEM_LAYOUT_ROW_FLOW
@ ITEM_LAYOUT_BOX
@ ITEM_BUTTON
@ ITEM_LAYOUT_COLUMN
@ ITEM_LAYOUT_ABSOLUTE
@ ITEM_LAYOUT_PANEL_BODY
uiLayout * uiLayoutOverlap(uiLayout *layout)
void uiItemsFullEnumO_items(uiLayout *layout, wmOperatorType *ot, const PointerRNA &ptr, PropertyRNA *prop, IDProperty *properties, wmOperatorCallContext context, eUI_Item_Flag flag, const EnumPropertyItem *item_array, int totitem, int active)
static void ui_item_size(const uiItem *item, int *r_w, int *r_h)
void UI_block_layout_set_current(uiBlock *block, uiLayout *layout)
void uiItemMenuF(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg)
void uiLayoutSetContextString(uiLayout *layout, const char *name, blender::StringRef value)
static void ui_item_align(uiLayout *litem, short nr)
static void ui_litem_layout_grid_flow(uiLayout *litem)
static void ui_item_position(uiItem *item, const int x, const int y, const int w, const int h)
void ui_layout_remove_but(uiLayout *layout, const uiBut *but)
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
bContextStore * uiLayoutGetContextStore(uiLayout *layout)
static uiLayout * ui_item_local_sublayout(uiLayout *test, uiLayout *layout, bool align)
void uiItemPopoverPanel_ptr(uiLayout *layout, const bContext *C, PanelType *pt, const char *name, int icon)
bool uiLayoutGetRedAlert(uiLayout *layout)
constexpr uiTextIconPadFactor ui_text_pad_none
void uiItemMenuEnumFullO_ptr(uiLayout *layout, const bContext *C, wmOperatorType *ot, const char *propname, const char *name, int icon, PointerRNA *r_opptr)
void uiItemMenuEnumR(uiLayout *layout, PointerRNA *ptr, const char *propname, const char *name, int icon)
void ui_layout_add_but(uiLayout *layout, uiBut *but)
static void ui_litem_layout_root(uiLayout *litem)
void uiLayoutContextCopy(uiLayout *layout, const bContextStore *context)
static int ui_text_icon_width(uiLayout *layout, const char *name, const int icon, const bool compact)
void uiItemMenuFN(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *argN)
static void ui_item_enum_expand_elem_exec(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, const char *uiname, const int h, const eButType but_type, const bool icon_only, const EnumPropertyItem *item, const bool is_first)
void uiItemTabsEnumR_prop(uiLayout *layout, bContext *C, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *ptr_highlight, PropertyRNA *prop_highlight, bool icon_only)
static void ui_item_move(uiItem *item, const int delta_xmin, const int delta_xmax)
void uiItemPopoverPanel(uiLayout *layout, const bContext *C, const char *panel_type, const char *name, int icon)
static void ui_litem_layout_root_radial(uiLayout *litem)
static bool ui_item_is_radial_drawable(uiButtonItem *bitem)
std::optional< blender::StringRefNull > UI_but_asset_shelf_type_idname_get(const uiBut *but)
void uiItemM(uiLayout *layout, const char *menuname, const char *name, int icon)
static void ui_litem_estimate_panel_body(uiLayout *litem)
wmOperatorType * UI_but_operatortype_get_from_enum_menu(uiBut *but, PropertyRNA **r_prop)
void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv)
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *ptr)
bool uiLayoutEndsWithPanelHeader(const uiLayout &layout)
void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname, IDProperty *properties, wmOperatorCallContext context, eUI_Item_Flag flag, const int active)
bool enabled
double parameters[NUM_PARAMETERS]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
static ulong state[N]
#define G(x, y, z)
static void progress_bar(int, const char *)
std::unique_ptr< IDProperty, IDPropertyDeleter > create_group(StringRefNull prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
void RNA_property_boolean_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, bool value)
PropertyRNA * RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
bool RNA_property_array_check(PropertyRNA *prop)
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
bool RNA_property_is_unlink(PropertyRNA *prop)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
int RNA_enum_from_name(const EnumPropertyItem *item, const char *name)
bool RNA_struct_is_ID(const StructRNA *type)
bool RNA_enum_value_from_id(const EnumPropertyItem *item, const char *identifier, int *r_value)
eStringPropertySearchFlag RNA_property_string_search_flag(PropertyRNA *prop)
int RNA_property_ui_icon(const PropertyRNA *prop)
StructRNA * RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
const char * RNA_property_description(PropertyRNA *prop)
bool RNA_property_animateable(const PointerRNA *ptr, PropertyRNA *prop_orig)
void RNA_property_boolean_get_array(PointerRNA *ptr, PropertyRNA *prop, bool *values)
PropertyType RNA_property_type(PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
char RNA_property_array_item_char(PropertyRNA *prop, int index)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
const char * RNA_struct_identifier(const StructRNA *type)
void RNA_property_enum_items_gettexted(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
const char * RNA_property_translation_context(const PropertyRNA *prop)
int RNA_property_array_dimension(const PointerRNA *ptr, PropertyRNA *prop, int length[])
int RNA_property_flag(PropertyRNA *prop)
int RNA_struct_ui_icon(const StructRNA *type)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
const char * RNA_property_ui_name(const PropertyRNA *prop)
bool RNA_enum_name(const EnumPropertyItem *item, const int value, const char **r_name)
void RNA_property_enum_items_gettexted_all(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_struct_name_property(const StructRNA *type)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
PointerRNA RNA_main_pointer_create(Main *main)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_property_enum_items(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
bool RNA_property_boolean_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
const char * RNA_property_identifier(const PropertyRNA *prop)
__int64 int64_t
Definition stdint.h:89
ListBase paneltypes
const char * identifier
Definition RNA_types.hh:506
const char * name
Definition RNA_types.hh:510
const char * description
Definition RNA_types.hh:512
Definition DNA_ID.h:413
blender::Vector< LayoutPanelBody > bodies
blender::Vector< LayoutPanelHeader > headers
void * last
void * first
char opname[OP_MAX_TYPENAME]
char propname[MAX_IDPROP_NAME]
wmOperatorCallContext opcontext
MenuTypeFlag flag
const char * description
char label[BKE_ST_MAXNAME]
char idname[BKE_ST_MAXNAME]
void(* draw)(const bContext *C, Menu *menu)
void(* listener)(const wmRegionListenerParams *params)
char translation_context[BKE_ST_MAXNAME]
uiLayout * layout
uiLayout * header
uiLayout * body
void(* draw)(const bContext *C, Panel *panel)
bool(* poll)(const bContext *C, PanelType *pt)
void(* listener)(const wmRegionListenerParams *params)
char translation_context[BKE_ST_MAXNAME]
ListBase children
char label[BKE_ST_MAXNAME]
void(* draw_header)(const bContext *C, Panel *panel)
const char * description
PointerRNA * custom_data_ptr
LayoutPanels layout_panels
struct PanelType * type
struct Panel_Runtime * runtime
struct uiLayout * layout
ID * owner_id
Definition RNA_types.hh:40
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
float xmax
float xmin
float ymax
float ymin
eUIEmbossType emboss
ListBase layouts
PieMenuData pie_data
uiLayout * curlayout
uiPopupBlockHandle * handle
blender::Vector< std::unique_ptr< bContextStore > > contexts
ListBase buttons
uiButHandleFunc func
blender::Vector< uiButtonGroup > button_groups
PointerRNA decorated_rnapoin
PropertyRNA * decorated_rnaprop
eButProgressType progress_type
PropertyRNA * rnasearchprop
PointerRNA rnasearchpoin
const char * tip
void * custom_data
RadialDirection pie_dir
PropertyRNA * rnaprop
wmOperatorType * optype
eButType type
float search_weight
uiBlock * block
eUIEmbossType emboss
PointerRNA * opptr
uiMenuCreateFunc menu_create_func
const bContextStore * context
std::string drawstr
uiButArgNFree func_argN_free_fn
uiBut * next
std::string str
void * hold_argN
BIFIconID icon
uiButArgNCopy func_argN_copy_fn
PointerRNA rnapoin
uiLayout * layout
void * func_argN
virtual ~uiItem()=default
uiItem(const uiItem &)=default
uiItemType type
uiItemInternalFlag flag
uiItem()=default
const uiStyle * style
uiLayoutRoot * next
uiLayoutRoot * prev
wmOperatorCallContext opcontext
uiMenuHandleFunc handlefunc
uiLayout * layout
char heading[UI_MAX_NAME_STR]
uiLayoutRoot * root
bContextStore * context
eUIEmbossType emboss
uiLayout * child_items_layout
uiLayout * parent
blender::Vector< uiItem * > items
short templatespace
uiFontStyle widget
uint8_t modifier
Definition WM_types.hh:739
const char * name
Definition WM_types.hh:990
std::string(* get_description)(bContext *C, wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1074
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct wmEvent * eventstate
void WM_main_add_notifier(uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
std::optional< std::string > WM_key_event_operator_string(const bContext *C, const char *opname, wmOperatorCallContext opcontext, IDProperty *properties, const bool is_strict)
std::optional< std::string > WM_keymap_item_to_string(const wmKeyMapItem *kmi, const bool compact)
MenuType * WM_menutype_find(const char *idname, bool quiet)
bool WM_menutype_poll(bContext *C, MenuType *mt)
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
std::string WM_operator_pystring_ex(bContext *C, wmOperator *op, const bool all_args, const bool macro_args, wmOperatorType *ot, PointerRNA *opptr)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)
PanelType * WM_paneltype_find(const char *idname, bool quiet)
uint8_t flag
Definition wm_window.cc:138