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