UI: Add property decorator buttons

When use_property_split is enabled, this template adds
buttons to set keyframes, (Alternative to showing color).

See: T54951
This commit is contained in:
Campbell Barton 2018-06-16 14:48:21 +02:00
parent 8f2acda7d7
commit d8c2c63c00
Notes: blender-bot 2023-02-14 09:09:43 +01:00
Referenced by issue #54995, Implement Properties Editor Design
5 changed files with 155 additions and 3 deletions

View File

@ -964,6 +964,7 @@ void uiLayoutSetScaleX(uiLayout *layout, float scale);
void uiLayoutSetScaleY(uiLayout *layout, float scale);
void uiLayoutSetEmboss(uiLayout *layout, char emboss);
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep);
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep);
int uiLayoutGetOperatorContext(uiLayout *layout);
bool uiLayoutGetActive(uiLayout *layout);
@ -976,6 +977,7 @@ float uiLayoutGetScaleX(uiLayout *layout);
float uiLayoutGetScaleY(uiLayout *layout);
int uiLayoutGetEmboss(uiLayout *layout);
bool uiLayoutGetPropSep(uiLayout *layout);
bool uiLayoutGetPropDecorate(uiLayout *layout);
/* layout specifiers */
uiLayout *uiLayoutRow(uiLayout *layout, int align);
@ -1257,5 +1259,7 @@ void UI_widgetbase_draw_cache_end(void);
#define USE_UI_POPOVER_ONCE
bool UI_but_is_tool(const uiBut *but);
#define UI_but_is_decorator(but) \
((but)->func == ui_but_anim_decorate_cb)
#endif /* __UI_INTERFACE_H__ */

View File

@ -101,6 +101,23 @@ void ui_but_anim_flag(uiBut *but, float cfra)
but->flag |= UI_BUT_DRIVEN;
}
}
if (but->next && UI_but_is_decorator(but->next)) {
uiBut *but_decor = but->next;
int flag = but->flag;
if (flag & UI_BUT_DRIVEN) {
but_decor->icon = ICON_AUTO;
}
else if (flag & UI_BUT_ANIMATED_KEY) {
but_decor->icon = ICON_SPACE2;
}
else if (flag & UI_BUT_ANIMATED) {
but_decor->icon = ICON_SPACE3;
}
else {
but_decor->icon = ICON_DOT;
}
}
}
/**
@ -299,3 +316,35 @@ void ui_but_anim_paste_driver(bContext *C)
/* this operator calls UI_context_active_but_prop_get */
WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, NULL);
}
void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy))
{
uiBut *but = arg_but;
but = but->prev;
/* FIXME(campbell), swapping active pointer is weak. */
SWAP(struct uiHandleButtonData *, but->active, but->next->active);
if (but->flag & UI_BUT_DRIVEN) {
/* pass */
/* TODO: report? */
}
else if (but->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", false);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
WM_operator_properties_free(&props_ptr);
}
else {
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", false);
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);
}

View File

@ -814,6 +814,7 @@ bool ui_but_anim_expression_get(uiBut *but, char *str, size_t maxlen);
bool ui_but_anim_expression_set(uiBut *but, const char *str);
bool ui_but_anim_expression_create(uiBut *but, const char *str);
void ui_but_anim_autokey(struct bContext *C, uiBut *but, struct Scene *scene, float cfra);
void ui_but_anim_decorate_cb(struct bContext *C, void *arg_but, void *arg_dummy);
/* interface_eyedropper.c */
struct wmKeyMap *eyedropper_modal_keymap(struct wmKeyConfig *keyconf);

View File

