UI: Better split layout support for checkboxes

Makes the following layout changes possible:
{F8473498} {F8473499} {F8473502}

The next commit will contain many layout changes to make good use of
these new possibilities. The result should be more consistent, easier to
read and should give a more organized impression. Additionally, it
should be possible to replace many sub-panels with compacter layouts.

Main changes:
* Checkboxes now respect the property split layouts
* Add support for row and column headers (i.e.
  `uiLayout.column(heading="Foo")`, `uiLayout.row(heading="Bar")`). If the
  first property added to this layout doesn't insert anything into the label
  split column, the heading is inserted there. Otherwise, it's inserted as own
  item.
* Add support for manually inserting decorators for an existing item
  (`uiLayout.prop_decorator()`). That way layout creators can manually insert
  this, which was the only way I saw to support property split layouts with a
  checkbox before the actual property. {F8471883}
* Autogenerated layouts for operator properties look bad if there are only
  checkboxes (which only use half the region width). So before creating the
  layout, we iterate over visible properties and disable split layout if all
  are booleans. I think this is fine, if needed we could also add layout hints
  to operators.
* `uiTemplateOperatorPropertyButs()` now handles macros itself, the caller
  used to be responsible for this. Code that didn't handle these so far never
  used macros I think, so this change should be invisible.
* Remove manual property split layout from autogenerated operator properties
  layout.
* Padding of checkboxes is tweaked to make their label visually more connected
  to the checkboxes.
* Support split layout for menus (should work for `uiLayout.menu()`,
  `.operator_menu_enum()`, `.prop_menu_enum()`, maybe more)

Maniphest Task: https://developer.blender.org/T65965

Differential Revision: https://developer.blender.org/D7427

Reviewed by: Brecht Van Lommel, William Reynish, Pablo Vazques
This commit is contained in:
Julian Eisel 2020-04-17 16:40:25 +02:00
parent 20614d331d
commit 219049bb3b
Notes: blender-bot 2023-02-14 06:17:14 +01:00
Referenced by issue #82477, UI: 'use_property_split' w/ right alignment cuts off property text
Referenced by issue #65965, Checkboxes alignment tweaks
10 changed files with 604 additions and 299 deletions

View File

@ -250,6 +250,8 @@ enum {
UI_BUT_TEXT_RIGHT = 1 << 3,
/** Prevent the button to show any tooltip. */
UI_BUT_NO_TOOLTIP = 1 << 4,
/** Do not add the usual horizontal padding for text drawing. */
UI_BUT_NO_TEXT_PADDING = 1 << 5,
/* Button align flag, for drawing groups together.
* Used in 'uiBlock.flag', take care! */
@ -1774,6 +1776,8 @@ enum {
UI_ITEM_O_DEPRESS = 1 << 10,
UI_ITEM_R_COMPACT = 1 << 11,
UI_ITEM_R_CHECKBOX_INVERT = 1 << 12,
/** Don't add a real decorator item, just blank space. */
UI_ITEM_R_FORCE_BLANK_DECORATE = 1 << 13,
};
#define UI_HEADER_OFFSET ((void)0, 0.4f * UI_UNIT_X)
@ -1784,6 +1788,9 @@ enum {
UI_TEMPLATE_OP_PROPS_SHOW_EMPTY = 1 << 1,
UI_TEMPLATE_OP_PROPS_COMPACT = 1 << 2,
UI_TEMPLATE_OP_PROPS_HIDE_ADVANCED = 1 << 3,
/* Disable property split for the default layout (custom ui callbacks still have full control
* over the layout and can enable it). */
UI_TEMPLATE_OP_PROPS_NO_SPLIT_LAYOUT = 1 << 4,
};
/* used for transp checkers */
@ -1871,7 +1878,9 @@ bool uiLayoutGetPropDecorate(uiLayout *layout);
/* layout specifiers */
uiLayout *uiLayoutRow(uiLayout *layout, bool align);
uiLayout *uiLayoutRowWithHeading(uiLayout *layout, bool align, const char *heading);
uiLayout *uiLayoutColumn(uiLayout *layout, bool align);
uiLayout *uiLayoutColumnWithHeading(uiLayout *layout, bool align, const char *heading);
uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, bool align);
uiLayout *uiLayoutGridFlow(uiLayout *layout,
bool row_major,
@ -2046,11 +2055,11 @@ void uiTemplateOperatorSearch(uiLayout *layout);
void UI_but_func_menu_search(uiBut *but);
void uiTemplateMenuSearch(uiLayout *layout);
eAutoPropButsReturn uiTemplateOperatorPropertyButs(const struct bContext *C,
uiLayout *layout,
struct wmOperator *op,
const eButLabelAlign label_align,
const short flag);
void uiTemplateOperatorPropertyButs(const struct bContext *C,
uiLayout *layout,
struct wmOperator *op,
eButLabelAlign label_align,
short flag);
void uiTemplateHeader3D_mode(uiLayout *layout, struct bContext *C);
void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C);
void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C);
@ -2311,6 +2320,9 @@ void uiItemM_ptr(uiLayout *layout, struct MenuType *mt, const char *name, int ic
void uiItemM(uiLayout *layout, const char *menuname, const char *name, int icon);
/* menu contents */
void uiItemMContents(uiLayout *layout, const char *menuname);
/* Decorators */
void uiItemDecoratorR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index);
void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, int index);
/* value */
void uiItemV(uiLayout *layout, const char *name, int icon, int argval);
/* separator */

