Anim: treat untyped slot identifiers ("XXSlot") as wildcard
When assigning an Action to an ID, a slot can be automatically
assigned as well. This behaviour is now extended by making untyped
slot identifiers (like `XXSlot`) act as wildcards.
If the last-used slot identifier was 'untyped' (like `XXSlot`), and a
slot with the same name that is specific to the animated ID's type
exists, that slot will be chosen.
Similarly, if the last-used slot identifier was 'typed' (like
`OBSlot`), a slot `OBSlot` does NOT exist, but an untyped slot with
the same name exists (like `XXSlot`), that one will be chosen.
If there is any ambiguity in the matter, the more specific slot is
chosen. In other words, in this case:
- last_slot_identifier = `XXSlot`
- both `XXSlot` and `OBSlot` exist on the Action (where `OB`
represents the ID type of `animated_id`).
the `OBSlot` should be chosen. This means that `XXSlot` NOT being
auto-assigned if there is an alternative. Since untyped slots are
bound on assignment, this design keeps the Action as-is, which means
that the `XXSlot` remains untyped and thus the user is free to assign
this to another ID type if desired.
Pull Request: https://projects.blender.org/blender/blender/pulls/133653
This commit is contained in:
@@ -1351,10 +1351,49 @@ Slot *generic_slot_for_autoassign(const ID &animated_id,
|
||||
|
||||
/* Try the slot identifier, if it is set. */
|
||||
if (!last_slot_identifier.is_empty()) {
|
||||
/* If the last-used slot identifier was 'untyped', i.e. started with XX, see if something more
|
||||
* specific to this ID type exists.
|
||||
*
|
||||
* If there is any choice in the matter, the more specific slot is chosen. In other words, in
|
||||
* this case:
|
||||
*
|
||||
* - last_slot_identifier = `XXSlot`
|
||||
* - both `XXSlot` and `OBSlot` exist on the Action (where `OB` represents the ID type of
|
||||
* `animated_id`).
|
||||
*
|
||||
* the `OBSlot` should be chosen. This means that `XXSlot` NOT being auto-assigned if there is
|
||||
* an alternative. Since untyped slots are bound on assignment, this design keeps the Action
|
||||
* as-is, which means that the `XXSlot` remains untyped and thus the user is free to assign
|
||||
* this to another ID type if desired. */
|
||||
|
||||
const bool last_used_identifier_is_typed = last_slot_identifier.substr(0, 2) !=
|
||||
slot_untyped_prefix;
|
||||
if (!last_used_identifier_is_typed) {
|
||||
const std::string with_idtype_prefix = StringRef(animated_id.name, 2) +
|
||||
last_slot_identifier.substr(2);
|
||||
Slot *slot = action.slot_find_by_identifier(with_idtype_prefix);
|
||||
if (slot && slot->is_suitable_for(animated_id)) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
|
||||
/* See if the actual last-used slot identifier can be matched. */
|
||||
Slot *slot = action.slot_find_by_identifier(last_slot_identifier);
|
||||
if (slot && slot->is_suitable_for(animated_id)) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* If the last-used slot identifier was IDSomething, and XXSomething exists (where ID = the
|
||||
* ID code of the animated ID), fall back to the XX. If slot `IDSomething` existed, the code
|
||||
* above would have already returned it. */
|
||||
if (last_used_identifier_is_typed) {
|
||||
const std::string with_untyped_prefix = StringRef(slot_untyped_prefix) +
|
||||
last_slot_identifier.substr(2);
|
||||
Slot *slot = action.slot_find_by_identifier(with_untyped_prefix);
|
||||
if (slot && slot->is_suitable_for(animated_id)) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Search for the ID name (which includes the ID type). */
|
||||
|
||||
@@ -666,6 +666,45 @@ TEST_F(ActionLayersTest, generic_slot_for_autoassign)
|
||||
generic_slot_for_autoassign(cube->id, *this->action, cube->adt->last_slot_identifier));
|
||||
}
|
||||
|
||||
TEST_F(ActionLayersTest, generic_slot_for_autoassign_untyped_wildcarding)
|
||||
{
|
||||
/* Test the untyped slot "wildcard" behaviour, where OBSlot should be chosen when the last slot
|
||||
* identifier was "XXSlot", and vice versa. */
|
||||
|
||||
/* ===
|
||||
* Action has OBSlot, last-used slot is XXSlot. Should pick OBSlot. */
|
||||
AnimData *adt = BKE_animdata_ensure_id(&cube->id);
|
||||
STRNCPY_UTF8(adt->last_slot_identifier, "XXSlot");
|
||||
Slot &ob_slot = action->slot_add_for_id_type(ID_OB);
|
||||
action->slot_identifier_define(ob_slot, "OBSlot");
|
||||
|
||||
EXPECT_EQ(&ob_slot,
|
||||
generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
|
||||
|
||||
/* ===
|
||||
* Action has OBSlot and XXSlot, last-used slot is XXSlot. Should pick OBSlot. */
|
||||
Slot &xx_slot = action->slot_add();
|
||||
action->slot_identifier_define(xx_slot, "XXSlot");
|
||||
ASSERT_FALSE(xx_slot.has_idtype());
|
||||
ASSERT_STREQ("XXSlot", xx_slot.identifier);
|
||||
ASSERT_STREQ("XXSlot", adt->last_slot_identifier);
|
||||
EXPECT_EQ(&ob_slot,
|
||||
generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
|
||||
|
||||
/* ===
|
||||
* Action has OBSlot and XXSlot, last-used slot is OBSlot. Should pick OBSlot. */
|
||||
STRNCPY_UTF8(adt->last_slot_identifier, "OBSlot");
|
||||
EXPECT_EQ(&ob_slot,
|
||||
generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
|
||||
|
||||
/* ===
|
||||
* Action has XXSlot, last-used slot is OBSlot. Should pick XXSlot. */
|
||||
action->slot_remove(ob_slot);
|
||||
ASSERT_STREQ("OBSlot", adt->last_slot_identifier);
|
||||
EXPECT_EQ(&xx_slot,
|
||||
generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
|
||||
}
|
||||
|
||||
TEST_F(ActionLayersTest, active_slot)
|
||||
{
|
||||
{ /* Empty case, no slots exist yet. */
|
||||
|
||||
Reference in New Issue
Block a user