From e26e96df4b3787881265200c0e0bdc02512a5693 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Tue, 26 Aug 2025 16:25:31 -0700 Subject: [PATCH] Cleanup some things in shader editor code --- editor/shader/shader_create_dialog.cpp | 55 +++++++++++++++----------- editor/shader/shader_create_dialog.h | 1 + editor/shader/shader_editor_plugin.cpp | 28 +++++++------ scene/resources/shader.h | 1 + servers/rendering/shader_types.cpp | 4 ++ 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/editor/shader/shader_create_dialog.cpp b/editor/shader/shader_create_dialog.cpp index 452855829c4..12827beb8ee 100644 --- a/editor/shader/shader_create_dialog.cpp +++ b/editor/shader/shader_create_dialog.cpp @@ -67,19 +67,27 @@ void ShaderCreateDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - static const char *shader_types[3] = { "Shader", "VisualShader", "TextFile" }; - for (int i = 0; i < 3; i++) { - Ref icon = get_editor_theme_icon(shader_types[i]); - if (icon.is_valid()) { - type_menu->set_item_icon(i, icon); - } - } - + _refresh_type_icons(); path_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); } break; } } +void ShaderCreateDialog::_refresh_type_icons() { + for (int i = 0; i < type_menu->get_item_count(); i++) { + const String item_name = type_menu->get_item_text(i); + Ref icon = get_editor_theme_icon(item_name); + if (icon.is_valid()) { + type_menu->set_item_icon(i, icon); + } else { + icon = get_editor_theme_icon("TextFile"); + if (icon.is_valid()) { + type_menu->set_item_icon(i, icon); + } + } + } +} + void ShaderCreateDialog::_update_language_info() { type_data.clear(); @@ -87,16 +95,17 @@ void ShaderCreateDialog::_update_language_info() { ShaderTypeData shader_type_data; if (i == int(SHADER_TYPE_TEXT)) { shader_type_data.use_templates = true; - shader_type_data.extensions.push_back("gdshader"); shader_type_data.default_extension = "gdshader"; } else if (i == int(SHADER_TYPE_INC)) { - shader_type_data.extensions.push_back("gdshaderinc"); shader_type_data.default_extension = "gdshaderinc"; } else { shader_type_data.default_extension = "tres"; } + shader_type_data.extensions.push_back(shader_type_data.default_extension); + if (shader_type_data.default_extension != "tres") { + shader_type_data.extensions.push_back("tres"); + } shader_type_data.extensions.push_back("res"); - shader_type_data.extensions.push_back("tres"); type_data.push_back(shader_type_data); } } @@ -432,31 +441,33 @@ void ShaderCreateDialog::config(const String &p_base_path, bool p_built_in_enabl } String ShaderCreateDialog::_validate_path(const String &p_path) { - String p = p_path.strip_edges(); + ERR_FAIL_COND_V(current_type >= type_data.size(), TTR("Invalid shader type selected.")); + String stripped_file_path = p_path.strip_edges(); - if (p.is_empty()) { + if (stripped_file_path.is_empty()) { return TTR("Path is empty."); } - if (p.get_file().get_basename().is_empty()) { + if (stripped_file_path.get_file().get_basename().is_empty()) { return TTR("Filename is empty."); } - p = ProjectSettings::get_singleton()->localize_path(p); - if (!p.begins_with("res://")) { + stripped_file_path = ProjectSettings::get_singleton()->localize_path(stripped_file_path); + if (!stripped_file_path.begins_with("res://")) { return TTR("Path is not local."); } Ref d = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (d->change_dir(p.get_base_dir()) != OK) { + if (d->change_dir(stripped_file_path.get_base_dir()) != OK) { return TTR("Invalid base path."); } Ref f = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (f->dir_exists(p)) { + if (f->dir_exists(stripped_file_path)) { return TTR("A directory with the same name exists."); } - String extension = p.get_extension(); + const ShaderCreateDialog::ShaderTypeData ¤t_type_data = type_data.get(current_type); + const String file_extension = stripped_file_path.get_extension(); HashSet extensions; List::ConstIterator itr = type_data.begin(); @@ -472,10 +483,10 @@ String ShaderCreateDialog::_validate_path(const String &p_path) { bool match = false; for (const String &ext : extensions) { - if (ext.nocasecmp_to(extension) == 0) { + if (ext.nocasecmp_to(file_extension) == 0) { found = true; - for (const String &type_ext : type_data.get(current_type).extensions) { - if (type_ext.nocasecmp_to(extension) == 0) { + for (const String &type_ext : current_type_data.extensions) { + if (type_ext.nocasecmp_to(file_extension) == 0) { match = true; break; } diff --git a/editor/shader/shader_create_dialog.h b/editor/shader/shader_create_dialog.h index 18f8d7ea54a..d14bcafc309 100644 --- a/editor/shader/shader_create_dialog.h +++ b/editor/shader/shader_create_dialog.h @@ -93,6 +93,7 @@ class ShaderCreateDialog : public ConfirmationDialog { void _mode_changed(int p_mode = 0); void _browse_path(); void _file_selected(const String &p_file); + void _refresh_type_icons(); String _validate_path(const String &p_path); virtual void ok_pressed() override; void _create_new(); diff --git a/editor/shader/shader_editor_plugin.cpp b/editor/shader/shader_editor_plugin.cpp index 53c35ddbadf..12d4ef41171 100644 --- a/editor/shader/shader_editor_plugin.cpp +++ b/editor/shader/shader_editor_plugin.cpp @@ -137,33 +137,37 @@ void ShaderEditorPlugin::edit(Object *p_object) { if (!p_object) { return; } - EditedShader es; - - ShaderInclude *si = Object::cast_to(p_object); - if (si != nullptr) { + // First, check for ShaderInclude. + ShaderInclude *shader_include = Object::cast_to(p_object); + if (shader_include != nullptr) { + // Check if this shader include is already being edited. for (uint32_t i = 0; i < edited_shaders.size(); i++) { - if (edited_shaders[i].shader_inc.ptr() == si) { + if (edited_shaders[i].shader_inc.ptr() == shader_include) { shader_tabs->set_current_tab(i); shader_list->select(i); _switch_to_editor(edited_shaders[i].shader_editor); return; } } - es.shader_inc = Ref(si); + es.shader_inc = Ref(shader_include); es.shader_editor = memnew(TextShaderEditor); - es.shader_editor->edit_shader_include(si); + es.shader_editor->edit_shader_include(shader_include); } else { - Shader *s = Object::cast_to(p_object); + // If it's not a ShaderInclude, check for Shader. + Shader *shader = Object::cast_to(p_object); + ERR_FAIL_NULL_MSG(shader, "ShaderEditorPlugin: Unable to edit object " + p_object->to_string() + " because it is not a Shader or ShaderInclude."); + // Check if this shader is already being edited. for (uint32_t i = 0; i < edited_shaders.size(); i++) { - if (edited_shaders[i].shader.ptr() == s) { + if (edited_shaders[i].shader.ptr() == shader) { shader_tabs->set_current_tab(i); shader_list->select(i); _switch_to_editor(edited_shaders[i].shader_editor); return; } } - es.shader = Ref(s); + // If we did not return, the shader needs to be opened in a new shader editor. + es.shader = Ref(shader); Ref vs = es.shader; if (vs.is_valid()) { es.shader_editor = memnew(VisualShaderEditor); @@ -173,6 +177,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { es.shader_editor->edit_shader(es.shader); } + // TextShaderEditor-specific setup code. TextShaderEditor *text_shader_editor = Object::cast_to(es.shader_editor); if (text_shader_editor) { text_shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list)); @@ -434,7 +439,7 @@ void ShaderEditorPlugin::_make_script_list_context_menu() { } Control *control = shader_tabs->get_tab_control(selected); - bool is_valid_editor_control = Object::cast_to(control) || Object::cast_to(control); + bool is_valid_editor_control = Object::cast_to(control) != nullptr; _setup_popup_menu(is_valid_editor_control ? CONTEXT_VALID_ITEM : CONTEXT, context_menu); @@ -774,6 +779,7 @@ void ShaderEditorPlugin::_update_shader_editor_zoom_factor(CodeTextEditor *p_sha } void ShaderEditorPlugin::_switch_to_editor(ShaderEditor *p_editor) { + ERR_FAIL_NULL(p_editor); if (file_menu->get_parent() != nullptr) { file_menu->get_parent()->remove_child(file_menu); } diff --git a/scene/resources/shader.h b/scene/resources/shader.h index ff8841173cd..b7e6c81b81c 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -41,6 +41,7 @@ class Shader : public Resource { OBJ_SAVE_TYPE(Shader); public: + // Must be kept in sync with the List of shader types in `servers/rendering/shader_types.cpp`. enum Mode { MODE_SPATIAL, MODE_CANVAS_ITEM, diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index bca38384808..edc2c550f96 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -30,6 +30,8 @@ #include "shader_types.h" +#include "scene/resources/shader.h" + const HashMap &ShaderTypes::get_functions(RS::ShaderMode p_mode) const { return shader_modes[p_mode].functions; } @@ -516,11 +518,13 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["EMISSION"] = ShaderLanguage::TYPE_VEC3; shader_modes[RS::SHADER_FOG].functions["fog"].main_function = true; + // Must be kept in sync with the Shader::Mode enum. shader_types_list.push_back("spatial"); shader_types_list.push_back("canvas_item"); shader_types_list.push_back("particles"); shader_types_list.push_back("sky"); shader_types_list.push_back("fog"); + DEV_ASSERT(shader_types_list.size() == Shader::MODE_MAX); for (const String &type : shader_types_list) { shader_types.insert(type);