@ -50,6 +50,7 @@
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_screen.h"
#include "BKE_animsys.h"
#include "RNA_access.h"
@ -63,6 +64,10 @@
#include "interface_intern.h"
/* Show an icon button after each RNA button to use to quickly set keyframes,
* this is a way to display animation/driven/override status, see T54951. */
#define UI_PROP_DECORATE
/************************ Structs and Defines *************************/
#define UI_OPERATOR_ERROR_RET(_ot, _opname, return_statement) \
@ -132,6 +137,9 @@ enum {
UI_ITEM_BOX_ITEM = 1 << 2, /* The item is "inside" a box item */
UI_ITEM_PROP_SEP = 1 << 3,
/* 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,
};
typedef struct uiButtonItem {
@ -1477,6 +1485,18 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index
bool is_array;
const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0);
#ifdef UI_PROP_DECORATE
struct {
bool use_prop_decorate;
uiLayout *layout;
uiBut *but;
} ui_decorate = {
.use_prop_decorate = (
((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) &&
(use_prop_sep && ptr->id.data && id_can_have_animdata(ptr->id.data))),
};
#endif /* UI_PROP_DECORATE */
UI_block_layout_set_current(block, layout);
/* retrieve info */
@ -1558,14 +1578,24 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index
/* Split the label / property. */
if (use_prop_sep) {
uiLayout *layout_row = NULL;
#ifdef UI_PROP_DECORATE
if (ui_decorate.use_prop_decorate) {
layout_row = uiLayoutRow(layout, true);
layout_row->space = 0;
}
#endif /* UI_PROP_DECORATE */
if (name[0] == '\0') {
/* Ensure we get a column when text is not set. */
layout = uiLayoutColumn(layout, true);
layout = uiLayoutColumn(layout_row ? layout_row : layout, true);
layout->space = 0;
}
else {
const PropertySubType subtype = RNA_property_subtype(prop);
uiLayout *layout_split = uiLayoutSplit(layout, UI_ITEM_PROP_SEP_DIVIDE, true);
uiLayout *layout_split = uiLayoutSplit(
layout_row ? layout_row : layout,
UI_ITEM_PROP_SEP_DIVIDE, true);
layout_split->space = 0;
uiLayout *layout_sub = uiLayoutColumn(layout_split, true);
layout_sub->space = 0;
@ -1607,6 +1637,15 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index
layout->space = 0;
name = "";
}
#ifdef UI_PROP_DECORATE
if (ui_decorate.use_prop_decorate) {
ui_decorate.layout = uiLayoutColumn(layout_row, true);
ui_decorate.layout->space = 0;
UI_block_layout_set_current(block, layout);
ui_decorate.but = block->buttons.last;
}
#endif /* UI_PROP_DECORATE */
}
/* End split. */
@ -1655,6 +1694,39 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index
UI_but_flag_enable(but, UI_BUT_LIST_ITEM);
}
#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;
uiLayout *layout_col = uiLayoutColumn(ui_decorate.layout, false);
layout_col->space = 0;
layout_col->emboss = UI_EMBOSS_NONE;
int i;
for (i = 0; but_decorate; i++) {
/* 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);
}
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;
}
/* Order the decorator after the button we decorate, this is used so we can always
* do a quick lookup. */
BLI_remlink(&block->buttons, but);
BLI_insertlinkafter(&block->buttons, but_decorate, but);
but_decorate = but->next;
}
BLI_assert(len ? i < len : i == 1);
}
#endif /* UI_PROP_DECORATE */
if (no_bg) {
layout->emboss = prev_emboss;
}
@ -3435,7 +3507,7 @@ 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);
litem->item.flag = (layout->item.flag & (UI_ITEM_PROP_SEP | UI_ITEM_PROP_DECORATE));
BLI_addtail(&layout->items, litem);
}
@ -3700,6 +3772,16 @@ void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
SET_FLAG_FROM_TEST(layout->item.flag, is_sep, UI_ITEM_PROP_SEP);
}
bool uiLayoutGetPropDecorate(uiLayout *layout)
{
return (layout->item.flag & UI_ITEM_PROP_DECORATE) != 0;
}
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
{
SET_FLAG_FROM_TEST(layout->item.flag, is_sep, UI_ITEM_PROP_DECORATE);
}
bool uiLayoutGetActive(uiLayout *layout)
{
return layout->active;
@ -4002,6 +4084,9 @@ uiLayout *UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int s
layout = MEM_callocN(sizeof(uiLayout), "uiLayout");
layout->item.type = ITEM_LAYOUT_ROOT;
/* Only used when 'UI_ITEM_PROP_SEP' is set. */
layout->item.flag = UI_ITEM_PROP_DECORATE;
layout->x = x;
layout->y = y;
layout->root = root;

View File

@ -963,6 +963,16 @@ static void rna_UILayout_property_split_set(PointerRNA *ptr, int value)
uiLayoutSetPropSep(ptr->data, value);
}
static int rna_UILayout_property_decorate_get(PointerRNA *ptr)
{
return uiLayoutGetPropDecorate(ptr->data);
}
static void rna_UILayout_property_decorate_set(PointerRNA *ptr, int value)
{
uiLayoutSetPropDecorate(ptr->data, value);
}
#else /* RNA_RUNTIME */
static void rna_def_ui_layout(BlenderRNA *brna)
@ -1030,6 +1040,9 @@ static void rna_def_ui_layout(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_property_split", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_UILayout_property_split_get", "rna_UILayout_property_split_set");
prop = RNA_def_property(srna, "use_property_decorate", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_UILayout_property_decorate_get", "rna_UILayout_property_decorate_set");
}
static void rna_def_panel(BlenderRNA *brna)