Add more control over ID renaming behavior.

This commit adds low-level logic in BKE to support three behaviors in
case of name conflict when renaming an ID:
1. Always tweak new name of the renamed ID (never modify the other ID
  name).
2. Always set requested name in renamed ID, modifying as needed the
  other ID name.
3. Only modify the other ID name if it shares the same root name with the
  current renamed ID's name.

It also adds quite some changes to IDTemplate, Outliner code, and
RNA-defined UILayout code, and the lower-level UI button API, to allow
for the new behavior defined in the design (i.e. option three from above list).

When renaming from the UI either 'fails' (falls back to adjusted name) or forces
renaming another ID, an INFO report is displayed.

This commit also fixes several issues in existing code, especially
regarding undo handling in rename operations (which could lead to saving
the wrong name in undo step, and/or over-generating undo steps).

API wise, the bahavior when directly assigning a name to the `ID.name`
property remains unchanged (option one from the list above). But a new
API call `ID.rename` has been added, which offers all three behaviors.

Unittests were added to cover the new implemented behaviors (both at
BKE level, and the RNA/Py API).

This commit implements #119139 design.

Pull Request: https://projects.blender.org/blender/blender/pulls/126996
This commit is contained in:
Bastien Montagne
2024-09-20 13:36:50 +02:00
committed by Bastien Montagne
parent b200d1f975
commit 3e03576b09
29 changed files with 731 additions and 95 deletions

View File

@@ -205,8 +205,57 @@ class TestIdRename(TestHelper, unittest.TestCase):
data = dt
break
data.name = name
# This can fail currently, see #71244.
# ~ self.assertEqual(data.name, self.default_name + ".001")
self.assertEqual(data.name, self.default_name + ".001")
self.ensure_proper_order()
def test_rename_api(self):
self.clear_container()
self.add_items_with_randomized_names(100)
self.ensure_proper_order()
name = self.default_name
data = self.data_container[0]
data.name = name
data_other = None
self.assertEqual(data.name, name)
for dt in self.data_container:
if dt is not data:
data_other = dt
break
name_other = "Other_" + name
data_other.name = name_other
data_other.rename(name, mode='NEVER')
self.assertEqual(data.name, name)
self.assertEqual(data_other.name, name + ".001")
self.ensure_proper_order()
data_other.rename(name, mode='NEVER')
self.assertEqual(data.name, name)
self.assertEqual(data_other.name, name + ".001")
self.ensure_proper_order()
data_other.name = name_other
data.name = name
data_other.rename(name, mode='ALWAYS')
self.assertEqual(data.name, name + ".001")
self.assertEqual(data_other.name, name)
self.ensure_proper_order()
data.rename(name, mode='ALWAYS')
self.assertEqual(data.name, name)
self.assertEqual(data_other.name, name + ".001")
self.ensure_proper_order()
data_other.name = name_other
data.name = name
data_other.rename(name, mode='SAME_ROOT')
self.assertEqual(data.name, name)
self.assertEqual(data_other.name, name + ".001")
self.ensure_proper_order()
data_other.rename(name, mode='SAME_ROOT')
self.assertEqual(data.name, name + ".001")
self.assertEqual(data_other.name, name)
self.ensure_proper_order()