diff options
Diffstat (limited to 'source/blender/editors/interface/interface_layout.c')
-rw-r--r-- | source/blender/editors/interface/interface_layout.c | 415 |
1 files changed, 320 insertions, 95 deletions
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index fed35ccff59..884e43b4026 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -39,7 +39,7 @@ #include "BLT_translation.h" -#include "BKE_animsys.h" +#include "BKE_anim_data.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_idprop.h" @@ -75,7 +75,7 @@ } \ (void)0 -#define UI_ITEM_PROP_SEP_DIVIDE 0.5f +#define UI_ITEM_PROP_SEP_DIVIDE 0.4f /* uiLayoutRoot */ @@ -135,10 +135,11 @@ enum { UI_ITEM_BOX_ITEM = 1 << 2, /* The item is "inside" a box item */ UI_ITEM_PROP_SEP = 1 << 3, + UI_ITEM_INSIDE_PROP_SEP = 1 << 4, /* Show an icon button next to each property (to set keyframes, show status). * Enabled by default, depends on 'UI_ITEM_PROP_SEP'. */ - UI_ITEM_PROP_DECORATE = 1 << 4, - UI_ITEM_PROP_DECORATE_NO_PAD = 1 << 5, + UI_ITEM_PROP_DECORATE = 1 << 5, + UI_ITEM_PROP_DECORATE_NO_PAD = 1 << 6, }; typedef struct uiButtonItem { @@ -151,8 +152,11 @@ struct uiLayout { uiLayoutRoot *root; bContextStore *context; + uiLayout *parent; ListBase items; + char heading[UI_MAX_NAME_STR]; + /** Sub layout to add child items, if not the layout itself. */ uiLayout *child_items_layout; @@ -945,19 +949,25 @@ static uiBut *ui_item_with_label(uiLayout *layout, PropertyType type; PropertySubType subtype; int prop_but_width = w_hint; +#ifdef UI_PROP_DECORATE + uiLayout *layout_prop_decorate = NULL; const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0); +#endif /* Always align item with label since text is already given enough space not to overlap. */ sub = uiLayoutRow(layout, true); UI_block_layout_set_current(block, sub); +#ifdef UI_PROP_DECORATE if (name[0]) { - int w_label; - if (use_prop_sep) { - w_label = (int)((w_hint * 2) * UI_ITEM_PROP_SEP_DIVIDE); + layout_prop_decorate = uiItemL_respect_property_split(layout, name, 0); } - else { + else +#endif + { + int w_label; + if (ui_layout_variable_size(layout)) { /* w_hint is width for label in this case. * Use a default width for property button(s) */ @@ -967,13 +977,7 @@ static uiBut *ui_item_with_label(uiLayout *layout, else { w_label = w_hint / 3; } - } - - uiBut *but_label = uiDefBut( - block, UI_BTYPE_LABEL, 0, name, x, y, w_label, h, NULL, 0.0, 0.0, 0, 0, ""); - if (use_prop_sep) { - but_label->drawflag |= UI_BUT_TEXT_RIGHT; - but_label->drawflag &= ~UI_BUT_TEXT_LEFT; + uiDefBut(block, UI_BTYPE_LABEL, 0, name, x, y, w_label, h, NULL, 0.0, 0.0, 0, 0, ""); } } @@ -1053,7 +1057,7 @@ static uiBut *ui_item_with_label(uiLayout *layout, if (layout->item.flag & UI_ITEM_PROP_SEP) { if ((layout->item.flag & UI_ITEM_PROP_DECORATE) && (layout->item.flag & UI_ITEM_PROP_DECORATE_NO_PAD) == 0) { - uiItemL(sub, NULL, ICON_BLANK1); + uiItemL(layout_prop_decorate ? layout_prop_decorate : sub, NULL, ICON_BLANK1); } } #endif /* UI_PROP_DECORATE */ @@ -1780,6 +1784,7 @@ static void ui_item_rna_size(uiLayout *layout, PropertyType type; PropertySubType subtype; int len, w = 0, h; + bool is_checkbox_only = false; /* arbitrary extended width by type */ type = RNA_property_type(prop); @@ -1791,6 +1796,10 @@ static void ui_item_rna_size(uiLayout *layout, name = "non-empty text"; } else if (type == PROP_BOOLEAN) { + if (icon == ICON_NONE) { + /* Exception for checkboxes, they need a little less space to align nicely. */ + is_checkbox_only = true; + } icon = ICON_DOT; } else if (type == PROP_ENUM) { @@ -1850,6 +1859,9 @@ static void ui_item_rna_size(uiLayout *layout, if (type == PROP_BOOLEAN && name[0]) { w += UI_UNIT_X / 5; } + else if (is_checkbox_only) { + w -= UI_UNIT_X / 4; + } else if (type == PROP_ENUM && !icon_only) { w += UI_UNIT_X / 4; } @@ -1862,6 +1874,78 @@ static void ui_item_rna_size(uiLayout *layout, *r_h = h; } +static bool ui_item_rna_is_expand(PropertyRNA *prop, int index, int item_flag) +{ + const bool is_array = RNA_property_array_check(prop); + const int subtype = RNA_property_subtype(prop); + return is_array && (index == RNA_NO_INDEX) && + ((item_flag & UI_ITEM_R_EXPAND) || + !ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA, PROP_DIRECTION)); +} + +/** + * Find first layout ancestor (or self) with a heading set. + * + * \returns the layout to add the heading to as fallback (i.e. if it can't be placed in a split + * layout). Its #uiLayout.heading member can be cleared to mark the heading as added (so + * it's not added multiple times). Returns a pointer to the heading + */ +static uiLayout *ui_layout_heading_find(uiLayout *cur_layout) +{ + for (uiLayout *parent = cur_layout; parent; parent = parent->parent) { + if (parent->heading[0]) { + return parent; + } + } + + return NULL; +} + +static void ui_layout_heading_label_add(uiLayout *layout, + uiLayout *heading_layout, + bool right_align, + bool respect_prop_split) +{ + const int prev_alignment = layout->alignment; + + if (right_align) { + uiLayoutSetAlignment(layout, UI_LAYOUT_ALIGN_RIGHT); + } + + if (respect_prop_split) { + uiItemL_respect_property_split(layout, heading_layout->heading, ICON_NONE); + } + else { + uiItemL(layout, heading_layout->heading, ICON_NONE); + } + /* After adding the heading label, we have to mark it somehow as added, so it's not added again + * for other items in this layout. For now just clear it. */ + heading_layout->heading[0] = '\0'; + + layout->alignment = prev_alignment; +} + +/** + * Hack to add further items in a row into the second part of the split layout, so the label part + * keeps a fixed size. + * \return The layout to place further items in for the split layout. + */ +static uiLayout *ui_item_prop_split_layout_hack(uiLayout *layout_parent, uiLayout *layout_split) +{ + /* Tag item as using property split layout, this is inherited to children so they can get special + * treatment if needed. */ + layout_parent->item.flag |= UI_ITEM_INSIDE_PROP_SEP; + + if (layout_parent->item.type == ITEM_LAYOUT_ROW) { + /* Prevent further splits within the row. */ + uiLayoutSetPropSep(layout_parent, false); + + layout_parent->child_items_layout = uiLayoutRow(layout_split, true); + return layout_parent->child_items_layout; + } + return layout_split; +} + void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, @@ -1874,13 +1958,18 @@ void uiItemFullR(uiLayout *layout, uiBlock *block = layout->root->block; char namestr[UI_MAX_NAME_STR]; const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0); - - /* By default 'use_prop_sep' uses a separate column for labels. - * This is an exception for check-boxes otherwise only the small checkbox region is clickable. + const bool inside_prop_sep = ((layout->item.flag & UI_ITEM_INSIDE_PROP_SEP) != 0); + /* Columns can define a heading to insert. If the first item added to a split layout doesn't have + * a label to display in the first column, the heading is inserted there. Otherwise it's inserted + * as a new row before the first item. */ + uiLayout *heading_layout = ui_layout_heading_find(layout); + /* Although checkboxes use the split layout, they are an exception and should only place their + * label in the second column, to not make that almost empty. * * Keep using 'use_prop_sep' instead of disabling it entirely because * we need the ability to have decorators still. */ bool use_prop_sep_split_label = use_prop_sep; + bool use_split_empty_name = (flag & UI_ITEM_R_SPLIT_EMPTY_NAME); #ifdef UI_PROP_DECORATE struct { @@ -1991,6 +2080,9 @@ void uiItemFullR(uiLayout *layout, if (use_prop_sep) { if (type == PROP_BOOLEAN && (icon == ICON_NONE) && !icon_only) { use_prop_sep_split_label = false; + /* For check-boxes we make an exception: We allow showing them in a split row even without + * label. It typically relates to its neighbor items, so no need for an extra label. */ + use_split_empty_name = true; } } #endif @@ -2017,6 +2109,7 @@ void uiItemFullR(uiLayout *layout, /* Split the label / property. */ uiLayout *layout_parent = layout; + if (use_prop_sep) { uiLayout *layout_row = NULL; #ifdef UI_PROP_DECORATE @@ -2027,21 +2120,26 @@ void uiItemFullR(uiLayout *layout, } #endif /* UI_PROP_DECORATE */ - if ((name[0] == '\0') || (use_prop_sep_split_label == false)) { + if ((name[0] == '\0') && !use_split_empty_name) { /* Ensure we get a column when text is not set. */ layout = uiLayoutColumn(layout_row ? layout_row : layout, true); layout->space = 0; + if (heading_layout) { + ui_layout_heading_label_add(layout, heading_layout, false, false); + } } else { - const PropertySubType subtype = RNA_property_subtype(prop); uiLayout *layout_split = uiLayoutSplit( layout_row ? layout_row : layout, UI_ITEM_PROP_SEP_DIVIDE, true); + bool label_added = false; layout_split->space = 0; uiLayout *layout_sub = uiLayoutColumn(layout_split, true); layout_sub->space = 0; - if ((index == RNA_NO_INDEX && is_array) && - ((!expand && ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA, PROP_DIRECTION)) == 0)) { + if (!use_prop_sep_split_label) { + /* Pass */ + } + else if (ui_item_rna_is_expand(prop, index, flag)) { char name_with_suffix[UI_MAX_DRAW_STR + 2]; char str[2] = {'\0'}; for (int a = 0; a < len; a++) { @@ -2070,6 +2168,8 @@ void uiItemFullR(uiLayout *layout, ""); but->drawflag |= UI_BUT_TEXT_RIGHT; but->drawflag &= ~UI_BUT_TEXT_LEFT; + + label_added = true; } } else { @@ -2078,16 +2178,17 @@ void uiItemFullR(uiLayout *layout, block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); but->drawflag |= UI_BUT_TEXT_RIGHT; but->drawflag &= ~UI_BUT_TEXT_LEFT; + + label_added = true; } } - /* Hack to add further items in a row into the second part of - * the split layout, so the label part keeps a fixed size. */ - if (layout_parent && layout_parent->item.type == ITEM_LAYOUT_ROW) { - layout_split = uiLayoutRow(layout_split, true); - layout_parent->child_items_layout = layout_split; + if (!label_added && heading_layout) { + ui_layout_heading_label_add(layout_sub, heading_layout, true, false); } + layout_split = ui_item_prop_split_layout_hack(layout_parent, layout_split); + /* Watch out! We can only write into the new layout now. */ if ((type == PROP_ENUM) && (flag & UI_ITEM_R_EXPAND)) { /* Expanded enums each have their own name. */ @@ -2102,7 +2203,9 @@ void uiItemFullR(uiLayout *layout, } } else { - name = ""; + if (use_prop_sep_split_label) { + name = ""; + } layout = uiLayoutColumn(layout_split, true); } layout->space = 0; @@ -2121,9 +2224,20 @@ void uiItemFullR(uiLayout *layout, #endif /* UI_PROP_DECORATE */ } /* End split. */ + else if (heading_layout) { + /* Could not add heading to split layout, fallback to inserting it to the layout with the + * heading itself. */ + ui_layout_heading_label_add(heading_layout, heading_layout, false, false); + } /* array property */ if (index == RNA_NO_INDEX && is_array) { + if (inside_prop_sep) { + /* Within a split row, add array items to a column so they match the column layout of + * previous items (e.g. transform vector with lock icon for each item). */ + layout = uiLayoutColumn(layout, true); + } + ui_item_array(layout, block, name, @@ -2203,12 +2317,6 @@ void uiItemFullR(uiLayout *layout, if (layout->activate_init) { UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); } - - if (use_prop_sep && (use_prop_sep_split_label == false)) { - /* When the button uses it's own text right align it. */ - but->drawflag |= UI_BUT_TEXT_RIGHT; - but->drawflag &= ~UI_BUT_TEXT_LEFT; - } } /* The resulting button may have the icon set since boolean button drawing @@ -2231,50 +2339,21 @@ void uiItemFullR(uiLayout *layout, #ifdef UI_PROP_DECORATE if (ui_decorate.use_prop_decorate) { - const bool is_anim = RNA_property_animateable(ptr, prop); uiBut *but_decorate = ui_decorate.but ? ui_decorate.but->next : block->buttons.first; + const bool use_blank_decorator = (flag & UI_ITEM_R_FORCE_BLANK_DECORATE); uiLayout *layout_col = uiLayoutColumn(ui_decorate.layout, false); layout_col->space = 0; layout_col->emboss = UI_EMBOSS_NONE; + int i; for (i = 0; i < ui_decorate.len && but_decorate; i++) { + PointerRNA *ptr_dec = use_blank_decorator ? NULL : &but_decorate->rnapoin; + PropertyRNA *prop_dec = use_blank_decorator ? NULL : but_decorate->rnaprop; + /* The icons are set in 'ui_but_anim_flag' */ - if (is_anim) { - but = uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_DOT, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Animate property")); - UI_but_func_set(but, ui_but_anim_decorate_cb, but, NULL); - but->flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK; - } - else { - /* We may show other information here in future, for now use empty space. */ - but = uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_BLANK1, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - ""); - but->flag |= UI_BUT_DISABLED; - } + uiItemDecoratorR_prop(layout_col, ptr_dec, prop_dec, but_decorate->rnaindex); + but = block->buttons.last; + /* Order the decorator after the button we decorate, this is used so we can always * do a quick lookup. */ BLI_remlink(&block->buttons, but); @@ -2566,6 +2645,13 @@ static void search_id_collection(StructRNA *ptype, PointerRNA *r_ptr, PropertyRN RNA_STRUCT_END; } +static void ui_rna_collection_search_arg_free_fn(void *ptr) +{ + uiRNACollectionSearch *coll_search = ptr; + UI_butstore_free(coll_search->butstore_block, coll_search->butstore); + MEM_freeN(ptr); +} + void ui_but_add_search( uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop) { @@ -2598,7 +2684,10 @@ void ui_but_add_search( coll_search->target_prop = prop; coll_search->search_ptr = *searchptr; coll_search->search_prop = searchprop; - coll_search->but_changed = &but->changed; + coll_search->search_but = but; + coll_search->butstore_block = but->block; + coll_search->butstore = UI_butstore_create(coll_search->butstore_block); + UI_butstore_register(coll_search->butstore, &coll_search->search_but); if (RNA_property_type(prop) == PROP_ENUM) { /* XXX, this will have a menu string, @@ -2608,9 +2697,9 @@ void ui_but_add_search( UI_but_func_search_set(but, ui_searchbox_create_generic, - ui_rna_collection_search_cb, + ui_rna_collection_search_update_fn, coll_search, - MEM_freeN, + ui_rna_collection_search_arg_free_fn, NULL, NULL); } @@ -2737,6 +2826,7 @@ static uiBut *ui_item_menu(uiLayout *layout, bool force_menu) { uiBlock *block = layout->root->block; + uiLayout *heading_layout = ui_layout_heading_find(layout); uiBut *but; int w, h; @@ -2766,12 +2856,16 @@ static uiBut *ui_item_menu(uiLayout *layout, } } + if (heading_layout) { + ui_layout_heading_label_add(layout, heading_layout, true, true); + } + if (name[0] && icon) { but = uiDefIconTextMenuBut(block, func, arg, icon, name, 0, 0, w, h, tip); } else if (icon) { but = uiDefIconMenuBut(block, func, arg, icon, 0, 0, w, h, tip); - if (force_menu) { + if (force_menu && name[0]) { UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); } } @@ -2839,6 +2933,91 @@ void uiItemMContents(uiLayout *layout, const char *menuname) UI_menutype_draw(C, mt, layout); } +/** + * Insert a decorator item for a button with the same property as \a prop. + * To force inserting a blank dummy element, NULL can be passed for \a ptr and \a prop. + */ +void uiItemDecoratorR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index) +{ + uiBlock *block = layout->root->block; + uiBut *but = NULL; + + uiLayout *col; + UI_block_layout_set_current(block, layout); + col = uiLayoutColumn(layout, false); + col->space = 0; + col->emboss = UI_EMBOSS_NONE; + + if (ELEM(NULL, ptr, prop) || !RNA_property_animateable(ptr, prop)) { + but = uiDefIconBut(block, + UI_BTYPE_BUT, + 0, + ICON_BLANK1, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + NULL, + 0.0, + 0.0, + 0.0, + 0.0, + ""); + but->flag |= UI_BUT_DISABLED; + return; + } + + const bool is_expand = ui_item_rna_is_expand(prop, index, 0); + const bool is_array = RNA_property_array_check(prop); + + /* Loop for the array-case, but only do in case of an expanded array. */ + for (int i = 0; i < (is_expand ? RNA_property_array_length(ptr, prop) : 1); i++) { + but = uiDefIconBut(block, + UI_BTYPE_BUT, + 0, + ICON_DOT, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + NULL, + 0.0, + 0.0, + 0.0, + 0.0, + TIP_("Animate property")); + UI_but_func_set(but, ui_but_anim_decorate_cb, but, NULL); + but->flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK; + /* Reusing RNA search members, setting actual RNA data has many side-effects. */ + but->rnasearchpoin = *ptr; + but->rnasearchprop = prop; + /* ui_def_but_rna() sets non-array buttons to have a RNA index of 0. */ + but->custom_data = POINTER_FROM_INT((!is_array || is_expand) ? i : index); + } +} + +/** + * Insert a decorator item for a button with the same property as \a prop. + * To force inserting a blank dummy element, NULL can be passed for \a ptr and \a propname. + */ +void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, int index) +{ + PropertyRNA *prop = NULL; + + if (ptr && propname) { + /* validate arguments */ + prop = RNA_struct_find_property(ptr, propname); + if (!prop) { + ui_item_disabled(layout, propname); + RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); + return; + } + } + + /* ptr and prop are allowed to be NULL here. */ + uiItemDecoratorR_prop(layout, ptr, prop, index); +} + /* popover */ void uiItemPopoverPanel_ptr( uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon) @@ -2897,7 +3076,7 @@ void uiItemPopoverPanelFromGroup(uiLayout *layout, return; } - for (PanelType *pt = art->paneltypes.first; pt; pt = pt->next) { + LISTBASE_FOREACH (PanelType *, pt, &art->paneltypes) { /* Causes too many panels, check context. */ if (pt->parent_id[0] == '\0') { if (/* (*context == '\0') || */ STREQ(pt->context, context)) { @@ -2982,28 +3161,40 @@ void uiItemL(uiLayout *layout, const char *name, int icon) } /** - * Helper to add a label, which handles logic for split property layout if needed. - * - * Normally, we handle the split layout in #uiItemFullR(), but there are other cases where we may - * want to use the logic. For those this helper was added, although it will likely have to be - * extended to support more cases. - * Ideally, #uiItemFullR() could just call this, but it currently has too many special needs. - * - * \return the layout to place the item(s) associated to the label in. + * Normally, we handle the split layout in #uiItemFullR(), but there are other cases where the + * logic is needed. Ideally, #uiItemFullR() could just call this, but it currently has too many + * special needs. + */ +uiPropertySplitWrapper uiItemPropertySplitWrapperCreate(uiLayout *parent_layout) +{ + uiPropertySplitWrapper split_wrapper = {NULL}; + + uiLayout *layout_row = uiLayoutRow(parent_layout, true); + uiLayout *layout_split = uiLayoutSplit(layout_row, UI_ITEM_PROP_SEP_DIVIDE, true); + + layout_split->space = 0; + split_wrapper.label_column = uiLayoutColumn(layout_split, true); + split_wrapper.label_column->alignment = UI_LAYOUT_ALIGN_RIGHT; + split_wrapper.property_row = ui_item_prop_split_layout_hack(parent_layout, layout_split); + split_wrapper.decorate_column = uiLayoutColumn(layout_row, true); + + return split_wrapper; +} + +/* + * Helper to add a label and creates a property split layout if needed. */ uiLayout *uiItemL_respect_property_split(uiLayout *layout, const char *text, int icon) { if (layout->item.flag & UI_ITEM_PROP_SEP) { - uiLayout *layout_split = uiLayoutSplit(layout, UI_ITEM_PROP_SEP_DIVIDE, true); - uiLayout *layout_sub = uiLayoutColumn(layout_split, true); - - layout_split->space = layout_sub->space = layout->space = 0; - layout_sub->alignment = UI_LAYOUT_ALIGN_RIGHT; + uiBlock *block = uiLayoutGetBlock(layout); + uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout); + /* Further items added to 'layout' will automatically be added to split_wrapper.property_row */ - uiItemL_(layout_sub, text, icon); + uiItemL_(split_wrapper.label_column, text, icon); + UI_block_layout_set_current(block, split_wrapper.property_row); - /* Give caller a new sub-row to place items in. */ - return uiLayoutRow(layout_split, true); + return split_wrapper.decorate_column; } else { char namestr[UI_MAX_NAME_STR]; @@ -4444,13 +4635,24 @@ static void ui_litem_init_from_parent(uiLayout *litem, uiLayout *layout, int ali litem->redalert = layout->redalert; litem->w = layout->w; litem->emboss = layout->emboss; - litem->item.flag = (layout->item.flag & (UI_ITEM_PROP_SEP | UI_ITEM_PROP_DECORATE)); + litem->item.flag = (layout->item.flag & + (UI_ITEM_PROP_SEP | UI_ITEM_PROP_DECORATE | UI_ITEM_INSIDE_PROP_SEP)); if (layout->child_items_layout) { BLI_addtail(&layout->child_items_layout->items, litem); + litem->parent = layout->child_items_layout; } else { BLI_addtail(&layout->items, litem); + litem->parent = layout; + } +} + +static void ui_layout_heading_set(uiLayout *layout, const char *heading) +{ + BLI_assert(layout->heading[0] == '\0'); + if (heading) { + STRNCPY(layout->heading, heading); } } @@ -4470,6 +4672,16 @@ uiLayout *uiLayoutRow(uiLayout *layout, bool align) return litem; } +/** + * See #uiLayoutColumnWithHeading(). + */ +uiLayout *uiLayoutRowWithHeading(uiLayout *layout, bool align, const char *heading) +{ + uiLayout *litem = uiLayoutRow(layout, align); + ui_layout_heading_set(litem, heading); + return litem; +} + uiLayout *uiLayoutColumn(uiLayout *layout, bool align) { uiLayout *litem; @@ -4485,6 +4697,19 @@ uiLayout *uiLayoutColumn(uiLayout *layout, bool align) return litem; } +/** + * Variant of #uiLayoutColumn() that sets a heading label for the layout if the first item is + * added through #uiItemFullR(). If split layout is used and the item has no string to add to the + * first split-column, the heading is added there instead. Otherwise the heading inserted with a + * new row. + */ +uiLayout *uiLayoutColumnWithHeading(uiLayout *layout, bool align, const char *heading) +{ + uiLayout *litem = uiLayoutColumn(layout, align); + ui_layout_heading_set(litem, heading); + return litem; +} + uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, bool align) { uiLayoutItemFlow *flow; @@ -5352,7 +5577,7 @@ static void ui_paneltype_draw_impl(bContext *C, PanelType *pt, uiLayout *layout, MEM_freeN(panel); /* Draw child panels. */ - for (LinkData *link = pt->children.first; link; link = link->next) { + LISTBASE_FOREACH (LinkData *, link, &pt->children) { PanelType *child_pt = link->data; if (child_pt->poll == NULL || child_pt->poll(C, child_pt)) { |