Anim: UI template for Action selectors
This PR is just taken over from @dr.sybren with minor comments addressed. Original PR: #125493 Co-authored-by: Sybren A. Stüvel <sybren@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/126561
This commit is contained in:
committed by
Christoph Lendenfeld
parent
02e721275f
commit
51fd355c01
@@ -243,7 +243,7 @@ class DOPESHEET_HT_editor_buttons:
|
||||
|
||||
layout.separator_spacer()
|
||||
|
||||
layout.template_ID(st, "action", new="action.new", unlink="action.unlink")
|
||||
layout.template_action(context.object, new="action.new", unlink="action.unlink")
|
||||
|
||||
# Show slot selector.
|
||||
if context.preferences.experimental.use_animation_baklava:
|
||||
|
||||
@@ -2495,6 +2495,23 @@ void uiTemplateAnyID(uiLayout *layout,
|
||||
const char *propname,
|
||||
const char *proptypename,
|
||||
const char *text);
|
||||
|
||||
/**
|
||||
* Action selector.
|
||||
*
|
||||
* This is a specialisation of #uiTemplateID, hard-coded to assign Actions to the given ID.
|
||||
* Such a specialisation is necessary, as the RNA property (`id.animation_data.action`) does not
|
||||
* exist when the ID's `adt` pointer is `nullptr`. In that case uiTemplateID will not be able
|
||||
* to find the RNA type of that property, which in turn it needs to determine the type of IDs to
|
||||
* show.
|
||||
*/
|
||||
void uiTemplateAction(uiLayout *layout,
|
||||
const bContext *C,
|
||||
ID *id,
|
||||
const char *newop,
|
||||
const char *unlinkop,
|
||||
const char *text);
|
||||
|
||||
/**
|
||||
* Search menu to pick an item from a collection.
|
||||
* A version of uiTemplateID that works for non-ID types.
|
||||
@@ -3236,7 +3253,7 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C,
|
||||
*
|
||||
* This is for browsing and editing the ID-blocks used.
|
||||
*/
|
||||
void UI_context_active_but_prop_get_templateID(bContext *C,
|
||||
void UI_context_active_but_prop_get_templateID(const bContext *C,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop);
|
||||
ID *UI_context_active_but_get_tab_ID(bContext *C);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_cachefile_types.h"
|
||||
#include "DNA_collection_types.h"
|
||||
@@ -44,6 +45,7 @@
|
||||
#include "BLF_api.hh"
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_anim_data.hh"
|
||||
#include "BKE_blender_version.h"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_colorband.hh"
|
||||
@@ -545,7 +547,7 @@ static uiBlock *id_search_menu(bContext *C, ARegion *region, void *arg_litem)
|
||||
|
||||
static void template_id_cb(bContext *C, void *arg_litem, void *arg_event);
|
||||
|
||||
void UI_context_active_but_prop_get_templateID(bContext *C,
|
||||
void UI_context_active_but_prop_get_templateID(const bContext *C,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop)
|
||||
{
|
||||
@@ -1818,6 +1820,54 @@ void uiTemplateID(uiLayout *layout,
|
||||
false);
|
||||
}
|
||||
|
||||
void uiTemplateAction(uiLayout *layout,
|
||||
const bContext *C,
|
||||
ID *id,
|
||||
const char *newop,
|
||||
const char *unlinkop,
|
||||
const char *text)
|
||||
{
|
||||
if (!id_can_have_animdata(id)) {
|
||||
RNA_warning("Cannot show Action selector for non-animatable ID: %s", id->name + 2);
|
||||
return;
|
||||
}
|
||||
|
||||
PropertyRNA *adt_action_prop = RNA_struct_type_find_property(&RNA_AnimData, "action");
|
||||
BLI_assert(adt_action_prop);
|
||||
BLI_assert(RNA_property_type(adt_action_prop) == PROP_POINTER);
|
||||
|
||||
/* Construct a pointer with the animated ID as owner, even when `adt` may be `nullptr`.
|
||||
* This way it is possible to use this RNA pointer to get/set `adt->action`, as that RNA property
|
||||
* has a getter and setter that only need the owner ID and are null-safe regarding the `adt`
|
||||
* itself. */
|
||||
AnimData *adt = BKE_animdata_from_id(id);
|
||||
PointerRNA adt_ptr = RNA_pointer_create(id, &RNA_AnimData, adt);
|
||||
|
||||
/* This must be heap-allocated because template_ID() will call MEM_dupallocN()
|
||||
* on the pointer we pass, and that doesn't like stack-allocated stuff. */
|
||||
TemplateID *template_ui = MEM_cnew<TemplateID>(__func__);
|
||||
BLI_SCOPED_DEFER([&]() { MEM_freeN(template_ui); });
|
||||
template_ui->ptr = adt_ptr;
|
||||
template_ui->prop = adt_action_prop;
|
||||
template_ui->prv_rows = 0;
|
||||
template_ui->prv_cols = 0;
|
||||
template_ui->scale = 1.0f;
|
||||
template_ui->filter = UI_TEMPLATE_ID_FILTER_ALL;
|
||||
|
||||
int flag = UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE;
|
||||
if (newop) {
|
||||
flag |= UI_ID_ADD_NEW;
|
||||
}
|
||||
|
||||
template_ui->idcode = ID_AC;
|
||||
template_ui->idlb = which_libbase(CTX_data_main(C), ID_AC);
|
||||
BLI_assert(template_ui->idlb);
|
||||
|
||||
uiLayout *row = uiLayoutRow(layout, true);
|
||||
template_ID(
|
||||
C, row, template_ui, &RNA_Action, flag, newop, nullptr, unlinkop, text, false, false);
|
||||
}
|
||||
|
||||
void uiTemplateIDBrowse(uiLayout *layout,
|
||||
bContext *C,
|
||||
PointerRNA *ptr,
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_interface_c.hh"
|
||||
|
||||
#include "action_intern.hh"
|
||||
|
||||
@@ -52,7 +53,29 @@
|
||||
|
||||
AnimData *ED_actedit_animdata_from_context(const bContext *C, ID **r_adt_id_owner)
|
||||
{
|
||||
{ /* Support use from the layout.template_action() UI template. */
|
||||
PointerRNA ptr = {nullptr};
|
||||
PropertyRNA *prop = nullptr;
|
||||
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
|
||||
/* template_action() sets a RNA_AnimData pointer, whereas other code may set
|
||||
* other pointer types. This code here only deals with the former. */
|
||||
if (prop && ptr.type == &RNA_AnimData) {
|
||||
if (!RNA_property_editable(&ptr, prop)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (r_adt_id_owner) {
|
||||
*r_adt_id_owner = ptr.owner_id;
|
||||
}
|
||||
AnimData *adt = static_cast<AnimData *>(ptr.data);
|
||||
return adt;
|
||||
}
|
||||
}
|
||||
|
||||
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
|
||||
if (!saction) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
AnimData *adt = nullptr;
|
||||
|
||||
@@ -159,6 +182,15 @@ static void actedit_change_action(bContext *C, bAction *act)
|
||||
|
||||
static bool action_new_poll(bContext *C)
|
||||
{
|
||||
{ /* Support use from the layout.template_action() UI template. */
|
||||
PointerRNA ptr = {nullptr};
|
||||
PropertyRNA *prop = nullptr;
|
||||
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
|
||||
if (prop) {
|
||||
return RNA_property_editable(&ptr, prop);
|
||||
}
|
||||
}
|
||||
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
/* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
|
||||
@@ -569,6 +601,7 @@ void ACTION_OT_stash_and_create(wmOperatorType *ot)
|
||||
void ED_animedit_unlink_action(
|
||||
bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports, bool force_delete)
|
||||
{
|
||||
BLI_assert(id);
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
|
||||
/* If the old action only has a single user (that it's about to lose),
|
||||
@@ -654,6 +687,20 @@ void ED_animedit_unlink_action(
|
||||
|
||||
static bool action_unlink_poll(bContext *C)
|
||||
{
|
||||
{
|
||||
ID *animated_id = nullptr;
|
||||
AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
|
||||
if (animated_id) {
|
||||
if (!BKE_id_is_editable(CTX_data_main(C), animated_id)) {
|
||||
return false;
|
||||
}
|
||||
if (!adt) {
|
||||
return false;
|
||||
}
|
||||
return adt->action != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (ED_operator_action_active(C)) {
|
||||
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
|
||||
AnimData *adt = ED_actedit_animdata_from_context(C, nullptr);
|
||||
@@ -670,11 +717,12 @@ static bool action_unlink_poll(bContext *C)
|
||||
|
||||
static int action_unlink_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
AnimData *adt = ED_actedit_animdata_from_context(C, nullptr);
|
||||
ID *animated_id = nullptr;
|
||||
AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
|
||||
bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
|
||||
|
||||
if (adt && adt->action) {
|
||||
ED_animedit_unlink_action(C, nullptr, adt, adt->action, op->reports, force_delete);
|
||||
ED_animedit_unlink_action(C, animated_id, adt, adt->action, op->reports, force_delete);
|
||||
}
|
||||
|
||||
/* Unlink is also abused to exit NLA tweak mode. */
|
||||
|
||||
@@ -143,10 +143,24 @@ static void rna_AnimData_dependency_update(Main *bmain, Scene *scene, PointerRNA
|
||||
|
||||
static int rna_AnimData_action_editable(const PointerRNA *ptr, const char ** /*r_info*/)
|
||||
{
|
||||
AnimData *adt = (AnimData *)ptr->data;
|
||||
BLI_assert(ptr->type == &RNA_AnimData);
|
||||
AnimData *adt = static_cast<AnimData *>(ptr->data);
|
||||
if (!adt) {
|
||||
return PROP_EDITABLE;
|
||||
}
|
||||
return BKE_animdata_action_editable(adt) ? PROP_EDITABLE : PropertyFlag(0);
|
||||
}
|
||||
|
||||
static PointerRNA rna_AnimData_action_get(PointerRNA *ptr)
|
||||
{
|
||||
ID &animated_id = *ptr->owner_id;
|
||||
animrig::Action *action = animrig::get_action(animated_id);
|
||||
if (!action) {
|
||||
return PointerRNA_NULL;
|
||||
};
|
||||
return RNA_id_pointer_create(&action->id);
|
||||
}
|
||||
|
||||
static void rna_AnimData_action_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/)
|
||||
{
|
||||
# ifdef WITH_ANIM_BAKLAVA
|
||||
@@ -1563,10 +1577,21 @@ static void rna_def_animdata(BlenderRNA *brna)
|
||||
|
||||
/* Active Action */
|
||||
prop = RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "Action");
|
||||
/* this flag as well as the dynamic test must be defined for this to be editable... */
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, nullptr, "rna_AnimData_action_set", nullptr, "rna_Action_id_poll");
|
||||
prop,
|
||||
/* Define a getter that is NULL-safe, so that an RNA_AnimData prop with `ptr->data = nullptr`
|
||||
* can still be used to get the property. In that case it will always return nullptr, of
|
||||
* course, but it won't crash Blender. */
|
||||
"rna_AnimData_action_get",
|
||||
/* Similarly, for the setter, the NULL-safety allows constructing the AnimData struct on
|
||||
* assignment of this "action" property. This is possible because RNA has typed NULL
|
||||
* pointers, and thus it knows which setter to call even when `ptr->data` is NULL. */
|
||||
"rna_AnimData_action_set",
|
||||
nullptr,
|
||||
"rna_Action_id_poll");
|
||||
RNA_def_property_editable_func(prop, "rna_AnimData_action_editable");
|
||||
RNA_def_property_ui_text(prop, "Action", "Active Action for this data-block");
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA_ACTCHANGE, "rna_AnimData_dependency_update");
|
||||
|
||||
@@ -576,6 +576,19 @@ static void rna_uiTemplateAnyID(uiLayout *layout,
|
||||
uiTemplateAnyID(layout, ptr, propname, proptypename, name);
|
||||
}
|
||||
|
||||
static void rna_uiTemplateAction(uiLayout *layout,
|
||||
bContext *C,
|
||||
ID *id,
|
||||
const char *newop,
|
||||
const char *unlinkop,
|
||||
const char *name,
|
||||
const char *text_ctxt,
|
||||
const bool translate)
|
||||
{
|
||||
name = rna_translate_ui_text(name, text_ctxt, nullptr, nullptr, translate);
|
||||
uiTemplateAction(layout, C, id, newop, unlinkop, name);
|
||||
}
|
||||
|
||||
void rna_uiTemplateList(uiLayout *layout,
|
||||
bContext *C,
|
||||
const char *listtype_name,
|
||||
@@ -1716,6 +1729,14 @@ void RNA_api_ui_layout(StructRNA *srna)
|
||||
"",
|
||||
"Optionally limit the items which can be selected");
|
||||
|
||||
func = RNA_def_function(srna, "template_action", "rna_uiTemplateAction");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
parm = RNA_def_pointer(func, "id", "ID", "", "The data-block for which to select an Action");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
|
||||
RNA_def_string(func, "new", nullptr, 0, "", "Operator identifier to create a new ID block");
|
||||
RNA_def_string(func, "unlink", nullptr, 0, "", "Operator identifier to unlink the ID block");
|
||||
api_ui_item_common_text(func);
|
||||
|
||||
func = RNA_def_function(srna, "template_search", "uiTemplateSearch");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
api_ui_item_rna_common(func);
|
||||
|
||||
Reference in New Issue
Block a user