Merge pull request #117998 from KoBeWi/rework_to_make_work

Rework copy-pasting section/category values
This commit is contained in:
Thaddeus Crews
2026-04-01 18:10:25 -05:00
2 changed files with 60 additions and 116 deletions
+52 -116
View File
@@ -725,6 +725,28 @@ void EditorProperty::_notification(int p_what) {
get_parent()->connect(SceneStringName(theme_changed), callable_mp(this, &EditorProperty::_update_property_bg));
_update_property_bg();
}
Node *parent = get_parent();
Node *prev_parent = nullptr;
while (parent && parent != inspector) {
if (prev_parent && parent->get_meta(SNAME("_has_categories"), false).operator bool()) {
for (int i = prev_parent->get_index() - 1; i >= 0; i--) {
EditorInspectorCategory *category = Object::cast_to<EditorInspectorCategory>(parent->get_child(i));
if (category) {
category->register_property(this);
break;
}
}
break;
}
EditorInspectorSection *section = Object::cast_to<EditorInspectorSection>(parent);
if (section) {
section->register_property(this);
}
prev_parent = parent;
parent = parent->get_parent();
}
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -1762,6 +1784,13 @@ void EditorInspectorCategory::_notification(int p_what) {
connect(SceneStringName(theme_changed), callable_mp(this, &EditorInspectorCategory::_theme_changed));
} break;
case NOTIFICATION_ENTER_TREE: {
VBoxContainer *parent_vbox = Object::cast_to<VBoxContainer>(get_parent());
if (parent_vbox) {
parent_vbox->set_meta(SNAME("_has_categories"), true);
}
} break;
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
@@ -1928,20 +1957,9 @@ void EditorInspectorCategory::_collect_properties(const Object *p_object, LocalV
void EditorInspectorCategory::_handle_menu_option(int p_option) {
switch (p_option) {
case MENU_COPY_VALUE: {
const Object *object = EditorInterface::get_singleton()->get_inspector()->get_edited_object();
String category_name = info.name;
if (!EditorNode::get_editor_data().is_type_recognized(info.name) && ResourceLoader::exists(info.hint_string, "Script")) {
Ref<Script> scr = ResourceLoader::load(info.hint_string, "Script");
if (scr.is_valid()) {
category_name = scr->get_doc_class_name();
}
}
Dictionary clipboard;
LocalVector<String> properties;
_collect_properties(object, properties);
for (const String &property_name : properties) {
clipboard[property_name] = object->get(property_name);
for (const EditorProperty *property : category_properties) {
clipboard[property->get_edited_property()] = property->get_edited_property_value();
}
EditorInspector::set_property_clipboard(EditorInspector::PropertyClipboard::Type::CATEGORY, clipboard);
} break;
@@ -1950,78 +1968,21 @@ void EditorInspectorCategory::_handle_menu_option(int p_option) {
if (EditorInspector::get_property_clipboard_type() != EditorInspector::PropertyClipboard::Type::CATEGORY) {
break;
}
Object *object = EditorInterface::get_singleton()->get_inspector()->get_edited_object();
const Dictionary clipboard = EditorInspector::get_property_clipboard_value();
String category_name = info.name;
if (!EditorNode::get_editor_data().is_type_recognized(info.name) && ResourceLoader::exists(info.hint_string, "Script")) {
Ref<Script> scr = ResourceLoader::load(info.hint_string, "Script");
if (scr.is_valid()) {
category_name = scr->get_doc_class_name();
}
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (const MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(object)) {
const Node *es = EditorNode::get_singleton()->get_edited_scene();
const String action_name = vformat(TTR("Set category %s on %d nodes"), category_name, multi_node_edit->get_node_count());
ur->create_action(action_name);
for (int i = 0; i < multi_node_edit->get_node_count(); i++) {
const NodePath E = multi_node_edit->get_node(i);
Node *n = es->get_node(E);
if (!n) {
continue;
}
for (const KeyValue<Variant, Variant> &pair : clipboard) {
String property_name = pair.key;
ur->add_do_property(n, property_name, pair.value);
ur->add_undo_property(n, property_name, n->get(property_name));
}
ur->create_action(TTR("Paste category properties"));
for (EditorProperty *property : category_properties) {
if (property->is_read_only()) {
continue;
}
ur->commit_action();
} else if (EditorDebuggerRemoteObjects *remote_objects = Object::cast_to<EditorDebuggerRemoteObjects>(object)) {
const int size = remote_objects->remote_object_ids.size();
ur->create_action(size == 1 ? vformat(TTR("Set category %s"), category_name) : vformat(TTR("Set %s on %d objects"), category_name, size), UndoRedo::MERGE_ENDS);
for (const KeyValue<Variant, Variant> &pair : clipboard) {
const String property_name = pair.key;
String name = property_name;
if (!remote_objects->prop_values.has(name) || String(name).begins_with("Constants/")) {
continue;
}
// Change it back to the real name when fetching.
if (name == "Script") {
name = "script";
} else if (name.begins_with("Metadata/")) {
name = name.replace_first("Metadata/", "metadata/");
}
Dictionary values = remote_objects->prop_values[property_name];
Dictionary old_values = values.duplicate();
for (const uint64_t key : values.keys()) {
values.set(key, pair.value);
}
ur->add_do_method(remote_objects, SNAME("emit_signal"), SNAME("values_edited"), name, values, "");
ur->add_undo_method(remote_objects, SNAME("emit_signal"), SNAME("values_edited"), name, old_values, "");
const StringName &property_name = property->get_edited_property();
if (clipboard.has(property_name)) {
ur->add_do_property(property->get_edited_object(), property_name, clipboard[property_name]);
ur->add_undo_property(property->get_edited_object(), property_name, property->get_edited_property_value());
}
ur->commit_action();
} else {
const String action_name = vformat(TTR("Set category %s on node %s"), category_name, object->get("name"));
ur->create_action(action_name);
for (const KeyValue<Variant, Variant> &pair : clipboard) {
const String property_name = pair.key;
ur->add_do_property(object, property_name, pair.value);
ur->add_undo_property(object, property_name, object->get(property_name));
}
ur->commit_action();
}
ur->commit_action();
} break;
case MENU_OPEN_DOCS: {
@@ -2862,57 +2823,32 @@ void EditorInspectorSection::_collect_properties(LocalVector<String> &r_properti
void EditorInspectorSection::menu_option(int p_option) const {
switch (p_option) {
case MENU_COPY_VALUE: {
LocalVector<String> properties;
Dictionary clipboard;
_collect_properties(properties);
for (const String &property_name : properties) {
clipboard[property_name] = object->get(property_name);
for (const EditorProperty *property : section_properties) {
clipboard[property->get_edited_property()] = property->get_edited_property_value();
}
EditorInspector::set_property_clipboard(EditorInspector::PropertyClipboard::Type::SECTION, clipboard);
} break;
case MENU_PASTE_VALUE: {
if (EditorInspector::get_property_clipboard_type() != EditorInspector::PropertyClipboard::Type::SECTION) {
break;
}
const Dictionary clipboard = EditorInspector::get_property_clipboard_value();
LocalVector<String> properties;
_collect_properties(properties);
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (const MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(object)) {
const Node *es = EditorNode::get_singleton()->get_edited_scene();
const String action_name = vformat(TTR("Set section values on %d nodes"), multi_node_edit->get_node_count());
ur->create_action(action_name);
for (int i = 0; i < multi_node_edit->get_node_count(); i++) {
const NodePath E = multi_node_edit->get_node(i);
Node *n = es->get_node(E);
if (!n) {
continue;
}
for (const String &property_name : properties) {
ur->add_do_property(n, property_name, clipboard[property_name]);
ur->add_undo_property(n, property_name, n->get(property_name));
}
ur->create_action(TTR("Paste section properties"));
for (EditorProperty *property : section_properties) {
if (property->is_read_only()) {
continue;
}
ur->commit_action();
} else {
const String action_name = vformat(TTR("Set section values on node %s"), object->get("name"));
ur->create_action(action_name);
for (const String &property_name : properties) {
ur->add_do_property(object, property_name, clipboard[property_name]);
ur->add_undo_property(object, property_name, object->get(property_name));
const StringName &property_name = property->get_edited_property();
if (clipboard.has(property_name)) {
ur->add_do_property(property->get_edited_object(), property_name, clipboard[property_name]);
ur->add_undo_property(property->get_edited_object(), property_name, property->get_edited_property_value());
}
ur->commit_action();
}
ur->commit_action();
} break;
}
}
+8
View File
@@ -415,6 +415,8 @@ class EditorInspectorCategory : public Control {
bool is_favorite = false;
bool menu_icon_dirty = true;
LocalVector<EditorProperty *> category_properties;
void _collect_properties(const Object *p_object, LocalVector<String> &r_properties) const;
void _handle_menu_option(int p_option);
void _popup_context_menu(const Point2i &p_position);
@@ -434,6 +436,8 @@ public:
void set_property_info(const PropertyInfo &p_info);
void set_doc_class_name(const String &p_name);
void register_property(EditorProperty *p_property) { category_properties.push_back(p_property); }
virtual Size2 get_minimum_size() const override;
virtual Control *make_custom_tooltip(const String &p_text) const override;
@@ -481,6 +485,8 @@ class EditorInspectorSection : public Container {
HashSet<StringName> revertable_properties;
bool can_revert = false;
LocalVector<EditorProperty *> section_properties;
void _test_unfold();
int _get_header_height();
Ref<Texture2D> _get_arrow();
@@ -563,6 +569,8 @@ public:
void _collect_properties(LocalVector<String> &r_properties) const;
void menu_option(int p_option) const;
void register_property(EditorProperty *p_property) { section_properties.push_back(p_property); }
EditorInspectorSection();
~EditorInspectorSection();
};