View File

@ -641,6 +641,26 @@ static int ui_but_calc_float_precision(uiBut *but, double value)
/* ************** BLOCK ENDING FUNCTION ************* */
bool ui_but_rna_equals(const uiBut *a, const uiBut *b)
{
return ui_but_rna_equals_ex(a, &b->rnapoin, b->rnaprop, b->rnaindex);
}
bool ui_but_rna_equals_ex(const uiBut *but,
const PointerRNA *ptr,
const PropertyRNA *prop,
int index)
{
if (but->rnapoin.data != ptr->data) {
return false;
}
if (but->rnaprop != prop || but->rnaindex != index) {
return false;
}
return true;
}
/* NOTE: if but->poin is allocated memory for every defbut, things fail... */
static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
{
@ -649,10 +669,7 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
if (but->retval != oldbut->retval) {
return false;
}
if (but->rnapoin.data != oldbut->rnapoin.data) {
return false;
}
if (but->rnaprop != oldbut->rnaprop || but->rnaindex != oldbut->rnaindex) {
if (!ui_but_rna_equals(but, oldbut)) {
return false;
}
if (but->func != oldbut->func) {

View File

@ -28,6 +28,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
@ -114,10 +115,38 @@ void ui_but_anim_flag(uiBut *but, float cfra)
}
}
static uiBut *ui_but_anim_decorate_find_attached_button(uiBut *but_decorate)
{
uiBut *but_iter = NULL;
BLI_assert(UI_but_is_decorator(but_decorate));
BLI_assert(but_decorate->rnasearchpoin.data && but_decorate->rnasearchprop);
LISTBASE_CIRCULAR_BACKWARD_BEGIN (&but_decorate->block->buttons, but_iter, but_decorate->prev) {
if (but_iter != but_decorate && ui_but_rna_equals_ex(but_decorate,
&but_iter->rnasearchpoin,
but_iter->rnasearchprop,
POINTER_AS_INT(but_iter->custom_data))) {
return but_iter;
}
}
LISTBASE_CIRCULAR_BACKWARD_END(&but_decorate->block->buttons, but_iter, but_decorate->prev);
return NULL;
}
void ui_but_anim_decorate_update_from_flag(uiBut *but)
{
BLI_assert(UI_but_is_decorator(but) && but->prev);
int flag = but->prev->flag;
const uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but);
if (!but_anim) {
printf("Could not find button with matching property to decorate (%s.%s)",
RNA_struct_identifier(but->rnapoin.type),
RNA_property_identifier(but->rnaprop));
}
int flag = but_anim->flag;
if (flag & UI_BUT_DRIVEN) {
but->icon = ICON_DECORATE_DRIVER;
}
@ -289,22 +318,26 @@ void ui_but_anim_paste_driver(bContext *C)
void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy))
{
wmWindowManager *wm = CTX_wm_manager(C);
uiBut *but = arg_but;
but = but->prev;
uiBut *but_decorate = arg_but;
uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but_decorate);
if (!but_anim) {
return;
}
/* FIXME(campbell), swapping active pointer is weak. */
SWAP(struct uiHandleButtonData *, but->active, but->next->active);
SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->active);
wm->op_undo_depth++;
if (but->flag & UI_BUT_DRIVEN) {
if (but_anim->flag & UI_BUT_DRIVEN) {
/* pass */
/* TODO: report? */
}
else if (but->flag & UI_BUT_ANIMATED_KEY) {
else if (but_anim->flag & UI_BUT_ANIMATED_KEY) {
PointerRNA props_ptr;
wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_delete_button", false);
WM_operator_properties_create_ptr(&props_ptr, ot);
RNA_boolean_set(&props_ptr, "all", but->rnaindex == -1);
RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
WM_operator_properties_free(&props_ptr);
}
@ -312,11 +345,11 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)
PointerRNA props_ptr;
wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_insert_button", false);
WM_operator_properties_create_ptr(&props_ptr, ot);
RNA_boolean_set(&props_ptr, "all", but->rnaindex == -1);
RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
WM_operator_properties_free(&props_ptr);
}
SWAP(struct uiHandleButtonData *, but->active, but->next->active);
SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->active);
wm->op_undo_depth--;
}

