Rewrote rename logic to be less buggy and more efficient, fixes #23803 and probably many recent bugs using GraphEdit
This commit is contained in:
parent
8cb54182ad
commit
27d7772381
@ -959,7 +959,9 @@ void Node::set_human_readable_collision_renaming(bool p_enabled) {
|
|||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
String Node::validate_child_name(Node *p_child) {
|
String Node::validate_child_name(Node *p_child) {
|
||||||
|
|
||||||
return _generate_serial_child_name(p_child);
|
StringName name = p_child->data.name;
|
||||||
|
_generate_serial_child_name(p_child, name);
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -972,7 +974,9 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) {
|
|||||||
//this approach to autoset node names is human readable but very slow
|
//this approach to autoset node names is human readable but very slow
|
||||||
//it's turned on while running in the editor
|
//it's turned on while running in the editor
|
||||||
|
|
||||||
p_child->data.name = _generate_serial_child_name(p_child);
|
StringName name = p_child->data.name;
|
||||||
|
_generate_serial_child_name(p_child, name);
|
||||||
|
p_child->data.name = name;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -1008,74 +1012,120 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String Node::_generate_serial_child_name(Node *p_child) {
|
// Return s + 1 as if it were an integer
|
||||||
|
String increase_numeric_string(const String &s) {
|
||||||
|
|
||||||
String name = p_child->data.name;
|
String res = s;
|
||||||
|
bool carry = res.length() > 0;
|
||||||
|
|
||||||
if (name == "") {
|
for (int i = res.length() - 1; i >= 0; i--) {
|
||||||
|
if (!carry) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CharType n = s[i];
|
||||||
|
if (n == '9') { // keep carry as true: 9 + 1
|
||||||
|
res[i] = '0';
|
||||||
|
} else {
|
||||||
|
res[i] = s[i] + 1;
|
||||||
|
carry = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (carry) {
|
||||||
|
res = "1" + res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::_generate_serial_child_name(const Node *p_child, StringName &name) const {
|
||||||
|
|
||||||
|
if (name == StringName()) {
|
||||||
|
//no name and a new nade is needed, create one.
|
||||||
|
|
||||||
name = p_child->get_class();
|
name = p_child->get_class();
|
||||||
// Adjust casing according to project setting. The current type name is expected to be in PascalCase.
|
// Adjust casing according to project setting. The current type name is expected to be in PascalCase.
|
||||||
switch (ProjectSettings::get_singleton()->get("node/name_casing").operator int()) {
|
switch (ProjectSettings::get_singleton()->get("node/name_casing").operator int()) {
|
||||||
case NAME_CASING_PASCAL_CASE:
|
case NAME_CASING_PASCAL_CASE:
|
||||||
break;
|
break;
|
||||||
case NAME_CASING_CAMEL_CASE:
|
case NAME_CASING_CAMEL_CASE: {
|
||||||
name[0] = name.to_lower()[0];
|
String n = name;
|
||||||
break;
|
n[0] = n.to_lower()[0];
|
||||||
|
name = n;
|
||||||
|
} break;
|
||||||
case NAME_CASING_SNAKE_CASE:
|
case NAME_CASING_SNAKE_CASE:
|
||||||
name = name.camelcase_to_underscore(true);
|
name = String(name).camelcase_to_underscore(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//quickly test if proposed name exists
|
||||||
|
int cc = data.children.size(); //children count
|
||||||
|
const Node *const *children_ptr = data.children.ptr();
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
bool exists = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < cc; i++) {
|
||||||
|
if (children_ptr[i] == p_child) { //exclude self in renaming if its already a child
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (children_ptr[i]->data.name == name) {
|
||||||
|
exists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
return; //if it does not exist, it does not need validation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Extract trailing number
|
// Extract trailing number
|
||||||
|
String name_string = name;
|
||||||
String nums;
|
String nums;
|
||||||
for (int i = name.length() - 1; i >= 0; i--) {
|
for (int i = name_string.length() - 1; i >= 0; i--) {
|
||||||
CharType n = name[i];
|
CharType n = name_string[i];
|
||||||
if (n >= '0' && n <= '9') {
|
if (n >= '0' && n <= '9') {
|
||||||
nums = String::chr(name[i]) + nums;
|
nums = String::chr(name_string[i]) + nums;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String nnsep = _get_name_num_separator();
|
String nnsep = _get_name_num_separator();
|
||||||
int num = 0;
|
int name_last_index = name_string.length() - nnsep.length() - nums.length();
|
||||||
bool explicit_zero = false;
|
|
||||||
if (nums.length() > 0 && name.substr(name.length() - nnsep.length() - nums.length(), nnsep.length()) == nnsep) {
|
// Assign the base name + separator to name if we have numbers preceded by a separator
|
||||||
// Base name + Separator + Number
|
if (nums.length() > 0 && name_string.substr(name_last_index, nnsep.length()) == nnsep) {
|
||||||
num = nums.to_int();
|
name_string = name_string.substr(0, name_last_index + nnsep.length()).strip_edges();
|
||||||
name = name.substr(0, name.length() - nnsep.length() - nums.length()); // Keep base name
|
} else {
|
||||||
if (num == 0) {
|
nums = "";
|
||||||
explicit_zero = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_places = nums.length();
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
String attempt = (name + (num > 0 || explicit_zero ? nnsep + itos(num).pad_zeros(num_places) : "")).strip_edges();
|
StringName attempt = name_string + nums;
|
||||||
bool found = false;
|
bool exists = false;
|
||||||
for (int i = 0; i < data.children.size(); i++) {
|
|
||||||
if (data.children[i] == p_child)
|
for (int i = 0; i < cc; i++) {
|
||||||
|
if (children_ptr[i] == p_child) {
|
||||||
continue;
|
continue;
|
||||||
if (data.children[i]->data.name == attempt) {
|
}
|
||||||
found = true;
|
if (children_ptr[i]->data.name == attempt) {
|
||||||
break;
|
exists = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
|
||||||
return attempt;
|
if (!exists) {
|
||||||
|
name = attempt;
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (num == 0) {
|
if (nums.length() == 0) {
|
||||||
if (explicit_zero) {
|
// Name was undecorated so skip to 2 for a more natural result
|
||||||
// Name ended in separator + 0; user expects to get to separator + 1
|
nums = "2";
|
||||||
num = 1;
|
name_string += nnsep; // Add separator because nums.length() > 0 was false
|
||||||
} else {
|
|
||||||
// Name was undecorated so skip to 2 for a more natural result
|
|
||||||
num = 2;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
num++;
|
nums = increase_numeric_string(nums);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ private:
|
|||||||
void _replace_connections_target(Node *p_new_target);
|
void _replace_connections_target(Node *p_new_target);
|
||||||
|
|
||||||
void _validate_child_name(Node *p_child, bool p_force_human_readable = false);
|
void _validate_child_name(Node *p_child, bool p_force_human_readable = false);
|
||||||
String _generate_serial_child_name(Node *p_child);
|
void _generate_serial_child_name(const Node *p_child, StringName &name) const;
|
||||||
|
|
||||||
void _propagate_reverse_notification(int p_notification);
|
void _propagate_reverse_notification(int p_notification);
|
||||||
void _propagate_deferred_notification(int p_notification, bool p_reverse);
|
void _propagate_deferred_notification(int p_notification, bool p_reverse);
|
||||||
|
Loading…
Reference in New Issue
Block a user