Browse Source

Add custom icons to script classes.

Will Nations 7 years ago
parent
commit
6d9cc032e7

+ 1 - 1
core/script_language.h

@@ -315,7 +315,7 @@ public:
 	virtual void frame();
 
 	virtual bool handles_global_class_type(const String &p_type) const { return false; }
-	virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const { return String(); }
+	virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const { return String(); }
 
 	virtual ~ScriptLanguage() {}
 };

+ 13 - 18
editor/create_dialog.cpp

@@ -157,6 +157,15 @@ Ref<Texture> CreateDialog::_get_editor_icon(const String &p_type) const {
 		return get_icon(p_type, "EditorIcons");
 	}
 
+	if (ScriptServer::is_global_class(p_type)) {
+		RES icon = ResourceLoader::load(EditorNode::get_editor_data().script_class_get_icon_path(p_type));
+		if (icon.is_valid())
+			return icon;
+		icon = get_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons");
+		if (icon.is_valid())
+			return icon;
+	}
+
 	const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types();
 	for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) {
 		const Vector<EditorData::CustomType> &ct = E->value();
@@ -180,7 +189,7 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p
 		return;
 
 	bool cpp_type = ClassDB::class_exists(p_type);
-	EditorData &ed = EditorNode::get_singleton()->get_editor_data();
+	EditorData &ed = EditorNode::get_editor_data();
 
 	if (p_type == base_type)
 		return;
@@ -262,13 +271,7 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p
 	const String &description = EditorHelp::get_doc_data()->class_list[p_type].brief_description;
 	item->set_tooltip(0, description);
 
-	if (cpp_type && has_icon(p_type, "EditorIcons")) {
-
-		item->set_icon(0, get_icon(p_type, "EditorIcons"));
-	} else if (!cpp_type && has_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons")) {
-
-		item->set_icon(0, get_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons"));
-	}
+	item->set_icon(0, _get_editor_icon(p_type));
 
 	p_types[p_type] = item;
 }
@@ -287,7 +290,7 @@ void CreateDialog::_update_search() {
 	HashMap<String, TreeItem *> types;
 
 	TreeItem *root = search_options->create_item();
-	EditorData &ed = EditorNode::get_singleton()->get_editor_data();
+	EditorData &ed = EditorNode::get_editor_data();
 
 	root->set_text(0, base_type);
 	if (has_icon(base_type, "EditorIcons")) {
@@ -490,16 +493,8 @@ Object *CreateDialog::instance_selected() {
 			custom = md;
 
 		if (custom != String()) {
-
 			if (ScriptServer::is_global_class(custom)) {
-				RES script = ResourceLoader::load(ScriptServer::get_global_class_path(custom));
-				ERR_FAIL_COND_V(!script.is_valid(), NULL);
-
-				Object *obj = ClassDB::instance(ScriptServer::get_global_class_base(custom));
-				ERR_FAIL_COND_V(!obj, NULL);
-
-				obj->set_script(script.get_ref_ptr());
-				return obj;
+				return EditorNode::get_editor_data().script_class_instance(custom);
 			}
 			return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom);
 		} else {

+ 45 - 0
editor/editor_data.cpp

@@ -888,11 +888,56 @@ StringName EditorData::script_class_get_base(const String &p_class) {
 	return script->get_language()->get_global_class_name(base_script->get_path());
 }
 
+Object *EditorData::script_class_instance(const String &p_class) {
+	if (ScriptServer::is_global_class(p_class)) {
+		Object *obj = ClassDB::instance(ScriptServer::get_global_class_base(p_class));
+		if (obj) {
+			RES script = ResourceLoader::load(ScriptServer::get_global_class_path(p_class));
+			if (script.is_valid())
+				obj->set_script(script.get_ref_ptr());
+
+			RES icon = ResourceLoader::load(script_class_get_icon_path(p_class));
+			if (icon.is_valid())
+				obj->set_meta("_editor_icon", icon);
+
+			return obj;
+		}
+	}
+	return NULL;
+}
+
+void EditorData::script_class_save_icon_paths() {
+	List<StringName> keys;
+	_script_class_icon_paths.get_key_list(&keys);
+
+	Dictionary d;
+	for (List<StringName>::Element *E = keys.front(); E; E = E->next()) {
+		d[E->get()] = _script_class_icon_paths[E->get()];
+	}
+
+	ProjectSettings::get_singleton()->set("_global_script_class_icons", d);
+	ProjectSettings::get_singleton()->save();
+}
+
+void EditorData::script_class_load_icon_paths() {
+	script_class_clear_icon_paths();
+
+	Dictionary d = ProjectSettings::get_singleton()->get("_global_script_class_icons");
+	List<Variant> keys;
+	d.get_key_list(&keys);
+
+	for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+		String key = E->get().operator String();
+		_script_class_icon_paths[key] = d[key];
+	}
+}
+
 EditorData::EditorData() {
 
 	current_edited_scene = -1;
 
 	//load_imported_scenes_from_globals();
+	script_class_load_icon_paths();
 }
 
 ///////////

+ 8 - 0
editor/editor_data.h

@@ -146,6 +146,8 @@ private:
 
 	bool _find_updated_instances(Node *p_root, Node *p_node, Set<String> &checked_paths);
 
+	HashMap<StringName, String> _script_class_icon_paths;
+
 public:
 	EditorPlugin *get_editor(Object *p_object);
 	EditorPlugin *get_subeditor(Object *p_object);
@@ -213,6 +215,12 @@ public:
 
 	bool script_class_is_parent(const String &p_class, const String &p_inherits);
 	StringName script_class_get_base(const String &p_class);
+	Object *script_class_instance(const String &p_class);
+	String script_class_get_icon_path(const String &p_class) const { return _script_class_icon_paths.has(p_class) ? _script_class_icon_paths[p_class] : String(); }
+	void script_class_set_icon_path(const String &p_class, const String &p_icon_path) { _script_class_icon_paths[p_class] = p_icon_path; }
+	void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); }
+	void script_class_save_icon_paths();
+	void script_class_load_icon_paths();
 
 	EditorData();
 };

