Add custom icons to script classes.

This commit is contained in:
Will Nations 2018-07-28 22:36:43 -05:00
parent b4f579b5ba
commit 6d9cc032e7
12 changed files with 165 additions and 38 deletions

View File

@ -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() {}
};

View File

@ -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 {

View File

@ -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();
}
///////////

View File

@ -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();
};

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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->());
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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: {

View File

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