Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow exporting custom resources from/to GDScript, VisualScript, C#, and PluginScript. #48201

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions editor/editor_quick_open.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "editor_quick_open.h"

#include "core/os/keyboard.h"
#include "editor/editor_node.h"

void EditorQuickOpen::popup_dialog(const StringName &p_base, bool p_enable_multi, bool p_dontclear) {
base_type = p_base;
Expand All @@ -57,17 +58,29 @@ void EditorQuickOpen::_build_search_cache(EditorFileSystemDirectory *p_efsd) {

Vector<String> base_types = String(base_type).split(String(","));
for (int i = 0; i < p_efsd->get_file_count(); i++) {
String file_type = p_efsd->get_file_type(i);
String file = p_efsd->get_file_path(i);
String engine_type = p_efsd->get_file_type(i);
// TODO: Fix lack of caching for resource's script's global class name (if applicable).
String script_type;
if (_load_resources) {
Ref<Resource> res = ResourceLoader::load(file);
if (res.is_valid()) {
Ref<Script> scr = res->get_script();
if (scr.is_valid()) {
script_type = scr->get_language()->get_global_class_name(file);
}
}
}
String actual_type = script_type.is_empty() ? engine_type : script_type;
// Iterate all possible base types.
for (String &parent_type : base_types) {
if (ClassDB::is_parent_class(file_type, parent_type)) {
String file = p_efsd->get_file_path(i);
if (ClassDB::is_parent_class(engine_type, parent_type) || EditorNode::get_editor_data().script_class_is_parent(script_type, parent_type)) {
files.push_back(file.substr(6, file.length()));

// Store refs to used icons.
String ext = file.get_extension();
if (!icons.has(ext)) {
icons.insert(ext, get_theme_icon((has_theme_icon(file_type, SNAME("EditorIcons")) ? file_type : String("Object")), SNAME("EditorIcons")));
icons.insert(ext, get_theme_icon((has_theme_icon(actual_type, SNAME("EditorIcons")) ? actual_type : String("Object")), SNAME("EditorIcons")));
}

// Stop testing base types as soon as we got a match.
Expand Down
1 change: 1 addition & 0 deletions editor/editor_quick_open.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class EditorQuickOpen : public ConfirmationDialog {
Tree *search_options = nullptr;
StringName base_type;
bool allow_multi_select = false;
bool _load_resources = true;

Vector<String> files;
OAHashMap<String, Ref<Texture2D>> icons;
Expand Down
146 changes: 91 additions & 55 deletions editor/editor_resource_picker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ void EditorResourcePicker::_update_resource() {
if (edited_resource.is_valid() && edited_resource->get_path().is_resource_file()) {
resource_path = edited_resource->get_path() + "\n";
}
String class_name = _get_resource_type(edited_resource);

if (preview_rect) {
preview_rect->set_texture(Ref<Texture2D>());
Expand All @@ -64,16 +65,20 @@ void EditorResourcePicker::_update_resource() {
assign_button->set_text(TTR("<empty>"));
assign_button->set_tooltip_text("");
} else {
assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), "Object"));
assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), SNAME("Object")));

if (!edited_resource->get_name().is_empty()) {
assign_button->set_text(edited_resource->get_name());
} else if (edited_resource->get_path().is_resource_file()) {
assign_button->set_text(edited_resource->get_path().get_file());
} else {
assign_button->set_text(edited_resource->get_class());
assign_button->set_text(class_name);
}
assign_button->set_tooltip_text(resource_path + TTR("Type:") + " " + edited_resource->get_class());

if (edited_resource->get_path().is_resource_file()) {
resource_path = edited_resource->get_path() + "\n";
}
assign_button->set_tooltip_text(resource_path + TTR("Type:") + " " + class_name);

// Preview will override the above, so called at the end.
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(edited_resource, this, "_update_resource_preview", edited_resource->get_instance_id());
Expand Down Expand Up @@ -134,16 +139,29 @@ void EditorResourcePicker::_file_selected(const String &p_path) {
if (!base_type.is_empty()) {
bool any_type_matches = false;

String res_type = loaded_resource->get_class();
Ref<Script> res_script = loaded_resource->get_script();
bool is_global_class = false;
if (res_script.is_valid()) {
String script_type = EditorNode::get_editor_data().script_class_get_name(res_script->get_path());
if (!script_type.is_empty()) {
is_global_class = true;
res_type = script_type;
}
}

for (int i = 0; i < base_type.get_slice_count(","); i++) {
String base = base_type.get_slice(",", i);
if (loaded_resource->is_class(base)) {
any_type_matches = true;

any_type_matches = is_global_class ? EditorNode::get_editor_data().script_class_is_parent(res_type, base) : loaded_resource->is_class(base);

if (!any_type_matches) {
break;
}
}

if (!any_type_matches) {
EditorNode::get_singleton()->show_warning(vformat(TTR("The selected resource (%s) does not match any type expected for this property (%s)."), loaded_resource->get_class(), base_type));
EditorNode::get_singleton()->show_warning(vformat(TTR("The selected resource (%s) does not match any type expected for this property (%s)."), res_type, base_type));
return;
}
}
Expand Down Expand Up @@ -227,16 +245,19 @@ void EditorResourcePicker::_update_menu_items() {
// Add options to copy/paste resource.
Ref<Resource> cb = EditorSettings::get_singleton()->get_resource_clipboard();
bool paste_valid = false;
if (is_editable()) {
if (cb.is_valid()) {
if (base_type.is_empty()) {
paste_valid = true;
} else {
for (int i = 0; i < base_type.get_slice_count(","); i++) {
if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) {
paste_valid = true;
break;
}
if (is_editable() && cb.is_valid()) {
if (base_type.is_empty()) {
paste_valid = true;
} else {
String res_type = _get_resource_type(cb);

for (int i = 0; i < base_type.get_slice_count(","); i++) {
String base = base_type.get_slice(",", i);

paste_valid = ClassDB::is_parent_class(res_type, base) || EditorNode::get_editor_data().script_class_is_parent(res_type, base);

if (!paste_valid) {
break;
}
}
}
Expand Down Expand Up @@ -281,6 +302,9 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
for (int i = 0; i < base_type.get_slice_count(","); i++) {
String base = base_type.get_slice(",", i);
ResourceLoader::get_recognized_extensions_for_type(base, &extensions);
if (ScriptServer::is_global_class(base)) {
ResourceLoader::get_recognized_extensions_for_type(ScriptServer::get_global_class_native_base(base), &extensions);
}
}

HashSet<String> valid_extensions;
Expand Down Expand Up @@ -408,13 +432,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
Variant obj;

if (ScriptServer::is_global_class(intype)) {
obj = ClassDB::instantiate(ScriptServer::get_global_class_native_base(intype));
if (obj) {
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(intype));
if (script.is_valid()) {
((Object *)obj)->set_script(script);
}
}
obj = EditorNode::get_editor_data().script_class_instance(intype);
} else {
obj = ClassDB::instantiate(intype);
}
Expand Down Expand Up @@ -512,23 +530,40 @@ void EditorResourcePicker::_button_draw() {
void EditorResourcePicker::_button_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;

if (mb.is_valid()) {
if (mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
// Only attempt to update and show the menu if we have
// a valid resource or the Picker is editable, as
// there will otherwise be nothing to display.
if (edited_resource.is_valid() || is_editable()) {
_update_menu_items();

Vector2 pos = get_screen_position() + mb->get_position();
edit_menu->reset_size();
edit_menu->set_position(pos);
edit_menu->popup();
}
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
// Only attempt to update and show the menu if we have
// a valid resource or the Picker is editable, as
// there will otherwise be nothing to display.
if (edited_resource.is_valid() || is_editable()) {
_update_menu_items();

Vector2 pos = get_screen_position() + mb->get_position();
edit_menu->reset_size();
edit_menu->set_position(pos);
edit_menu->popup();
}
}
}

String EditorResourcePicker::_get_resource_type(const Ref<Resource> &p_resource) const {
if (p_resource.is_null()) {
return String();
}
String res_type = p_resource->get_class();

Ref<Script> res_script = p_resource->get_script();
if (res_script.is_null()) {
return res_type;
}

// TODO: Replace with EditorFileSystem when PR #60606 is merged to use cached resource type.
String script_type = EditorNode::get_editor_data().script_class_get_name(res_script->get_path());
if (!script_type.is_empty()) {
res_type = script_type;
}
return res_type;
}

void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<String> *p_vector) const {
Vector<String> allowed_types = base_type.split(",");
int size = allowed_types.size();
Expand All @@ -550,7 +585,9 @@ void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<Strin
List<StringName> allowed_subtypes;

List<StringName> inheriters;
ClassDB::get_inheriters_from_class(base, &inheriters);
if (!ScriptServer::is_global_class(base)) {
ClassDB::get_inheriters_from_class(base, &inheriters);
}
for (const StringName &subtype_name : inheriters) {
p_vector->insert(subtype_name);
allowed_subtypes.push_back(subtype_name);
Expand Down Expand Up @@ -602,32 +639,29 @@ bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const {
}
} else if (drag_data.has("type") && String(drag_data["type"]) == "resource") {
res = drag_data["resource"];
} else if (drag_data.has("type") && String(drag_data["type"]) == "files") {
Vector<String> files = drag_data["files"];

// TODO: Extract the typename of the dropped filepath's resource in a more performant way, without fully loading it.
if (files.size() == 1) {
String file = files[0];
res = ResourceLoader::load(file);
}
}

HashSet<String> allowed_types;
_get_allowed_types(true, &allowed_types);

if (res.is_valid() && _is_type_valid(res->get_class(), allowed_types)) {
return true;
}
if (res.is_valid()) {
String res_type = _get_resource_type(res);

if (res.is_valid() && res->get_script()) {
StringName custom_class = EditorNode::get_singleton()->get_object_custom_type_name(res->get_script());
if (_is_type_valid(custom_class, allowed_types)) {
if (_is_type_valid(res_type, allowed_types)) {
return true;
}
}

if (drag_data.has("type") && String(drag_data["type"]) == "files") {
Vector<String> files = drag_data["files"];

if (files.size() == 1) {
String file = files[0];

String file_type = EditorFileSystem::get_singleton()->get_file_type(file);
if (!file_type.is_empty() && _is_type_valid(file_type, allowed_types)) {
return true;
}
StringName custom_class = EditorNode::get_singleton()->get_object_custom_type_name(res.ptr());
if (_is_type_valid(custom_class, allowed_types)) {
return true;
}
}

Expand Down Expand Up @@ -685,8 +719,10 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
HashSet<String> allowed_types;
_get_allowed_types(false, &allowed_types);

String res_type = _get_resource_type(dropped_resource);

// If the accepted dropped resource is from the extended list, it requires conversion.
if (!_is_type_valid(dropped_resource->get_class(), allowed_types)) {
if (!_is_type_valid(res_type, allowed_types)) {
for (const String &E : allowed_types) {
String at = E.strip_edges();

Expand Down
1 change: 1 addition & 0 deletions editor/editor_resource_picker.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class EditorResourcePicker : public HBoxContainer {
void _button_draw();
void _button_input(const Ref<InputEvent> &p_event);

String _get_resource_type(const Ref<Resource> &p_resource) const;
void _get_allowed_types(bool p_with_convert, HashSet<String> *p_vector) const;
bool _is_drop_valid(const Dictionary &p_drag_data) const;
bool _is_type_valid(const String p_type_name, HashSet<String> p_allowed_types) const;
Expand Down
Loading