+ 19 - 8
editor/editor_file_system.cpp

@@ -133,6 +133,10 @@ String EditorFileSystemDirectory::get_file_script_class_extends(int p_idx) const
 	return files[p_idx]->script_class_extends;
 }
 
+String EditorFileSystemDirectory::get_file_script_class_icon_path(int p_idx) const {
+	return files[p_idx]->script_class_icon_path;
+}
+
 StringName EditorFileSystemDirectory::get_file_type(int p_idx) const {
 
 	ERR_FAIL_INDEX_V(p_idx, files.size(), "");
@@ -233,6 +237,7 @@ void EditorFileSystem::_scan_filesystem() {
 				fc.import_valid = split[4].to_int64() != 0;
 				fc.script_class_name = split[5].get_slice("<>", 0);
 				fc.script_class_extends = split[5].get_slice("<>", 1);
+				fc.script_class_icon_path = split[5].get_slice("<>", 2);
 
 				String deps = split[6].strip_edges();
 				if (deps.length()) {
@@ -721,6 +726,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
 				fi->import_valid = fc->import_valid;
 				fi->script_class_name = fc->script_class_name;
 				fi->script_class_extends = fc->script_class_extends;
+				fi->script_class_icon_path = fc->script_class_icon_path;
 
 				if (fc->type == String()) {
 					fi->type = ResourceLoader::get_resource_type(path);
@@ -731,7 +737,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
 			} else {
 
 				fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path);
-				fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
+				fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
 				fi->modified_time = 0;
 				fi->import_modified_time = 0;
 				fi->import_valid = ResourceLoader::is_import_valid(path);
@@ -753,10 +759,11 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
 				fi->import_valid = true;
 				fi->script_class_name = fc->script_class_name;
 				fi->script_class_extends = fc->script_class_extends;
+				fi->script_class_icon_path = fc->script_class_icon_path;
 			} else {
 				//new or modified time
 				fi->type = ResourceLoader::get_resource_type(path);
-				fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
+				fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
 				fi->deps = _get_dependencies(path);
 				fi->modified_time = mt;
 				fi->import_modified_time = 0;
@@ -855,7 +862,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
 					fi->modified_time = FileAccess::get_modified_time(path);
 					fi->import_modified_time = 0;
 					fi->type = ResourceLoader::get_resource_type(path);
-					fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
+					fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
 					fi->import_valid = ResourceLoader::is_import_valid(path);
 
 					{
@@ -1110,7 +1117,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
 
 	for (int i = 0; i < p_dir->files.size(); i++) {
 
-		String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends;
+		String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path;
 		s += "::";
 		for (int j = 0; j < p_dir->files[i]->deps.size(); j++) {
 
@@ -1316,19 +1323,22 @@ Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) {
 	return ret;
 }
 
-String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const {
+String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const {
 
 	for (int i = 0; i < ScriptServer::get_language_count(); i++) {
 		if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) {
 			String global_name;
 			String extends;
+			String icon_path;
 
-			global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends);
+			global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends, &icon_path);
 			*r_extends = extends;
+			*r_icon_path = icon_path;
 			return global_name;
 		}
 	}
 	*r_extends = String();
+	*r_icon_path = String();
 	return String();
 }
 
@@ -1346,8 +1356,8 @@ void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) {
 				lang = ScriptServer::get_language(j)->get_name();
 			}
 		}
-
 		ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i));
