Blender V5.0
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
8
9#include <algorithm>
10#include <cmath>
11#include <cstdlib>
12#include <cstring>
13
14#include "MEM_guardedalloc.h"
15
16#include "DNA_screen_types.h"
17#include "DNA_userdef_types.h"
18
19#include "BLI_array.hh"
20#include "BLI_dynstr.h"
21#include "BLI_listbase.h"
22#include "BLI_math_base.h"
23#include "BLI_path_utils.hh"
24#include "BLI_rect.h"
25#include "BLI_string_ref.hh"
26#include "BLI_string_utf8.h"
27#include "BLI_utildefines.h"
28
29#include "BLT_translation.hh"
30
31#include "BKE_context.hh"
32#include "BKE_global.hh"
33#include "BKE_idprop.hh"
34#include "BKE_lib_id.hh"
35#include "BKE_path_templates.hh"
36#include "BKE_screen.hh"
37
38#include "RNA_access.hh"
39#include "RNA_prototypes.hh"
40
42
43#include "ED_id_management.hh"
44
45#include "WM_api.hh"
46#include "WM_types.hh"
47
48#include "fmt/format.h"
49#include "interface_intern.hh"
50
53struct uiButtonItem;
54
55/* Show an icon button after each RNA button to use to quickly set keyframes,
56 * this is a way to display animation/driven/override status, see #54951. */
57#define UI_PROP_DECORATE
58/* Alternate draw mode where some buttons can use single icon width,
59 * giving more room for the text at the expense of nicely aligned text. */
60#define UI_PROP_SEP_ICON_WIDTH_EXCEPTION
61
62/* -------------------------------------------------------------------- */
65
66#define UI_OPERATOR_ERROR_RET(_ot, _opname) \
67 if (ot == nullptr) { \
68 ui_item_disabled(this, _opname); \
69 RNA_warning("'%s' unknown operator", _opname); \
70 return PointerRNA_NULL; \
71 } \
72 (void)0
73
74#define UI_ITEM_PROP_SEP_DIVIDE 0.4f
75
76/* uiLayoutRoot */
77
94
95namespace blender::ui {
96/* Item */
97
98enum class ItemType : int8_t {
100
112 LayoutRadial, /* AKA: menu pie. */
113
115#if 0
116 TemplateColumnFlow,
117 TemplateSplit,
118 TemplateBox,
119
120 TemplateHeader,
121 TemplateHeaderID,
122#endif
123};
124
125enum class ItemInternalFlag : uint8_t {
127 FixedSize = 1 << 1,
128
129 BoxItem = 1 << 2, /* The item is "inside" a box item */
130 PropSep = 1 << 3,
132 /* Show an icon button next to each property (to set keyframes, show status).
133 * Enabled by default, depends on 'ItemInternalFlag::PropSep'. */
134 PropDecorate = 1 << 5,
136};
138
139
141 static void inside_property_split_set(uiItem *item, bool inside_prop_sep)
142 {
144 }
145
146 [[nodiscard]] static bool use_property_decorate_no_pad(const uiItem *item)
147 {
148 return bool(item->flag_ & ItemInternalFlag::PropDecorateNoPad);
149 };
150
151 [[nodiscard]] static bool box_item(const uiItem *item)
152 {
153 return bool(item->flag_ & ItemInternalFlag::BoxItem);
154 }
155 static void box_item_set(uiItem *item, bool box_item)
156 {
158 }
159
160 [[nodiscard]] static bool auto_fixed_size(const uiItem *item)
161 {
162 return bool(item->flag_ & ItemInternalFlag::AutoFixedSize);
163 }
168};
169
172 static void init_from_parent(uiLayout *item, uiLayout *layout, int align);
173
174 static void layout_add_but(uiLayout *layout, uiBut *but);
175 static void layout_remove_but(uiLayout *layout, const uiBut *but);
176 static void layout_estimate(uiLayout *layout);
177 static void layout_resolve(uiLayout *layout);
178 static uiButtonItem *ui_layout_find_button_item(const uiLayout *layout, const uiBut *but);
179 static uiLayout *ui_item_prop_split_layout_hack(uiLayout *layout_parent, uiLayout *layout_split);
180};
181
182} // namespace blender::ui
183
186
188
190{
191 return type_;
192};
193
195
198
199struct uiButtonItem : public uiItem {
200 uiBut *but = nullptr;
202};
203
211
219
222 void resolve_impl() override;
223};
224
225struct LayoutOverlap : public uiLayout {
227
228 void estimate_impl() override;
229 void resolve_impl() override;
230};
231
232struct LayoutRadial : public uiLayout {
234
235 void estimate_impl() override {};
236 void resolve_impl() override;
237};
238
239struct LayoutAbsolute : public uiLayout {
241
242 void estimate_impl() override;
243 void resolve_impl() override;
244};
245
246struct uiLayoutItemFlow : public uiLayout {
247 int number = 0;
248 int totcol = 0;
249 uiLayoutItemFlow() : uiLayout(uiItemType::LayoutColumnFlow, nullptr) {}
250
251 void estimate_impl() override;
252 void resolve_impl() override;
253};
254
256 /* Extra parameters */
257 bool row_major = false; /* Fill first row first, instead of filling first column first. */
258 bool even_columns = false; /* Same width for all columns. */
259 bool even_rows = false; /* Same height for all rows. */
266 int columns_len = 0;
267
268 /* Pure internal runtime storage. */
269 int tot_items = 0, tot_columns = 0, tot_rows = 0;
270
272
273 void estimate_impl() override;
274 void resolve_impl() override;
275};
276
278 uiBut *roundbox = nullptr;
280
281 void estimate_impl() override;
282 void resolve_impl() override;
283};
284
293
298
300 float percentage = 0.0f;
302
303 void estimate_impl() override;
304 void resolve_impl() override;
305};
306
308
309/* -------------------------------------------------------------------- */
312
314{
315 const int len = name.size();
316
317 if (len != 0 && len + 1 < UI_MAX_NAME_STR) {
318 memcpy(namestr, name.data(), len);
319 namestr[len] = ':';
320 namestr[len + 1] = '\0';
321 return namestr;
322 }
323
324 return name;
325}
326
328{
329 const int len = name.size();
330
331 if (len != 0 && len + 1 < UI_MAX_NAME_STR) {
332 memcpy(namestr, name.data(), len);
333 namestr[len] = ':';
334 namestr[len + 1] = '\0';
335 return namestr;
336 }
337
338 return name;
339}
340
341static int ui_item_fit(const int item,
342 const int pos,
343 const int all,
344 const int available,
345 const bool is_last,
346 const blender::ui::LayoutAlign alignment,
347 float *extra_pixel)
348{
349 /* available == 0 is unlimited */
350 if (ELEM(0, available, all)) {
351 return item;
352 }
353
354 if (all > available) {
355 /* contents is bigger than available space */
356 if (is_last) {
357 return available - pos;
358 }
359
360 const float width = *extra_pixel + (item * available) / float(all);
361 *extra_pixel = width - int(width);
362 return int(width);
363 }
364
365 /* contents is smaller or equal to available space */
366 if (alignment == blender::ui::LayoutAlign::Expand) {
367 if (is_last) {
368 return available - pos;
369 }
370
371 const float width = *extra_pixel + (item * available) / float(all);
372 *extra_pixel = width - int(width);
373 return int(width);
374 }
375 return item;
376}
377
378/* variable button size in which direction? */
379#define UI_ITEM_VARY_X 1
380#define UI_ITEM_VARY_Y 2
381
391
393{
394 /* Note that this code is probably a bit unreliable, we'd probably want to know whether it's
395 * variable in X and/or Y, etc. But for now it mimics previous one,
396 * with addition of variable flag set for children of grid-flow layouts. */
397 return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size();
398}
399
405 float text;
406 float icon;
408};
409
418constexpr uiTextIconPadFactor ui_text_pad_default = {1.50f, 0.25f, 0.0f};
419
421constexpr uiTextIconPadFactor ui_text_pad_compact = {1.25f, 0.35f, 0.0f};
422
424constexpr uiTextIconPadFactor ui_text_pad_none = {0.25f, 1.50f, 0.0f};
425
430 const StringRef name,
431 int icon,
432 const uiTextIconPadFactor &pad_factor,
433 const uiFontStyle *fstyle)
434{
435 const int unit_x = UI_UNIT_X * (layout->scale_x() ? layout->scale_x() : 1.0f);
436
437 /* When there is no text, always behave as if this is an icon-only button
438 * since it's not useful to return empty space. */
439 if (icon && name.is_empty()) {
440 return unit_x * (1.0f + pad_factor.icon_only);
441 }
442
443 if (ui_layout_variable_size(layout)) {
444 if (!icon && name.is_empty()) {
445 return unit_x * (1.0f + pad_factor.icon_only);
446 }
447
449 layout->fixed_size_set(true);
450 }
451
452 float margin = pad_factor.text;
453 if (icon) {
454 margin += pad_factor.icon;
455 }
456
457 const float aspect = layout->block()->aspect;
458 return UI_fontstyle_string_width_with_block_aspect(fstyle, name, aspect) +
459 int(ceilf(unit_x * margin));
460 }
461 return unit_x * 10;
462}
463
464static int ui_text_icon_width(uiLayout *layout,
465 const StringRef name,
466 const int icon,
467 const bool compact)
468{
471}
472
473static void ui_item_size(const uiItem *item, int *r_w, int *r_h)
474{
475 if (item->type() == uiItemType::Button) {
476 const uiButtonItem *bitem = static_cast<const uiButtonItem *>(item);
477
478 if (r_w) {
479 *r_w = BLI_rctf_size_x(&bitem->but->rect);
480 }
481 if (r_h) {
482 *r_h = BLI_rctf_size_y(&bitem->but->rect);
483 }
484 }
485 else {
486 const uiLayout *litem = static_cast<const uiLayout *>(item);
487
488 if (r_w) {
489 *r_w = litem->w_;
490 }
491 if (r_h) {
492 *r_h = litem->h_;
493 }
494 }
495}
496
497static void ui_item_offset(const uiItem *item, int *r_x, int *r_y)
498{
499 if (item->type() == uiItemType::Button) {
500 const uiButtonItem *bitem = static_cast<const uiButtonItem *>(item);
501
502 if (r_x) {
503 *r_x = bitem->but->rect.xmin;
504 }
505 if (r_y) {
506 *r_y = bitem->but->rect.ymin;
507 }
508 }
509 else {
510 if (r_x) {
511 *r_x = 0;
512 }
513 if (r_y) {
514 *r_y = 0;
515 }
516 }
517}
518
519static void ui_item_position(uiItem *item, const int x, const int y, const int w, const int h)
520{
521 if (item->type() == uiItemType::Button) {
522 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
523
524 bitem->but->rect.xmin = x;
525 bitem->but->rect.ymin = y;
526 bitem->but->rect.xmax = x + w;
527 bitem->but->rect.ymax = y + h;
528
529 ui_but_update(bitem->but); /* For `strlen`. */
530 }
531 else {
532 uiLayout *litem = static_cast<uiLayout *>(item);
533
534 litem->x_ = x;
535 litem->y_ = y + h;
536 litem->w_ = w;
537 litem->h_ = h;
538 }
539}
540
541static void ui_item_move(uiItem *item, const int delta_xmin, const int delta_xmax)
542{
543 if (item->type() == uiItemType::Button) {
544 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
545
546 bitem->but->rect.xmin += delta_xmin;
547 bitem->but->rect.xmax += delta_xmax;
548
549 ui_but_update(bitem->but); /* For `strlen`. */
550 }
551 else {
552 uiLayout *litem = static_cast<uiLayout *>(item);
553
554 if (delta_xmin > 0) {
555 litem->x_ += delta_xmin;
556 }
557 else {
558 litem->w_ += delta_xmax;
559 }
560 }
561}
562
564
565/* -------------------------------------------------------------------- */
568
570{
571 switch (this->type()) {
572 case uiItemType::LayoutRow:
573 case uiItemType::LayoutRoot:
574 case uiItemType::LayoutOverlap:
575 case uiItemType::LayoutPanelHeader:
576 case uiItemType::LayoutGridFlow:
578 case uiItemType::LayoutColumn:
579 case uiItemType::LayoutColumnFlow:
580 case uiItemType::LayoutSplit:
581 case uiItemType::LayoutAbsolute:
582 case uiItemType::LayoutBox:
583 case uiItemType::LayoutPanelBody:
584 default:
586 }
587}
588
589static uiLayout *ui_item_local_sublayout(uiLayout *test, uiLayout *layout, bool align)
590{
591 uiLayout *sub;
593 sub = &layout->row(align);
594 }
595 else {
596 sub = &layout->column(align);
597 }
598
599 sub->space_ = 0;
600 return sub;
601}
602
603static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
604{
605 wmWindow *win = CTX_wm_window(C);
606 uiBut *but = static_cast<uiBut *>(arg_but);
607 PointerRNA *ptr = &but->rnapoin;
608 PropertyRNA *prop = but->rnaprop;
609 const int index = POINTER_AS_INT(arg_index);
610 const bool shift = win->eventstate->modifier & KM_SHIFT;
611 const int len = RNA_property_array_length(ptr, prop);
612
613 if (!shift) {
614 BLI_assert(index < len);
616 value_array.fill(false);
617 value_array[index] = true;
618
619 RNA_property_boolean_set_array(ptr, prop, value_array.data());
620
621 RNA_property_update(C, ptr, prop);
622
623 for (const std::unique_ptr<uiBut> &cbut : but->block->buttons) {
624 ui_but_update(cbut.get());
625 }
626 }
627}
628
629/* create buttons for an item with an RNA array */
630static void ui_item_array(uiLayout *layout,
631 uiBlock *block,
632 const StringRef name,
633 int icon,
635 PropertyRNA *prop,
636 const int len,
637 int x,
638 const int y,
639 int w,
640 const int /*h*/,
641 const bool expand,
642 const bool slider,
643 const int toggle,
644 const bool icon_only,
645 const bool compact,
646 const bool show_text)
647{
648 const uiStyle *style = layout->root()->style;
649
650 /* retrieve type and subtype */
651 const PropertyType type = RNA_property_type(prop);
652 const PropertySubType subtype = RNA_property_subtype(prop);
653
654 uiLayout *sub = ui_item_local_sublayout(layout, layout, true);
656
657 /* create label */
658 if (!name.is_empty() && show_text) {
659 uiDefBut(block, ButType::Label, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
660 }
661
662 /* create buttons */
663 if (type == PROP_BOOLEAN && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) {
664 /* special check for layer layout */
665 const int cols = (len >= 20) ? 2 : 1;
666 const int colbuts = len / (2 * cols);
667 uint layer_used = 0;
668 uint layer_active = 0;
669
670 blender::ui::block_layout_set_current(block, &layout->absolute(false));
671
672 const int butw = UI_UNIT_X * 0.75;
673 const int buth = UI_UNIT_X * 0.75;
674
675 for (int b = 0; b < cols; b++) {
677
678 for (int a = 0; a < colbuts; a++) {
679 const int layer_num = a + b * colbuts;
680 const uint layer_flag = (1u << layer_num);
681
682 if (layer_used & layer_flag) {
683 if (layer_active & layer_flag) {
684 icon = ICON_LAYER_ACTIVE;
685 }
686 else {
687 icon = ICON_LAYER_USED;
688 }
689 }
690 else {
691 icon = ICON_BLANK1;
692 }
693
694 uiBut *but = uiDefAutoButR(
695 block, ptr, prop, layer_num, "", icon, x + butw * a, y + buth, butw, buth);
696 if (subtype == PROP_LAYER_MEMBER) {
697 UI_but_func_set(but, ui_layer_but_cb, but, POINTER_FROM_INT(layer_num));
698 }
699 }
700 for (int a = 0; a < colbuts; a++) {
701 const int layer_num = a + len / 2 + b * colbuts;
702 const uint layer_flag = (1u << layer_num);
703
704 if (layer_used & layer_flag) {
705 if (layer_active & layer_flag) {
706 icon = ICON_LAYER_ACTIVE;
707 }
708 else {
709 icon = ICON_LAYER_USED;
710 }
711 }
712 else {
713 icon = ICON_BLANK1;
714 }
715
716 uiBut *but = uiDefAutoButR(
717 block, ptr, prop, layer_num, "", icon, x + butw * a, y, butw, buth);
718 if (subtype == PROP_LAYER_MEMBER) {
719 UI_but_func_set(but, ui_layer_but_cb, but, POINTER_FROM_INT(layer_num));
720 }
721 }
722 UI_block_align_end(block);
723
724 x += colbuts * butw + style->buttonspacex;
725 }
726 }
727 else if (subtype == PROP_MATRIX) {
728 int totdim, dim_size[/*RNA_MAX_ARRAY_DIMENSION*/ 3];
729 int row, col;
730
731 blender::ui::block_layout_set_current(block, &layout->absolute(true));
732
733 totdim = RNA_property_array_dimension(ptr, prop, dim_size);
734 if (totdim != 2) {
735 /* Only 2D matrices supported in UI so far. */
736 return;
737 }
738
739 w /= dim_size[1];
740 // h /= dim_size[0]; /* UNUSED */
741
742 for (int a = 0; a < len; a++) {
743 /* We are going over flat array indices (the way matrices are stored internally [also check
744 * logic in #pyrna_py_from_array_index()]) -- and they are not ordered "row first" -- , so
745 * map these to rows/colums. */
746 col = a % dim_size[1];
747 row = a / dim_size[1];
748
749 uiBut *but = uiDefAutoButR(block,
750 ptr,
751 prop,
752 a,
753 "",
754 ICON_NONE,
755 x + w * col,
756 y + (dim_size[0] * UI_UNIT_Y) - (row * UI_UNIT_Y),
757 w,
758 UI_UNIT_Y);
759 if (slider && but->type == ButType::Num) {
760 uiButNumber *number_but = (uiButNumber *)but;
761 const float step_size = number_but->step_size;
762 const float precision = number_but->precision;
764 uiButNumberSlider *slider_but = reinterpret_cast<uiButNumberSlider *>(but);
765 slider_but->step_size = step_size;
766 slider_but->precision = precision;
767 }
768 }
769 }
770 else if (subtype == PROP_DIRECTION && !expand) {
771 uiDefButR_prop(block,
773 0,
774 name,
775 x,
776 y,
777 UI_UNIT_X * 3,
778 UI_UNIT_Y * 3,
779 ptr,
780 prop,
781 -1,
782 0,
783 0,
784 std::nullopt);
785 }
786 else {
787 /* NOTE: this block of code is a bit arbitrary and has just been made
788 * to work with common cases, but may need to be re-worked */
789
790 /* special case, boolean array in a menu, this could be used in a more generic way too */
791 if (ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA) && !expand && ELEM(len, 3, 4)) {
792 uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, 0, 0, w, UI_UNIT_Y);
793 }
794 else {
795 /* Even if 'expand' is false, we expand anyway. */
796
797 /* Layout for known array sub-types. */
798 char str[3] = {'\0'};
799
800 if (!icon_only && show_text) {
801 if (type != PROP_BOOLEAN) {
802 str[1] = ':';
803 }
804 }
805
806 /* Show check-boxes for rna on a non-emboss block (menu for eg). */
807 bool *boolarr = nullptr;
808 if (type == PROP_BOOLEAN && ELEM(layout->block()->emboss,
811 {
812 boolarr = MEM_calloc_arrayN<bool>(len, __func__);
813 RNA_property_boolean_get_array(ptr, prop, boolarr);
814 }
815
816 const char *str_buf = show_text ? str : "";
817 for (int a = 0; a < len; a++) {
818 if (!icon_only && show_text) {
819 str[0] = RNA_property_array_item_char(prop, a);
820 }
821 if (boolarr) {
822 icon = boolarr[a] ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
823 }
824
825 const int width_item = ((compact && type == PROP_BOOLEAN) ?
826 min_ii(w, ui_text_icon_width(layout, str_buf, icon, false)) :
827 w);
828
829 uiBut *but = uiDefAutoButR(
830 block, ptr, prop, a, str_buf, icon, 0, 0, width_item, UI_UNIT_Y);
831 if (slider && but->type == ButType::Num) {
832 uiButNumber *number_but = (uiButNumber *)but;
833 const float step_size = number_but->step_size;
834 const float precision = number_but->precision;
836 uiButNumberSlider *slider_but = reinterpret_cast<uiButNumberSlider *>(but);
837 slider_but->step_size = step_size;
838 slider_but->precision = precision;
839 }
840 if ((toggle == 1) && but->type == ButType::Checkbox) {
841 but->type = ButType::Toggle;
842 }
843 if ((a == 0) && (subtype == PROP_AXISANGLE)) {
845 }
846 }
847
848 if (boolarr) {
849 MEM_freeN(boolarr);
850 }
851 }
852 }
853
855}
856
857static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2)
858{
859 wmWindow *win = CTX_wm_window(C);
860
861 if ((win->eventstate->modifier & KM_SHIFT) == 0) {
862 uiBut *but = (uiBut *)arg1;
863 const int enum_value = POINTER_AS_INT(arg2);
864
865 int current_value = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
866 if (!(current_value & enum_value)) {
867 current_value = enum_value;
868 }
869 else {
870 current_value &= enum_value;
871 }
872 RNA_property_enum_set(&but->rnapoin, but->rnaprop, current_value);
873 }
874}
875
880 uiBlock *block,
882 PropertyRNA *prop,
883 const std::optional<StringRef> uiname,
884 const int h,
885 const ButType but_type,
886 const bool icon_only,
887 const EnumPropertyItem *item,
888 const bool is_first)
889{
890 const char *name = (!uiname || !uiname->is_empty()) ? item->name : "";
891 const int icon = item->icon;
892 const int value = item->value;
893 const int itemw = ui_text_icon_width(block->curlayout, icon_only ? "" : name, icon, false);
894
895 uiBut *but;
896 if (icon && name[0] && !icon_only) {
898 block, but_type, 0, icon, name, 0, 0, itemw, h, ptr, prop, -1, 0, value, std::nullopt);
899 }
900 else if (icon) {
901 const int w = (is_first) ? itemw : ceilf(itemw - U.pixelsize);
902 but = uiDefIconButR_prop(
903 block, but_type, 0, icon, 0, 0, w, h, ptr, prop, -1, 0, value, std::nullopt);
904 }
905 else {
906 but = uiDefButR_prop(
907 block, but_type, 0, name, 0, 0, itemw, h, ptr, prop, -1, 0, value, std::nullopt);
908 }
909
910 if (RNA_property_flag(prop) & PROP_ENUM_FLAG) {
911 /* If this is set, assert since we're clobbering someone else's callback. */
912 /* Buttons get their block's func by default, so we cannot assert in that case either. */
913 BLI_assert(ELEM(but->func, nullptr, block->func));
915 }
916
919 }
920
921 /* Allow quick, inaccurate swipe motions to switch tabs
922 * (no need to keep cursor over them). */
923 if (but_type == ButType::Tab) {
924 but->flag |= UI_BUT_DRAG_LOCK;
925 }
926}
927
929 uiBlock *block,
931 PropertyRNA *prop,
932 const std::optional<StringRef> uiname,
933 const int h,
934 const ButType but_type,
935 const bool icon_only)
936{
937 /* XXX: The way this function currently handles uiname parameter
938 * is insane and inconsistent with general UI API:
939 *
940 * - uiname is the *enum property* label.
941 * - when it is nullptr or empty, we do not draw *enum items* labels,
942 * this doubles the icon_only parameter.
943 * - we *never* draw (i.e. really use) the enum label uiname, it is just used as a mere flag!
944 *
945 * Unfortunately, fixing this implies an API "soft break", so better to defer it for later... :/
946 * - mont29
947 */
948
950
951 const bool radial = (layout->root()->type == blender::ui::LayoutType::PieMenu);
952
953 bool free;
954 const EnumPropertyItem *item_array;
955 if (radial) {
957 static_cast<bContext *>(block->evil_C), ptr, prop, &item_array, nullptr, &free);
958 }
959 else {
961 static_cast<bContext *>(block->evil_C), ptr, prop, &item_array, nullptr, &free);
962 }
963
964 /* We don't want nested rows, cols in menus. */
965 uiLayout *layout_radial = nullptr;
966 if (radial) {
967 if (layout->root()->layout == layout) {
968 layout_radial = &layout->menu_pie();
969 blender::ui::block_layout_set_current(block, layout_radial);
970 }
971 else {
972 if (layout->type() == uiItemType::LayoutRadial) {
973 layout_radial = layout;
974 }
976 }
977 }
978 else if (ELEM(layout->type(), uiItemType::LayoutGridFlow, uiItemType::LayoutColumnFlow) ||
980 {
982 }
983 else {
985 }
986
987 for (const EnumPropertyItem *item = item_array; item->identifier; item++) {
988 const bool is_first = item == item_array;
989
990 if (!item->identifier[0]) {
991 const EnumPropertyItem *next_item = item + 1;
992
993 /* Separate items, potentially with a label. */
994 if (next_item->identifier) {
995 /* Item without identifier but with name:
996 * Add group label for the following items. */
997 if (item->name) {
998 if (!is_first) {
999 block->curlayout->separator();
1000 }
1001 block->curlayout->label(item->name, item->icon);
1002 }
1003 else if (radial && layout_radial) {
1004 layout_radial->separator();
1005 }
1006 else {
1007 block->curlayout->separator();
1008 }
1009 }
1010 continue;
1011 }
1012
1014 layout, block, ptr, prop, uiname, h, but_type, icon_only, item, is_first);
1015 }
1016
1018
1019 if (free) {
1020 MEM_freeN(item_array);
1021 }
1022}
1023static void ui_item_enum_expand(uiLayout *layout,
1024 uiBlock *block,
1025 PointerRNA *ptr,
1026 PropertyRNA *prop,
1027 const std::optional<StringRef> uiname,
1028 const int h,
1029 const bool icon_only)
1030{
1031 ui_item_enum_expand_exec(layout, block, ptr, prop, uiname, h, ButType::Row, icon_only);
1032}
1034 bContext *C,
1035 uiBlock *block,
1036 PointerRNA *ptr,
1037 PropertyRNA *prop,
1038 PointerRNA *ptr_highlight,
1039 PropertyRNA *prop_highlight,
1040 const std::optional<StringRef> uiname,
1041 const int h,
1042 const bool icon_only)
1043{
1044 const int start_size = block->buttons.size();
1045
1046 ui_item_enum_expand_exec(layout, block, ptr, prop, uiname, h, ButType::Tab, icon_only);
1047
1048 if (block->buttons.is_empty()) {
1049 return;
1050 }
1051
1052 BLI_assert(start_size != block->buttons.size());
1053
1054 for (int i = start_size; i < block->buttons.size(); i++) {
1055 uiBut *tab = block->buttons[i].get();
1057 if (icon_only) {
1059 }
1060 }
1061
1062 const bool use_custom_highlight = (prop_highlight != nullptr);
1063
1064 if (use_custom_highlight) {
1065 const int highlight_array_len = RNA_property_array_length(ptr_highlight, prop_highlight);
1066 blender::Array<bool, 64> highlight_array(highlight_array_len);
1067 RNA_property_boolean_get_array(ptr_highlight, prop_highlight, highlight_array.data());
1068 const int end = std::min<int>(start_size + highlight_array_len, block->buttons.size());
1069 for (int i = start_size; i < end; i++) {
1070 uiBut *tab_but = block->buttons[i].get();
1071 SET_FLAG_FROM_TEST(tab_but->flag, !highlight_array[i], UI_BUT_INACTIVE);
1072 }
1073 }
1074}
1075
1076/* callback for keymap item change button */
1077static void ui_keymap_but_cb(bContext * /*C*/, void *but_v, void * /*key_v*/)
1078{
1079 uiBut *but = static_cast<uiBut *>(but_v);
1081 const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but;
1082
1084 &but->rnapoin, "shift", (hotkey_but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING);
1086 &but->rnapoin, "ctrl", (hotkey_but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING);
1088 &but->rnapoin, "alt", (hotkey_but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING);
1090 &but->rnapoin, "oskey", (hotkey_but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING);
1092 &but->rnapoin, "hyper", (hotkey_but->modifier_key & KM_HYPER) ? KM_MOD_HELD : KM_NOTHING);
1093}
1094
1102 uiBlock *block,
1103 const StringRef name,
1104 const int icon,
1105 PointerRNA *ptr,
1106 PropertyRNA *prop,
1107 const int index,
1108 const int x,
1109 const int y,
1110 const int w_hint,
1111 const int h,
1112 const int flag)
1113{
1114 uiLayout *sub = layout;
1115 int prop_but_width = w_hint;
1116#ifdef UI_PROP_DECORATE
1117 uiLayout *layout_prop_decorate = nullptr;
1118 const bool use_prop_sep = layout->use_property_split();
1119 const bool use_prop_decorate = use_prop_sep && layout->use_property_decorate() &&
1121#endif
1122
1123 const bool is_keymapitem_ptr = RNA_struct_is_a(ptr->type, &RNA_KeyMapItem);
1124 if ((flag & UI_ITEM_R_FULL_EVENT) && !is_keymapitem_ptr) {
1125 RNA_warning("Data is not a keymap item struct: %s. Ignoring 'full_event' option.",
1126 RNA_struct_identifier(ptr->type));
1127 }
1128
1130
1131 /* Only add new row if more than 1 item will be added. */
1132 if (!name.is_empty()
1133#ifdef UI_PROP_DECORATE
1134 || use_prop_decorate
1135#endif
1136 )
1137 {
1138 /* Also avoid setting 'align' if possible. Set the space to zero instead as aligning a large
1139 * number of labels can end up aligning thousands of buttons when displaying key-map search (a
1140 * heavy operation), see: #78636. */
1141 sub = &layout->row(layout->align());
1142 sub->space_ = 0;
1143 }
1144
1145 if (!name.is_empty()) {
1146#ifdef UI_PROP_DECORATE
1147 if (use_prop_sep) {
1148 layout_prop_decorate = uiItemL_respect_property_split(layout, name, ICON_NONE);
1149 }
1150 else
1151#endif
1152 {
1153 int w_label;
1154 if (ui_layout_variable_size(layout)) {
1155 /* In this case, a pure label without additional padding.
1156 * Use a default width for property button(s). */
1157 prop_but_width = UI_UNIT_X * 5;
1158 w_label = ui_text_icon_width_ex(
1159 layout, name, ICON_NONE, ui_text_pad_none, UI_FSTYLE_WIDGET);
1160 }
1161 else {
1162 w_label = w_hint / 3;
1163 }
1164 uiDefBut(block, ButType::Label, 0, name, x, y, w_label, h, nullptr, 0.0, 0.0, "");
1165 }
1166 }
1167
1168 const PropertyType type = RNA_property_type(prop);
1169 const PropertySubType subtype = RNA_property_subtype(prop);
1170
1171 uiBut *but;
1172 if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH)) {
1173 blender::ui::block_layout_set_current(block, &sub->row(true));
1174 but = uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, prop_but_width - UI_UNIT_X, h);
1175
1176 if (but != nullptr) {
1177 if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH)) {
1179 if (BLI_path_is_rel(but->drawstr.c_str())) {
1181 }
1182 }
1183 }
1184 }
1185
1186 /* #BUTTONS_OT_file_browse calls #UI_context_active_but_prop_get_filebrowser. */
1187 uiDefIconButO(block,
1189 subtype == PROP_DIRPATH ? "BUTTONS_OT_directory_browse" :
1190 "BUTTONS_OT_file_browse",
1192 ICON_FILEBROWSER,
1193 x,
1194 y,
1195 UI_UNIT_X,
1196 h,
1197 std::nullopt);
1198 }
1199 else if (flag & UI_ITEM_R_EVENT) {
1200 but = uiDefButR_prop(block,
1202 0,
1203 name,
1204 x,
1205 y,
1206 prop_but_width,
1207 h,
1208 ptr,
1209 prop,
1210 index,
1211 0,
1212 0,
1213 std::nullopt);
1214 }
1215 else if ((flag & UI_ITEM_R_FULL_EVENT) && is_keymapitem_ptr) {
1216 std::string kmi_str =
1217 WM_keymap_item_to_string(static_cast<const wmKeyMapItem *>(ptr->data), false).value_or("");
1218
1219 but = uiDefButR_prop(block,
1221 0,
1222 kmi_str,
1223 x,
1224 y,
1225 prop_but_width,
1226 h,
1227 ptr,
1228 prop,
1229 0,
1230 0,
1231 0,
1232 std::nullopt);
1233 UI_but_func_set(but, ui_keymap_but_cb, but, nullptr);
1234 }
1235 else {
1236 const std::optional<StringRefNull> str = (type == PROP_ENUM && !(flag & UI_ITEM_R_ICON_ONLY)) ?
1237 std::nullopt :
1238 std::make_optional<StringRefNull>("");
1239 but = uiDefAutoButR(block, ptr, prop, index, str, icon, x, y, prop_but_width, h);
1240 }
1241
1242 /* Highlight in red on path template validity errors. */
1243 if (but != nullptr && ELEM(but->type, ButType::Text)) {
1244 /* We include PROP_NONE here because some plain string properties are used
1245 * as parts of paths. For example, the sub-paths in the compositor's File
1246 * Output node. */
1249 const std::string path = RNA_property_string_get(ptr, prop);
1251 const std::optional<blender::bke::path_templates::VariableMap> variables =
1253 static_cast<const bContext *>(block->evil_C), ptr, prop);
1254 BLI_assert(variables.has_value());
1255
1256 if (!BKE_path_validate_template(path, *variables).is_empty()) {
1258 }
1259 }
1260 }
1261 }
1262 }
1263
1264 if (flag & UI_ITEM_R_IMMEDIATE) {
1266 }
1267
1268#ifdef UI_PROP_DECORATE
1269 /* Only for alignment. */
1270 if (use_prop_decorate) { /* Note that sep flag may have been unset meanwhile. */
1271 (layout_prop_decorate ? layout_prop_decorate : sub)->label(nullptr, ICON_BLANK1);
1272 }
1273#endif /* UI_PROP_DECORATE */
1274
1276 return but;
1277}
1278
1280 PointerRNA *r_ptr,
1281 PropertyRNA **r_prop,
1282 bool *r_is_undo,
1283 bool *r_is_userdef)
1284{
1286 uiBut *prevbut = nullptr;
1287
1288 *r_ptr = {};
1289 *r_prop = nullptr;
1290 *r_is_undo = false;
1291 *r_is_userdef = false;
1292
1293 if (!region) {
1294 return;
1295 }
1296
1297 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
1298 for (const std::unique_ptr<uiBut> &but : block->buttons) {
1299 if (but && but->rnapoin.data) {
1300 if (RNA_property_type(but->rnaprop) == PROP_STRING) {
1301 prevbut = but.get();
1302 }
1303 }
1304 /* find the button before the active one */
1305 if ((but->flag & UI_BUT_LAST_ACTIVE) && prevbut) {
1306 *r_ptr = prevbut->rnapoin;
1307 *r_prop = prevbut->rnaprop;
1308 *r_is_undo = (prevbut->flag & UI_BUT_UNDO) != 0;
1309 *r_is_userdef = UI_but_is_userdef(prevbut);
1310 return;
1311 }
1312 }
1313 }
1314}
1315
1317
1318/* -------------------------------------------------------------------- */
1321
1326{
1327 if (but->tip == nullptr || but->tip[0] == '\0') {
1328 if (item->description && item->description[0] &&
1329 !(but->optype && but->optype->get_description))
1330 {
1331 but->tip = item->description;
1332 }
1333 }
1334}
1335
1336/* disabled item */
1337static void ui_item_disabled(uiLayout *layout, const char *name)
1338{
1339 uiBlock *block = layout->block();
1340
1342
1343 if (!name) {
1344 name = "";
1345 }
1346
1347 const int w = ui_text_icon_width(layout, name, 0, false);
1348
1349 uiBut *but = uiDefBut(block, ButType::Label, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
1350 UI_but_disable(but, "");
1351}
1352
1360 std::optional<StringRef> name,
1361 int icon,
1362 const blender::wm::OpCallContext context,
1363 const eUI_Item_Flag flag,
1364 PointerRNA *r_opptr)
1365{
1366 /* Take care to fill 'r_opptr' whatever happens. */
1367 uiBlock *block = layout->block();
1368
1369 std::string operator_name;
1370 if (!name) {
1371 if (ot && ot->srna && (flag & UI_ITEM_R_ICON_ONLY) == 0) {
1372 operator_name = WM_operatortype_name(ot, nullptr);
1373 name = operator_name.c_str();
1374 }
1375 else {
1376 name = "";
1377 }
1378 }
1379
1380 if (layout->root()->type == blender::ui::LayoutType::Menu && !icon) {
1381 icon = ICON_BLANK1;
1382 }
1383
1386
1387 const int w = ui_text_icon_width(layout, *name, icon, false);
1388
1389 const blender::ui::EmbossType prev_emboss = layout->emboss_or_undefined();
1390 if (flag & UI_ITEM_R_NO_BG) {
1392 }
1393
1394 /* create the button */
1395 uiBut *but;
1396 if (icon) {
1397 if (!name->is_empty()) {
1399 block, ButType::But, ot, context, icon, *name, 0, 0, w, UI_UNIT_Y, std::nullopt);
1400 }
1401 else {
1402 but = uiDefIconButO_ptr(
1403 block, ButType::But, ot, context, icon, 0, 0, w, UI_UNIT_Y, std::nullopt);
1404 }
1405 }
1406 else {
1407 but = uiDefButO_ptr(block, ButType::But, ot, context, *name, 0, 0, w, UI_UNIT_Y, std::nullopt);
1408 }
1409
1410 BLI_assert(but->optype != nullptr);
1411
1412 if (flag & UI_ITEM_R_NO_BG) {
1413 layout->emboss_set(prev_emboss);
1414 }
1415
1416 if (flag & UI_ITEM_O_DEPRESS) {
1417 but->flag |= UI_SELECT_DRAW;
1418 }
1419
1420 if (flag & UI_ITEM_R_ICON_ONLY) {
1422 }
1423
1424 if (layout->red_alert()) {
1426 }
1427
1428 if (layout->active_default()) {
1430 }
1431
1432 /* assign properties */
1433 if (r_opptr) {
1435 opptr->data = blender::bke::idprop::create_group("wmOperatorProperties").release();
1436 *r_opptr = *opptr;
1437 }
1438
1439 return but;
1440}
1441
1442static void ui_item_menu_hold(bContext *C, ARegion *butregion, uiBut *but)
1443{
1444 uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE);
1445 uiLayout *layout = UI_popup_menu_layout(pup);
1446 uiBlock *block = layout->block();
1447 UI_popup_menu_but_set(pup, butregion, but);
1448
1449 block->flag |= UI_BLOCK_POPUP_HOLD;
1450
1451 char direction = UI_DIR_DOWN;
1452 if (but->drawstr.empty()) {
1453 switch (RGN_ALIGN_ENUM_FROM_MASK(butregion->alignment)) {
1454 case RGN_ALIGN_LEFT:
1455 direction = UI_DIR_RIGHT;
1456 break;
1457 case RGN_ALIGN_RIGHT:
1458 direction = UI_DIR_LEFT;
1459 break;
1460 case RGN_ALIGN_BOTTOM:
1461 direction = UI_DIR_UP;
1462 break;
1463 default:
1464 direction = UI_DIR_DOWN;
1465 break;
1466 }
1467 }
1468 UI_block_direction_set(block, direction);
1469
1470 const char *menu_id = static_cast<const char *>(but->hold_argN);
1471 MenuType *mt = WM_menutype_find(menu_id, true);
1472 if (mt) {
1473 layout->context_set_from_but(but);
1474 UI_menutype_draw(C, mt, layout);
1475 }
1476 else {
1477 layout->label(RPT_("Menu Missing:"), ICON_NONE);
1478 layout->label(menu_id, ICON_NONE);
1479 }
1480 UI_popup_menu_end(C, pup);
1481}
1482
1484 std::optional<StringRef> name,
1485 const int icon,
1487 const eUI_Item_Flag flag)
1488{
1490 uiItemFullO_ptr_ex(this, ot, name, icon, context, flag, &ptr);
1491 return ptr;
1492}
1493
1495 std::optional<StringRef> name,
1496 int icon,
1498 const eUI_Item_Flag flag,
1499 const char *menu_id)
1500{
1502 uiBut *but = uiItemFullO_ptr_ex(this, ot, name, icon, context, flag, &ptr);
1504 return ptr;
1505}
1506
1508 const std::optional<StringRef> name,
1509 int icon,
1511 const eUI_Item_Flag flag)
1512{
1513 wmOperatorType *ot = WM_operatortype_find(opname.c_str(), false); /* print error next */
1514 UI_OPERATOR_ERROR_RET(ot, opname.c_str());
1515 return this->op(ot, name, icon, context, flag);
1516}
1517
1519{
1520 return (layout->type() == uiItemType::LayoutRadial) ||
1521 ((layout->type() == uiItemType::LayoutRoot) &&
1523}
1524
1526 const PointerRNA &ptr,
1528 IDProperty *properties,
1531 const EnumPropertyItem *item_array,
1532 int totitem,
1533 int active)
1534{
1535 const StringRefNull propname = RNA_property_identifier(prop);
1537 RNA_warning("%s.%s, not an enum type", RNA_struct_identifier(ptr.type), propname.c_str());
1538 return;
1539 }
1540
1541 uiLayout *target, *split = nullptr;
1542 uiBlock *block = this->block();
1543 const bool radial = ui_layout_is_radial(this);
1544
1545 if (radial) {
1546 target = &this->menu_pie();
1547 }
1550 {
1551 target = this;
1553
1554 /* Add a blank button to the beginning of the row. */
1557 0,
1558 ICON_BLANK1,
1559 0,
1560 0,
1561 1.25f * UI_UNIT_X,
1562 UI_UNIT_Y,
1563 nullptr,
1564 0,
1565 0,
1566 std::nullopt);
1567 }
1568 else {
1569 split = &this->split(0.0f, false);
1570 target = &split->column(this->align());
1571 }
1572
1573 bool last_iter = false;
1574 const EnumPropertyItem *item = item_array;
1575 for (int i = 1; item->identifier && !last_iter; i++, item++) {
1576 /* Handle over-sized pies. */
1577 if (radial && (totitem > PIE_MAX_ITEMS) && (i >= PIE_MAX_ITEMS)) {
1578 if (item->name) { /* only visible items */
1579 const EnumPropertyItem *tmp;
1580
1581 /* Check if there are more visible items for the next level. If not, we don't
1582 * add a new level and add the remaining item instead of the 'more' button. */
1583 for (tmp = item + 1; tmp->identifier; tmp++) {
1584 if (tmp->name) {
1585 break;
1586 }
1587 }
1588
1589 if (tmp->identifier) { /* only true if loop above found item and did early-exit */
1591 block, ot, propname, properties, item_array, totitem, context, flag);
1592 /* break since rest of items is handled in new pie level */
1593 break;
1594 }
1595 last_iter = true;
1596 }
1597 else {
1598 continue;
1599 }
1600 }
1601
1602 if (item->identifier[0]) {
1603 PointerRNA tptr = target->op(
1604 ot, (flag & UI_ITEM_R_ICON_ONLY) ? nullptr : item->name, item->icon, context, flag);
1605 if (properties) {
1606 IDP_CopyPropertyContent(tptr.data_as<IDProperty>(), properties);
1607 }
1608 RNA_property_enum_set(&tptr, prop, item->value);
1609
1610 uiBut *but = block->buttons.last().get();
1611
1612 if (active == (i - 1)) {
1613 but->flag |= UI_SELECT_DRAW;
1614 }
1615
1616 ui_but_tip_from_enum_item(but, item);
1617 }
1618 else {
1619 if (item->name) {
1620 if (item != item_array && !radial && split != nullptr) {
1621 target = &split->column(this->align());
1622 }
1623
1624 uiBut *but;
1625 if (item->icon || radial) {
1626 target->label(item->name, item->icon);
1627
1628 but = block->buttons.last().get();
1629 }
1630 else {
1631 /* Do not use uiLayout::label here, as our root layout is a menu one,
1632 * it will add a fake blank icon! */
1633 but = uiDefBut(block,
1635 0,
1636 item->name,
1637 0,
1638 0,
1639 UI_UNIT_X * 5,
1640 UI_UNIT_Y,
1641 nullptr,
1642 0.0,
1643 0.0,
1644 "");
1645 target->separator();
1646 }
1647 ui_but_tip_from_enum_item(but, item);
1648 }
1649 else {
1650 if (radial) {
1651 /* invisible dummy button to ensure all items are
1652 * always at the same position */
1653 target->separator();
1654 }
1655 else {
1656 /* XXX bug here, columns draw bottom item badly */
1657 target->separator();
1658 }
1659 }
1660 }
1661 }
1662}
1663
1665 const StringRefNull propname,
1666 IDProperty *properties,
1669 const int active)
1670{
1671 wmOperatorType *ot = WM_operatortype_find(opname.c_str(), false); /* print error next */
1672
1673 if (!ot || !ot->srna) {
1674 ui_item_disabled(this, opname.c_str());
1675 RNA_warning("%s '%s'", ot ? "operator missing srna" : "unknown operator", opname.c_str());
1676 return;
1677 }
1678
1681 /* so the context is passed to itemf functions (some need it) */
1684
1685 /* don't let bad properties slip through */
1686 BLI_assert((prop == nullptr) || (RNA_property_type(prop) == PROP_ENUM));
1687
1688 uiBlock *block = this->block();
1689 if (prop && RNA_property_type(prop) == PROP_ENUM) {
1690 const EnumPropertyItem *item_array = nullptr;
1691 int totitem;
1692 bool free;
1693
1694 if (ui_layout_is_radial(this)) {
1695 /* XXX: While "_all()" guarantees spatial stability,
1696 * it's bad when an enum has > 8 items total,
1697 * but only a small subset will ever be shown at once
1698 * (e.g. Mode Switch menu, after the introduction of GP editing modes).
1699 */
1700#if 0
1702 static_cast<bContext *>(block->evil_C), &ptr, prop, &item_array, &totitem, &free);
1703#else
1705 static_cast<bContext *>(block->evil_C), &ptr, prop, &item_array, &totitem, &free);
1706#endif
1707 }
1708 else {
1709 bContext *C = static_cast<bContext *>(block->evil_C);
1710 const bContextStore *previous_ctx = CTX_store_get(C);
1712 RNA_property_enum_items_gettexted(C, &ptr, prop, &item_array, &totitem, &free);
1713 CTX_store_set(C, previous_ctx);
1714 }
1715
1716 /* add items */
1717 this->op_enum_items(ot, ptr, prop, properties, context, flag, item_array, totitem, active);
1718
1719 if (free) {
1720 MEM_freeN(item_array);
1721 }
1722 }
1723 else if (prop && RNA_property_type(prop) != PROP_ENUM) {
1724 RNA_warning("%s.%s, not an enum type", RNA_struct_identifier(ptr.type), propname.c_str());
1725 return;
1726 }
1727 else {
1728 RNA_warning("%s.%s not found", RNA_struct_identifier(ptr.type), propname.c_str());
1729 return;
1730 }
1731}
1732
1733void uiLayout::op_enum(const StringRefNull opname, const StringRefNull propname)
1734{
1735 this->op_enum(opname, propname, nullptr, root_->opcontext, UI_ITEM_NONE);
1736}
1737
1738PointerRNA uiLayout::op(wmOperatorType *ot, const std::optional<StringRef> name, int icon)
1739{
1740 return this->op(ot, name, icon, root_->opcontext, UI_ITEM_NONE);
1741}
1742
1743PointerRNA uiLayout::op(const StringRefNull opname, const std::optional<StringRef> name, int icon)
1744{
1745 return this->op(opname, name, icon, root_->opcontext, UI_ITEM_NONE);
1746}
1747
1748/* RNA property items */
1749
1750static void ui_item_rna_size(uiLayout *layout,
1752 int icon,
1753 PointerRNA *ptr,
1754 PropertyRNA *prop,
1755 int index,
1756 bool icon_only,
1757 bool compact,
1758 int *r_w,
1759 int *r_h)
1760{
1761 int w = 0, h;
1762
1763 /* arbitrary extended width by type */
1764 const PropertyType type = RNA_property_type(prop);
1765 const PropertySubType subtype = RNA_property_subtype(prop);
1766 const int len = RNA_property_array_length(ptr, prop);
1767
1768 bool is_checkbox_only = false;
1769 if (name.is_empty() && !icon_only) {
1770 if (ELEM(type, PROP_STRING, PROP_POINTER)) {
1771 name = "non-empty text";
1772 }
1773 else if (type == PROP_BOOLEAN) {
1774 if (icon == ICON_NONE) {
1775 /* Exception for check-boxes, they need a little less space to align nicely. */
1776 is_checkbox_only = true;
1777 }
1778 icon = ICON_DOT;
1779 }
1780 else if (type == PROP_ENUM) {
1781 /* Find the longest enum item name, instead of using a dummy text! */
1782 const EnumPropertyItem *item_array;
1783 bool free;
1784 RNA_property_enum_items_gettexted(static_cast<bContext *>(layout->block()->evil_C),
1785 ptr,
1786 prop,
1787 &item_array,
1788 nullptr,
1789 &free);
1790
1791 for (const EnumPropertyItem *item = item_array; item->identifier; item++) {
1792 if (item->identifier[0]) {
1793 w = max_ii(w, ui_text_icon_width(layout, item->name, item->icon, compact));
1794 }
1795 }
1796 if (free) {
1797 MEM_freeN(item_array);
1798 }
1799 }
1800 }
1801
1802 if (!w) {
1803 if (type == PROP_ENUM && icon_only) {
1804 w = ui_text_icon_width(layout, "", ICON_BLANK1, compact);
1805 if (index != RNA_ENUM_VALUE) {
1806 w += 0.6f * UI_UNIT_X;
1807 }
1808 }
1809 else {
1810 /* not compact for float/int buttons, looks too squashed */
1812 layout, name, icon, ELEM(type, PROP_FLOAT, PROP_INT) ? false : compact);
1813 }
1814 }
1815 h = UI_UNIT_Y;
1816
1817 /* increase height for arrays */
1818 if (index == RNA_NO_INDEX && len > 0) {
1819 if (name.is_empty() && icon == ICON_NONE) {
1820 h = 0;
1821 }
1822 if (layout->use_property_split()) {
1823 h = 0;
1824 }
1825 if (ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) {
1826 h += 2 * UI_UNIT_Y;
1827 }
1828 else if (subtype == PROP_MATRIX) {
1829 int dim_size[/*RNA_MAX_ARRAY_DIMENSION*/ 3];
1830 RNA_property_array_dimension(ptr, prop, dim_size);
1831 h += dim_size[0] * UI_UNIT_Y;
1832 }
1833 else {
1834 h += len * UI_UNIT_Y;
1835 }
1836 }
1837
1838 /* Increase width requirement if in a variable size layout. */
1839 if (ui_layout_variable_size(layout)) {
1840 if (type == PROP_BOOLEAN && !name.is_empty()) {
1841 w += UI_UNIT_X / 5;
1842 }
1843 else if (is_checkbox_only) {
1844 w -= UI_UNIT_X / 4;
1845 }
1846 else if (type == PROP_ENUM && !icon_only) {
1847 w += UI_UNIT_X / 4;
1848 }
1849 else if (ELEM(type, PROP_FLOAT, PROP_INT)) {
1850 w += UI_UNIT_X * 3;
1851 }
1852 }
1853
1854 *r_w = w;
1855 *r_h = h;
1856}
1857
1858static bool ui_item_rna_is_expand(PropertyRNA *prop, int index, const eUI_Item_Flag item_flag)
1859{
1860 const bool is_array = RNA_property_array_check(prop);
1861 const int subtype = RNA_property_subtype(prop);
1862 return is_array && (index == RNA_NO_INDEX) &&
1863 ((item_flag & UI_ITEM_R_EXPAND) ||
1865}
1866
1875{
1876 for (uiLayout *parent = cur_layout; parent; parent = parent->parent()) {
1877 if (!parent->heading().is_empty()) {
1878 return parent;
1879 }
1880 }
1881
1882 return nullptr;
1883}
1884
1886 uiLayout *heading_layout,
1887 bool right_align,
1888 bool respect_prop_split)
1889{
1890 const blender::ui::LayoutAlign prev_alignment = layout->alignment();
1891
1892 if (right_align) {
1894 }
1895
1896 if (respect_prop_split) {
1897 uiItemL_respect_property_split(layout, heading_layout->heading(), ICON_NONE);
1898 }
1899 else {
1900 layout->label(heading_layout->heading(), ICON_NONE);
1901 }
1902 /* After adding the heading label, we have to mark it somehow as added, so it's not added again
1903 * for other items in this layout. For now just clear it. */
1904 heading_layout->heading_reset();
1905
1906 layout->alignment_set(prev_alignment);
1907}
1908
1915 uiLayout *layout_split)
1916{
1917 /* Tag item as using property split layout, this is inherited to children so they can get special
1918 * treatment if needed. */
1919 ItemInternal::inside_property_split_set(layout_parent, true);
1920
1921 if (layout_parent->type() == uiItemType::LayoutRow) {
1922 /* Prevent further splits within the row. */
1923 layout_parent->use_property_split_set(false);
1924
1925 layout_parent->child_items_layout_ = &layout_split->row(true);
1926 return layout_parent->child_items_layout_;
1927 }
1928 return layout_split;
1929}
1930
1933 int index,
1934 int value,
1936 const std::optional<StringRef> name_opt,
1937 int icon,
1938 const std::optional<StringRef> placeholder)
1939{
1940
1941 uiBlock *block = this->block();
1942 char namestr[UI_MAX_NAME_STR];
1943 const bool use_prop_sep = this->use_property_split();
1944 const bool inside_prop_sep = bool(flag_ & uiItemInternalFlag::InsidePropSep);
1945 /* Columns can define a heading to insert. If the first item added to a split layout doesn't have
1946 * a label to display in the first column, the heading is inserted there. Otherwise it's inserted
1947 * as a new row before the first item. */
1948 uiLayout *heading_layout = ui_layout_heading_find(this);
1949 /* Although check-boxes use the split layout, they are an exception and should only place their
1950 * label in the second column, to not make that almost empty.
1951 *
1952 * Keep using 'use_prop_sep' instead of disabling it entirely because
1953 * we need the ability to have decorators still. */
1954 bool use_prop_sep_split_label = use_prop_sep;
1955 bool use_split_empty_name = (flag & UI_ITEM_R_SPLIT_EMPTY_NAME);
1956
1957#ifdef UI_PROP_DECORATE
1958 struct DecorateInfo {
1959 bool use_prop_decorate;
1960 int len;
1961 uiLayout *layout;
1962 uiBut *but;
1963 };
1964 DecorateInfo ui_decorate{};
1965 ui_decorate.use_prop_decorate = this->use_property_decorate() && use_prop_sep;
1966
1967#endif /* UI_PROP_DECORATE */
1968
1971
1972 /* retrieve info */
1974 const bool is_array = RNA_property_array_check(prop);
1975 const int len = (is_array) ? RNA_property_array_length(ptr, prop) : 0;
1976 const bool is_id_name_prop = (ptr->owner_id == ptr->data && type == PROP_STRING &&
1978
1979 const bool icon_only = (flag & UI_ITEM_R_ICON_ONLY) != 0;
1980
1981 /* Boolean with -1 to signify that the value depends on the presence of an icon. */
1982 const int toggle = ((flag & UI_ITEM_R_TOGGLE) ? 1 : ((flag & UI_ITEM_R_ICON_NEVER) ? 0 : -1));
1983 const bool no_icon = (toggle == 0);
1984
1985 /* set name and icon */
1986 StringRef name = name_opt.value_or(icon_only ? "" : RNA_property_ui_name(prop));
1987
1988 if (type != PROP_BOOLEAN) {
1990 }
1991
1992 if (flag & UI_ITEM_R_ICON_ONLY) {
1993 /* pass */
1994 }
1996 if (use_prop_sep == false) {
1997 name = ui_item_name_add_colon(name, namestr);
1998 }
1999 }
2000 else if (type == PROP_BOOLEAN && is_array && index == RNA_NO_INDEX) {
2001 if (use_prop_sep == false) {
2002 name = ui_item_name_add_colon(name, namestr);
2003 }
2004 }
2005 else if (type == PROP_ENUM && index != RNA_ENUM_VALUE) {
2006 if (flag & UI_ITEM_R_COMPACT) {
2007 name = "";
2008 }
2009 else {
2010 if (use_prop_sep == false) {
2011 name = ui_item_name_add_colon(name, namestr);
2012 }
2013 }
2014 }
2015
2016 if (no_icon == false) {
2017 if (icon == ICON_NONE) {
2018 icon = RNA_property_ui_icon(prop);
2019 }
2020
2021 /* Menus and pie-menus don't show checkbox without this. */
2022 if ((root_->type == blender::ui::LayoutType::Menu) ||
2023 /* Use check-boxes only as a fallback in pie-menu's, when no icon is defined. */
2024 ((root_->type == blender::ui::LayoutType::PieMenu) && (icon == ICON_NONE)))
2025 {
2026 const int prop_flag = RNA_property_flag(prop);
2027 if (type == PROP_BOOLEAN) {
2028 if ((is_array == false) || (index != RNA_NO_INDEX)) {
2029 if (prop_flag & PROP_ICONS_CONSECUTIVE) {
2030 icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
2031 }
2032 else if (is_array) {
2033 icon = RNA_property_boolean_get_index(ptr, prop, index) ? ICON_CHECKBOX_HLT :
2034 ICON_CHECKBOX_DEHLT;
2035 }
2036 else {
2037 icon = RNA_property_boolean_get(ptr, prop) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
2038 }
2039 }
2040 }
2041 else if (type == PROP_ENUM) {
2042 if (index == RNA_ENUM_VALUE) {
2043 const int enum_value = RNA_property_enum_get(ptr, prop);
2044 if (prop_flag & PROP_ICONS_CONSECUTIVE) {
2045 icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
2046 }
2047 else if (prop_flag & PROP_ENUM_FLAG) {
2048 icon = (enum_value & value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
2049 }
2050 else {
2051 /* Only a single value can be chosen, so display as radio buttons. */
2052 icon = (enum_value == value) ? ICON_RADIOBUT_ON : ICON_RADIOBUT_OFF;
2053 }
2054 }
2055 }
2056 }
2057 }
2058
2059#ifdef UI_PROP_SEP_ICON_WIDTH_EXCEPTION
2060 if (use_prop_sep) {
2061 if (type == PROP_BOOLEAN && (icon == ICON_NONE) && !icon_only) {
2062 use_prop_sep_split_label = false;
2063 /* For check-boxes we make an exception: We allow showing them in a split row even without
2064 * label. It typically relates to its neighbor items, so no need for an extra label. */
2065 use_split_empty_name = true;
2066 }
2067 }
2068#endif
2069
2072 }
2073
2074 const bool slider = (flag & UI_ITEM_R_SLIDER) != 0;
2075 const bool expand = (flag & UI_ITEM_R_EXPAND) != 0;
2076 const bool no_bg = (flag & UI_ITEM_R_NO_BG) != 0;
2077 const bool compact = (flag & UI_ITEM_R_COMPACT) != 0;
2078
2079 /* get size */
2080 int w, h;
2081 ui_item_rna_size(this, name, icon, ptr, prop, index, icon_only, compact, &w, &h);
2082
2083 const blender::ui::EmbossType prev_emboss = emboss_;
2084 if (no_bg) {
2086 }
2087
2088 uiBut *but = nullptr;
2089
2090 /* Split the label / property. */
2091 uiLayout *layout_parent = this;
2092 uiLayout *layout = this;
2093 if (use_prop_sep) {
2094 uiLayout *layout_row = nullptr;
2095#ifdef UI_PROP_DECORATE
2096 if (ui_decorate.use_prop_decorate) {
2097 layout_row = &layout->row(true);
2098 layout_row->space_ = 0;
2099 ui_decorate.len = max_ii(1, len);
2100 }
2101#endif /* UI_PROP_DECORATE */
2102
2103 if (name.is_empty() && !use_split_empty_name) {
2104 /* Ensure we get a column when text is not set. */
2105 layout = &(layout_row ? layout_row : layout)->column(true);
2106 layout->space_ = 0;
2107 if (heading_layout) {
2108 ui_layout_heading_label_add(layout, heading_layout, false, false);
2109 }
2110 }
2111 else {
2112 uiLayout *layout_split =
2113 &(layout_row ? layout_row : layout)->split(UI_ITEM_PROP_SEP_DIVIDE, true);
2114 bool label_added = false;
2115 uiLayout *layout_sub = &layout_split->column(true);
2116 layout_sub->space_ = 0;
2117
2119 layout_sub->enabled_set(false);
2120 }
2121
2122 if (!use_prop_sep_split_label) {
2123 /* Pass */
2124 }
2125 else if (ui_item_rna_is_expand(prop, index, flag)) {
2126 fmt::memory_buffer name_with_suffix;
2127 char str[2] = {'\0'};
2128 for (int a = 0; a < len; a++) {
2130 const bool use_prefix = (a == 0 && !name.is_empty());
2131 if (use_prefix) {
2132 fmt::format_to(fmt::appender(name_with_suffix), "{} {}", name, str[0]);
2133 }
2134 but = uiDefBut(block,
2136 0,
2137 use_prefix ? StringRef(name_with_suffix.data(), name_with_suffix.size()) :
2138 str,
2139 0,
2140 0,
2141 w,
2142 UI_UNIT_Y,
2143 nullptr,
2144 0.0,
2145 0.0,
2146 "");
2149
2150 label_added = true;
2151 }
2152 }
2153 else {
2154 but = uiDefBut(block, ButType::Label, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
2157
2158 label_added = true;
2159 }
2160
2161 if (!label_added && heading_layout) {
2162 ui_layout_heading_label_add(layout_sub, heading_layout, true, false);
2163 }
2164
2165 layout_split = LayoutInternal::ui_item_prop_split_layout_hack(layout_parent, layout_split);
2166
2167 /* Watch out! We can only write into the new layout now. */
2168 if ((type == PROP_ENUM) && (flag & UI_ITEM_R_EXPAND)) {
2169 /* Expanded enums each have their own name. */
2170
2171 /* Often expanded enum's are better arranged into a row,
2172 * so check the existing layout. */
2174 layout = &layout_split->row(true);
2175 }
2176 else {
2177 layout = &layout_split->column(true);
2178 }
2179 }
2180 else {
2181 if (use_prop_sep_split_label) {
2182 name = "";
2183 }
2184 layout = &layout_split->column(true);
2185 }
2186 layout->space_ = 0;
2187 }
2188
2189#ifdef UI_PROP_DECORATE
2190 if (ui_decorate.use_prop_decorate) {
2191 ui_decorate.layout = &layout_row->column(true);
2192 ui_decorate.layout->space_ = 0;
2194 ui_decorate.but = block->last_but();
2195
2196 /* Clear after. */
2197 layout->flag_ |= uiItemInternalFlag::PropDecorateNoPad;
2198 }
2199#endif /* UI_PROP_DECORATE */
2200 }
2201 /* End split. */
2202 else if (heading_layout) {
2203 /* Could not add heading to split layout, fall back to inserting it to the layout with the
2204 * heading itself. */
2205 ui_layout_heading_label_add(heading_layout, heading_layout, false, false);
2206 }
2207
2208 /* array property */
2209 if (index == RNA_NO_INDEX && is_array) {
2210 if (inside_prop_sep) {
2211 /* Within a split row, add array items to a column so they match the column layout of
2212 * previous items (e.g. transform vector with lock icon for each item). */
2213 layout = &layout->column(true);
2214 }
2215
2216 ui_item_array(layout,
2217 block,
2218 name,
2219 icon,
2220 ptr,
2221 prop,
2222 len,
2223 0,
2224 0,
2225 w,
2226 h,
2227 expand,
2228 slider,
2229 toggle,
2230 icon_only,
2231 compact,
2232 !use_prop_sep_split_label);
2233 }
2234 /* enum item */
2235 else if (type == PROP_ENUM && index == RNA_ENUM_VALUE) {
2236 if (icon && !name.is_empty() && !icon_only) {
2238 block, ButType::Row, 0, icon, name, 0, 0, w, h, ptr, prop, -1, 0, value, std::nullopt);
2239 }
2240 else if (icon) {
2242 block, ButType::Row, 0, icon, 0, 0, w, h, ptr, prop, -1, 0, value, std::nullopt);
2243 }
2244 else {
2246 block, ButType::Row, 0, name, 0, 0, w, h, ptr, prop, -1, 0, value, std::nullopt);
2247 }
2248 }
2249 /* expanded enum */
2250 else if (type == PROP_ENUM && expand) {
2251 ui_item_enum_expand(layout, block, ptr, prop, name, h, icon_only);
2252 }
2253 /* property with separate label */
2255 but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag);
2256
2257 if (is_id_name_prop) {
2258 Main *bmain = CTX_data_main(static_cast<bContext *>(block->evil_C));
2259 ID *id = ptr->owner_id;
2261 but, [bmain, id](const std::string &new_name) { ED_id_rename(*bmain, *id, new_name); });
2262 }
2263
2264 bool results_are_suggestions = false;
2265 if (type == PROP_STRING) {
2267 if (search_flag & PROP_STRING_SEARCH_SUGGESTION) {
2268 results_are_suggestions = true;
2269 }
2270 }
2271 but = ui_but_add_search(but, ptr, prop, nullptr, nullptr, nullptr, results_are_suggestions);
2272
2273 if (layout->red_alert()) {
2275 }
2276
2277 if (layout->activate_init()) {
2279 }
2280 }
2281 /* single button */
2282 else {
2283 but = uiDefAutoButR(block, ptr, prop, index, name, icon, 0, 0, w, h);
2284
2285 if (slider && but->type == ButType::Num) {
2286 uiButNumber *number_but = (uiButNumber *)but;
2287 const float step_size = number_but->step_size;
2288 const float precision = number_but->precision;
2290 uiButNumberSlider *slider_but = reinterpret_cast<uiButNumberSlider *>(but);
2291 slider_but->step_size = step_size;
2292 slider_but->precision = precision;
2293 }
2294
2296 if (ELEM(but->type,
2301 {
2303 }
2304 }
2305
2306 if ((toggle == 1) && but->type == ButType::Checkbox) {
2307 but->type = ButType::Toggle;
2308 }
2309
2310 if (layout->red_alert()) {
2312 }
2313
2314 if (layout->activate_init()) {
2316 }
2317 }
2318
2319 /* The resulting button may have the icon set since boolean button drawing
2320 * is being 'helpful' and adding an icon for us.
2321 * In this case we want the ability not to have an icon.
2322 *
2323 * We could pass an argument not to set the icon to begin with however this is the one case
2324 * the functionality is needed. */
2325 if (but && no_icon) {
2326 if ((icon == ICON_NONE) && (but->icon != ICON_NONE)) {
2328 }
2329 }
2330
2331 /* Mark non-embossed text-fields inside a list-box. */
2332 if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == ButType::Text) &&
2334 {
2336 }
2337
2338 if (but) {
2339 if (placeholder) {
2340 UI_but_placeholder_set(but, *placeholder);
2341 }
2344 }
2345 }
2346
2347#ifdef UI_PROP_DECORATE
2348 if (ui_decorate.use_prop_decorate) {
2349 uiBut *but_decorate = ui_decorate.but ? block->next_but(ui_decorate.but) : block->first_but();
2350
2351 /* Move temporarily last buts to avoid multiple reallocations while inserting decorators. */
2353 tmp.reserve(ui_decorate.len);
2354 while (but_decorate && but_decorate != block->buttons.last().get()) {
2355 tmp.append(block->buttons.pop_last());
2356 }
2357 const bool use_blank_decorator = (flag & UI_ITEM_R_FORCE_BLANK_DECORATE);
2358 uiLayout *layout_col = &ui_decorate.layout->column(false);
2359 layout_col->space_ = 0;
2361
2362 int i;
2363 for (i = 0; i < ui_decorate.len && but_decorate; i++) {
2364 PointerRNA *ptr_dec = use_blank_decorator ? nullptr : &but_decorate->rnapoin;
2365 PropertyRNA *prop_dec = use_blank_decorator ? nullptr : but_decorate->rnaprop;
2366
2367 /* The icons are set in 'ui_but_anim_flag' */
2368 layout_col->decorator(ptr_dec, prop_dec, but_decorate->rnaindex);
2369 but = block->buttons.last().get();
2370
2371 if (!tmp.is_empty()) {
2372 block->buttons.append(tmp.pop_last());
2373 but_decorate = block->buttons.last().get();
2374 }
2375 else {
2376 but_decorate = nullptr;
2377 }
2378 }
2379 while (!tmp.is_empty()) {
2380 block->buttons.append(tmp.pop_last());
2381 }
2382 BLI_assert(ELEM(i, 1, ui_decorate.len));
2383
2385 }
2386#endif /* UI_PROP_DECORATE */
2387
2388 if (no_bg) {
2389 emboss_ = prev_emboss;
2390 }
2391
2392 /* ensure text isn't added to icon_only buttons */
2393 if (but && icon_only) {
2394 BLI_assert(but->str.empty());
2395 }
2396}
2397
2399 const StringRefNull propname,
2400 const eUI_Item_Flag flag,
2401 const std::optional<StringRef> name,
2402 int icon)
2403{
2405
2406 if (!prop) {
2407 ui_item_disabled(this, propname.c_str());
2408 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname.c_str());
2409 return;
2410 }
2411
2412 this->prop(ptr, prop, RNA_NO_INDEX, 0, flag, name, icon);
2413}
2414
2417 int index,
2418 int value,
2419 const eUI_Item_Flag flag,
2420 const std::optional<StringRefNull> name,
2421 int icon,
2422 const char *panel_type)
2423{
2424 uiBlock *block = this->block();
2425 int i = block->buttons.size();
2426 this->prop(ptr, prop, index, value, flag, name, icon);
2427 for (; i < block->buttons.size(); i++) {
2428 uiBut *but = block->buttons[i].get();
2429 if (but->rnaprop == prop && ELEM(but->type, ButType::Menu, ButType::Color)) {
2431 break;
2432 }
2433 }
2434 if (i == block->buttons.size()) {
2435 const StringRefNull propname = RNA_property_identifier(prop);
2436 ui_item_disabled(this, panel_type);
2437 RNA_warning("property could not use a popover: %s.%s (%s)",
2439 propname.c_str(),
2440 panel_type);
2441 }
2442}
2443
2446 int index,
2447 int value,
2448 const eUI_Item_Flag flag,
2449 const std::optional<StringRefNull> name,
2450 int icon,
2451 const char *menu_type)
2452{
2453 uiBlock *block = this->block();
2454 int i = block->buttons.size();
2455 this->prop(ptr, prop, index, value, flag, name, icon);
2456 while (i < block->buttons.size()) {
2457 uiBut *but = block->buttons[i].get();
2458 if (but->rnaprop == prop && but->type == ButType::Menu) {
2460 break;
2461 }
2462 i++;
2463 }
2464 if (i == block->buttons.size()) {
2465 const StringRefNull propname = RNA_property_identifier(prop);
2466 ui_item_disabled(this, menu_type);
2467 RNA_warning("property could not use a menu: %s.%s (%s)",
2469 propname.c_str(),
2470 menu_type);
2471 }
2472}
2473
2476 int value,
2477 const std::optional<StringRefNull> name,
2478 int icon)
2479{
2481 const StringRefNull propname = RNA_property_identifier(prop);
2482 ui_item_disabled(this, propname.c_str());
2483 RNA_warning("property not an enum: %s.%s", RNA_struct_identifier(ptr->type), propname.c_str());
2484 return;
2485 }
2486
2487 this->prop(ptr, prop, RNA_ENUM_VALUE, value, UI_ITEM_NONE, name, icon);
2488}
2489
2492 const char *value,
2493 const std::optional<StringRefNull> name,
2494 int icon)
2495{
2497 const StringRefNull propname = RNA_property_identifier(prop);
2498 ui_item_disabled(this, propname.c_str());
2499 RNA_warning("not an enum property: %s.%s", RNA_struct_identifier(ptr->type), propname.c_str());
2500 return;
2501 }
2502
2503 const EnumPropertyItem *item;
2504 bool free;
2506 static_cast<bContext *>(this->block()->evil_C), ptr, prop, &item, nullptr, &free);
2507
2508 int ivalue;
2509 if (!RNA_enum_value_from_id(item, value, &ivalue)) {
2510 const StringRefNull propname = RNA_property_identifier(prop);
2511 if (free) {
2512 MEM_freeN(item);
2513 }
2514 ui_item_disabled(this, propname.c_str());
2515 RNA_warning("enum property value not found: %s", value);
2516 return;
2517 }
2518
2519 for (int a = 0; item[a].identifier; a++) {
2520 if (item[a].identifier[0] == '\0') {
2521 /* Skip enum item separators. */
2522 continue;
2523 }
2524 if (item[a].value == ivalue) {
2525 const StringRefNull item_name = name.value_or(
2528
2529 this->prop(ptr, prop, RNA_ENUM_VALUE, ivalue, flag, item_name, icon ? icon : item[a].icon);
2530 break;
2531 }
2532 }
2533
2534 if (free) {
2535 MEM_freeN(item);
2536 }
2537}
2538
2540 const StringRefNull propname,
2541 const char *value,
2542 const std::optional<StringRefNull> name,
2543 int icon)
2544{
2546 if (UNLIKELY(prop == nullptr)) {
2547 ui_item_disabled(this, propname.c_str());
2549 "enum property not found: %s.%s", RNA_struct_identifier(ptr->type), propname.c_str());
2550 return;
2551 }
2552 this->prop_enum(ptr, prop, value, name, icon);
2553}
2554
2556{
2557 uiBlock *block = this->block();
2558
2560
2561 if (!prop) {
2562 ui_item_disabled(this, propname.c_str());
2564 "enum property not found: %s.%s", RNA_struct_identifier(ptr->type), propname.c_str());
2565 return;
2566 }
2567
2569 RNA_warning("not an enum property: %s.%s", RNA_struct_identifier(ptr->type), propname.c_str());
2570 return;
2571 }
2572
2573 uiLayout *split = &this->split(0.0f, false);
2574 uiLayout *column = &split->column(false);
2575
2576 int totitem;
2577 const EnumPropertyItem *item;
2578 bool free;
2580 static_cast<bContext *>(block->evil_C), ptr, prop, &item, &totitem, &free);
2581
2582 for (int i = 0; i < totitem; i++) {
2583 if (item[i].identifier[0]) {
2584 column->prop_enum(ptr, prop, item[i].value, item[i].name, item[i].icon);
2585 ui_but_tip_from_enum_item(block->buttons.last().get(), &item[i]);
2586 }
2587 else {
2588 if (item[i].name) {
2589 if (i != 0) {
2590 column = &split->column(false);
2591 }
2592
2593 column->label(item[i].name, ICON_NONE);
2594 uiBut *bt = block->buttons.last().get();
2596
2597 ui_but_tip_from_enum_item(bt, &item[i]);
2598 }
2599 else {
2600 column->separator();
2601 }
2602 }
2603 }
2604
2605 if (free) {
2606 MEM_freeN(item);
2607 }
2608}
2609
2610/* Pointer RNA button with search */
2611
2612static void search_id_collection(StructRNA *ptype, PointerRNA *r_ptr, PropertyRNA **r_prop)
2613{
2614 /* look for collection property in Main */
2615 /* NOTE: using global Main is OK-ish here, UI shall not access other Mains anyway. */
2617
2618 *r_prop = nullptr;
2619
2620 RNA_STRUCT_BEGIN (r_ptr, iprop) {
2621 /* if it's a collection and has same pointer type, we've got it */
2622 if (RNA_property_type(iprop) == PROP_COLLECTION) {
2623 StructRNA *srna = RNA_property_pointer_type(r_ptr, iprop);
2624
2625 if (ptype == srna) {
2626 *r_prop = iprop;
2627 break;
2628 }
2629 }
2630 }
2632}
2633
2635{
2636 uiRNACollectionSearch *coll_search = static_cast<uiRNACollectionSearch *>(ptr);
2637 UI_butstore_free(coll_search->butstore_block, coll_search->butstore);
2638 MEM_delete(coll_search);
2639}
2640
2642 PointerRNA *ptr,
2643 PropertyRNA *prop,
2644 PointerRNA *searchptr,
2645 PropertyRNA *searchprop,
2646 PropertyRNA *item_searchprop,
2647 const bool results_are_suggestions)
2648{
2649 /* for ID's we do automatic lookup */
2650 bool has_search_fn = false;
2651
2652 PointerRNA sptr;
2653 if (!searchprop) {
2654 if (RNA_property_type(prop) == PROP_STRING) {
2655 has_search_fn = (RNA_property_string_search_flag(prop) != 0);
2656 }
2657 if (RNA_property_type(prop) == PROP_POINTER) {
2658 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
2659 search_id_collection(ptype, &sptr, &searchprop);
2660 searchptr = &sptr;
2661 }
2662 }
2663
2664 /* turn button into search button */
2665 if (has_search_fn || searchprop) {
2666 uiRNACollectionSearch *coll_search = MEM_new<uiRNACollectionSearch>(__func__);
2667 uiButSearch *search_but;
2668
2670 search_but = (uiButSearch *)but;
2671
2672 if (searchptr) {
2673 search_but->rnasearchpoin = *searchptr;
2674 search_but->rnasearchprop = searchprop;
2675 }
2676
2677 but->hardmax = std::max(but->hardmax, 256.0f);
2679 if (RNA_property_is_unlink(prop)) {
2680 but->flag |= UI_BUT_VALUE_CLEAR;
2681 }
2682
2683 coll_search->target_ptr = *ptr;
2684 coll_search->target_prop = prop;
2685
2686 if (searchptr) {
2687 coll_search->search_ptr = *searchptr;
2688 coll_search->search_prop = searchprop;
2689 coll_search->item_search_prop = item_searchprop;
2690 }
2691 else {
2692 /* Rely on `has_search_fn`. */
2693 coll_search->search_ptr = PointerRNA_NULL;
2694 coll_search->search_prop = nullptr;
2695 coll_search->item_search_prop = nullptr;
2696 }
2697
2698 coll_search->search_but = but;
2699 coll_search->butstore_block = but->block;
2700 coll_search->butstore = UI_butstore_create(coll_search->butstore_block);
2701 UI_butstore_register(coll_search->butstore, &coll_search->search_but);
2702
2703 if (RNA_property_type(prop) == PROP_ENUM) {
2704 /* XXX, this will have a menu string,
2705 * but in this case we just want the text */
2706 but->str.clear();
2707 }
2708
2709 UI_but_func_search_set_results_are_suggestions(but, results_are_suggestions);
2710
2714 coll_search,
2715 false,
2717 nullptr,
2718 nullptr);
2719 /* If this is called multiple times for the same button, an earlier call may have taken the
2720 * else branch below so the button was disabled. Now we have a searchprop, so it can be enabled
2721 * again. */
2722 but->flag &= ~UI_BUT_DISABLED;
2723 }
2724 else if (but->type == ButType::SearchMenu) {
2725 /* In case we fail to find proper searchprop,
2726 * so other code might have already set but->type to search menu... */
2727 but->flag |= UI_BUT_DISABLED;
2728 }
2729
2730 return but;
2731}
2732
2735 PointerRNA *searchptr,
2736 PropertyRNA *searchprop,
2737 PropertyRNA *item_searchprop,
2738 const std::optional<StringRefNull> name_opt,
2739 int icon,
2740 bool results_are_suggestions)
2741{
2742 const bool use_prop_sep = this->use_property_split();
2743 uiBlock *block = this->block();
2745
2748 RNA_warning("Property %s.%s must be a pointer, string or enum",
2751 return;
2752 }
2753 if (RNA_property_type(searchprop) != PROP_COLLECTION) {
2754 RNA_warning("search collection property is not a collection type: %s.%s",
2755 RNA_struct_identifier(searchptr->type),
2756 RNA_property_identifier(searchprop));
2757 return;
2758 }
2759 if (item_searchprop && RNA_property_type(item_searchprop) != PROP_STRING) {
2760 RNA_warning("Search collection items' property is not a string type: %s.%s",
2761 RNA_struct_identifier(RNA_property_pointer_type(searchptr, searchprop)),
2762 RNA_property_identifier(item_searchprop));
2763 return;
2764 }
2765
2766 /* get icon & name */
2767 if (icon == ICON_NONE) {
2768 const StructRNA *icontype;
2769 if (type == PROP_POINTER) {
2770 icontype = RNA_property_pointer_type(ptr, prop);
2771 }
2772 else {
2773 icontype = RNA_property_pointer_type(searchptr, searchprop);
2774 }
2775
2776 icon = RNA_struct_ui_icon(icontype);
2777 }
2778 StringRefNull name = name_opt.value_or(RNA_property_ui_name(prop));
2779
2780 char namestr[UI_MAX_NAME_STR];
2781 if (use_prop_sep == false) {
2782 name = ui_item_name_add_colon(name, namestr);
2783 }
2784
2785 /* create button */
2786
2787 int w, h;
2788 ui_item_rna_size(this, name, icon, ptr, prop, 0, false, false, &w, &h);
2789 w += UI_UNIT_X; /* X icon needs more space */
2790 uiBut *but = ui_item_with_label(this, block, name, icon, ptr, prop, 0, 0, 0, w, h, 0);
2791
2792 but = ui_but_add_search(
2793 but, ptr, prop, searchptr, searchprop, item_searchprop, results_are_suggestions);
2794}
2795
2797 const StringRefNull propname,
2798 PointerRNA *searchptr,
2799 const StringRefNull searchpropname,
2800 const std::optional<StringRefNull> name,
2801 int icon)
2802{
2803 /* validate arguments */
2805 if (!prop) {
2806 RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname.c_str());
2807 return;
2808 }
2809 PropertyRNA *searchprop = RNA_struct_find_property(searchptr, searchpropname.c_str());
2810 if (!searchprop) {
2811 RNA_warning("search collection property not found: %s.%s",
2812 RNA_struct_identifier(searchptr->type),
2813 searchpropname.c_str());
2814 return;
2815 }
2816
2817 this->prop_search(ptr, prop, searchptr, searchprop, nullptr, name, icon, false);
2818}
2819
2820void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt)
2821{
2822 MenuType *mt = (MenuType *)arg_mt;
2823 UI_menutype_draw(C, mt, layout);
2824}
2825
2826void ui_item_paneltype_func(bContext *C, uiLayout *layout, void *arg_pt)
2827{
2828 PanelType *pt = (PanelType *)arg_pt;
2829 UI_paneltype_draw(C, pt, layout);
2830}
2831
2833 const StringRef name,
2834 int icon,
2835 uiMenuCreateFunc func,
2836 void *arg,
2837 void *argN,
2838 const std::optional<StringRef> tip,
2839 bool force_menu,
2840 uiButArgNFree func_argN_free_fn = MEM_freeN,
2841 uiButArgNCopy func_argN_copy_fn = MEM_dupallocN)
2842{
2843 uiBlock *block = layout->block();
2844 uiLayout *heading_layout = ui_layout_heading_find(layout);
2845
2848
2849 if (layout->root()->type == blender::ui::LayoutType::Menu && !icon) {
2850 icon = ICON_BLANK1;
2851 }
2852
2854 if (layout->root()->type == blender::ui::LayoutType::Header) { /* Ugly! */
2855 if (icon == ICON_NONE && force_menu) {
2856 /* pass */
2857 }
2858 else if (force_menu) {
2859 pad_factor.text = 1.85;
2860 pad_factor.icon_only = 0.6f;
2861 }
2862 else {
2863 pad_factor.text = 0.75f;
2864 }
2865 }
2866
2867 const int w = ui_text_icon_width_ex(layout, name, icon, pad_factor, UI_FSTYLE_WIDGET);
2868 const int h = UI_UNIT_Y;
2869
2870 if (heading_layout) {
2871 ui_layout_heading_label_add(layout, heading_layout, true, true);
2872 }
2873
2874 uiBut *but;
2875 if (!name.is_empty() && icon) {
2876 but = uiDefIconTextMenuBut(block, func, arg, icon, name, 0, 0, w, h, tip);
2877 }
2878 else if (icon) {
2879 but = uiDefIconMenuBut(block, func, arg, icon, 0, 0, w, h, tip);
2880 if (force_menu && !name.is_empty()) {
2882 }
2883 }
2884 else {
2885 but = uiDefMenuBut(block, func, arg, name, 0, 0, w, h, tip);
2886 }
2887
2888 if (argN) {
2889 /* ugly! */
2890 if (arg != argN) {
2891 but->poin = (char *)but;
2892 }
2893 but->func_argN = argN;
2894 but->func_argN_free_fn = func_argN_free_fn;
2895 but->func_argN_copy_fn = func_argN_copy_fn;
2896 }
2897
2898 if (ELEM(layout->root()->type,
2901 /* We never want a drop-down in menu! */
2902 (force_menu && layout->root()->type != blender::ui::LayoutType::Menu))
2903 {
2905 }
2906
2907 return but;
2908}
2909
2910void uiLayout::menu(MenuType *mt, const std::optional<StringRef> name_opt, int icon)
2911{
2912 uiBlock *block = this->block();
2913 bContext *C = static_cast<bContext *>(block->evil_C);
2914 if (WM_menutype_poll(C, mt) == false) {
2915 return;
2916 }
2917
2918 const StringRef name = name_opt.value_or(CTX_IFACE_(mt->translation_context, mt->label));
2919
2920 if (root_->type == blender::ui::LayoutType::Menu && !icon) {
2921 icon = ICON_BLANK1;
2922 }
2923
2924 ui_item_menu(this,
2925 name,
2926 icon,
2928 mt,
2929 nullptr,
2930 mt->description ? TIP_(mt->description) : "",
2931 false);
2932}
2933
2934void uiLayout::menu(const StringRef menuname, const std::optional<StringRef> name, int icon)
2935{
2936 MenuType *mt = WM_menutype_find(menuname, false);
2937 if (mt == nullptr) {
2938 RNA_warning("not found %s", std::string(menuname).c_str());
2939 return;
2940 }
2941 this->menu(mt, name, icon);
2942}
2943
2945{
2946 MenuType *mt = WM_menutype_find(menuname, false);
2947 if (mt == nullptr) {
2948 RNA_warning("not found %s", std::string(menuname).c_str());
2949 return;
2950 }
2951
2952 uiBlock *block = this->block();
2953 bContext *C = static_cast<bContext *>(block->evil_C);
2954 if (WM_menutype_poll(C, mt) == false) {
2955 return;
2956 }
2957
2958 UI_menutype_draw(C, mt, this);
2959}
2960
2962{
2963 uiBlock *block = this->block();
2964
2966 uiLayout &col = this->column(false);
2967 col.space_ = 0;
2969
2970 if (ELEM(nullptr, ptr, prop) || !RNA_property_animateable(ptr, prop)) {
2971 uiBut *but = uiDefIconBut(block,
2973 0,
2974 ICON_BLANK1,
2975 0,
2976 0,
2977 UI_UNIT_X,
2978 UI_UNIT_Y,
2979 nullptr,
2980 0.0,
2981 0.0,
2982 "");
2983 but->flag |= UI_BUT_DISABLED;
2984 return;
2985 }
2986
2987 const bool is_expand = ui_item_rna_is_expand(prop, index, UI_ITEM_NONE);
2988 const bool is_array = RNA_property_array_check(prop);
2989
2990 /* Loop for the array-case, but only do in case of an expanded array. */
2991 for (int i = 0; i < (is_expand ? RNA_property_array_length(ptr, prop) : 1); i++) {
2994 0,
2995 ICON_DOT,
2996 0,
2997 0,
2998 UI_UNIT_X,
2999 UI_UNIT_Y,
3000 nullptr,
3001 0.0,
3002 0.0,
3003 TIP_("Animate property"));
3004
3005 UI_but_func_set(but, ui_but_anim_decorate_cb, but, nullptr);
3007 /* Decorators have their own RNA data, using the normal #uiBut RNA members has many
3008 * side-effects. */
3009 but->decorated_rnapoin = *ptr;
3010 but->decorated_rnaprop = prop;
3011 /* ui_def_but_rna() sets non-array buttons to have a RNA index of 0. */
3012 but->decorated_rnaindex = (!is_array || is_expand) ? i : index;
3013 }
3014}
3015
3016void uiLayout::decorator(PointerRNA *ptr, const std::optional<StringRefNull> propname, int index)
3017{
3018 PropertyRNA *prop = nullptr;
3019
3020 if (ptr && propname) {
3021 /* validate arguments */
3022 prop = RNA_struct_find_property(ptr, propname->c_str());
3023 if (!prop) {
3024 ui_item_disabled(this, propname->c_str());
3026 "property not found: %s.%s", RNA_struct_identifier(ptr->type), propname->c_str());
3027 return;
3028 }
3029 }
3030
3031 /* ptr and prop are allowed to be nullptr here. */
3032 this->decorator(ptr, prop, index);
3033}
3034
3036 PanelType *pt,
3037 const std::optional<StringRef> name_opt,
3038 int icon)
3039{
3040 uiLayout *layout = this;
3041 const StringRef name = name_opt.value_or(CTX_IFACE_(pt->translation_context, pt->label));
3042
3043 if (root_->type == blender::ui::LayoutType::Menu && !icon) {
3044 icon = ICON_BLANK1;
3045 }
3046
3047 const bContextStore *previous_ctx = CTX_store_get(C);
3048 /* Set context for polling (and panel header drawing). */
3049 CTX_store_set(const_cast<bContext *>(C), context_);
3050
3051 const bool ok = (pt->poll == nullptr) || pt->poll(C, pt);
3052 if (ok && (pt->draw_header != nullptr)) {
3053 layout = &this->row(true);
3054 Panel panel{};
3055 Panel_Runtime panel_runtime{};
3056 panel.runtime = &panel_runtime;
3057 panel.type = pt;
3058 panel.layout = layout;
3059 panel.flag = PNL_POPOVER;
3060 pt->draw_header(C, &panel);
3061 }
3062
3063 CTX_store_set(const_cast<bContext *>(C), previous_ctx);
3064
3065 uiBut *but = ui_item_menu(
3066 layout, name, icon, ui_item_paneltype_func, pt, nullptr, TIP_(pt->description), true);
3067 but->type = ButType::Popover;
3068
3069 /* Override button size when there is no icon or label. */
3070 if (layout->root()->type == blender::ui::LayoutType::VerticalBar && !icon && name.is_empty()) {
3071 but->rect.xmax = but->rect.xmin + UI_UNIT_X;
3072 }
3073
3074 if (!ok) {
3075 but->flag |= UI_BUT_DISABLED;
3076 }
3077}
3078
3079void uiLayout::popover(const bContext *C,
3080 const StringRef panel_type,
3081 std::optional<blender::StringRef> name_opt,
3082 int icon)
3083{
3084 PanelType *pt = WM_paneltype_find(panel_type, true);
3085 if (pt == nullptr) {
3086 RNA_warning("Panel type not found '%s'", std::string(panel_type).c_str());
3087 return;
3088 }
3089 this->popover(C, pt, name_opt, icon);
3090}
3091
3093 bContext *C, int space_id, int region_id, const char *context, const char *category)
3094{
3095 SpaceType *st = BKE_spacetype_from_id(space_id);
3096 if (st == nullptr) {
3097 RNA_warning("space type not found %d", space_id);
3098 return;
3099 }
3100 ARegionType *art = BKE_regiontype_from_id(st, region_id);
3101 if (art == nullptr) {
3102 RNA_warning("region type not found %d", region_id);
3103 return;
3104 }
3105
3106 LISTBASE_FOREACH (PanelType *, pt, &art->paneltypes) {
3107 /* Causes too many panels, check context. */
3108 if (pt->parent_id[0] == '\0') {
3109 if (/* (*context == '\0') || */ STREQ(pt->context, context)) {
3110 if ((*category == '\0') || STREQ(pt->category, category)) {
3111 if (pt->poll == nullptr || pt->poll(C, pt)) {
3112 this->popover(C, pt, std::nullopt, ICON_NONE);
3113 }
3114 }
3115 }
3116 }
3117 }
3118}
3119
3125 const StringRef name,
3126 int icon,
3127 std::optional<blender::StringRef> tooltip = std::nullopt,
3128 const ButType but_type = ButType::Label)
3129{
3130 uiBlock *block = layout->block();
3131
3134
3135 if (layout->root()->type == blender::ui::LayoutType::Menu && !icon) {
3136 icon = ICON_BLANK1;
3137 }
3138
3139 const int w = ui_text_icon_width_ex(layout, name, icon, ui_text_pad_none, UI_FSTYLE_WIDGET);
3140 uiBut *but;
3141 if (icon && !name.is_empty()) {
3142 but = uiDefIconTextBut(block, but_type, 0, icon, name, 0, 0, w, UI_UNIT_Y, nullptr, tooltip);
3143 }
3144 else if (icon) {
3145 but = uiDefIconBut(block, but_type, 0, icon, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, tooltip);
3146 }
3147 else {
3148 but = uiDefBut(block, but_type, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, tooltip);
3149 }
3150
3151 /* to compensate for string size padding in ui_text_icon_width,
3152 * make text aligned right if the layout is aligned right.
3153 */
3154 if (layout->alignment() == blender::ui::LayoutAlign::Right) {
3155 but->drawflag &= ~UI_BUT_TEXT_LEFT; /* default, needs to be unset */
3157 }
3158
3159 /* Mark as a label inside a list-box. */
3160 if (block->flag & UI_BLOCK_LIST_ITEM) {
3161 but->flag |= UI_BUT_LIST_ITEM;
3162 }
3163
3164 if (layout->red_alert()) {
3166 }
3167
3168 return but;
3169}
3170
3172 uiLayout *layout, const StringRef name, int icon, const bool highlight, const bool redalert)
3173{
3174 uiBut *but = uiItem_simple(layout, name, icon);
3175
3176 if (highlight) {
3177 /* TODO: add another flag for this. */
3179 }
3180
3181 if (redalert) {
3183 }
3184
3185 return but;
3186}
3187
3188void uiLayout::label(const StringRef name, int icon)
3189{
3190 uiItem_simple(this, name, icon);
3191}
3192
3194{
3195 uiPropertySplitWrapper split_wrapper = {nullptr};
3196
3197 uiLayout *layout_row = &parent_layout->row(true);
3198 uiLayout *layout_split = &layout_row->split(UI_ITEM_PROP_SEP_DIVIDE, true);
3199
3200 split_wrapper.label_column = &layout_split->column(true);
3203 layout_split);
3204 split_wrapper.decorate_column = parent_layout->use_property_decorate() ?
3205 &layout_row->column(true) :
3206 nullptr;
3207
3208 return split_wrapper;
3209}
3210
3212{
3213 if (layout->use_property_split()) {
3214 uiBlock *block = layout->block();
3215 const uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout);
3216 /* Further items added to 'layout' will automatically be added to split_wrapper.property_row */
3217
3218 uiItem_simple(split_wrapper.label_column, text, icon);
3220
3221 return split_wrapper.decorate_column;
3222 }
3223
3224 char namestr[UI_MAX_NAME_STR];
3225 text = ui_item_name_add_colon(text, namestr);
3226 uiItem_simple(layout, text, icon);
3227
3228 return nullptr;
3229}
3230
3232{
3233 uiBut *but = uiItem_simple(layout, name, icon);
3234
3235 if (ptr && ptr->type) {
3236 if (RNA_struct_is_ID(ptr->type)) {
3237 UI_but_drag_set_id(but, ptr->owner_id);
3238 }
3239 }
3240}
3241
3243 const int icon,
3244 std::function<void(bContext &)> func,
3245 std::optional<blender::StringRef> tooltip)
3246{
3247 uiBut *but = uiItem_simple(this, name, icon, tooltip, ButType::But);
3248 UI_but_func_set(but, std::move(func));
3249 return but;
3250}
3251
3253{
3254 uiBlock *block = this->block();
3255 const bool is_menu = ui_block_is_menu(block);
3256 const bool is_pie = ui_block_is_pie_menu(block);
3257 if (is_menu && !UI_block_can_add_separator(block)) {
3258 return;
3259 }
3260
3261 /* Sizing of spaces should not depend on line width. */
3262 const int space = (is_menu) ? int(7.0f * UI_SCALE_FAC * factor) :
3263 int(6.0f * UI_SCALE_FAC * factor);
3264
3265 ButType but_type;
3266
3267 switch (type) {
3269 but_type = ButType::SeprLine;
3270 break;
3272 but_type = (is_menu && !is_pie) ? ButType::SeprLine : ButType::Sepr;
3273 break;
3274 default:
3275 but_type = ButType::Sepr;
3276 }
3277
3278 bool is_vertical_bar = (w_ == 0) && but_type == ButType::SeprLine;
3279
3281 uiBut *but = uiDefBut(block,
3282 but_type,
3283 0,
3284 "",
3285 0,
3286 0,
3287 space,
3288 is_vertical_bar ? UI_UNIT_Y : space,
3289 nullptr,
3290 0.0,
3291 0.0,
3292 "");
3293
3294 if (but_type == ButType::SeprLine) {
3295 uiButSeparatorLine *but_line = static_cast<uiButSeparatorLine *>(but);
3296 but_line->is_vertical = is_vertical_bar;
3297 }
3298}
3299
3300void uiLayout::progress_indicator(const char *text,
3301 const float factor,
3302 const blender::ui::ButProgressType progress_type)
3303{
3304 const bool has_text = text && text[0];
3305 uiBlock *block = this->block();
3306 short width;
3307
3308 if (progress_type == blender::ui::ButProgressType::Bar) {
3309 width = UI_UNIT_X * 5;
3310 }
3311 else if (has_text) {
3312 width = UI_UNIT_X * 8;
3313 }
3314 else {
3315 width = UI_UNIT_X;
3316 }
3317
3319 uiBut *but = uiDefBut(block,
3321 0,
3322 (text) ? text : "",
3323 0,
3324 0,
3325 width,
3326 short(UI_UNIT_Y),
3327 nullptr,
3328 0.0,
3329 0.0,
3330 "");
3331
3332 if (has_text && (progress_type == blender::ui::ButProgressType::Ring)) {
3333 /* For progress bar, centered is okay, left aligned for ring/pie. */
3334 but->drawflag |= UI_BUT_TEXT_LEFT;
3335 }
3336
3337 uiButProgress *progress_bar = static_cast<uiButProgress *>(but);
3338 progress_bar->progress_type = progress_type;
3339 progress_bar->progress_factor = factor;
3340}
3341
3343{
3344 uiBlock *block = this->block();
3345 const bool is_popup = ui_block_is_popup_any(block);
3346
3347 if (is_popup) {
3348 printf("Error: separator_spacer() not supported in popups.\n");
3349 return;
3350 }
3351
3352 if (block->direction & UI_DIR_RIGHT) {
3353 printf("Error: separator_spacer() only supported in horizontal blocks.\n");
3354 return;
3355 }
3356
3358 uiDefBut(
3359 block, ButType::SeprSpacer, 0, "", 0, 0, 0.3f * UI_UNIT_X, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
3360}
3361
3362void uiLayout::menu_fn(const StringRefNull name, int icon, uiMenuCreateFunc func, void *arg)
3363{
3364 if (!func) {
3365 return;
3366 }
3367
3368 ui_item_menu(this, name, icon, func, arg, nullptr, "", false);
3369}
3370
3372 int icon,
3373 uiMenuCreateFunc func,
3374 void *argN)
3375{
3376 if (!func) {
3377 return;
3378 }
3379
3380 /* Second 'argN' only ensures it gets freed. */
3381 ui_item_menu(this, name, icon, func, argN, argN, "", false);
3382}
3383
3386 /* don't use pointers to the strings because python can dynamically
3387 * allocate strings and free before the menu draws, see #27304. */
3391};
3392
3393/* Obtain the active menu item based on the calling button's text. */
3395{
3397
3398 if (!ot) {
3399 return -1;
3400 }
3401
3403 const EnumPropertyItem *item_array = nullptr;
3404 bool free;
3405 int totitem;
3407 /* so the context is passed to itemf functions (some need it) */
3410 if (!prop) {
3411 return -1;
3412 }
3413 RNA_property_enum_items_gettexted(C, &ptr, prop, &item_array, &totitem, &free);
3414 int active = RNA_enum_from_name(item_array, but->str.c_str());
3415 if (free) {
3416 MEM_freeN(item_array);
3417 }
3418
3419 return active;
3420}
3421
3422static void menu_item_enum_opname_menu(bContext *C, uiLayout *layout, void *arg)
3423{
3424 uiBut *but = static_cast<uiBut *>(arg);
3425 MenuItemLevel *lvl = static_cast<MenuItemLevel *>(but->func_argN);
3426 /* Use the operator properties from the button owning the menu. */
3427 BLI_assert(but->opptr);
3428 IDProperty *op_props = but->opptr->data_as<IDProperty>();
3429
3430 /* The calling but's str _probably_ contains the active
3431 * menu item name, set in #uiLayout::op_menu_enum. */
3432 const int active = menu_item_enum_opname_menu_active(C, but, lvl);
3433
3434 layout->operator_context_set(lvl->opcontext);
3435 layout->op_enum(lvl->opname, lvl->propname, op_props, lvl->opcontext, UI_ITEM_NONE, active);
3436
3437 /* override default, needed since this was assumed pre 2.70 */
3439}
3440
3443 const StringRefNull propname,
3444 std::optional<StringRefNull> name,
3445 int icon)
3446{
3447 /* Caller must check */
3448 BLI_assert(ot->srna != nullptr);
3449
3450 std::string operator_name;
3451 if (!name) {
3452 operator_name = WM_operatortype_name(ot, nullptr);
3453 name = operator_name.c_str();
3454 }
3455
3456 if (root_->type == blender::ui::LayoutType::Menu && !icon) {
3457 icon = ICON_BLANK1;
3458 }
3459
3460 MenuItemLevel *lvl = MEM_new<MenuItemLevel>("MenuItemLevel");
3461 STRNCPY_UTF8(lvl->opname, ot->idname);
3462 STRNCPY_UTF8(lvl->propname, propname.c_str());
3463 lvl->opcontext = root_->opcontext;
3464
3465 uiBut *but = ui_item_menu(this,
3466 *name,
3467 icon,
3469 nullptr,
3470 lvl,
3471 std::nullopt,
3472 true,
3475 /* Use the menu button as owner for the operator properties, which will then be passed to the
3476 * individual menu items. */
3477 but->opptr = MEM_new<PointerRNA>("uiButOpPtr");
3479 BLI_assert(but->opptr->data == nullptr);
3480 WM_operator_properties_alloc(&but->opptr, (IDProperty **)&but->opptr->data, ot->idname);
3481
3482 /* add hotkey here, lower UI code can't detect it */
3483 if ((this->block()->flag & UI_BLOCK_LOOP) && (ot->prop && ot->invoke)) {
3484 if (std::optional<std::string> shortcut_str = WM_key_event_operator_string(
3485 C, ot->idname, root_->opcontext, nullptr, false))
3486 {
3487 ui_but_add_shortcut(but, shortcut_str->c_str(), false);
3488 }
3489 }
3490 return *but->opptr;
3491}
3492
3494 const StringRefNull opname,
3495 const StringRefNull propname,
3497 int icon)
3498{
3499 wmOperatorType *ot = WM_operatortype_find(opname.c_str(), false); /* print error next */
3500
3501 UI_OPERATOR_ERROR_RET(ot, opname.c_str());
3502
3503 if (!ot->srna) {
3504 ui_item_disabled(this, opname.c_str());
3505 RNA_warning("operator missing srna '%s'", opname.c_str());
3506 return PointerRNA_NULL;
3507 }
3508
3509 return this->op_menu_enum(C, ot, propname, name, icon);
3510}
3511
3512static void menu_item_enum_rna_menu(bContext * /*C*/, uiLayout *layout, void *arg)
3513{
3514 MenuItemLevel *lvl = (MenuItemLevel *)(((uiBut *)arg)->func_argN);
3515
3516 layout->operator_context_set(lvl->opcontext);
3517 layout->props_enum(&lvl->rnapoin, lvl->propname);
3518}
3519
3522 const std::optional<StringRefNull> name,
3523 int icon)
3524{
3525 if (root_->type == blender::ui::LayoutType::Menu && !icon) {
3526 icon = ICON_BLANK1;
3527 }
3528
3529 MenuItemLevel *lvl = MEM_new<MenuItemLevel>("MenuItemLevel");
3530 lvl->rnapoin = *ptr;
3532 lvl->opcontext = root_->opcontext;
3533
3534 ui_item_menu(this,
3535 name.value_or(RNA_property_ui_name(prop)),
3536 icon,
3538 nullptr,
3539 lvl,
3541 false,
3544}
3545
3547 PointerRNA *ptr,
3549 PointerRNA *ptr_highlight,
3550 PropertyRNA *prop_highlight,
3551 bool icon_only)
3552{
3553 uiBlock *block = this->block();
3554
3557 C,
3558 block,
3559 ptr,
3560 prop,
3561 ptr_highlight,
3562 prop_highlight,
3563 std::nullopt,
3564 UI_UNIT_Y,
3565 icon_only);
3566}
3567
3569
3570/* -------------------------------------------------------------------- */
3573
3575{
3576 layout->estimate();
3577}
3578
3580{
3581 layout->resolve();
3582}
3583
3584/* single-row layout */
3586{
3587 if (this->type() == uiItemType::LayoutRoot) {
3588 return;
3589 }
3590 int itemw, itemh;
3591 bool min_size_flag = true;
3592
3593 w_ = 0;
3594 h_ = 0;
3595
3596 if (this->items().is_empty()) {
3597 return;
3598 }
3599
3600 const uiItem *item_last = this->items().last();
3601 for (uiItem *item : this->items()) {
3602 const bool is_item_last = (item == item_last);
3603 ui_item_size(item, &itemw, &itemh);
3604
3605 min_size_flag = min_size_flag && item->fixed_size();
3606
3607 w_ += itemw;
3608 h_ = std::max(itemh, h_);
3609
3610 if (!is_item_last) {
3611 w_ += space_;
3612 }
3613 }
3614
3615 if (min_size_flag) {
3616 this->fixed_size_set(true);
3617 }
3618}
3619
3620static int ui_litem_min_width(int itemw)
3621{
3622 return std::min(2 * UI_UNIT_X, itemw);
3623}
3624
3626{
3627 if (this->items().is_empty()) {
3628 return;
3629 }
3630
3631 int last_free_item_idx = -1;
3632 int x, neww, newtotw, itemw, minw, itemh, offset;
3633 int freew, fixedx, freex, flag = 0, lastw = 0;
3634 float extra_pixel;
3635
3636 const int y = y_;
3637 int w = w_;
3638 int totw = 0;
3639 int tot = 0;
3640
3641 for (uiItem *item : this->items()) {
3642 ui_item_size(item, &itemw, &itemh);
3643 totw += itemw;
3644 tot++;
3645 }
3646
3647 if (totw == 0) {
3648 return;
3649 }
3650
3651 if (w != 0) {
3652 w -= (tot - 1) * space_;
3653 }
3654 int fixedw = 0;
3655
3656 const uiItem *item_last = this->items().last();
3657
3658 /* keep clamping items to fixed minimum size until all are done */
3659 do {
3660 freew = 0;
3661 x = 0;
3662 flag = 0;
3663 newtotw = totw;
3664 extra_pixel = 0.0f;
3665
3666 for (uiItem *item : this->items()) {
3668 continue;
3669 }
3670 const bool is_item_last = (item == item_last);
3671
3672 ui_item_size(item, &itemw, &itemh);
3673 minw = ui_litem_min_width(itemw);
3674
3675 if (w - lastw > 0) {
3676 neww = ui_item_fit(
3677 itemw, x, totw, w - lastw, is_item_last, this->alignment(), &extra_pixel);
3678 }
3679 else {
3680 neww = 0; /* no space left, all will need clamping to minimum size */
3681 }
3682
3683 x += neww;
3684
3685 bool min_flag = item->fixed_size();
3686 /* ignore min flag for rows with right or center alignment */
3687 if (item->type() != uiItemType::Button &&
3688 ELEM((static_cast<uiLayout *>(item))->alignment(),
3692 {
3693 min_flag = false;
3694 }
3695
3696 if ((neww < minw || min_flag) && w != 0) {
3697 /* fixed size */
3699 if (item->type() != uiItemType::Button && item->fixed_size()) {
3700 minw = itemw;
3701 }
3702 fixedw += minw;
3703 flag = 1;
3704 newtotw -= itemw;
3705 }
3706 else {
3707 /* keep free size */
3709 freew += itemw;
3710 }
3711 }
3712
3713 totw = newtotw;
3714 lastw = fixedw;
3715 } while (flag);
3716
3717 freex = 0;
3718 fixedx = 0;
3719 extra_pixel = 0.0f;
3720 x = x_;
3721
3722 int item_idx = -1;
3723 for (uiItem *item : this->items()) {
3724 item_idx++;
3725 const bool is_item_last = (item == item_last);
3726 ui_item_size(item, &itemw, &itemh);
3727 minw = ui_litem_min_width(itemw);
3728
3730 /* fixed minimum size items */
3731 if (item->type() != uiItemType::Button && item->fixed_size()) {
3732 minw = itemw;
3733 }
3734 itemw = ui_item_fit(
3735 minw, fixedx, fixedw, min_ii(w, fixedw), is_item_last, this->alignment(), &extra_pixel);
3736 fixedx += itemw;
3737 }
3738 else {
3739 /* free size item */
3740 itemw = ui_item_fit(
3741 itemw, freex, freew, w - fixedw, is_item_last, this->alignment(), &extra_pixel);
3742 freex += itemw;
3743 last_free_item_idx = item_idx;
3744 }
3745
3746 /* align right/center */
3747 offset = 0;
3749 if (freew + fixedw > 0 && freew + fixedw < w) {
3750 offset = w - (fixedw + freew);
3751 }
3752 }
3753 else if (this->alignment() == blender::ui::LayoutAlign::Center) {
3754 if (freew + fixedw > 0 && freew + fixedw < w) {
3755 offset = (w - (fixedw + freew)) / 2;
3756 }
3757 }
3758
3759 /* position item */
3760 ui_item_position(item, x + offset, y - itemh, itemw, itemh);
3761
3762 x += itemw;
3763 if (!is_item_last) {
3764 x += space_;
3765 }
3766 }
3767
3768 /* add extra pixel */
3769 int extra_pixel_move = w_ - (x - x_);
3770 if (extra_pixel_move > 0 && this->alignment() == blender::ui::LayoutAlign::Expand &&
3771 last_free_item_idx >= 0 && item_last && ItemInternal::auto_fixed_size(item_last))
3772 {
3773 ui_item_move(this->items()[last_free_item_idx], 0, extra_pixel_move);
3774 blender::Span<uiItem *> items_after_last_free = this->items().drop_front(last_free_item_idx +
3775 1);
3776 for (uiItem *item : items_after_last_free) {
3777 ui_item_move(item, extra_pixel_move, extra_pixel_move);
3778 }
3779 }
3780
3781 w_ = x - x_;
3782 h_ = y_ - y;
3783 x_ = x;
3784 y_ = y;
3785}
3786
3787static int spaces_after_column_item(const uiLayout *litem,
3788 const uiItem *item,
3789 const uiItem *next_item,
3790 const bool is_box)
3791{
3792 if (next_item == nullptr) {
3793 return 0;
3794 }
3795 if (item->type() == uiItemType::LayoutPanelHeader &&
3796 next_item->type() == uiItemType::LayoutPanelHeader)
3797 {
3798 /* No extra space between layout panel headers. */
3799 return 0;
3800 }
3801 if (item->type() == uiItemType::LayoutPanelBody &&
3802 !ELEM(next_item->type(), uiItemType::LayoutPanelHeader, uiItemType::LayoutPanelBody))
3803 {
3804 /* One for the end of the panel and one at the start of the parent panel. */
3805 return 2;
3806 }
3807 if (!is_box) {
3808 return 1;
3809 }
3810 if (item != litem->items().first()) {
3811 return 1;
3812 }
3813 return 0;
3814}
3815
3816/* single-column layout */
3818{
3819 if (this->type() == uiItemType::LayoutRoot) {
3820 return;
3821 }
3822 const bool is_box = this->type() == uiItemType::LayoutBox;
3823 int itemw, itemh;
3824 bool min_size_flag = true;
3825
3826 w_ = 0;
3827 h_ = 0;
3828
3829 for (auto *iter = this->items().begin(); iter != this->items().end(); iter++) {
3830 uiItem *item = *iter;
3831 ui_item_size(item, &itemw, &itemh);
3832
3833 min_size_flag = min_size_flag && item->fixed_size();
3834
3835 w_ = std::max(w_, itemw);
3836 h_ += itemh;
3837
3838 const uiItem *next_item = (item == this->items().last()) ? nullptr : *(iter + 1);
3839 const int spaces_num = spaces_after_column_item(this, item, next_item, is_box);
3840 h_ += spaces_num * space_;
3841 }
3842
3843 if (min_size_flag) {
3844 this->fixed_size_set(true);
3845 }
3846}
3847
3849{
3850 const bool is_box = this->type() == uiItemType::LayoutBox;
3851 const bool is_menu = this->type() == uiItemType::LayoutRoot &&
3852 this->root()->type == blender::ui::LayoutType::Menu;
3853 const int x = x_;
3854 int y = y_;
3855
3856 for (auto *iter = this->items().begin(); iter != this->items().end(); iter++) {
3857 uiItem *item = *iter;
3858 int itemw, itemh;
3859 ui_item_size(item, &itemw, &itemh);
3860
3861 y -= itemh;
3862 ui_item_position(item, x, y, is_menu ? itemw : w_, itemh);
3863
3864 const uiItem *next_item = (item == this->items().last()) ? nullptr : *(iter + 1);
3865 const int spaces_num = spaces_after_column_item(this, item, next_item, is_box);
3866 y -= spaces_num * space_;
3867
3868 if (is_box) {
3869 ItemInternal::box_item_set(item, true);
3870 }
3871 }
3872
3873 h_ = y_ - y;
3874 x_ = x;
3875 y_ = y;
3876}
3877
3878/* calculates the angle of a specified button in a radial menu,
3879 * stores a float vector in unit circle */
3880static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum)
3881{
3882 if (itemnum >= PIE_MAX_ITEMS) {
3883 itemnum %= PIE_MAX_ITEMS;
3884 printf("Warning: Pie menus with more than %i items are currently unsupported\n",
3886 }
3887
3889 ui_but_pie_dir(dir, vec);
3890
3891 return dir;
3892}
3893
3895{
3896
3897 if ((item->type() == uiItemType::Button) &&
3898 ((static_cast<uiButtonItem *>(item))->but->type == ButType::Label))
3899 {
3900 return false;
3901 }
3902
3903 return true;
3904}
3905
3907{
3908
3910 return false;
3911 }
3912
3913 return true;
3914}
3915
3917{
3918 int itemh, itemw;
3919 int itemnum = 0;
3920
3921 /* For the radial layout we will use Matt Ebb's design
3922 * for radiation, see http://mattebb.com/weblog/radiation/
3923 * also the old code at #5103. */
3924
3925 const int pie_radius = U.pie_menu_radius * UI_SCALE_FAC;
3926
3927 const int x = x_;
3928 const int y = y_;
3929
3930 int minx = x, miny = y, maxx = x, maxy = y;
3931
3932 this->block()->pie_data.pie_dir_mask = 0;
3933
3934 for (uiItem *item : this->items()) {
3935 /* Not all button types are drawn in a radial menu, do filtering here. */
3936 if (!ui_item_is_radial_displayable(item)) {
3937 continue;
3938 }
3939
3940 float vec[2];
3941 const RadialDirection dir = ui_get_radialbut_vec(vec, itemnum);
3942 const float factor[2] = {
3943 (vec[0] > 0.01f) ? 0.0f : ((vec[0] < -0.01f) ? -1.0f : -0.5f),
3944 (vec[1] > 0.99f) ? 0.0f : ((vec[1] < -0.99f) ? -1.0f : -0.5f),
3945 };
3946 itemnum++;
3947
3948 /* Enable for non-buttons because a direction may reference a layout, see: #112610. */
3949 bool use_dir = true;
3950
3951 if (item->type() == uiItemType::Button) {
3952 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
3953
3954 bitem->but->pie_dir = dir;
3955 /* Scale the buttons. */
3956 bitem->but->rect.ymax *= 1.5f;
3957 /* Add a little bit more here to include number. */
3958 bitem->but->rect.xmax += 1.5f * UI_UNIT_X;
3959 /* Enable drawing as pie item if supported by widget. */
3960 if (ui_item_is_radial_drawable(bitem)) {
3962 bitem->but->drawflag |= UI_BUT_ICON_LEFT;
3963 }
3964
3965 if (ELEM(bitem->but->type, ButType::Sepr, ButType::SeprLine)) {
3966 use_dir = false;
3967 }
3968 }
3969
3970 if (use_dir) {
3971 this->block()->pie_data.pie_dir_mask |= 1 << int(dir);
3972 }
3973
3974 ui_item_size(item, &itemw, &itemh);
3975
3976 ui_item_position(item,
3977 x + (vec[0] * pie_radius) + (factor[0] * itemw),
3978 y + (vec[1] * pie_radius) + (factor[1] * itemh),
3979 itemw,
3980 itemh);
3981
3982 minx = min_ii(minx, x + (vec[0] * pie_radius) - (itemw / 2));
3983 maxx = max_ii(maxx, x + (vec[0] * pie_radius) + (itemw / 2));
3984 miny = min_ii(miny, y + (vec[1] * pie_radius) - (itemh / 2));
3985 maxy = max_ii(maxy, y + (vec[1] * pie_radius) + (itemh / 2));
3986 }
3987
3988 x_ = minx;
3989 y_ = miny;
3990 w_ = maxx - minx;
3991 h_ = maxy - miny;
3992}
3993
3995{
3996 /* nothing to do */
3997}
3999{
4000 /* Nothing to do. */
4001}
4002
4004{
4005 /* first item is pie menu title, align on center of menu */
4006 uiItem *item = this->items().first();
4007
4008 if (item->type() == uiItemType::Button) {
4009 int itemh, itemw, x, y;
4010 x = x_;
4011 y = y_;
4012
4013 ui_item_size(item, &itemw, &itemh);
4014
4016 item, x - itemw / 2, y + UI_SCALE_FAC * (U.pie_menu_threshold + 9.0f), itemw, itemh);
4017 }
4018}
4019
4020/* panel header layout */
4022{
4023 BLI_assert(this->items().size() == 1);
4024 uiItem *item = this->items().first();
4025
4026 ui_item_size(item, &w_, &h_);
4027}
4028
4030{
4031 Panel *panel = this->root_panel();
4032
4033 BLI_assert(this->items().size() == 1);
4034 uiItem *item = this->items().first();
4035
4036 int w, h;
4037 ui_item_size(item, &w, &h);
4038 y_ -= h;
4039 ui_item_position(item, x_, y_, w_, h);
4040 const float offset = UI_style_get_dpi()->panelspace;
4041 panel->runtime->layout_panels.headers.append(
4042 {float(y_) - offset, float(y_ + h_) - offset, open_prop_owner, open_prop_name});
4043}
4044
4045/* panel body layout */
4047{
4048 Panel *panel = this->root_panel();
4050 const float offset = UI_style_get_dpi()->panelspace;
4051 panel->runtime->layout_panels.bodies.append({
4052 float(y_ - space_) - offset,
4053 float(y_ + h_ + space_) - offset,
4054 });
4055}
4056
4057/* box layout */
4059{
4060 const uiStyle *style = this->root()->style;
4061
4063
4064 int boxspace = style->boxspace;
4065 if (this->root()->type == blender::ui::LayoutType::Header) {
4066 boxspace = 0;
4067 }
4068 w_ += 2 * boxspace;
4069 h_ += 2 * boxspace;
4070}
4071
4073{
4074 const uiStyle *style = this->root()->style;
4075
4076 int boxspace = style->boxspace;
4077 if (this->root()->type == blender::ui::LayoutType::Header) {
4078 boxspace = 0;
4079 }
4080
4081 const int w = w_;
4082 const int h = h_;
4083
4084 x_ += boxspace;
4085 y_ -= boxspace;
4086
4087 if (w != 0) {
4088 w_ -= 2 * boxspace;
4089 }
4090 if (h != 0) {
4091 h_ -= 2 * boxspace;
4092 }
4093
4095
4096 x_ -= boxspace;
4097 y_ -= boxspace;
4098
4099 if (w != 0) {
4100 w_ += 2 * boxspace;
4101 }
4102 if (h != 0) {
4103 h_ += 2 * boxspace;
4104 }
4105
4106 /* roundbox around the sublayout */
4107 uiBut *but = this->roundbox;
4108 but->rect.xmin = x_;
4109 but->rect.ymin = y_;
4110 but->rect.xmax = x_ + w_;
4111 but->rect.ymax = y_ + h_;
4112}
4113
4114/* multi-column layout, automatically flowing to the next */
4116{
4117 const uiStyle *style = this->root()->style;
4118 uiLayoutItemFlow *flow = this;
4119
4120 int itemw, itemh, maxw = 0;
4121
4122 /* compute max needed width and total height */
4123 int toth = 0;
4124 int totitem = 0;
4125 for (uiItem *item : this->items()) {
4126 ui_item_size(item, &itemw, &itemh);
4127 maxw = std::max(maxw, itemw);
4128 toth += itemh;
4129 totitem++;
4130 }
4131
4132 if (flow->number <= 0) {
4133 /* auto compute number of columns, not very good */
4134 if (maxw == 0) {
4135 flow->totcol = 1;
4136 return;
4137 }
4138
4139 flow->totcol = max_ii(this->root()->emw / maxw, 1);
4140 flow->totcol = min_ii(flow->totcol, totitem);
4141 }
4142 else {
4143 flow->totcol = flow->number;
4144 }
4145
4146 /* compute sizes */
4147 int x = 0;
4148 int y = 0;
4149 int emy = 0;
4150 int miny = 0;
4151
4152 maxw = 0;
4153 const int emh = toth / flow->totcol;
4154
4155 /* create column per column */
4156 int col = 0;
4157 for (uiItem *item : this->items()) {
4158 ui_item_size(item, &itemw, &itemh);
4159
4160 y -= itemh + style->buttonspacey;
4161 miny = min_ii(miny, y);
4162 emy -= itemh;
4163 maxw = max_ii(itemw, maxw);
4164
4165 /* decide to go to next one */
4166 if (col < flow->totcol - 1 && emy <= -emh) {
4167 x += maxw + space_;
4168 maxw = 0;
4169 y = 0;
4170 emy = 0; /* need to reset height again for next column */
4171 col++;
4172 }
4173 }
4174
4175 w_ = x;
4176 h_ = y_ - miny;
4177}
4178
4180{
4181 const uiStyle *style = this->root()->style;
4182 int col, emh, itemw, itemh;
4183
4184 /* compute max needed width and total height */
4185 int toth = 0;
4186 for (uiItem *item : this->items()) {
4187 ui_item_size(item, &itemw, &itemh);
4188 toth += itemh;
4189 }
4190
4191 /* compute sizes */
4192 int x = x_;
4193 int y = y_;
4194 int emy = 0;
4195 int miny = 0;
4196
4197 emh = toth / this->totcol;
4198
4199 /* create column per column */
4200 col = 0;
4201 int w = (w_ - (this->totcol - 1) * style->columnspace) / this->totcol;
4202 for (uiItem *item : this->items()) {
4203 ui_item_size(item, &itemw, &itemh);
4204
4205 itemw = (this->alignment() == blender::ui::LayoutAlign::Expand) ? w : min_ii(w, itemw);
4206
4207 y -= itemh;
4208 emy -= itemh;
4209 ui_item_position(item, x, y, itemw, itemh);
4210 y -= style->buttonspacey;
4211 miny = min_ii(miny, y);
4212
4213 /* decide to go to next one */
4214 if (col < this->totcol - 1 && emy <= -emh) {
4215 x += w + style->columnspace;
4216 y = y_;
4217 emy = 0; /* need to reset height again for next column */
4218 col++;
4219
4220 const int remaining_width = w_ - (x - x_);
4221 const int remaining_width_between_columns = (this->totcol - col - 1) * style->columnspace;
4222 const int remaining_columns = this->totcol - col;
4223 w = (remaining_width - remaining_width_between_columns) / remaining_columns;
4224 }
4225 }
4226
4227 h_ = y_ - miny;
4228 x_ = x;
4229 y_ = miny;
4230}
4231
4232/* multi-column and multi-row layout. */
4234 /* General layout control settings. */
4235 bool row_major : 1; /* Fill rows before columns */
4236 bool even_columns : 1; /* All columns will have same width. */
4237 bool even_rows : 1; /* All rows will have same height. */
4238 int space_x; /* Space between columns. */
4239 int space_y; /* Space between rows. */
4240 /* Real data about current position and size of this layout item
4241 * (either estimated, or final values). */
4242 int litem_w; /* Layout item width. */
4243 int litem_x; /* Layout item X position. */
4244 int litem_y; /* Layout item Y position. */
4245 /* Actual number of columns and rows to generate (computed from first pass usually). */
4246 int tot_columns; /* Number of columns. */
4247 int tot_rows; /* Number of rows. */
4248};
4249
4251 int *tot_items; /* Total number of items in this grid layout. */
4252 /* Width / X pos data. */
4253 float *global_avg_w; /* Computed average width of the columns. */
4254 int *cos_x_array; /* Computed X coordinate of each column. */
4255 int *widths_array; /* Computed width of each column. */
4256 int *tot_w; /* Computed total width. */
4257 /* Height / Y pos data. */
4258 int *global_max_h; /* Computed height of the tallest item in the grid. */
4259 int *cos_y_array; /* Computed Y coordinate of each column. */
4260 int *heights_array; /* Computed height of each column. */
4261 int *tot_h; /* Computed total height. */
4262};
4263
4265 const UILayoutGridFlowInput *parameters,
4266 UILayoutGridFlowOutput *results)
4267{
4268 float tot_w = 0.0f, tot_h = 0.0f;
4269 float global_avg_w = 0.0f, global_totweight_w = 0.0f;
4270 int global_max_h = 0;
4271
4272 BLI_assert(parameters->tot_columns != 0 ||
4273 (results->cos_x_array == nullptr && results->widths_array == nullptr &&
4274 results->tot_w == nullptr));
4275 BLI_assert(parameters->tot_rows != 0 ||
4276 (results->cos_y_array == nullptr && results->heights_array == nullptr &&
4277 results->tot_h == nullptr));
4278
4279 if (results->tot_items) {
4280 *results->tot_items = 0;
4281 }
4282
4283 if (items.is_empty()) {
4284 if (results->global_avg_w) {
4285 *results->global_avg_w = 0.0f;
4286 }
4287 if (results->global_max_h) {
4288 *results->global_max_h = 0;
4289 }
4290 return;
4291 }
4292
4293 blender::Array<float, 64> avg_w(parameters->tot_columns, 0.0f);
4294 blender::Array<float, 64> totweight_w(parameters->tot_columns, 0.0f);
4295 blender::Array<int, 64> max_h(parameters->tot_rows, 0);
4296
4297 int i = 0;
4298 for (const uiItem *item : items) {
4299 int item_w, item_h;
4300 ui_item_size(item, &item_w, &item_h);
4301
4302 global_avg_w += float(item_w * item_w);
4303 global_totweight_w += float(item_w);
4304 global_max_h = max_ii(global_max_h, item_h);
4305
4306 if (parameters->tot_rows != 0 && parameters->tot_columns != 0) {
4307 const int index_col = parameters->row_major ? i % parameters->tot_columns :
4308 i / parameters->tot_rows;
4309 const int index_row = parameters->row_major ? i / parameters->tot_columns :
4310 i % parameters->tot_rows;
4311
4312 avg_w[index_col] += float(item_w * item_w);
4313 totweight_w[index_col] += float(item_w);
4314
4315 max_h[index_row] = max_ii(max_h[index_row], item_h);
4316 }
4317
4318 if (results->tot_items) {
4319 (*results->tot_items)++;
4320 }
4321 i++;
4322 }
4323
4324 /* Finalize computing of column average sizes */
4325 global_avg_w /= global_totweight_w;
4326 if (parameters->tot_columns != 0) {
4327 for (i = 0; i < parameters->tot_columns; i++) {
4328 avg_w[i] /= totweight_w[i];
4329 tot_w += avg_w[i];
4330 }
4331 if (parameters->even_columns) {
4332 tot_w = ceilf(global_avg_w) * parameters->tot_columns;
4333 }
4334 }
4335 /* Finalize computing of rows max sizes */
4336 if (parameters->tot_rows != 0) {
4337 for (i = 0; i < parameters->tot_rows; i++) {
4338 tot_h += max_h[i];
4339 }
4340 if (parameters->even_rows) {
4341 tot_h = global_max_h * parameters->tot_columns;
4342 }
4343 }
4344
4345 /* Compute positions and sizes of all cells. */
4346 if (results->cos_x_array != nullptr && results->widths_array != nullptr) {
4347 /* We enlarge/narrow columns evenly to match available width. */
4348 const float wfac = float(parameters->litem_w -
4349 (parameters->tot_columns - 1) * parameters->space_x) /
4350 tot_w;
4351
4352 for (int col = 0; col < parameters->tot_columns; col++) {
4353 results->cos_x_array[col] = (col ? results->cos_x_array[col - 1] +
4354 results->widths_array[col - 1] + parameters->space_x :
4355 parameters->litem_x);
4356 if (parameters->even_columns) {
4357 /* (< remaining width > - < space between remaining columns >) / < remaining columns > */
4358 results->widths_array[col] = (((parameters->litem_w -
4359 (results->cos_x_array[col] - parameters->litem_x)) -
4360 (parameters->tot_columns - col - 1) * parameters->space_x) /
4361 (parameters->tot_columns - col));
4362 }
4363 else if (col == parameters->tot_columns - 1) {
4364 /* Last column copes width rounding errors... */
4365 results->widths_array[col] = parameters->litem_w -
4366 (results->cos_x_array[col] - parameters->litem_x);
4367 }
4368 else {
4369 results->widths_array[col] = int(avg_w[col] * wfac);
4370 }
4371 }
4372 }
4373 if (results->cos_y_array != nullptr && results->heights_array != nullptr) {
4374 for (int row = 0; row < parameters->tot_rows; row++) {
4375 if (parameters->even_rows) {
4376 results->heights_array[row] = global_max_h;
4377 }
4378 else {
4379 results->heights_array[row] = max_h[row];
4380 }
4381 results->cos_y_array[row] = (row ? results->cos_y_array[row - 1] - parameters->space_y -
4382 results->heights_array[row] :
4383 parameters->litem_y - results->heights_array[row]);
4384 }
4385 }
4386
4387 if (results->global_avg_w) {
4388 *results->global_avg_w = global_avg_w;
4389 }
4390 if (results->global_max_h) {
4391 *results->global_max_h = global_max_h;
4392 }
4393 if (results->tot_w) {
4394 *results->tot_w = int(tot_w) + parameters->space_x * (parameters->tot_columns - 1);
4395 }
4396 if (results->tot_h) {
4397 *results->tot_h = tot_h + parameters->space_y * (parameters->tot_rows - 1);
4398 }
4399}
4400
4402{
4403 const uiStyle *style = this->root()->style;
4404 uiLayoutItemGridFlow *gflow = this;
4405
4406 const int space_x = style->columnspace;
4407 const int space_y = style->buttonspacey;
4408
4409 /* Estimate average needed width and height per item. */
4410 {
4411 float avg_w;
4412 int max_h;
4413
4415 input.row_major = gflow->row_major;
4416 input.even_columns = gflow->even_columns;
4417 input.even_rows = gflow->even_rows;
4418 input.litem_w = w_;
4419 input.litem_x = x_;
4420 input.litem_y = y_;
4421 input.space_x = space_x;
4422 input.space_y = space_y;
4424 output.tot_items = &gflow->tot_items;
4425 output.global_avg_w = &avg_w;
4426 output.global_max_h = &max_h;
4428
4429 if (gflow->tot_items == 0) {
4430 w_ = h_ = 0;
4431 gflow->tot_columns = gflow->tot_rows = 0;
4432 return;
4433 }
4434
4435 /* Even in varying column width case,
4436 * we fix our columns number from weighted average width of items,
4437 * a proper solving of required width would be too costly,
4438 * and this should give reasonably good results in all reasonable cases. */
4439 if (gflow->columns_len > 0) {
4440 gflow->tot_columns = gflow->columns_len;
4441 }
4442 else {
4443 if (avg_w == 0.0f) {
4444 gflow->tot_columns = 1;
4445 }
4446 else {
4447 gflow->tot_columns = min_ii(max_ii(int(w_ / avg_w), 1), gflow->tot_items);
4448 }
4449 }
4450 gflow->tot_rows = int(ceilf(float(gflow->tot_items) / gflow->tot_columns));
4451
4452 /* Try to tweak number of columns and rows to get better filling of last column or row,
4453 * and apply 'modulo' value to number of columns or rows.
4454 * Note that modulo does not prevent ending with fewer columns/rows than modulo, if mandatory
4455 * to avoid empty column/row. */
4456 {
4457 const int modulo = (gflow->columns_len < -1) ? -gflow->columns_len : 0;
4458 const int step = modulo ? modulo : 1;
4459
4460 if (gflow->row_major) {
4461 /* Adjust number of columns to be multiple of given modulo. */
4462 if (modulo && gflow->tot_columns % modulo != 0 && gflow->tot_columns > modulo) {
4463 gflow->tot_columns = gflow->tot_columns - (gflow->tot_columns % modulo);
4464 }
4465 /* Find smallest number of columns conserving computed optimal number of rows. */
4466 for (gflow->tot_rows = int(ceilf(float(gflow->tot_items) / gflow->tot_columns));
4467 (gflow->tot_columns - step) > 0 &&
4468 int(ceilf(float(gflow->tot_items) / (gflow->tot_columns - step))) <= gflow->tot_rows;
4469 gflow->tot_columns -= step)
4470 {
4471 /* pass */
4472 }
4473 }
4474 else {
4475 /* Adjust number of rows to be multiple of given modulo. */
4476 if (modulo && gflow->tot_rows % modulo != 0) {
4477 gflow->tot_rows = min_ii(gflow->tot_rows + modulo - (gflow->tot_rows % modulo),
4478 gflow->tot_items);
4479 }
4480 /* Find smallest number of rows conserving computed optimal number of columns. */
4481 for (gflow->tot_columns = int(ceilf(float(gflow->tot_items) / gflow->tot_rows));
4482 (gflow->tot_rows - step) > 0 &&
4483 int(ceilf(float(gflow->tot_items) / (gflow->tot_rows - step))) <= gflow->tot_columns;
4484 gflow->tot_rows -= step)
4485 {
4486 /* pass */
4487 }
4488 }
4489 }
4490
4491 /* Set evenly-spaced axes size
4492 * (quick optimization in case we have even columns and rows). */
4493 if (gflow->even_columns && gflow->even_rows) {
4494 w_ = int(gflow->tot_columns * avg_w) + space_x * (gflow->tot_columns - 1);
4495 h_ = int(gflow->tot_rows * max_h) + space_y * (gflow->tot_rows - 1);
4496 return;
4497 }
4498 }
4499
4500 /* Now that we have our final number of columns and rows,
4501 * we can compute actual needed space for non-evenly sized axes. */
4502 {
4503 int tot_w, tot_h;
4505 input.row_major = gflow->row_major;
4506 input.even_columns = gflow->even_columns;
4507 input.even_rows = gflow->even_rows;
4508 input.litem_w = w_;
4509 input.litem_x = x_;
4510 input.litem_y = y_;
4511 input.space_x = space_x;
4512 input.space_y = space_y;
4513 input.tot_columns = gflow->tot_columns;
4514 input.tot_rows = gflow->tot_rows;
4516 output.tot_w = &tot_w;
4517 output.tot_h = &tot_h;
4519
4520 w_ = tot_w;
4521 h_ = tot_h;
4522 }
4523}
4524
4526{
4527 const uiStyle *style = this->root()->style;
4528
4529 if (this->tot_items == 0) {
4530 w_ = h_ = 0;
4531 return;
4532 }
4533
4534 BLI_assert(this->tot_columns > 0);
4535 BLI_assert(this->tot_rows > 0);
4536
4537 const int space_x = style->columnspace;
4538 const int space_y = style->buttonspacey;
4539
4541 blender::Array<int, 64> heights(this->tot_rows);
4543 blender::Array<int, 64> cos_y(this->tot_rows);
4544
4545 /* This time we directly compute coordinates and sizes of all cells. */
4547 input.row_major = this->row_major;
4548 input.even_columns = this->even_columns;
4549 input.even_rows = this->even_rows;
4550 input.litem_w = w_;
4551 input.litem_x = x_;
4552 input.litem_y = y_;
4553 input.space_x = space_x;
4554 input.space_y = space_y;
4555 input.tot_columns = this->tot_columns;
4556 input.tot_rows = this->tot_rows;
4558 output.cos_x_array = cos_x.data();
4559 output.cos_y_array = cos_y.data();
4560 output.widths_array = widths.data();
4561 output.heights_array = heights.data();
4563
4564 int i = 0;
4565 for (uiItem *item : this->items()) {
4566 const int col = this->row_major ? i % this->tot_columns : i / this->tot_rows;
4567 const int row = this->row_major ? i / this->tot_columns : i % this->tot_rows;
4568 int item_w, item_h;
4569 ui_item_size(item, &item_w, &item_h);
4570
4571 const int w = widths[col];
4572 const int h = heights[row];
4573
4574 item_w = (this->alignment() == blender::ui::LayoutAlign::Expand) ? w : min_ii(w, item_w);
4575 item_h = (this->alignment() == blender::ui::LayoutAlign::Expand) ? h : min_ii(h, item_h);
4576
4577 ui_item_position(item, cos_x[col], cos_y[row], item_w, item_h);
4578 i++;
4579 }
4580
4581 h_ = y_ - cos_y[this->tot_rows - 1];
4582 x_ = (cos_x[this->tot_columns - 1] - x_) + widths[this->tot_columns - 1];
4583 y_ = y_ - h_;
4584}
4585
4586/* free layout */
4588{
4589 int minx = 1e6;
4590 int miny = 1e6;
4591 w_ = 0;
4592 h_ = 0;
4593
4594 for (uiItem *item : this->items()) {
4595 int itemx, itemy, itemw, itemh;
4596 ui_item_offset(item, &itemx, &itemy);
4597 ui_item_size(item, &itemw, &itemh);
4598
4599 minx = min_ii(minx, itemx);
4600 miny = min_ii(miny, itemy);
4601
4602 w_ = std::max(w_, itemx + itemw);
4603 h_ = std::max(h_, itemy + itemh);
4604 }
4605
4606 w_ -= minx;
4607 h_ -= miny;
4608}
4609
4611{
4612 float scalex = 1.0f, scaley = 1.0f;
4613 int x, y, newx, newy, itemx, itemy, itemh, itemw;
4614
4615 int minx = 1e6;
4616 int miny = 1e6;
4617 int totw = 0;
4618 int toth = 0;
4619
4620 for (uiItem *item : this->items()) {
4621 ui_item_offset(item, &itemx, &itemy);
4622 ui_item_size(item, &itemw, &itemh);
4623
4624 minx = min_ii(minx, itemx);
4625 miny = min_ii(miny, itemy);
4626
4627 totw = max_ii(totw, itemx + itemw);
4628 toth = max_ii(toth, itemy + itemh);
4629 }
4630
4631 totw -= minx;
4632 toth -= miny;
4633
4634 if (w_ && totw > 0) {
4635 scalex = float(w_) / float(totw);
4636 }
4637 if (h_ && toth > 0) {
4638 scaley = float(h_) / float(toth);
4639 }
4640
4641 x = x_;
4642 y = y_ - scaley * toth;
4643
4644 for (uiItem *item : this->items()) {
4645 ui_item_offset(item, &itemx, &itemy);
4646 ui_item_size(item, &itemw, &itemh);
4647
4648 if (scalex != 1.0f) {
4649 newx = (itemx - minx) * scalex;
4650 itemw = (itemx - minx + itemw) * scalex - newx;
4651 itemx = minx + newx;
4652 }
4653
4654 if (scaley != 1.0f) {
4655 newy = (itemy - miny) * scaley;
4656 itemh = (itemy - miny + itemh) * scaley - newy;
4657 itemy = miny + newy;
4658 }
4659
4660 ui_item_position(item, x + itemx - minx, y + itemy - miny, itemw, itemh);
4661 }
4662
4663 w_ = scalex * totw;
4664 h_ = y_ - y;
4665 x_ = x + w_;
4666 y_ = y;
4667}
4668
4669/* split layout */
4675
4677{
4678 float extra_pixel = 0.0f;
4679 const int tot = int(this->items().size());
4680
4681 if (tot == 0) {
4682 return;
4683 }
4684
4685 int x = x_;
4686 const int y = y_;
4687
4688 const float percentage = (this->percentage == 0.0f) ? 1.0f / float(tot) : this->percentage;
4689
4690 const int w = (w_ - (tot - 1) * space_);
4691 int colw = w * percentage;
4692 colw = std::max(colw, 0);
4693
4694 const uiItem *item_last = this->items().last();
4695 for (uiItem *item : this->items()) {
4696 const bool is_item_last = (item == item_last);
4697 int itemh;
4698 ui_item_size(item, nullptr, &itemh);
4699
4700 ui_item_position(item, x, y - itemh, colw, itemh);
4701 x += colw;
4702
4703 if (!is_item_last) {
4704 const float width = extra_pixel + (w - int(w * percentage)) / (float(tot) - 1);
4705 extra_pixel = width - int(width);
4706 colw = int(width);
4707 colw = std::max(colw, 0);
4708
4709 x += space_;
4710 }
4711 }
4712
4713 w_ = x - x_;
4714 h_ = y_ - y;
4715 x_ = x;
4716 y_ = y;
4717}
4718
4719/* overlap layout */
4721{
4722 w_ = 0;
4723 h_ = 0;
4724
4725 for (uiItem *item : this->items()) {
4726 int itemw, itemh;
4727 ui_item_size(item, &itemw, &itemh);
4728
4729 w_ = std::max(itemw, w_);
4730 h_ = std::max(itemh, h_);
4731 }
4732}
4733
4735{
4736
4737 const int x = x_;
4738 const int y = y_;
4739
4740 for (uiItem *item : this->items()) {
4741 int itemw, itemh;
4742 ui_item_size(item, &itemw, &itemh);
4743 ui_item_position(item, x, y - itemh, w_, itemh);
4744
4745 h_ = std::max(h_, itemh);
4746 }
4747
4748 x_ = x;
4749 y_ = y - h_;
4750}
4751
4753{
4754 litem->root_ = layout->root_;
4755 litem->align_ = align;
4756 /* Children of grid-flow layout shall never have "ideal big size" returned as estimated size. */
4757 litem->variable_size_ = layout->variable_size_ || layout->type() == uiItemType::LayoutGridFlow;
4758 litem->active_ = true;
4759 litem->enabled_ = true;
4760 litem->context_ = layout->context_;
4761 litem->redalert_ = layout->redalert_;
4762 litem->w_ = layout->w_;
4763 litem->emboss_ = layout->emboss_;
4766
4767 if (layout->child_items_layout_) {
4768 layout->child_items_layout_->items_.append(litem);
4769 litem->parent_ = layout->child_items_layout_;
4770 }
4771 else {
4772 layout->items_.append(litem);
4773 litem->parent_ = layout;
4774 }
4775}
4776
4778{
4779 uiLayout *litem = MEM_new<LayoutRow>(__func__, nullptr);
4781
4782 litem->space_ = (align) ? 0 : root_->style->buttonspacex;
4783
4785
4786 return *litem;
4787}
4788
4790 PointerRNA *open_prop_owner,
4791 const StringRefNull open_prop_name)
4792{
4793 const ARegion *region = CTX_wm_region(C);
4794
4795 const bool is_real_open = RNA_boolean_get(open_prop_owner, open_prop_name.c_str());
4796 const bool search_filter_active = region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE;
4797 const bool is_open = is_real_open || search_filter_active;
4798
4799 PanelLayout panel_layout{};
4800 {
4801 uiLayoutItemPanelHeader *header_litem = MEM_new<uiLayoutItemPanelHeader>(__func__);
4802 LayoutInternal::init_from_parent(header_litem, this, false);
4803
4804 header_litem->open_prop_owner = *open_prop_owner;
4805 header_litem->open_prop_name = open_prop_name;
4806
4807 uiLayout *row = &header_litem->row(true);
4808 row->ui_units_y_set(1.2f);
4809
4810 uiBlock *block = row->block();
4811 const int icon = is_open ? ICON_DOWNARROW_HLT : ICON_RIGHTARROW;
4812 const int width = ui_text_icon_width(this, "", icon, false);
4813 uiDefIconTextBut(block, ButType::Label, 0, icon, "", 0, 0, width, UI_UNIT_Y, nullptr, "");
4814
4815 panel_layout.header = row;
4816 }
4817
4818 if (!is_open) {
4819 return panel_layout;
4820 }
4821
4822 uiLayoutItemPanelBody *body_litem = MEM_new<uiLayoutItemPanelBody>(__func__);
4823 body_litem->space_ = root_->style->templatespace;
4824 LayoutInternal::init_from_parent(body_litem, this, false);
4825 blender::ui::block_layout_set_current(this->block(), body_litem);
4826 panel_layout.body = body_litem;
4827
4828 return panel_layout;
4829}
4830
4832 PointerRNA *open_prop_owner,
4833 const StringRefNull open_prop_name,
4834 PointerRNA *bool_prop_owner,
4835 const StringRefNull bool_prop_name,
4836 const std::optional<StringRef> label)
4837{
4838 PanelLayout panel_layout = this->panel_prop(C, open_prop_owner, open_prop_name);
4839
4840 uiLayout *panel_header = panel_layout.header;
4841 panel_header->flag_ &= ~(uiItemInternalFlag::PropSep | uiItemInternalFlag::PropDecorate |
4842 uiItemInternalFlag::InsidePropSep);
4843 panel_header->prop(bool_prop_owner, bool_prop_name, UI_ITEM_NONE, label, ICON_NONE);
4844
4845 return panel_layout;
4846}
4847
4849 PointerRNA *open_prop_owner,
4850 const StringRefNull open_prop_name,
4851 const StringRef label)
4852{
4853 PanelLayout panel_layout = this->panel_prop(C, open_prop_owner, open_prop_name);
4854 panel_layout.header->label(label, ICON_NONE);
4855
4856 return panel_layout.body;
4857}
4858
4859PanelLayout uiLayout::panel(const bContext *C, const StringRef idname, const bool default_closed)
4860{
4861 Panel *root_panel = this->root_panel();
4862 BLI_assert(root_panel != nullptr);
4863
4865 root_panel, idname, default_closed);
4866 PointerRNA state_ptr = RNA_pointer_create_discrete(nullptr, &RNA_LayoutPanelState, state);
4867
4868 return this->panel_prop(C, &state_ptr, "is_open");
4869}
4870
4872 const StringRef idname,
4873 const bool default_closed,
4874 const StringRef label)
4875{
4876 PanelLayout panel_layout = this->panel(C, idname, default_closed);
4877 panel_layout.header->label(label, ICON_NONE);
4878
4879 return panel_layout.body;
4880}
4881
4883{
4884 if (layout.items().is_empty()) {
4885 return false;
4886 }
4887 const uiItem *item = layout.items().last();
4888 return item->type() == uiItemType::LayoutPanelHeader;
4889}
4890
4892{
4893 uiLayout &litem = this->row(align);
4894 litem.heading_ = heading;
4895 return litem;
4896}
4897
4899{
4900 uiLayout *litem = MEM_new<LayoutColumn>(__func__, nullptr);
4902
4903 litem->space_ = (align) ? 0 : root_->style->buttonspacey;
4904
4906
4907 return *litem;
4908}
4909
4911{
4912 uiLayout &litem = this->column(align);
4913 litem.heading_ = heading;
4914 return litem;
4915}
4916
4918{
4919 uiLayoutItemFlow *flow = MEM_new<uiLayoutItemFlow>(__func__);
4921
4922 flow->space_ = flow->align() ? 0 : root_->style->columnspace;
4923 flow->number = number;
4924
4926
4927 return *flow;
4928}
4929
4931 bool row_major, int columns_len, bool even_columns, bool even_rows, bool align)
4932{
4933 uiLayoutItemGridFlow *flow = MEM_new<uiLayoutItemGridFlow>(__func__);
4935
4936 flow->space_ = flow->align() ? 0 : root_->style->columnspace;
4937 flow->row_major = row_major;
4938 flow->columns_len = columns_len;
4939 flow->even_columns = even_columns;
4940 flow->even_rows = even_rows;
4941
4943
4944 return *flow;
4945}
4946
4948{
4949 uiLayoutItemBx *box = MEM_new<uiLayoutItemBx>(__func__);
4950 LayoutInternal::init_from_parent(box, layout, false);
4951
4952 box->space_ = layout->root()->style->columnspace;
4953
4955
4956 box->roundbox = uiDefBut(layout->block(), type, 0, "", 0, 0, 0, 0, nullptr, 0.0, 0.0, "");
4957
4958 return box;
4959}
4960
4962{
4963 /* radial layouts are only valid for radial menus */
4965 return *ui_item_local_sublayout(this, this, false);
4966 }
4967
4968 /* only one radial wheel per root layout is allowed, so check and return that, if it exists */
4969 for (uiItem *item : root_->layout->items()) {
4970 if (item->type() == uiItemType::LayoutRadial) {
4971 uiLayout *litem = static_cast<uiLayout *>(item);
4973 return *litem;
4974 }
4975 }
4976
4977 uiLayout *litem = MEM_new<LayoutRadial>(__func__);
4978 LayoutInternal::init_from_parent(litem, this, false);
4979
4981
4982 return *litem;
4983}
4984
4986{
4987 return *ui_layout_box(this, ButType::Roundbox);
4988}
4989
4991{
4992 for (uiItem *item : layout->items()) {
4993 if (item->type() != uiItemType::Button) {
4994 ui_layout_list_set_labels_active(static_cast<uiLayout *>(item));
4995 }
4996 else {
4997 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
4998 if (bitem->but->flag & UI_BUT_LIST_ITEM) {
5000 }
5001 }
5002 }
5003}
5004
5006{
5008 uiBut *but = item_box->roundbox;
5009
5010 but->custom_data = ui_list;
5011
5012 but->rnapoin = *actptr;
5013 but->rnaprop = actprop;
5014
5015 /* only for the undo string */
5016 if (but->flag & UI_BUT_UNDO) {
5017 but->tip = RNA_property_description(actprop);
5018 }
5019
5020 return *item_box;
5021}
5022
5024{
5025 uiLayout *litem = MEM_new<LayoutAbsolute>(__func__);
5027
5029
5030 return *litem;
5031}
5032
5034{
5035 uiBlock *block = this->block();
5036 absolute(false);
5037
5038 return block;
5039}
5040
5042{
5043 uiLayout *litem = MEM_new<LayoutOverlap>(__func__);
5044 LayoutInternal::init_from_parent(litem, this, false);
5045
5047
5048 return *litem;
5049}
5050
5051uiLayout &uiLayout::split(float percentage, bool align)
5052{
5053 uiLayoutItemSplit *split = MEM_new<uiLayoutItemSplit>(__func__);
5055
5056 split->space_ = root_->style->columnspace;
5057 split->percentage = percentage;
5058
5060
5061 return *split;
5062}
5063
5068
5070{
5071 return bool(flag_ & uiItemInternalFlag::PropSep);
5072}
5073
5075{
5076 SET_FLAG_FROM_TEST(flag_, is_sep, uiItemInternalFlag::PropSep);
5077}
5078
5080{
5081 return bool(flag_ & uiItemInternalFlag::PropDecorate);
5082}
5083
5085{
5086 SET_FLAG_FROM_TEST(flag_, is_sep, uiItemInternalFlag::PropDecorate);
5087}
5088
5090{
5091 return this->block()->panel;
5092}
5093
5095{
5097 return this->block()->emboss;
5098 }
5099 return emboss_;
5100}
5101
5103{
5104 return 5 * UI_SCALE_FAC;
5105}
5106
5108{
5109 uiBlock *block = layout->block();
5110 uiLayout *row = &layout->row(true);
5111 row->fixed_size_set(true);
5112
5113 uiDefBut(
5114 block, ButType::Sepr, 0, "", 0, 0, uiLayoutListItemPaddingWidth(), 0, nullptr, 0.0, 0.0, "");
5115
5116 /* Restore. */
5118}
5119
5121
5122/* -------------------------------------------------------------------- */
5125
5126/* Disabled for performance reasons, but this could be turned on in the future. */
5127// #define PROPERTY_SEARCH_USE_TOOLTIPS
5128
5129static bool block_search_panel_label_matches(const uiBlock *block, const char *search_string)
5130{
5131 if ((block->panel != nullptr) && (block->panel->type != nullptr)) {
5132 if (BLI_strcasestr(block->panel->type->label, search_string)) {
5133 return true;
5134 }
5135 }
5136 return false;
5137}
5138
5142static bool button_matches_search_filter(uiBut *but, const char *search_filter)
5143{
5144 /* Do the shorter checks first for better performance in case there is a match. */
5145 if (BLI_strcasestr(but->str.c_str(), search_filter)) {
5146 return true;
5147 }
5148
5149 if (but->optype != nullptr) {
5150 if (BLI_strcasestr(but->optype->name, search_filter)) {
5151 return true;
5152 }
5153 }
5154
5155 if (but->rnaprop != nullptr) {
5156 if (BLI_strcasestr(RNA_property_ui_name(but->rnaprop), search_filter)) {
5157 return true;
5158 }
5159#ifdef PROPERTY_SEARCH_USE_TOOLTIPS
5160 if (BLI_strcasestr(RNA_property_description(but->rnaprop), search_filter)) {
5161 return true;
5162 }
5163#endif
5164
5165 /* Search through labels of enum property items if they are in a drop-down menu.
5166 * Unfortunately we have no #bContext here so we cannot search through RNA enums
5167 * with dynamic entries (or "itemf" functions) which require context. */
5168 if (but->type == ButType::Menu) {
5169 PointerRNA *ptr = &but->rnapoin;
5170 PropertyRNA *enum_prop = but->rnaprop;
5171 int items_len;
5172 const EnumPropertyItem *items_array = nullptr;
5173 bool free;
5174 RNA_property_enum_items_gettexted(nullptr, ptr, enum_prop, &items_array, &items_len, &free);
5175 if (items_array == nullptr) {
5176 return false;
5177 }
5178
5179 bool found = false;
5180 for (int i = 0; i < items_len; i++) {
5181 /* Check for nullptr name field which enums use for separators. */
5182 if (items_array[i].name == nullptr) {
5183 continue;
5184 }
5185 if (BLI_strcasestr(items_array[i].name, search_filter)) {
5186 found = true;
5187 break;
5188 }
5189 }
5190 if (free) {
5191 MEM_freeN((EnumPropertyItem *)items_array);
5192 }
5193 if (found) {
5194 return true;
5195 }
5196 }
5197 }
5198
5199 return false;
5200}
5201
5205static bool button_group_has_search_match(const uiButtonGroup &group, const char *search_filter)
5206{
5207 for (uiBut *but : group.buttons) {
5208 if (button_matches_search_filter(but, search_filter)) {
5209 return true;
5210 }
5211 }
5212
5213 return false;
5214}
5215
5225static bool block_search_filter_tag_buttons(uiBlock *block, const char *search_filter)
5226{
5227 bool has_result = false;
5228 for (const uiButtonGroup &group : block->button_groups) {
5229 if (button_group_has_search_match(group, search_filter)) {
5230 has_result = true;
5231 }
5232 else {
5233 for (uiBut *but : group.buttons) {
5235 }
5236 }
5237 }
5238 return has_result;
5239}
5240
5241bool UI_block_apply_search_filter(uiBlock *block, const char *search_filter)
5242{
5243 if (search_filter == nullptr || search_filter[0] == '\0') {
5244 return false;
5245 }
5246
5247 Panel *panel = block->panel;
5248
5249 if (panel != nullptr) {
5250 /* Panels for active blocks should always have a valid `panel->type`,
5251 * otherwise they wouldn't be created. */
5252 if (panel->type->flag & PANEL_TYPE_NO_SEARCH) {
5253 return false;
5254 }
5255 }
5256
5257 const bool panel_label_matches = block_search_panel_label_matches(block, search_filter);
5258
5259 const bool has_result = (panel_label_matches) ?
5260 true :
5261 block_search_filter_tag_buttons(block, search_filter);
5262
5263 if (panel != nullptr) {
5264 if (has_result) {
5266 }
5267 }
5268
5269 return has_result;
5270}
5271
5273
5274/* -------------------------------------------------------------------- */
5277
5278static void ui_item_scale(uiLayout *litem, const float scale[2])
5279{
5280 int x, y, w, h;
5281
5282 for (auto riter = litem->items().rbegin(); riter != litem->items().rend(); riter++) {
5283 uiItem *item = *riter;
5284 if (item->type() != uiItemType::Button) {
5285 uiLayout *subitem = static_cast<uiLayout *>(item);
5286 ui_item_scale(subitem, scale);
5287 }
5288
5289 ui_item_size(item, &w, &h);
5290 ui_item_offset(item, &x, &y);
5291
5292 if (scale[0] != 0.0f) {
5293 x *= scale[0];
5294 w *= scale[0];
5295 }
5296
5297 if (scale[1] != 0.0f) {
5298 y *= scale[1];
5299 h *= scale[1];
5300 }
5301
5302 ui_item_position(item, x, y, w, h);
5303 }
5304}
5305
5307{
5308 if (this->type() != uiItemType::Button) {
5309
5310 if (this->items().is_empty()) {
5311 w_ = 0;
5312 h_ = 0;
5313 return;
5314 }
5315
5316 for (uiItem *subitem : this->items()) {
5317 if (subitem->type() == uiItemType::Button) {
5318 continue;
5319 }
5320 static_cast<uiLayout *>(subitem)->estimate();
5321 }
5322
5323 if (this->scale_x() != 0.0f || this->scale_y() != 0.0f) {
5324 ui_item_scale(this, blender::float2{this->scale_x(), this->scale_y()});
5325 }
5326 this->estimate_impl();
5327
5328 /* Force fixed size. */
5329 if (this->ui_units_x() > 0) {
5330 w_ = UI_UNIT_X * this->ui_units_x();
5331 }
5332 if (this->ui_units_y() > 0) {
5333 h_ = UI_UNIT_Y * this->ui_units_y();
5334 }
5335 }
5336}
5337
5338static void ui_item_align(uiLayout *litem, short nr)
5339{
5340 for (auto riter = litem->items().rbegin(); riter != litem->items().rend(); riter++) {
5341 uiItem *item = *riter;
5342 if (item->type() == uiItemType::Button) {
5343 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5344 if (!bitem->but->alignnr) {
5345 bitem->but->alignnr = nr;
5346 }
5347 }
5348 else if (item->type() == uiItemType::LayoutAbsolute) {
5349 /* pass */
5350 }
5351 else if (item->type() == uiItemType::LayoutOverlap) {
5352 /* pass */
5353 }
5354 else if (item->type() == uiItemType::LayoutBox) {
5355 uiLayoutItemBx *box = static_cast<uiLayoutItemBx *>(item);
5356 if (!box->roundbox->alignnr) {
5357 box->roundbox->alignnr = nr;
5358 }
5359 }
5360 else {
5361 uiLayout *litem = static_cast<uiLayout *>(item);
5362 if (litem->align()) {
5363 ui_item_align(litem, nr);
5364 }
5365 }
5366 }
5367}
5368
5369static void ui_item_flag(uiLayout *litem, int flag)
5370{
5371 for (auto riter = litem->items().rbegin(); riter != litem->items().rend(); riter++) {
5372 uiItem *item = *riter;
5373 if (item->type() == uiItemType::Button) {
5374 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5375 bitem->but->flag |= flag;
5376 }
5377 else {
5378 ui_item_flag(static_cast<uiLayout *>(item), flag);
5379 }
5380 }
5381}
5382
5384{
5385
5386 if (this->items().is_empty()) {
5387 return;
5388 }
5389
5390 if (this->align()) {
5391 ui_item_align(this, ++this->block()->alignnr);
5392 }
5393 if (!this->active()) {
5395 }
5396 if (!this->enabled()) {
5398 }
5399 this->resolve_impl();
5400
5401 for (uiItem *subitem : this->items()) {
5402 if (ItemInternal::box_item(this)) {
5403 ItemInternal::box_item_set(subitem, true);
5404 }
5405 if (subitem->type() == uiItemType::Button) {
5406 if (ItemInternal::box_item(this)) {
5407 uiButtonItem *sub_bitem = static_cast<uiButtonItem *>(subitem);
5408 sub_bitem->but->drawflag |= UI_BUT_BOX_ITEM;
5409 }
5410 continue;
5411 }
5412 static_cast<uiLayout *>(subitem)->resolve();
5413 }
5414}
5415
5417{
5418 if (layout->root()->handlefunc) {
5419 UI_block_func_handle_set(block, layout->root()->handlefunc, layout->root()->argv);
5420 }
5421
5424 return {layout->x_, layout->y_};
5425}
5426
5427static void ui_layout_free(uiLayout *layout)
5428{
5429 for (uiItem *item : layout->items()) {
5430 if (item->type() == uiItemType::Button) {
5431 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5432
5433 bitem->but->layout = nullptr;
5434 MEM_delete(item);
5435 }
5436 else {
5437 uiLayout *litem = static_cast<uiLayout *>(item);
5438 ui_layout_free(litem);
5439 }
5440 }
5441
5442 MEM_delete(layout);
5443}
5444
5446{
5447 if (root->padding) {
5448 /* add an invisible button for padding */
5449 uiBlock *block = root->block;
5450 uiLayout *prev_layout = block->curlayout;
5451
5452 block->curlayout = root->layout;
5453 uiDefBut(
5454 block, ButType::Sepr, 0, "", 0, 0, root->padding, root->padding, nullptr, 0.0, 0.0, "");
5455 block->curlayout = prev_layout;
5456 }
5457}
5458
5459namespace blender::ui {
5461 LayoutDirection dir,
5462 LayoutType type,
5463 int x,
5464 int y,
5465 int size,
5466 int em,
5467 int padding,
5468 const uiStyle *style)
5469{
5470 uiLayoutRoot *root = MEM_callocN<uiLayoutRoot>(__func__);
5471 root->type = type;
5472 root->style = style;
5473 root->block = block;
5474 root->padding = padding;
5476 const char *func = __func__;
5477 uiLayout *layout = [&]() -> uiLayout * {
5478 switch (type) {
5480 return MEM_new<LayoutColumn>(func, root);
5482 return MEM_new<LayoutRootPieMenu>(func, root);
5483 case LayoutType::Header:
5484 return MEM_new<LayoutRow>(func, uiItemType::LayoutRoot, root);
5485 default:
5486 return MEM_new<LayoutColumn>(func, uiItemType::LayoutRoot, root);
5487 }
5488 }();
5489
5490 /* Only used when 'uiItemInternalFlag::PropSep' is set. */
5491 layout->use_property_decorate_set(true);
5492
5493 layout->x_ = x;
5494 layout->y_ = y;
5495 layout->space_ = style->templatespace;
5496 layout->active_set(true);
5497 layout->enabled_set(true);
5499
5501 layout->space_ = 0;
5502 }
5503
5504 if (dir == LayoutDirection::Horizontal) {
5505 layout->h_ = size;
5506 layout->root()->emh = em * UI_UNIT_Y;
5507 }
5508 else {
5509 layout->w_ = size;
5510 layout->root()->emw = em * UI_UNIT_X;
5511 }
5512
5513 block->curlayout = layout;
5514 root->layout = layout;
5515 BLI_addtail(&block->layouts, root);
5516
5518
5519 return *layout;
5520}
5521
5522} // namespace blender::ui
5523
5525{
5526 return root_->block;
5527}
5528
5530{
5531 return root_->opcontext;
5532}
5533
5535{
5536 uiButtonItem *bitem = MEM_new<uiButtonItem>(__func__);
5537 bitem->but = but;
5538
5539 int w, h;
5540 ui_item_size((uiItem *)bitem, &w, &h);
5541 /* XXX uiBut hasn't scaled yet
5542 * we can flag the button as not expandable, depending on its size */
5543 if (w <= 2 * UI_UNIT_X && but->str.empty()) {
5544 bitem->fixed_size_set(true);
5545 }
5546
5547 if (layout->child_items_layout_) {
5548 layout->child_items_layout_->items_.append(bitem);
5549 }
5550 else {
5551 layout->items_.append(bitem);
5552 }
5553 but->layout = layout;
5554 but->search_weight = layout->search_weight_;
5555
5556 if (layout->context_) {
5557 but->context = layout->context_;
5558 layout->context_->used = true;
5559 }
5560
5562 but->emboss = layout->emboss_;
5563 }
5564
5565 ui_button_group_add_but(layout->block(), but);
5566}
5567
5569{
5570 LayoutInternal::layout_add_but(layout, but);
5571};
5572
5574{
5575 const blender::Vector<uiItem *> &child_list = layout->child_items_layout_ ?
5576 layout->child_items_layout_->items() :
5577 layout->items();
5578
5579 for (uiItem *item : child_list) {
5580 if (item->type() == uiItemType::Button) {
5581 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5582
5583 if (bitem->but == but) {
5584 return bitem;
5585 }
5586 }
5587 else {
5589 static_cast<uiLayout *>(item), but);
5590 if (nested_item) {
5591 return nested_item;
5592 }
5593 }
5594 }
5595
5596 return nullptr;
5597}
5598
5600{
5601 blender::Vector<uiItem *> &child_list = layout->child_items_layout_ ?
5602 layout->child_items_layout_->items_ :
5603 layout->items_;
5604 const int64_t removed_num = child_list.remove_if([but](auto item) {
5605 if (item->type() == uiItemType::Button) {
5606 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5607 return (bitem->but == but);
5608 }
5609 return false;
5610 });
5611
5612 BLI_assert(removed_num <= 1);
5613 UNUSED_VARS_NDEBUG(removed_num);
5614}
5615
5616void ui_layout_remove_but(uiLayout *layout, const uiBut *but)
5617{
5619}
5620
5621bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but)
5622{
5624 layout, static_cast<const uiBut *>(old_but_ptr));
5625 if (!bitem) {
5626 return false;
5627 }
5628
5629 bitem->but = new_but;
5630 return true;
5631}
5632
5634{
5635 SET_FLAG_FROM_TEST(flag_, fixed_size, uiItemInternalFlag::FixedSize);
5636}
5637
5639{
5640 return bool(flag_ & uiItemInternalFlag::FixedSize);
5641}
5642
5644{
5645 root_->opcontext = opcontext;
5646}
5647
5648void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv)
5649{
5650 layout->root()->handlefunc = handlefunc;
5651 layout->root()->argv = argv;
5652}
5653
5654namespace blender::ui {
5655
5657{
5658 block->curlayout = layout;
5659}
5660
5662{
5663 LISTBASE_FOREACH_MUTABLE (uiLayoutRoot *, root, &block->layouts) {
5664 ui_layout_free(root->layout);
5665 MEM_freeN(root);
5666 }
5667}
5668
5670{
5671 BLI_assert(block->active);
5672 int2 block_size = {0, 0};
5673
5674 block->curlayout = nullptr;
5675
5676 LISTBASE_FOREACH_MUTABLE (uiLayoutRoot *, root, &block->layouts) {
5678
5679 /* nullptr in advance so we don't interfere when adding button */
5680 block_size = ui_layout_end(block, root->layout);
5681 ui_layout_free(root->layout);
5682 MEM_freeN(root);
5683 }
5684
5685 BLI_listbase_clear(&block->layouts);
5686 return block_size;
5687}
5689{
5690 return !BLI_listbase_is_empty(&block->layouts);
5691}
5692
5693} // namespace blender::ui
5694
5696 const StructRNA *type) const
5697{
5698 if (!context_) {
5699 return nullptr;
5700 }
5702}
5703
5705{
5706 uiBlock *block = this->block();
5707 context_ = CTX_store_add(block->contexts, name, ptr);
5708}
5709std::optional<blender::StringRefNull> uiLayout::context_string_get(
5710 const blender::StringRef name) const
5711{
5712 if (!context_) {
5713 return std::nullopt;
5714 }
5716}
5717
5719{
5720 uiBlock *block = this->block();
5721 context_ = CTX_store_add(block->contexts, name, value);
5722}
5723
5724std::optional<int64_t> uiLayout::context_int_get(const blender::StringRef name) const
5725{
5726 if (!context_) {
5727 return std::nullopt;
5728 }
5730}
5731
5733{
5734 uiBlock *block = this->block();
5735 context_ = CTX_store_add(block->contexts, name, value);
5736}
5737
5739{
5740 uiBlock *block = this->block();
5742}
5743
5745 uiButToolTipFunc func,
5746 void *arg,
5747 uiCopyArgFunc copy_arg,
5748 uiFreeArgFunc free_arg)
5749{
5750 bool arg_used = false;
5751
5752 for (uiItem *item : layout->items()) {
5753 /* Each button will call free_arg for "its" argument, so we need to
5754 * duplicate the allocation for each button after the first. */
5755 if (copy_arg != nullptr && arg_used) {
5756 arg = copy_arg(arg);
5757 }
5758
5759 if (item->type() == uiItemType::Button) {
5760 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5761 if (bitem->but->type == ButType::Decorator) {
5762 continue;
5763 }
5764 UI_but_func_tooltip_set(bitem->but, func, arg, free_arg);
5765 arg_used = true;
5766 }
5767 else {
5768 uiLayoutSetTooltipFunc(static_cast<uiLayout *>(item), func, arg, copy_arg, free_arg);
5769 arg_used = true;
5770 }
5771 }
5772
5773 if (free_arg != nullptr && !arg_used) {
5774 /* Free the original copy of arg in case the layout is empty. */
5775 free_arg(arg);
5776 }
5777}
5778
5781 void *arg,
5782 uiCopyArgFunc copy_arg,
5783 uiFreeArgFunc free_arg)
5784{
5785 bool arg_used = false;
5786
5787 for (uiItem *item : layout->items()) {
5788 /* Each button will call free_arg for "its" argument, so we need to
5789 * duplicate the allocation for each button after the first. */
5790 if (copy_arg != nullptr && arg_used) {
5791 arg = copy_arg(arg);
5792 }
5793
5794 if (item->type() == uiItemType::Button) {
5795 uiButtonItem *bitem = static_cast<uiButtonItem *>(item);
5796 if (bitem->but->type == ButType::Decorator) {
5797 continue;
5798 }
5799 UI_but_func_tooltip_custom_set(bitem->but, func, arg, free_arg);
5800 }
5801 else {
5802 uiLayoutSetTooltipCustomFunc(static_cast<uiLayout *>(item), func, arg, copy_arg, free_arg);
5803 }
5804 arg_used = true;
5805 }
5806
5807 if (free_arg != nullptr && !arg_used) {
5808 /* Free the original copy of arg in case the layout is empty. */
5809 free_arg(arg);
5810 }
5811}
5812
5814{
5815 if (but->opptr) {
5816 this->context_ptr_set("button_operator", but->opptr);
5817 }
5818
5819 if (but->rnapoin.data && but->rnaprop) {
5820 /* TODO: index could be supported as well */
5821 PointerRNA ptr_prop = RNA_pointer_create_discrete(nullptr, &RNA_Property, but->rnaprop);
5822 this->context_ptr_set("button_prop", &ptr_prop);
5823 this->context_ptr_set("button_pointer", &but->rnapoin);
5824 }
5825}
5826
5828{
5829 if (r_prop != nullptr) {
5830 *r_prop = nullptr;
5831 }
5832
5834 MenuItemLevel *lvl = static_cast<MenuItemLevel *>(but->func_argN);
5836 if ((ot != nullptr) && (r_prop != nullptr)) {
5837 *r_prop = RNA_struct_type_find_property(ot->srna, lvl->propname);
5838 }
5839 return ot;
5840 }
5841 return nullptr;
5842}
5843
5845{
5847 return (MenuType *)but->poin;
5848 }
5849 return nullptr;
5850}
5851
5853{
5855 return (PanelType *)but->poin;
5856 }
5857 return nullptr;
5858}
5859
5860std::optional<blender::StringRefNull> UI_but_asset_shelf_type_idname_get(const uiBut *but)
5861{
5863}
5864
5866{
5867 Menu menu{};
5868 menu.layout = layout;
5869 menu.type = mt;
5870
5871 if (G.debug & G_DEBUG_WM) {
5872 printf("%s: opening menu \"%s\"\n", __func__, mt->idname);
5873 }
5874
5875 uiBlock *block = layout->block();
5876 if (bool(mt->flag & MenuTypeFlag::SearchOnKeyPress)) {
5878 }
5879 if (mt->listener) {
5880 /* Forward the menu type listener to the block we're drawing in. */
5882 }
5883
5884 bContextStore context_store;
5885 if (layout->context()) {
5886 context_store = *layout->context();
5887 }
5888 const bContextStore *previous_context_store = CTX_store_get(C);
5889 if (previous_context_store) {
5890 context_store.entries.extend(previous_context_store->entries);
5891 }
5892 CTX_store_set(C, &context_store);
5893
5894 mt->draw(C, &menu);
5895
5896 CTX_store_set(C, previous_context_store);
5897}
5898
5899static bool ui_layout_has_panel_label(const uiLayout *layout, const PanelType *pt)
5900{
5901 for (uiItem *subitem : layout->items()) {
5902 if (subitem->type() == uiItemType::Button) {
5903 uiButtonItem *bitem = static_cast<uiButtonItem *>(subitem);
5904 if (!(bitem->but->flag & UI_HIDDEN) &&
5905 bitem->but->str == CTX_IFACE_(pt->translation_context, pt->label))
5906 {
5907 return true;
5908 }
5909 }
5910 else {
5911 uiLayout *litem = static_cast<uiLayout *>(subitem);
5912 if (ui_layout_has_panel_label(litem, pt)) {
5913 return true;
5914 }
5915 }
5916 }
5917
5918 return false;
5919}
5920
5921static void ui_paneltype_draw_impl(bContext *C, PanelType *pt, uiLayout *layout, bool show_header)
5922{
5923 uiBlock *block = layout->block();
5924 Panel *panel = BKE_panel_new(pt);
5925 panel->flag = PNL_POPOVER;
5926
5927 if (pt->listener) {
5929 }
5930
5931 /* This check may be paranoid, this function might run outside the context of a popup or can run
5932 * in popovers that are not supposed to support refreshing, see #ui_popover_create_block. */
5933 if (block->handle && block->handle->region) {
5934 /* Allow popovers to contain collapsible sections, see #uiLayout::popover. */
5935 UI_popup_dummy_panel_set(block->handle->region, block);
5936 }
5937
5938 uiItem *item_last = layout->items().is_empty() ? nullptr : layout->items().last();
5939
5940 /* Draw main panel. */
5941 if (show_header) {
5942 uiLayout *row = &layout->row(false);
5943 if (pt->draw_header) {
5944 panel->layout = row;
5945 pt->draw_header(C, panel);
5946 panel->layout = nullptr;
5947 }
5948
5949 /* draw_header() is often used to add a checkbox to the header. If we add the label like below
5950 * the label is disconnected from the checkbox, adding a weird looking gap. As workaround, let
5951 * the checkbox add the label instead. */
5952 if (!ui_layout_has_panel_label(row, pt)) {
5953 row->label(CTX_IFACE_(pt->translation_context, pt->label), ICON_NONE);
5954 }
5955 }
5956
5957 panel->layout = layout;
5958 pt->draw(C, panel);
5959 panel->layout = nullptr;
5960 BLI_assert(panel->runtime->custom_data_ptr == nullptr);
5961
5962 BKE_panel_free(panel);
5963
5964 /* Draw child panels. */
5965 LISTBASE_FOREACH (LinkData *, link, &pt->children) {
5966 PanelType *child_pt = static_cast<PanelType *>(link->data);
5967
5968 if (child_pt->poll == nullptr || child_pt->poll(C, child_pt)) {
5969 /* Add space if something was added to the layout. */
5970 if (!layout->items().is_empty() && item_last != layout->items().last()) {
5971 layout->separator();
5972 item_last = layout->items().last();
5973 }
5974
5975 uiLayout *col = &layout->column(false);
5976 ui_paneltype_draw_impl(C, child_pt, col, true);
5977 }
5978 }
5979}
5980
5982{
5983 if (layout->context()) {
5984 CTX_store_set(C, layout->context());
5985 }
5986
5987 ui_paneltype_draw_impl(C, pt, layout, false);
5988
5989 if (layout->context()) {
5990 CTX_store_set(C, nullptr);
5991 }
5992}
5993
5995
5996/* -------------------------------------------------------------------- */
6006
6008{
6009 uiBut *but = bitem->but;
6010 BLI_dynstr_appendf(ds, "'type':%d, ", int(but->type));
6011 BLI_dynstr_appendf(ds, "'draw_string':'''%s''', ", but->drawstr.c_str());
6012 /* Not exactly needed, rna has this. */
6013 BLI_dynstr_appendf(ds, "'tip':'''%s''', ", std::string(but->tip).c_str());
6014
6015 if (but->optype) {
6016 std::string opstr = WM_operator_pystring_ex(static_cast<bContext *>(but->block->evil_C),
6017 nullptr,
6018 false,
6019 true,
6020 but->optype,
6021 but->opptr);
6022 BLI_dynstr_appendf(ds, "'operator':'''%s''', ", opstr.c_str());
6023 }
6024
6025 {
6026 PropertyRNA *prop = nullptr;
6028 if (ot) {
6029 std::string opstr = WM_operator_pystring_ex(
6030 static_cast<bContext *>(but->block->evil_C), nullptr, false, true, ot, nullptr);
6031 BLI_dynstr_appendf(ds, "'operator':'''%s''', ", opstr.c_str());
6032 BLI_dynstr_appendf(ds, "'property':'''%s''', ", prop ? RNA_property_identifier(prop) : "");
6033 }
6034 }
6035
6036 if (but->rnaprop) {
6038 "'rna':'%s.%s[%d]', ",
6041 but->rnaindex);
6042 }
6043}
6044
6046{
6047 BLI_dynstr_append(ds, "[");
6048
6049 for (const uiItem *item : items) {
6050
6051 BLI_dynstr_append(ds, "{");
6052
6053#define CASE_ITEM(type, name) \
6054 case type: { \
6055 BLI_dynstr_append(ds, "'type': '"); \
6056 BLI_dynstr_append(ds, name); \
6057 BLI_dynstr_append(ds, "', "); \
6058 break; \
6059 } \
6060 ((void)0)
6061
6062 switch (item->type()) {
6063 CASE_ITEM(uiItemType::Button, "BUTTON");
6064 CASE_ITEM(uiItemType::LayoutRow, "LAYOUT_ROW");
6065 CASE_ITEM(uiItemType::LayoutPanelHeader, "LAYOUT_PANEL_HEADER");
6066 CASE_ITEM(uiItemType::LayoutPanelBody, "LAYOUT_PANEL_BODY");
6067 CASE_ITEM(uiItemType::LayoutColumn, "LAYOUT_COLUMN");
6068 CASE_ITEM(uiItemType::LayoutColumnFlow, "LAYOUT_COLUMN_FLOW");
6069 CASE_ITEM(uiItemType::LayoutRowFlow, "LAYOUT_ROW_FLOW");
6070 CASE_ITEM(uiItemType::LayoutBox, "LAYOUT_BOX");
6071 CASE_ITEM(uiItemType::LayoutAbsolute, "LAYOUT_ABSOLUTE");
6072 CASE_ITEM(uiItemType::LayoutSplit, "LAYOUT_SPLIT");
6073 CASE_ITEM(uiItemType::LayoutOverlap, "LAYOUT_OVERLAP");
6074 CASE_ITEM(uiItemType::LayoutRoot, "LAYOUT_ROOT");
6075 CASE_ITEM(uiItemType::LayoutGridFlow, "LAYOUT_GRID_FLOW");
6076 CASE_ITEM(uiItemType::LayoutRadial, "LAYOUT_RADIAL");
6077 }
6078
6079#undef CASE_ITEM
6080
6081 switch (item->type()) {
6082 case uiItemType::Button:
6083 ui_layout_introspect_button(ds, static_cast<const uiButtonItem *>(item));
6084 break;
6085 default:
6086 BLI_dynstr_append(ds, "'items':");
6087 ui_layout_introspect_items(ds, (static_cast<const uiLayout *>(item))->items());
6088 break;
6089 }
6090
6091 BLI_dynstr_append(ds, "}");
6092
6093 if (item != items.last()) {
6094 BLI_dynstr_append(ds, ", ");
6095 }
6096 }
6097 /* Don't use a comma here as it's not needed and
6098 * causes the result to evaluate to a tuple of 1. */
6099 BLI_dynstr_append(ds, "]");
6100}
6101
6102const char *UI_layout_introspect(uiLayout *layout)
6103{
6104 DynStr *ds = BLI_dynstr_new();
6105 blender::Vector<uiItem *> layout_dummy_list(1, layout);
6106 ui_layout_introspect_items(ds, layout_dummy_list);
6107 const char *result = BLI_dynstr_get_cstring(ds);
6108 BLI_dynstr_free(ds);
6109 return result;
6110}
6111
6113
6114/* -------------------------------------------------------------------- */
6117
6119 const uiStyle *style,
6120 const int dialog_width,
6121 const eAlertIcon icon,
6122 const int icon_size)
6123{
6124 /* By default, the space between icon and text/buttons will be equal to the 'columnspace',
6125 * this extra padding will add some space by increasing the left column width,
6126 * making the icon placement more symmetrical, between the block edge and the text. */
6127 const float icon_padding = 5.0f * UI_SCALE_FAC;
6128 /* Calculate the factor of the fixed icon column depending on the block width. */
6129 const float split_factor = (float(icon_size) + icon_padding) /
6130 float(dialog_width - style->columnspace);
6131
6135 0,
6136 0,
6137 dialog_width,
6138 0,
6139 0,
6140 style);
6141
6142 if (icon == ALERT_ICON_INFO) {
6144 }
6145 else if (icon == ALERT_ICON_WARNING) {
6147 }
6148 else if (icon == ALERT_ICON_QUESTION) {
6150 }
6151 else if (icon == ALERT_ICON_ERROR) {
6153 }
6154 else {
6156 }
6157
6158 /* Split layout to put alert icon on left side. */
6159 uiLayout *split_block = &block_layout.split(split_factor, false);
6160
6161 /* Alert icon on the left. */
6162 uiLayout *layout = &split_block->row(false);
6163 /* Using 'align_left' with 'row' avoids stretching the icon along the width of column. */
6165 uiDefButAlert(block, icon, 0, 0, icon_size, icon_size);
6166
6167 /* The rest of the content on the right. */
6168 layout = &split_block->column(false);
6169
6170 return layout;
6171}
6172
6173uiLayout *uiItemsAlertBox(uiBlock *block, const int size, const eAlertIcon icon)
6174{
6175 const uiStyle *style = UI_style_get_dpi();
6176 const short icon_size = 40 * UI_SCALE_FAC;
6177 const int dialog_width = icon_size + (style->widget.points * size * UI_SCALE_FAC);
6178 return uiItemsAlertBox(block, style, dialog_width, icon, icon_size);
6179}
6180
6182
6184{
6185 return root_;
6186};
6188{
6189 return context_;
6190};
6192{
6193 return parent_;
6194};
6196{
6197 return heading_;
6198};
6200{
6201 heading_ = {};
6202}
6204{
6205 return items_;
6206};
6208{
6209 return align_;
6210}
6211[[nodiscard]] bool uiLayout::variable_size() const
6212{
6213 return variable_size_;
6214}
6216{
6217 return emboss_;
6218}
bContextStore * CTX_store_add_all(blender::Vector< std::unique_ptr< bContextStore > > &contexts, const bContextStore *context)
std::optional< blender::StringRefNull > CTX_store_string_lookup(const bContextStore *store, blender::StringRef name)
ARegion * CTX_wm_region_popup(const bContext *C)
bContextStore * CTX_store_add(blender::Vector< std::unique_ptr< bContextStore > > &contexts, blender::StringRef name, const PointerRNA *ptr)
wmWindow * CTX_wm_window(const bContext *C)
std::optional< int64_t > CTX_store_int_lookup(const bContextStore *store, blender::StringRef name)
const PointerRNA * CTX_store_ptr_lookup(const bContextStore *store, blender::StringRef name, const StructRNA *type=nullptr)
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)
@ G_DEBUG_WM
#define G_MAIN
void IDP_CopyPropertyContent(IDProperty *dst, const IDProperty *src) ATTR_NONNULL()
Definition idprop.cc:868
Functions and classes for evaluating template expressions in filepaths.
std::optional< blender::bke::path_templates::VariableMap > BKE_build_template_variables_for_prop(const bContext *C, PointerRNA *ptr, PropertyRNA *prop)
blender::Vector< blender::bke::path_templates::Error > BKE_path_validate_template(blender::StringRef path, const blender::bke::path_templates::VariableMap &template_variables)
bool BKE_path_contains_template_syntax(blender::StringRef path)
SpaceType * BKE_spacetype_from_id(int spaceid)
Definition screen.cc:257
ARegionType * BKE_regiontype_from_id(const SpaceType *st, int regionid)
Definition screen.cc:267
@ PANEL_TYPE_NO_SEARCH
void BKE_panel_free(Panel *panel)
Definition screen.cc:563
Panel * BKE_panel_new(PanelType *panel_type)
Definition screen.cc:546
LayoutPanelState * BKE_panel_layout_panel_state_ensure(Panel *panel, blender::StringRef idname, bool default_closed)
Definition screen.cc:520
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.cc:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
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.cc:56
void BLI_kdtree_nd_ free(KDTree *tree)
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
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:186
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ PNL_POPOVER
@ RGN_ALIGN_BOTTOM
@ RGN_ALIGN_LEFT
@ RGN_ALIGN_RIGHT
@ RGN_FLAG_SEARCH_FILTER_ACTIVE
#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:778
@ PROP_STRING_SEARCH_SUGGESTION
Definition RNA_types.hh:792
PropertyType
Definition RNA_types.hh:161
@ PROP_FLOAT
Definition RNA_types.hh:164
@ PROP_BOOLEAN
Definition RNA_types.hh:162
@ PROP_ENUM
Definition RNA_types.hh:166
@ PROP_INT
Definition RNA_types.hh:163
@ PROP_STRING
Definition RNA_types.hh:165
@ PROP_POINTER
Definition RNA_types.hh:167
@ PROP_COLLECTION
Definition RNA_types.hh:168
@ PROP_UNIT_ROTATION
Definition RNA_types.hh:178
@ PROP_PATH_SUPPORTS_BLEND_RELATIVE
Definition RNA_types.hh:456
@ PROP_ENUM_FLAG
Definition RNA_types.hh:404
@ PROP_ICONS_CONSECUTIVE
Definition RNA_types.hh:329
@ PROP_PATH_SUPPORTS_TEMPLATES
Definition RNA_types.hh:470
PropertySubType
Definition RNA_types.hh:232
@ PROP_MATRIX
Definition RNA_types.hh:265
@ PROP_DIRECTION
Definition RNA_types.hh:262
@ PROP_LAYER_MEMBER
Definition RNA_types.hh:278
@ PROP_FILENAME
Definition RNA_types.hh:238
@ PROP_COLOR
Definition RNA_types.hh:260
@ PROP_AXISANGLE
Definition RNA_types.hh:268
@ PROP_NONE
Definition RNA_types.hh:233
@ PROP_DIRPATH
Definition RNA_types.hh:237
@ PROP_COLOR_GAMMA
Definition RNA_types.hh:272
@ PROP_LAYER
Definition RNA_types.hh:277
@ PROP_FILEPATH
Definition RNA_types.hh:236
#define C
Definition RandGen.cpp:29
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)
uiBut * uiDefIconTextBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, std::optional< blender::StringRef > tip)
void UI_but_placeholder_set(uiBut *but, blender::StringRef placeholder_text)
#define UI_UNIT_Y
@ UI_BLOCK_LOOP
@ UI_BLOCK_NO_ACCELERATOR_KEYS
@ UI_BLOCK_LIST_ITEM
@ UI_BLOCK_POPUP_HOLD
void UI_but_func_tooltip_custom_set(uiBut *but, uiButToolTipCustomFunc func, void *arg, uiFreeArgFunc free_arg)
void UI_popup_menu_but_set(uiPopupMenu *pup, ARegion *butregion, uiBut *but)
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)
uiBut * uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, blender::StringRef str, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
uiBut * uiDefIconTextMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, blender::StringRef str, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
void UI_but_func_search_set_results_are_suggestions(uiBut *but, bool value)
@ 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_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg)
uiBut * uiDefIconBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_popup_dummy_panel_set(ARegion *region, uiBlock *block)
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)
MenuType * UI_but_menutype_get(const uiBut *but)
const uiStyle * UI_style_get_dpi()
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
void UI_but_drawflag_enable(uiBut *but, int flag)
@ UI_BUT2_FORCE_SEMI_MODAL_ACTIVE
void UI_but_flag2_enable(uiBut *but, int flag)
void UI_block_func_handle_set(uiBlock *block, uiBlockHandleFunc func, void *arg)
uiBut * uiDefIconButO(uiBlock *block, ButType type, blender::StringRefNull opname, blender::wm::OpCallContext opcontext, int icon, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
void *(*)(const void *argN) uiButArgNCopy
void(*)(void *argN) uiButArgNFree
uiBut * uiDefButR_prop(uiBlock *block, ButType type, int retval, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, std::optional< blender::StringRef > tip)
uiBut * uiDefIconTextButR_prop(uiBlock *block, ButType type, int retval, int icon, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, std::optional< blender::StringRef > tip)
uiBut * uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int index, std::optional< blender::StringRef > name, int icon, int x, int y, int width, int height)
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 * uiDefButAlert(uiBlock *block, int icon, int x, int y, short width, short height)
PointerRNA * UI_but_operator_ptr_ensure(uiBut *but)
uiBut * uiDefIconButR_prop(uiBlock *block, ButType type, int retval, int icon, int x, int y, short width, short height, PointerRNA *ptr, PropertyRNA *prop, int index, float min, float max, std::optional< blender::StringRef > tip)
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
uiBut * uiDefIconButO_ptr(uiBlock *block, ButType type, wmOperatorType *ot, blender::wm::OpCallContext opcontext, int icon, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
void UI_but_drawflag_disable(uiBut *but, int flag)
PanelType * UI_but_paneltype_get(const uiBut *but)
void UI_block_align_begin(uiBlock *block)
void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
uiBut * uiDefIconMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
int int UI_fontstyle_string_width_with_block_aspect(const uiFontStyle *fs, blender::StringRef str, float aspect) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
uiButStore * UI_butstore_create(uiBlock *block)
uiBut * uiDefIconTextButO_ptr(uiBlock *block, ButType type, wmOperatorType *ot, blender::wm::OpCallContext opcontext, int icon, blender::StringRef str, int x, int y, short width, short height, std::optional< blender::StringRef > 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)
#define UI_FSTYLE_WIDGET
uiBut * uiDefButO_ptr(uiBlock *block, ButType type, wmOperatorType *ot, blender::wm::OpCallContext opcontext, blender::StringRef str, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
void UI_but_drag_set_id(uiBut *but, ID *id)
@ UI_BUT_TEXT_RIGHT
@ UI_BUT_ICON_LEFT
@ UI_BUT_HAS_QUICK_TOOLTIP
@ UI_BUT_CHECKBOX_INVERT
@ UI_BUT_TEXT_LEFT
@ UI_BUT_BOX_ITEM
void UI_but_unit_type_set(uiBut *but, int unit_type)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_DIR_DOWN
@ UI_DIR_RIGHT
@ UI_DIR_LEFT
@ UI_DIR_UP
std::optional< blender::StringRefNull > UI_but_asset_shelf_type_idname_get(const uiBut *but)
wmOperatorType * UI_but_operatortype_get_from_enum_menu(uiBut *but, PropertyRNA **r_prop)
void UI_block_align_end(uiBlock *block)
@ ALERT_ICON_INFO
@ ALERT_ICON_WARNING
@ ALERT_ICON_QUESTION
@ ALERT_ICON_ERROR
@ 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
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)
uiLayout * uiItemL_respect_property_split(uiLayout *layout, blender::StringRef text, int icon)
void uiLayoutSetTooltipCustomFunc(uiLayout *layout, uiButToolTipCustomFunc func, void *arg, uiCopyArgFunc copy_arg, uiFreeArgFunc free_arg)
uiPropertySplitWrapper uiItemPropertySplitWrapperCreate(uiLayout *parent_layout)
int uiLayoutListItemPaddingWidth()
#define UI_ITEM_NONE
void UI_paneltype_draw(bContext *C, PanelType *pt, uiLayout *layout)
LayoutSeparatorType
void uiLayoutSetTooltipFunc(uiLayout *layout, uiButToolTipFunc func, void *arg, uiCopyArgFunc copy_arg, uiFreeArgFunc free_arg)
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
const char * UI_layout_introspect(uiLayout *layout)
void uiLayoutListItemAddPadding(uiLayout *layout)
void uiItemLDrag(uiLayout *layout, PointerRNA *ptr, blender::StringRef name, int icon)
uiBut * uiItemL_ex(uiLayout *layout, blender::StringRef name, int icon, bool highlight, bool redalert)
void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv)
bool uiLayoutEndsWithPanelHeader(const uiLayout &layout)
void(*)(void *arg) uiFreeArgFunc
void(*)(bContext *C, uiLayout *layout, void *arg1) uiMenuCreateFunc
std::string(*)(bContext *C, void *argN, blender::StringRef tip) uiButToolTipFunc
void(*)(bContext &C, uiTooltipData &data, uiBut *but, void *argN) uiButToolTipCustomFunc
void(*)(bContext *C, void *arg, int event) uiMenuHandleFunc
#define UI_MAX_NAME_STR
void *(*)(const void *arg) uiCopyArgFunc
@ KM_CTRL
Definition WM_types.hh:279
@ KM_ALT
Definition WM_types.hh:280
@ KM_HYPER
Definition WM_types.hh:292
@ KM_OSKEY
Definition WM_types.hh:282
@ KM_SHIFT
Definition WM_types.hh:278
@ KM_NOTHING
Definition WM_types.hh:310
#define KM_MOD_HELD
Definition WM_types.hh:326
#define U
iter begin(iter)
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
btMatrix3x3 absolute() const
Return the matrix with all values non negative.
Definition btVector3.h:364
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
int64_t size() const
void append(const T &value)
bool is_empty() const
void extend(Span< T > array)
const T * data() const
Definition BLI_array.hh:312
void fill(const T &value) const
Definition BLI_array.hh:272
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool is_empty() const
constexpr const char * c_str() const
int64_t remove_if(Predicate &&predicate)
void append(const T &value)
bool is_empty() const
void reserve(const int64_t min_capacity)
nullptr float
bool ED_id_rename(Main &bmain, ID &id, blender::StringRefNull name)
#define str(s)
uint pos
uint col
#define row_major
#define input
#define active
#define printf(...)
#define output
bool all(VecOp< bool, D >) RET
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
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))
uiBut * ui_but_change_type(uiBut *but, ButType new_type)
void ui_but_rna_menu_convert_to_panel_type(uiBut *but, const char *panel_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]
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
bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt)
bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but)
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_item_paneltype_func(bContext *C, uiLayout *layout, void *arg_pt)
void ui_panel_tag_search_filter_match(Panel *panel)
bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
void ui_layout_list_set_labels_active(uiLayout *layout)
RadialDirection
std::optional< blender::StringRefNull > UI_asset_shelf_idname_from_button_context(const uiBut *but)
uiBut * ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, PropertyRNA *item_searchprop, bool results_are_suggestions)
#define RNA_ENUM_VALUE
void ui_but_pie_dir(RadialDirection dir, float vec[2])
void ui_pie_menu_level_create(uiBlock *block, wmOperatorType *ot, blender::StringRefNull propname, IDProperty *properties, const EnumPropertyItem *items, int totitem, blender::wm::OpCallContext context, eUI_Item_Flag flag)
@ UI_SELECT_DRAW
@ UI_HIDDEN
@ UI_SELECT
@ UI_SEARCH_FILTER_NO_MATCH
void ui_rna_collection_search_update_fn(const bContext *C, void *arg, const char *str, uiSearchItems *items, bool is_first)
void ui_layout_remove_but(uiLayout *layout, const uiBut *but)
void ui_layout_add_but(uiLayout *layout, uiBut *but)
static uiBut * ui_item_menu(uiLayout *layout, const StringRef name, int icon, uiMenuCreateFunc func, void *arg, void *argN, const std::optional< StringRef > tip, bool force_menu, uiButArgNFree func_argN_free_fn=MEM_freeN, uiButArgNCopy func_argN_copy_fn=MEM_dupallocN)
static int ui_litem_min_width(int itemw)
#define CASE_ITEM(type, name)
static void ui_item_flag(uiLayout *litem, int flag)
static void ui_item_array(uiLayout *layout, uiBlock *block, const StringRef 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)
static void ui_layout_introspect_items(DynStr *ds, blender::Span< const uiItem * > items)
static uiLayoutItemBx * ui_layout_box(uiLayout *layout, ButType type)
blender::ui::ItemInternalFlag uiItemInternalFlag
static bool button_matches_search_filter(uiBut *but, const char *search_filter)
static bool button_group_has_search_match(const uiButtonGroup &group, const char *search_filter)
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)
static uiBut * uiItemFullO_ptr_ex(uiLayout *layout, wmOperatorType *ot, std::optional< StringRef > name, int icon, const blender::wm::OpCallContext context, const eUI_Item_Flag flag, PointerRNA *r_opptr)
static int menu_item_enum_opname_menu_active(bContext *C, uiBut *but, MenuItemLevel *lvl)
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)
#define UI_ITEM_PROP_SEP_DIVIDE
static bool ui_item_is_radial_displayable(uiItem *item)
static int ui_text_icon_width_ex(uiLayout *layout, const StringRef name, int icon, const uiTextIconPadFactor &pad_factor, const uiFontStyle *fstyle)
#define UI_ITEM_VARY_X
static void ui_item_enum_expand_elem_exec(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, const std::optional< StringRef > uiname, const int h, const ButType but_type, const bool icon_only, const EnumPropertyItem *item, const bool is_first)
static bool block_search_filter_tag_buttons(uiBlock *block, const char *search_filter)
static void ui_layout_heading_label_add(uiLayout *layout, uiLayout *heading_layout, bool right_align, bool respect_prop_split)
static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum)
static uiBut * ui_item_with_label(uiLayout *layout, uiBlock *block, const StringRef 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)
BLI_INLINE bool ui_layout_is_radial(const uiLayout *layout)
static void ui_layout_add_padding_button(uiLayoutRoot *root)
static void ui_layout_introspect_button(DynStr *ds, const uiButtonItem *bitem)
static int ui_layout_vary_direction(uiLayout *layout)
static void menu_item_enum_rna_menu(bContext *, uiLayout *layout, void *arg)
constexpr uiTextIconPadFactor ui_text_pad_default
static bool block_search_panel_label_matches(const uiBlock *block, const char *search_string)
static void ui_litem_grid_flow_compute(blender::Span< uiItem * > items, const UILayoutGridFlowInput *parameters, UILayoutGridFlowOutput *results)
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
#define UI_ITEM_VARY_Y
static void ui_rna_collection_search_arg_free_fn(void *ptr)
static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, const std::optional< StringRef > uiname, const int h, const bool icon_only)
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)
static bool ui_layout_has_panel_label(const uiLayout *layout, const PanelType *pt)
#define UI_PROP_DECORATE
static void ui_keymap_but_cb(bContext *, void *but_v, void *)
static void ui_item_rna_size(uiLayout *layout, StringRef name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, bool icon_only, bool compact, int *r_w, int *r_h)
static void ui_item_enum_expand_exec(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, const std::optional< StringRef > uiname, const int h, const ButType but_type, const bool icon_only)
static uiLayout * ui_layout_heading_find(uiLayout *cur_layout)
#define UI_OPERATOR_ERROR_RET(_ot, _opname)
constexpr uiTextIconPadFactor ui_text_pad_compact
static void menu_item_enum_opname_menu(bContext *C, uiLayout *layout, void *arg)
static void ui_item_scale(uiLayout *litem, const float scale[2])
static void search_id_collection(StructRNA *ptype, PointerRNA *r_ptr, PropertyRNA **r_prop)
static void ui_but_tip_from_enum_item(uiBut *but, const EnumPropertyItem *item)
uiLayout * uiItemL_respect_property_split(uiLayout *layout, StringRef text, int icon)
static StringRef ui_item_name_add_colon(StringRef name, char namestr[UI_MAX_NAME_STR])
static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2)
static void ui_item_menu_hold(bContext *C, ARegion *butregion, uiBut *but)
static void ui_item_enum_expand_tabs(uiLayout *layout, bContext *C, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *ptr_highlight, PropertyRNA *prop_highlight, const std::optional< StringRef > uiname, const int h, const bool icon_only)
static bool ui_layout_variable_size(uiLayout *layout)
static void ui_layout_free(uiLayout *layout)
static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index)
static int ui_text_icon_width(uiLayout *layout, const StringRef name, const int icon, const bool compact)
blender::ui::ItemType uiItemType
static void ui_paneltype_draw_impl(bContext *C, PanelType *pt, uiLayout *layout, bool show_header)
static void ui_item_size(const uiItem *item, int *r_w, int *r_h)
static int ui_item_fit(const int item, const int pos, const int all, const int available, const bool is_last, const blender::ui::LayoutAlign alignment, float *extra_pixel)
static void ui_item_align(uiLayout *litem, short nr)
static blender::int2 ui_layout_end(uiBlock *block, uiLayout *layout)
static void ui_item_position(uiItem *item, const int x, const int y, const int w, const int h)
static uiBut * uiItem_simple(uiLayout *layout, const StringRef name, int icon, std::optional< blender::StringRef > tooltip=std::nullopt, const ButType but_type=ButType::Label)
static uiLayout * ui_item_local_sublayout(uiLayout *test, uiLayout *layout, bool align)
constexpr uiTextIconPadFactor ui_text_pad_none
static void ui_item_move(uiItem *item, const int delta_xmin, const int delta_xmax)
static bool ui_item_is_radial_drawable(uiButtonItem *bitem)
bool enabled
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong state[N]
#define G(x, y, z)
static void progress_bar(int, const char *)
std::unique_ptr< IDProperty, IDPropertyDeleter > create_group(StringRef prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
int2 block_layout_resolve(uiBlock *block)
void block_layout_free(uiBlock *block)
uiLayout & block_layout(uiBlock *block, LayoutDirection direction, LayoutType type, int x, int y, int size, int em, int padding, const uiStyle *style)
void block_layout_set_current(uiBlock *block, uiLayout *layout)
bool block_layout_needs_resolving(const uiBlock *block)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
const char * name
#define ceilf
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)
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)
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)
const PointerRNA PointerRNA_NULL
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)
const char * RNA_property_ui_name(const PropertyRNA *prop, const PointerRNA *ptr)
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)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_enum_items_gettexted_all(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
bool RNA_property_editable(const PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_struct_name_property(const StructRNA *type)
std::string RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop)
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)
bool RNA_property_boolean_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
const char * RNA_property_identifier(const PropertyRNA *prop)
void RNA_property_boolean_set_array(PointerRNA *ptr, PropertyRNA *prop, const bool *values)
ListBase paneltypes
ARegionRuntimeHandle * runtime
const char * identifier
Definition RNA_types.hh:657
const char * name
Definition RNA_types.hh:661
const char * description
Definition RNA_types.hh:663
Definition DNA_ID.h:414
static bool use_property_decorate_no_pad(const uiItem *item)
void resolve_impl() override
void estimate_impl() override
LayoutColumn(uiItemType type, uiLayoutRoot *root)
void estimate_impl() override
LayoutColumn(uiLayoutRoot *root)
void resolve_impl() override
static uiLayout * ui_item_prop_split_layout_hack(uiLayout *layout_parent, uiLayout *layout_split)
void resolve_impl() override
void estimate_impl() override
void estimate_impl() override
void resolve_impl() override
LayoutRootPieMenu(uiLayoutRoot *root)
void resolve_impl() override
LayoutRow(uiItemType type, uiLayoutRoot *root)
void resolve_impl() override
void estimate_impl() override
LayoutRow(uiLayoutRoot *root)
char opname[OP_MAX_TYPENAME]
blender::wm::OpCallContext opcontext
char propname[MAX_IDPROP_NAME]
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
MenuType * type
void(* draw)(const bContext *C, Panel *panel)
bool(* poll)(const bContext *C, PanelType *pt)
void(* listener)(const wmRegionListenerParams *params)
char context[BKE_ST_MAXNAME]
char translation_context[BKE_ST_MAXNAME]
ListBase children
char category[BKE_ST_MAXNAME]
char label[BKE_ST_MAXNAME]
char parent_id[BKE_ST_MAXNAME]
void(* draw_header)(const bContext *C, Panel *panel)
const char * description
PointerRNA * custom_data_ptr
struct PanelType * type
struct Panel_Runtime * runtime
struct uiLayout * layout
T * data_as() const
Definition RNA_types.hh:124
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
blender::Vector< bContextStoreEntry > entries
static void box_item_set(uiItem *item, bool box_item)
static bool box_item(const uiItem *item)
static void inside_property_split_set(uiItem *item, bool inside_prop_sep)
static void auto_fixed_size_set(uiItem *item, bool auto_fixed_size)
static bool auto_fixed_size(const uiItem *item)
static bool use_property_decorate_no_pad(const uiItem *item)
static void layout_estimate(uiLayout *layout)
static uiLayout * ui_item_prop_split_layout_hack(uiLayout *layout_parent, uiLayout *layout_split)
static void layout_add_but(uiLayout *layout, uiBut *but)
static uiButtonItem * ui_layout_find_button_item(const uiLayout *layout, const uiBut *but)
static void layout_remove_but(uiLayout *layout, const uiBut *but)
static void layout_resolve(uiLayout *layout)
static void init_from_parent(uiLayout *item, uiLayout *layout, int align)
float xmax
float xmin
float ymax
float ymin
blender::Vector< std::unique_ptr< uiBut > > buttons
ListBase layouts
uiLayout * curlayout
uiPopupBlockHandle * handle
blender::ui::EmbossType emboss
uiBlockAlertLevel alert_level
uiButHandleFunc func
blender::Vector< uiButtonGroup > button_groups
PointerRNA decorated_rnapoin
PropertyRNA * decorated_rnaprop
wmEventModifierFlag modifier_key
PropertyRNA * rnasearchprop
PointerRNA rnasearchpoin
void * custom_data
RadialDirection pie_dir
blender::ui::EmbossType emboss
PropertyRNA * rnaprop
wmOperatorType * optype
uiButHandleFunc func
ButType type
float search_weight
uiBlock * block
PointerRNA * opptr
uiMenuCreateFunc menu_create_func
const bContextStore * context
std::string drawstr
uiButArgNFree func_argN_free_fn
std::string str
void * hold_argN
BIFIconID icon
uiButArgNCopy func_argN_copy_fn
PointerRNA rnapoin
uiLayout * layout
blender::StringRef tip
void * func_argN
blender::Vector< uiBut * > buttons
bool fixed_size() const
uiItem(blender::ui::ItemType type)
blender::ui::ItemType type() const
void fixed_size_set(bool fixed_size)
blender::ui::ItemType type_
blender::ui::ItemInternalFlag flag_
void estimate_impl() override
void resolve_impl() override
void estimate_impl() override
void resolve_impl() override
void resolve_impl() override
void estimate_impl() override
void resolve_impl() override
void estimate_impl() override
const uiStyle * style
uiLayoutRoot * next
uiLayoutRoot * prev
blender::wm::OpCallContext opcontext
uiMenuHandleFunc handlefunc
uiLayout * layout
blender::ui::LayoutType type
blender::ui::LayoutDirection local_direction() const
PointerRNA op_menu_enum(const bContext *C, wmOperatorType *ot, blender::StringRefNull propname, std::optional< blender::StringRefNull > name, int icon)
void use_property_decorate_set(bool is_sep)
virtual void estimate_impl()
float scale_x() const
bool active() const
uiLayout * parent() const
void prop_enum(PointerRNA *ptr, PropertyRNA *prop, int value, std::optional< blender::StringRefNull > name, int icon)
void alignment_set(blender::ui::LayoutAlign alignment)
void decorator(PointerRNA *ptr, PropertyRNA *prop, int index)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
void progress_indicator(const char *text, float factor, blender::ui::ButProgressType progress_type)
void menu_fn_argN_free(blender::StringRefNull name, int icon, uiMenuCreateFunc func, void *argN)
bContextStore * context_
void props_enum(PointerRNA *ptr, blender::StringRefNull propname)
void popover(const bContext *C, PanelType *pt, std::optional< blender::StringRef > name_opt, int icon)
uiLayout * child_items_layout_
uiBlock * absolute_block()
uiLayout & column_flow(int number, bool align)
bool use_property_decorate() const
blender::wm::OpCallContext operator_context() const
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
const bContextStore * context() const
void op_enum(blender::StringRefNull opname, blender::StringRefNull propname, IDProperty *properties, blender::wm::OpCallContext context, eUI_Item_Flag flag, const int active=-1)
uiBlock * block() const
uiLayout * parent_
blender::ui::LayoutAlign alignment() const
PanelLayout panel_prop_with_bool_header(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name, PointerRNA *bool_prop_owner, blender::StringRefNull bool_prop_name, std::optional< blender::StringRef > label)
blender::StringRef heading() const
blender::Vector< uiItem * > items_
uiLayoutRoot * root() const
void separator_spacer()
blender::ui::EmbossType emboss_or_undefined() const
float ui_units_y() const
blender::ui::EmbossType emboss_
void operator_context_set(blender::wm::OpCallContext opcontext)
void popover_group(bContext *C, int space_id, int region_id, const char *context, const char *category)
bool variable_size() const
float scale_y() const
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
bool active_default() const
void prop_search(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, PropertyRNA *item_searchpropname, std::optional< blender::StringRefNull > name, int icon, bool results_are_suggestions)
void active_set(bool active)
std::optional< int64_t > context_int_get(const blender::StringRef name) const
uiLayout & grid_flow(bool row_major, int columns_len, bool even_columns, bool even_rows, bool align)
blender::ui::EmbossType emboss() const
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void enabled_set(bool enabled)
uiLayout(blender::ui::ItemType type, uiLayoutRoot *root)
void menu_fn(blender::StringRefNull name, int icon, uiMenuCreateFunc func, void *arg)
int width() const
void context_ptr_set(blender::StringRef name, const PointerRNA *ptr)
uiLayoutRoot * root_
uiBut * button(blender::StringRef name, int icon, std::function< void(bContext &)> func, std::optional< blender::StringRef > tooltip=std::nullopt)
uiLayout & row(bool align)
bool red_alert() const
uiLayout & split(float percentage, bool align)
uiLayout & box()
void context_copy(const bContextStore *context)
void prop_menu_enum(PointerRNA *ptr, PropertyRNA *prop, std::optional< blender::StringRefNull > name, int icon)
bool align() const
void op_enum_items(wmOperatorType *ot, const PointerRNA &ptr, PropertyRNA *prop, IDProperty *properties, blender::wm::OpCallContext context, eUI_Item_Flag flag, const EnumPropertyItem *item_array, int totitem, int active=-1)
uiLayout & absolute(bool align)
uiLayout & list_box(uiList *ui_list, PointerRNA *actptr, PropertyRNA *actprop)
PointerRNA op_menu_hold(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag, const char *menu_id)
blender::Span< uiItem * > items() const
std::string heading_
void prop_with_menu(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRefNull > name, int icon, const char *menu_type)
bool activate_init() const
uiLayout & menu_pie()
void context_set_from_but(const uiBut *but)
bool use_property_split() const
void emboss_set(blender::ui::EmbossType emboss)
Panel * root_panel() const
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void prop_with_popover(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRefNull > name, int icon, const char *panel_type)
void menu(MenuType *mt, std::optional< blender::StringRef > name, int icon)
void context_int_set(blender::StringRef name, int64_t value)
std::optional< blender::StringRefNull > context_string_get(const blender::StringRef name) const
void use_property_split_set(bool value)
void menu_contents(blender::StringRef menuname)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
float ui_units_x() const
const PointerRNA * context_ptr_get(const blender::StringRef name, const StructRNA *type) const
uiLayout & overlap()
virtual void resolve_impl()
void prop_tabs_enum(bContext *C, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *ptr_highlight, PropertyRNA *prop_highlight, bool icon_only)
void context_string_set(blender::StringRef name, blender::StringRef value)
short buttonspacey
short buttonspacex
short templatespace
short panelspace
uiFontStyle widget
short columnspace
wmEventModifierFlag modifier
Definition WM_types.hh:774
const char * name
Definition WM_types.hh:1033
std::string(* get_description)(bContext *C, wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1121
struct wmEvent * eventstate
i
Definition text_draw.cc:230
uint len
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
std::optional< std::string > WM_key_event_operator_string(const bContext *C, const char *opname, blender::wm::OpCallContext 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 StringRef 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 StringRef idname, bool quiet)
uint8_t flag
Definition wm_window.cc:145