View File

@ -790,6 +790,11 @@ float ui_block_calc_pie_segment(struct uiBlock *block, const float event_xy[2]);
void ui_but_add_shortcut(uiBut *but, const char *key_str, const bool do_strip);
void ui_but_clipboard_free(void);
bool ui_but_rna_equals(const uiBut *a, const uiBut *b);
bool ui_but_rna_equals_ex(const uiBut *but,
const PointerRNA *ptr,
const PropertyRNA *prop,
int index);
uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new);
uiBut *ui_but_find_new(uiBlock *block_old, const uiBut *but_new);

View File

@ -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;
@ -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,48 @@ static void ui_item_rna_size(uiLayout *layout,
*r_h = h;
}
/**
* 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.
@ -1869,7 +1923,14 @@ static void ui_item_rna_size(uiLayout *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;
}
@ -1888,13 +1949,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 forbid_single_col = false;
#ifdef UI_PROP_DECORATE
struct {
@ -2005,6 +2071,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 checkboxes we make an expection: 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. */
forbid_single_col = true;
}
}
#endif
@ -2031,6 +2100,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
@ -2041,21 +2111,28 @@ void uiItemFullR(uiLayout *layout,
}
#endif /* UI_PROP_DECORATE */
if ((name[0] == '\0') || (use_prop_sep_split_label == false)) {
if ((name[0] == '\0') && !forbid_single_col) {
/* 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 ((index == RNA_NO_INDEX && is_array) &&
((!expand && ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA, PROP_DIRECTION)) == 0)) {
char name_with_suffix[UI_MAX_DRAW_STR + 2];
char str[2] = {'\0'};
for (int a = 0; a < len; a++) {
@ -2084,6 +2161,8 @@ void uiItemFullR(uiLayout *layout,
"");
but->drawflag |= UI_BUT_TEXT_RIGHT;
but->drawflag &= ~UI_BUT_TEXT_LEFT;
label_added = true;
}
}
else {
@ -2092,13 +2171,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;
}
}
if (layout_parent) {
layout_split = ui_item_prop_split_layout_hack(layout_parent, 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. */
@ -2113,7 +2196,9 @@ void uiItemFullR(uiLayout *layout,
}
}
else {
name = "";
if (use_prop_sep_split_label) {
name = "";
}
layout = uiLayoutColumn(layout_split, true);
}
layout->space = 0;
@ -2132,9 +2217,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,
@ -2214,12 +2310,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
@ -2242,50 +2332,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);
@ -2759,6 +2820,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;
@ -2788,6 +2850,10 @@ 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);
}
@ -2861,6 +2927,79 @@ 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;
const bool is_anim = ptr && prop && RNA_property_animateable(ptr, prop);
uiBut *but = NULL;
uiLayout *row;
UI_block_layout_set_current(block, layout);
row = uiLayoutRow(layout, false);
row->space = 0;
row->emboss = UI_EMBOSS_NONE;
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;
/* Reusing RNA search members, setting actual RNA data has many side-effects. */
but->rnasearchpoin = *ptr;
but->rnasearchprop = prop;
but->custom_data = POINTER_FROM_INT(index);
}
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;
}
}
void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, int index)
{
PropertyRNA *prop;
/* 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;
}
uiItemDecoratorR_prop(layout, ptr, prop, index);
}
/* popover */
void uiItemPopoverPanel_ptr(
uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon)
@ -3017,6 +3156,7 @@ uiLayout *uiItemL_respect_property_split(uiLayout *layout, const char *text, int
{
if (layout->item.flag & UI_ITEM_PROP_SEP) {
uiBlock *block = uiLayoutGetBlock(layout);
uiLayout *layout_row = uiLayoutRow(layout, true);
uiLayout *layout_split = uiLayoutSplit(layout_row, UI_ITEM_PROP_SEP_DIVIDE, true);
uiLayout *layout_sub = uiLayoutColumn(layout_split, true);
@ -3029,8 +3169,9 @@ uiLayout *uiItemL_respect_property_split(uiLayout *layout, const char *text, int
layout_split = ui_item_prop_split_layout_hack(layout, layout_split);
UI_block_layout_set_current(block, layout_split);
/* Give caller a new sub-row to place items in. */
return layout_row;
/* The decorator layout uses the row the split layout was inserted to. */
uiLayout *layout_decorator = layout_row;
return layout_decorator;
}
else {
char namestr[UI_MAX_NAME_STR];
@ -4471,13 +4612,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);
}
}
@ -4497,6 +4649,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;
@ -4512,6 +4674,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;

View File

@ -2428,21 +2428,196 @@ uiLayout *uiTemplateShaderFx(uiLayout *layout, bContext *UNUSED(C), PointerRNA *
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operator Redo Buttons Template
/** \name Operator Property Buttons Template
* \{ */
static void template_operator_redo_property_buts_draw(
const bContext *C, wmOperator *op, uiLayout *layout, int layout_flags, bool *r_has_advanced)
typedef struct uiTemplateOperatorPropertyPollParam {
const bContext *C;
wmOperator *op;
short flag;
} uiTemplateOperatorPropertyPollParam;
#ifdef USE_OP_RESET_BUT
static void ui_layout_operator_buts__reset_cb(bContext *UNUSED(C),
void *op_pt,
void *UNUSED(arg_dummy2))
{
WM_operator_properties_reset((wmOperator *)op_pt);
}
#endif
static bool ui_layout_operator_buts_poll_property(struct PointerRNA *UNUSED(ptr),
struct PropertyRNA *prop,
void *user_data)
{
uiTemplateOperatorPropertyPollParam *params = user_data;
if ((params->flag & UI_TEMPLATE_OP_PROPS_HIDE_ADVANCED) &&
(RNA_property_tags(prop) & OP_PROP_TAG_ADVANCED)) {
return false;
}
return params->op->type->poll_property(params->C, params->op, prop);
}
static eAutoPropButsReturn template_operator_property_buts_draw_single(
const bContext *C,
wmOperator *op,
uiLayout *layout,
const eButLabelAlign label_align,
int layout_flags)
{
uiBlock *block = uiLayoutGetBlock(layout);
eAutoPropButsReturn return_info = 0;
if (!op->properties) {
IDPropertyTemplate val = {0};
op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
}
/* poll() on this operator may still fail,
* at the moment there is no nice feedback when this happens just fails silently. */
if (!WM_operator_repeat_check(C, op)) {
UI_block_lock_set(block, true, "Operator can't' redo");
return return_info;
}
else {
/* useful for macros where only one of the steps can't be re-done */
UI_block_lock_clear(block);
}
if (layout_flags & UI_TEMPLATE_OP_PROPS_SHOW_TITLE) {
uiItemL(layout, WM_operatortype_name(op->type, op->ptr), ICON_NONE);
}
/* menu */
if (op->type->flag & OPTYPE_PRESET) {
/* XXX, no simple way to get WM_MT_operator_presets.bl_label
* from python! Label remains the same always! */
PointerRNA op_ptr;
uiLayout *row;
block->ui_operator = op;
row = uiLayoutRow(layout, true);
uiItemM(row, "WM_MT_operator_presets", NULL, ICON_NONE);
wmOperatorType *ot = WM_operatortype_find("WM_OT_operator_preset_add", false);
uiItemFullO_ptr(row, ot, "", ICON_ADD, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_string_set(&op_ptr, "operator", op->type->idname);
uiItemFullO_ptr(row, ot, "", ICON_REMOVE, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_string_set(&op_ptr, "operator", op->type->idname);
RNA_boolean_set(&op_ptr, "remove_active", true);
}
if (op->type->ui) {
op->layout = layout;
op->type->ui((bContext *)C, op);
op->layout = NULL;
/* UI_LAYOUT_OP_SHOW_EMPTY ignored. retun_info is ignored too. We could
* allow ot.ui callback to return this, but not needed right now. */
}
else {
wmWindowManager *wm = CTX_wm_manager(C);
PointerRNA ptr;
uiTemplateOperatorPropertyPollParam user_data = {.C = C, .op = op, .flag = layout_flags};
const bool use_prop_split = (layout_flags & UI_TEMPLATE_OP_PROPS_NO_SPLIT_LAYOUT) == 0;
RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
uiLayoutSetPropSep(layout, use_prop_split);
uiLayoutSetPropDecorate(layout, false);
/* main draw call */
return_info = uiDefAutoButsRNA(
layout,
&ptr,
op->type->poll_property ? ui_layout_operator_buts_poll_property : NULL,
op->type->poll_property ? &user_data : NULL,
op->type->prop,
label_align,
(layout_flags & UI_TEMPLATE_OP_PROPS_COMPACT));
if ((return_info & UI_PROP_BUTS_NONE_ADDED) &&
(layout_flags & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY)) {
uiItemL(layout, IFACE_("No Properties"), ICON_NONE);
}
}
#ifdef USE_OP_RESET_BUT
/* its possible that reset can do nothing if all have PROP_SKIP_SAVE enabled
* but this is not so important if this button is drawn in those cases
* (which isn't all that likely anyway) - campbell */
if (op->properties->len) {
uiBut *but;
uiLayout *col; /* needed to avoid alignment errors with previous buttons */
col = uiLayoutColumn(layout, false);
block = uiLayoutGetBlock(col);
but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
0,
ICON_FILE_REFRESH,
IFACE_("Reset"),
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0.0,
0.0,
0.0,
0.0,
TIP_("Reset operator defaults"));
UI_but_func_set(but, ui_layout_operator_buts__reset_cb, op, NULL);
}
#endif
/* set various special settings for buttons */
/* Only do this if we're not refreshing an existing UI. */
if (block->oldblock == NULL) {
const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0;
uiBut *but;
for (but = block->buttons.first; but; but = but->next) {
/* no undo for buttons for operator redo panels */
UI_but_flag_disable(but, UI_BUT_UNDO);
/* only for popups, see [#36109] */
/* if button is operator's default property, and a text-field, enable focus for it
* - this is used for allowing operators with popups to rename stuff with fewer clicks
*/
if (is_popup) {
if ((but->rnaprop == op->type->prop) && (but->type == UI_BTYPE_TEXT)) {
UI_but_focus_on_enter_event(CTX_wm_window(C), but);
}
}
}
}
return return_info;
}
static void template_operator_property_buts_draw_recursive(const bContext *C,
wmOperator *op,
uiLayout *layout,
const eButLabelAlign label_align,
int layout_flags,
bool *r_has_advanced)
{
if (op->type->flag & OPTYPE_MACRO) {
LISTBASE_FOREACH (wmOperator *, macro_op, &op->macro) {
template_operator_redo_property_buts_draw(C, macro_op, layout, layout_flags, r_has_advanced);
template_operator_property_buts_draw_recursive(
C, macro_op, layout, label_align, layout_flags, r_has_advanced);
}
}
else {
/* Might want to make label_align adjustable somehow. */
eAutoPropButsReturn return_info = uiTemplateOperatorPropertyButs(
C, layout, op, UI_BUT_LABEL_ALIGN_NONE, layout_flags);
eAutoPropButsReturn return_info = template_operator_property_buts_draw_single(
C, op, layout, label_align, layout_flags);
if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) {
if (r_has_advanced) {
*r_has_advanced = true;
@ -2451,6 +2626,61 @@ static void template_operator_redo_property_buts_draw(
}
}
static bool ui_layout_operator_properties_only_booleans(const bContext *C,
wmWindowManager *wm,
wmOperator *op,
int layout_flags)
{
if (op->type->flag & OPTYPE_MACRO) {
LISTBASE_FOREACH (wmOperator *, macro_op, &op->macro) {
if (!ui_layout_operator_properties_only_booleans(C, wm, macro_op, layout_flags)) {
return false;
}
}
}
else {
uiTemplateOperatorPropertyPollParam user_data = {.C = C, .op = op, .flag = layout_flags};
PointerRNA ptr;
RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
RNA_STRUCT_BEGIN (&ptr, prop) {
if (RNA_property_flag(prop) & PROP_HIDDEN) {
continue;
}
if (op->type->poll_property &&
!ui_layout_operator_buts_poll_property(&ptr, prop, &user_data)) {
continue;
}
if (RNA_property_type(prop) != PROP_BOOLEAN) {
return false;
}
}
RNA_STRUCT_END;
}
return true;
}
/**
* Draw Operator property buttons for redoing execution with different settings.
* This function does not initialize the layout,
* functions can be called on the layout before and after.
*/
void uiTemplateOperatorPropertyButs(
const bContext *C, uiLayout *layout, wmOperator *op, eButLabelAlign label_align, short flag)
{
wmWindowManager *wm = CTX_wm_manager(C);
/* If there are only checkbox items, don't use split layout by default. It looks weird if the
* checkboxes only use half the width. */
if (ui_layout_operator_properties_only_booleans(C, wm, op, flag)) {
flag |= UI_TEMPLATE_OP_PROPS_NO_SPLIT_LAYOUT;
}
template_operator_property_buts_draw_recursive(C, op, layout, label_align, flag, NULL);
}
void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
{
wmOperator *op = WM_operator_last_redo(C);
@ -2483,8 +2713,8 @@ void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
#endif
UI_block_func_handle_set(block, ED_undo_operator_repeat_cb_evt, op);
template_operator_redo_property_buts_draw(
C, op, layout, layout_flags, NULL /* &has_advanced */);
template_operator_property_buts_draw_recursive(
C, op, layout, UI_BUT_LABEL_ALIGN_NONE, layout_flags, NULL /* &has_advanced */);
/* Warning! this leaves the handle function for any other users of this block. */
#if 0
@ -7343,187 +7573,6 @@ void uiTemplateMenuSearch(uiLayout *layout)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operator Redo Properties Template
* \{ */
#ifdef USE_OP_RESET_BUT
static void ui_layout_operator_buts__reset_cb(bContext *UNUSED(C),
void *op_pt,
void *UNUSED(arg_dummy2))
{
WM_operator_properties_reset((wmOperator *)op_pt);
}
#endif
struct uiTemplateOperatorPropertyPollParam {
const bContext *C;
wmOperator *op;
short flag;
};
static bool ui_layout_operator_buts_poll_property(struct PointerRNA *UNUSED(ptr),
struct PropertyRNA *prop,
void *user_data)
{
struct uiTemplateOperatorPropertyPollParam *params = user_data;
if ((params->flag & UI_TEMPLATE_OP_PROPS_HIDE_ADVANCED) &&
(RNA_property_tags(prop) & OP_PROP_TAG_ADVANCED)) {
return false;
}
return params->op->type->poll_property(params->C, params->op, prop);
}
/**
* Draw Operator property buttons for redoing execution with different settings.
* This function does not initialize the layout,
* functions can be called on the layout before and after.
*/
eAutoPropButsReturn uiTemplateOperatorPropertyButs(const bContext *C,
uiLayout *layout,
wmOperator *op,
const eButLabelAlign label_align,
const short flag)
{
uiBlock *block = uiLayoutGetBlock(layout);
eAutoPropButsReturn return_info = 0;
if (!op->properties) {
IDPropertyTemplate val = {0};
op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
}
/* poll() on this operator may still fail,
* at the moment there is no nice feedback when this happens just fails silently. */
if (!WM_operator_repeat_check(C, op)) {
UI_block_lock_set(block, true, "Operator can't' redo");
return return_info;
}
else {
/* useful for macros where only one of the steps can't be re-done */
UI_block_lock_clear(block);
}
if (flag & UI_TEMPLATE_OP_PROPS_SHOW_TITLE) {
uiItemL(layout, WM_operatortype_name(op->type, op->ptr), ICON_NONE);
}
/* menu */
if (op->type->flag & OPTYPE_PRESET) {
/* XXX, no simple way to get WM_MT_operator_presets.bl_label
* from python! Label remains the same always! */
PointerRNA op_ptr;
uiLayout *row;
block->ui_operator = op;
row = uiLayoutRow(layout, true);
uiItemM(row, "WM_MT_operator_presets", NULL, ICON_NONE);
wmOperatorType *ot = WM_operatortype_find("WM_OT_operator_preset_add", false);
uiItemFullO_ptr(row, ot, "", ICON_ADD, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_string_set(&op_ptr, "operator", op->type->idname);
uiItemFullO_ptr(row, ot, "", ICON_REMOVE, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_string_set(&op_ptr, "operator", op->type->idname);
RNA_boolean_set(&op_ptr, "remove_active", true);
}
if (op->type->ui) {
op->layout = layout;
op->type->ui((bContext *)C, op);
op->layout = NULL;
/* UI_LAYOUT_OP_SHOW_EMPTY ignored. return_info is ignored too. We could
* allow ot.ui callback to return this, but not needed right now. */
}
else {
wmWindowManager *wm = CTX_wm_manager(C);
PointerRNA ptr;
struct uiTemplateOperatorPropertyPollParam user_data = {
.C = C,
.op = op,
.flag = flag,
};
RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
/* main draw call */
return_info = uiDefAutoButsRNA(
layout,
&ptr,
op->type->poll_property ? ui_layout_operator_buts_poll_property : NULL,
op->type->poll_property ? &user_data : NULL,
op->type->prop,
label_align,
(flag & UI_TEMPLATE_OP_PROPS_COMPACT));
if ((return_info & UI_PROP_BUTS_NONE_ADDED) && (flag & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY)) {
uiItemL(layout, IFACE_("No Properties"), ICON_NONE);
}
}
#ifdef USE_OP_RESET_BUT
/* its possible that reset can do nothing if all have PROP_SKIP_SAVE enabled
* but this is not so important if this button is drawn in those cases
* (which isn't all that likely anyway) - campbell */
if (op->properties->len) {
uiBut *but;
uiLayout *col; /* needed to avoid alignment errors with previous buttons */
col = uiLayoutColumn(layout, false);
block = uiLayoutGetBlock(col);
but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
0,
ICON_FILE_REFRESH,
IFACE_("Reset"),
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0.0,
0.0,
0.0,
0.0,
TIP_("Reset operator defaults"));
UI_but_func_set(but, ui_layout_operator_buts__reset_cb, op, NULL);
}
#endif
/* set various special settings for buttons */
/* Only do this if we're not refreshing an existing UI. */
if (block->oldblock == NULL) {
const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0;
uiBut *but;
for (but = block->buttons.first; but; but = but->next) {
/* no undo for buttons for operator redo panels */
UI_but_flag_disable(but, UI_BUT_UNDO);
/* only for popups, see [#36109] */
/* if button is operator's default property, and a text-field, enable focus for it
* - this is used for allowing operators with popups to rename stuff with fewer clicks
*/
if (is_popup) {
if ((but->rnaprop == op->type->prop) && (but->type == UI_BTYPE_TEXT)) {
UI_but_focus_on_enter_event(CTX_wm_window(C), but);
}
}
}
}
return return_info;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Running Jobs Template
* \{ */

View File

@ -294,7 +294,7 @@ eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout,
const bool compact)
{
eAutoPropButsReturn return_info = UI_PROP_BUTS_NONE_ADDED;
uiLayout *split, *col;
uiLayout *col;
const char *name;
RNA_STRUCT_BEGIN (ptr, prop) {
@ -325,19 +325,11 @@ eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout,
}
else {
BLI_assert(label_align == UI_BUT_LABEL_ALIGN_SPLIT_COLUMN);
split = uiLayoutSplit(layout, 0.5f, false);
col = uiLayoutColumn(split, false);
uiItemL(col, (is_boolean) ? "" : name, ICON_NONE);
col = uiLayoutColumn(split, false);
col = uiLayoutColumn(layout, true);
/* Let uiItemFullR() create the split layout. */
uiLayoutSetPropSep(col, true);
}
/* May need to add more cases here.
* don't override enum flag names */
/* name is shown above, empty name for button below */
name = (flag & PROP_ENUM_FLAG || is_boolean) ? NULL : "";
break;
}
case UI_BUT_LABEL_ALIGN_NONE:

View File

@ -2429,7 +2429,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
const bool show_menu_icon = ui_but_draw_menu_icon(but);
float alpha = (float)wcol->text[3] / 255.0f;
char password_str[UI_MAX_DRAW_STR];
bool no_text_padding = false;
bool no_text_padding = but->drawflag & UI_BUT_NO_TEXT_PADDING;
ui_but_text_password_hide(password_str, but, false);
@ -4161,10 +4161,10 @@ static void widget_optionbut(uiWidgetColors *wcol,
/* smaller */
delta = 1 + BLI_rcti_size_y(&recttemp) / 8;
recttemp.xmin += delta;
recttemp.ymin += delta;
recttemp.xmax -= delta;
recttemp.ymax -= delta;
BLI_rcti_resize(
&recttemp, BLI_rcti_size_x(&recttemp) - delta * 2, BLI_rcti_size_y(&recttemp) - delta * 2);
/* Keep one edge in place. */
BLI_rcti_translate(&recttemp, text_before_widget ? delta : -delta, 0);
rad = wcol->roundness * BLI_rcti_size_y(&recttemp);
round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad);
@ -4176,13 +4176,13 @@ static void widget_optionbut(uiWidgetColors *wcol,
widgetbase_draw(&wtb, wcol);
/* text space */
const float offset = BLI_rcti_size_y(rect) * 0.7 + delta;
/* Text space - factor is really just eyeballed. */
const float offset = delta * 0.9;
if (text_before_widget) {
rect->xmax -= offset;
rect->xmax = recttemp.xmin - offset;
}
else {
rect->xmin += offset;
rect->xmin = recttemp.xmax + offset;
}
}
@ -4739,9 +4739,14 @@ void ui_draw_but(const bContext *C, ARegion *region, uiStyle *style, uiBut *but,
case UI_BTYPE_CHECKBOX_N:
if (!(but->flag & UI_HAS_ICON)) {
wt = widget_type(UI_WTYPE_CHECKBOX);
if ((but->drawflag & (UI_BUT_TEXT_LEFT | UI_BUT_TEXT_RIGHT)) == 0) {
but->drawflag |= UI_BUT_TEXT_LEFT;
}
/* widget_optionbut() carefully sets the text rectangle for fine tuned paddings. If the
* text drawing were to add its own padding, DPI and zoom factor would be applied twice
* in the final padding, so it's difficult to control it. */
but->drawflag |= UI_BUT_NO_TEXT_PADDING;
}
else {
wt = widget_type(UI_WTYPE_TOGGLE);

View File

@ -693,7 +693,7 @@ void RNA_api_ui_layout(StructRNA *srna)
static float node_socket_color_default[] = {0.0f, 0.0f, 0.0f, 1.0f};
/* simple layout specifiers */
func = RNA_def_function(srna, "row", "uiLayoutRow");
func = RNA_def_function(srna, "row", "uiLayoutRowWithHeading");
parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
RNA_def_function_return(func, parm);
RNA_def_function_ui_description(
@ -701,8 +701,14 @@ void RNA_api_ui_layout(StructRNA *srna)
"Sub-layout. Items placed in this sublayout are placed next to each other "
"in a row");
RNA_def_boolean(func, "align", false, "", "Align buttons to each other");
RNA_def_string(func,
"heading",
NULL,
UI_MAX_NAME_STR,
"Heading",
"Label to insert into the layout for this row");
func = RNA_def_function(srna, "column", "uiLayoutColumn");
func = RNA_def_function(srna, "column", "uiLayoutColumnWithHeading");
parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
RNA_def_function_return(func, parm);
RNA_def_function_ui_description(
@ -710,6 +716,12 @@ void RNA_api_ui_layout(StructRNA *srna)
"Sub-layout. Items placed in this sublayout are placed under each other "
"in a column");
RNA_def_boolean(func, "align", false, "", "Align buttons to each other");
RNA_def_string(func,
"heading",
NULL,
UI_MAX_NAME_STR,
"Heading",
"Label to insert into the layout for this column");
func = RNA_def_function(srna, "column_flow", "uiLayoutColumnFlow");
RNA_def_int(func, "columns", 0, 0, INT_MAX, "", "Number of columns, 0 is automatic", 0, INT_MAX);
@ -887,6 +899,20 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
api_ui_item_common(func);
func = RNA_def_function(srna, "prop_decorator", "uiItemDecoratorR");
api_ui_item_rna_common(func);
RNA_def_int(func,
"index",
/* RNA_NO_INDEX == -1 */
-1,
-2,
INT_MAX,
"",
"The index of this button, when set a single member of an array can be accessed, "
"when set to -1 all array members are used",
-2,
INT_MAX);
for (int is_menu_hold = 0; is_menu_hold < 2; is_menu_hold++) {
func = (is_menu_hold) ? RNA_def_function(srna, "operator_menu_hold", "rna_uiItemOMenuHold") :
RNA_def_function(srna, "operator", "rna_uiItemO");

View File

@ -1349,17 +1349,8 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *region, void *arg_op)
}
uiLayout *col = uiLayoutColumn(layout, false);
if (op->type->flag & OPTYPE_MACRO) {
for (op = op->macro.first; op; op = op->next) {
uiTemplateOperatorPropertyButs(
C, col, op, UI_BUT_LABEL_ALIGN_NONE, UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
}
}
else {
uiTemplateOperatorPropertyButs(
C, col, op, UI_BUT_LABEL_ALIGN_NONE, UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
}
uiTemplateOperatorPropertyButs(
C, col, op, UI_BUT_LABEL_ALIGN_NONE, UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
UI_block_bounds_set_popup(block, 6 * U.dpi_fac, NULL);