+		EditorNode::get_editor_data().script_class_set_icon_path(files[i]->script_class_name, files[i]->script_class_icon_path);
 	}
 	for (int i = 0; i < p_dir->get_subdir_count(); i++) {
 		_scan_script_classes(p_dir->get_subdir(i));
@@ -1366,6 +1376,7 @@ void EditorFileSystem::update_script_classes() {
 	}
 
 	ScriptServer::save_global_classes();
+	EditorNode::get_editor_data().script_class_save_icon_paths();
 	emit_signal("script_classes_updated");
 }
 
@@ -1438,7 +1449,7 @@ void EditorFileSystem::update_file(const String &p_file) {
 	}
 
 	fs->files[cpos]->type = type;
-	fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends);
+	fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path);
 	fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
 	fs->files[cpos]->deps = _get_dependencies(p_file);
 	fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file);

+ 4 - 1
editor/editor_file_system.h

@@ -60,6 +60,7 @@ class EditorFileSystemDirectory : public Object {
 		bool verified; //used for checking changes
 		String script_class_name;
 		String script_class_extends;
+		String script_class_icon_path;
 	};
 
 	struct FileInfoSort {
@@ -90,6 +91,7 @@ public:
 	bool get_file_import_is_valid(int p_idx) const;
 	String get_file_script_class_name(int p_idx) const; //used for scripts
 	String get_file_script_class_extends(int p_idx) const; //used for scripts
+	String get_file_script_class_icon_path(int p_idx) const; //used for scripts
 
 	EditorFileSystemDirectory *get_parent();
 
@@ -163,6 +165,7 @@ class EditorFileSystem : public Node {
 		bool import_valid;
 		String script_class_name;
 		String script_class_extends;
+		String script_class_icon_path;
 	};
 
 	HashMap<String, FileCache> file_cache;
@@ -225,7 +228,7 @@ class EditorFileSystem : public Node {
 	volatile bool update_script_classes_queued;
 	void _queue_update_script_classes();
 
-	String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const;
+	String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
 
 protected:
 	void _notification(int p_what);

+ 15 - 3
editor/property_editor.cpp

@@ -287,7 +287,11 @@ void CustomPropertyEditor::_menu_option(int p_which) {
 					Object *obj = ClassDB::instance(intype);
 
 					if (!obj) {
-						obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+						if (ScriptServer::is_global_class(intype)) {
+							obj = EditorNode::get_editor_data().script_class_instance(intype);
+						} else {
+							obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+						}
 					}
 
 					ERR_BREAK(!obj);
@@ -1132,7 +1136,11 @@ void CustomPropertyEditor::_type_create_selected(int p_idx) {
 		Object *obj = ClassDB::instance(intype);
 
 		if (!obj) {
-			obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+			if (ScriptServer::is_global_class(intype)) {
+				obj = EditorNode::get_editor_data().script_class_instance(intype);
+			} else {
+				obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+			}
 		}
 
 		ERR_FAIL_COND(!obj);
@@ -1334,7 +1342,11 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
 					Object *obj = ClassDB::instance(intype);
 
 					if (!obj) {
-						obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+						if (ScriptServer::is_global_class(intype)) {
+							obj = EditorNode::get_editor_data().script_class_instance(intype);
+						} else {
+							obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+						}
 					}
 
 					ERR_BREAK(!obj);

+ 25 - 5
editor/scene_tree_dock.cpp

@@ -365,10 +365,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 				for (int i = 0; i < ScriptServer::get_language_count(); i++) {
 					ScriptLanguage *l = ScriptServer::get_language(i);
 					if (l->get_type() == existing->get_class()) {
-						if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) {
-							String name = l->get_global_class_name(existing->get_path(), NULL);
-							inherits = editor->get_editor_data().script_class_get_base(name);
-						} else if (l->can_inherit_from_file()) {
+						String name = l->get_global_class_name(existing->get_path());
+						if (ScriptServer::is_global_class(name)) {
+							if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) {
+								inherits = editor->get_editor_data().script_class_get_base(name);
+							} else if (l->can_inherit_from_file()) {
+								inherits = "\"" + existing->get_path() + "\"";
+							}
+						} else {
 							inherits = "\"" + existing->get_path() + "\"";
 						}
 					}
@@ -395,6 +399,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 					const RefPtr empty;
 					editor_data->get_undo_redo().add_do_method(E->get(), "set_script", empty);
 					editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing);
+
+					editor_data->get_undo_redo().add_do_method(E->get(), "set_meta", "_editor_icon", get_icon(E->get()->get_class(), "EditorIcons"));
+					editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", E->get()->get_meta("_editor_icon"));
 				}
 			}
 
@@ -402,7 +409,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 			editor_data->get_undo_redo().add_undo_method(this, "_update_script_button");
 
 			editor_data->get_undo_redo().commit_action();
-
 		} break;
 		case TOOL_MOVE_UP:
 		case TOOL_MOVE_DOWN: {
@@ -1490,9 +1496,23 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
 		Ref<Script> existing = E->get()->get_script();
 		editor_data->get_undo_redo().add_do_method(E->get(), "set_script", p_script.get_ref_ptr());
 		editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing);
+
+		String icon_path;
+		String name = p_script->get_language()->get_global_class_name(p_script->get_path(), NULL, &icon_path);
+		if (ScriptServer::is_global_class(name)) {
+			RES icon = ResourceLoader::load(icon_path);
+			editor_data->get_undo_redo().add_do_method(E->get(), "set_meta", "_editor_icon", icon);
+			String existing_name = existing.is_valid() ? existing->get_language()->get_global_class_name(existing->get_path()) : String();
+			if (existing.is_null() || !ScriptServer::is_global_class(existing_name)) {
+				editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", get_icon(E->get()->get_class(), "EditorIcons"));
+			} else {
+				editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", editor_data->script_class_get_icon_path(existing_name));
+			}
+		}
 	}
 
 	editor_data->get_undo_redo().commit_action();
+	print_line("test: " + String(Variant(selected.front()->get()->get_meta("_editor_icon"))));
 
 	editor->push_item(p_script.operator->());
 }

+ 7 - 1
modules/gdscript/gdscript.cpp

@@ -1811,7 +1811,7 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
 	return p_type == "GDScript";
 }
 
-String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type) const {
+String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
 
 	PoolVector<uint8_t> sourcef;
 	Error err;
@@ -1876,6 +1876,12 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
 				}
 			}
 		}
+		if (r_icon_path) {
+			if (c->icon_path.is_abs_path())
+				*r_icon_path = c->icon_path;
+			else if (c->icon_path.is_rel_path())
+				*r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
+		}
 		return c->name;
 	}
 

+ 1 - 1
modules/gdscript/gdscript.h

@@ -492,7 +492,7 @@ public:
 	/* GLOBAL CLASSES */
 
 	virtual bool handles_global_class_type(const String &p_type) const;
-	virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const;
+	virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const;
 
 	GDScriptLanguage();
 	~GDScriptLanguage();

+ 26 - 0
modules/gdscript/gdscript_parser.cpp

@@ -3429,6 +3429,32 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 
 				tokenizer->advance(2);
 
+				if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
+					tokenizer->advance();
+
+					if ((tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING)) {
+						Variant constant = tokenizer->get_token_constant();
+						String icon_path = constant.operator String();
+
+						String abs_icon_path = icon_path.is_rel_path() ? self_path.get_base_dir().plus_file(icon_path).simplify_path() : icon_path;
+						if (!FileAccess::exists(abs_icon_path)) {
+							_set_error("No class icon found at: " + abs_icon_path);
+							return;
+						}
+
+						p_class->icon_path = icon_path;
+
+						tokenizer->advance();
+					} else {
+						_set_error("Optional parameter after 'class_name' must be a string constant file path to an icon.");
+						return;
+					}
+
+				} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT) {
+					_set_error("Class icon must be separated by a comma.");
+					return;
+				}
+
 			} break;
 			case GDScriptTokenizer::TK_PR_TOOL: {
 

+ 1 - 0
modules/gdscript/gdscript_parser.h

@@ -149,6 +149,7 @@ public:
 		StringName extends_file;
 		Vector<StringName> extends_class;
 		DataType base_type;
+		String icon_path;
 
 		struct Member {
 			PropertyInfo _export;