Path: blob/master/editor/shader/text_shader_editor.cpp
9896 views
/**************************************************************************/1/* text_shader_editor.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "text_shader_editor.h"3132#include "core/config/project_settings.h"33#include "core/version_generated.gen.h"34#include "editor/editor_node.h"35#include "editor/editor_string_names.h"36#include "editor/file_system/editor_file_system.h"37#include "editor/settings/editor_settings.h"38#include "editor/themes/editor_scale.h"39#include "editor/themes/editor_theme_manager.h"40#include "scene/gui/separator.h"41#include "scene/gui/split_container.h"42#include "servers/rendering/shader_preprocessor.h"43#include "servers/rendering/shader_types.h"4445/*** SHADER SYNTAX HIGHLIGHTER ****/4647Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {48Dictionary color_map;4950for (const Point2i ®ion : disabled_branch_regions) {51if (p_line >= region.x && p_line <= region.y) {52// When "color_regions[0].p_start_key.length() > 2",53// disabled_branch_region causes color_region to break.54// This should be seen as a temporary solution.55CodeHighlighter::_get_line_syntax_highlighting_impl(p_line);5657Dictionary highlighter_info;58highlighter_info["color"] = disabled_branch_color;5960color_map[0] = highlighter_info;61return color_map;62}63}6465return CodeHighlighter::_get_line_syntax_highlighting_impl(p_line);66}6768void GDShaderSyntaxHighlighter::add_disabled_branch_region(const Point2i &p_region) {69ERR_FAIL_COND(p_region.x < 0);70ERR_FAIL_COND(p_region.y < 0);7172for (int i = 0; i < disabled_branch_regions.size(); i++) {73ERR_FAIL_COND_MSG(disabled_branch_regions[i].x == p_region.x, "Branch region with a start line '" + itos(p_region.x) + "' already exists.");74}7576Point2i disabled_branch_region;77disabled_branch_region.x = p_region.x;78disabled_branch_region.y = p_region.y;79disabled_branch_regions.push_back(disabled_branch_region);8081clear_highlighting_cache();82}8384void GDShaderSyntaxHighlighter::clear_disabled_branch_regions() {85disabled_branch_regions.clear();86clear_highlighting_cache();87}8889void GDShaderSyntaxHighlighter::set_disabled_branch_color(const Color &p_color) {90disabled_branch_color = p_color;91clear_highlighting_cache();92}9394/*** SHADER SCRIPT EDITOR ****/9596static bool saved_warnings_enabled = false;97static bool saved_treat_warning_as_errors = false;98static HashMap<ShaderWarning::Code, bool> saved_warnings;99static uint32_t saved_warning_flags = 0U;100101void ShaderTextEditor::_notification(int p_what) {102switch (p_what) {103case NOTIFICATION_THEME_CHANGED: {104if (is_visible_in_tree()) {105_load_theme_settings();106if (warnings.size() > 0 && last_compile_result == OK) {107warnings_panel->clear();108_update_warning_panel();109}110}111} break;112}113}114115Ref<Shader> ShaderTextEditor::get_edited_shader() const {116return shader;117}118119Ref<ShaderInclude> ShaderTextEditor::get_edited_shader_include() const {120return shader_inc;121}122123void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) {124set_edited_shader(p_shader, p_shader->get_code());125}126127void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const String &p_code) {128if (shader == p_shader) {129return;130}131if (shader.is_valid()) {132shader->disconnect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed));133}134shader = p_shader;135shader_inc = Ref<ShaderInclude>();136137set_edited_code(p_code);138139if (shader.is_valid()) {140shader->connect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed));141}142}143144void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc) {145set_edited_shader_include(p_shader_inc, p_shader_inc->get_code());146}147148void ShaderTextEditor::_shader_changed() {149// This function is used for dependencies (include changing changes main shader and forces it to revalidate)150if (block_shader_changed) {151return;152}153dependencies_version++;154_validate_script();155}156157void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc, const String &p_code) {158if (shader_inc == p_shader_inc) {159return;160}161if (shader_inc.is_valid()) {162shader_inc->disconnect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed));163}164shader_inc = p_shader_inc;165shader = Ref<Shader>();166167set_edited_code(p_code);168169if (shader_inc.is_valid()) {170shader_inc->connect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed));171}172}173174void ShaderTextEditor::set_edited_code(const String &p_code) {175_load_theme_settings();176177get_text_editor()->set_text(p_code);178get_text_editor()->clear_undo_history();179callable_mp((TextEdit *)get_text_editor(), &TextEdit::set_h_scroll).call_deferred(0);180callable_mp((TextEdit *)get_text_editor(), &TextEdit::set_v_scroll).call_deferred(0);181get_text_editor()->tag_saved_version();182183_validate_script();184_line_col_changed();185}186187void ShaderTextEditor::reload_text() {188ERR_FAIL_COND(shader.is_null() && shader_inc.is_null());189190String code;191if (shader.is_valid()) {192code = shader->get_code();193} else {194code = shader_inc->get_code();195}196197CodeEdit *te = get_text_editor();198int column = te->get_caret_column();199int row = te->get_caret_line();200int h = te->get_h_scroll();201int v = te->get_v_scroll();202203te->set_text(code);204te->set_caret_line(row);205te->set_caret_column(column);206te->set_h_scroll(h);207te->set_v_scroll(v);208209te->tag_saved_version();210211update_line_and_column();212}213214void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) {215warnings_panel = p_warnings_panel;216}217218void ShaderTextEditor::_load_theme_settings() {219CodeEdit *te = get_text_editor();220Color updated_marked_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color");221if (updated_marked_line_color != marked_line_color) {222for (int i = 0; i < te->get_line_count(); i++) {223if (te->get_line_background_color(i) == marked_line_color) {224te->set_line_background_color(i, updated_marked_line_color);225}226}227marked_line_color = updated_marked_line_color;228}229230syntax_highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color"));231syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color"));232syntax_highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color"));233syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color"));234235syntax_highlighter->clear_keyword_colors();236237const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");238const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");239240List<String> keywords;241ShaderLanguage::get_keyword_list(&keywords);242243for (const String &E : keywords) {244if (ShaderLanguage::is_control_flow_keyword(E)) {245syntax_highlighter->add_keyword_color(E, control_flow_keyword_color);246} else {247syntax_highlighter->add_keyword_color(E, keyword_color);248}249}250251List<String> pp_keywords;252ShaderPreprocessor::get_keyword_list(&pp_keywords, false);253254for (const String &E : pp_keywords) {255syntax_highlighter->add_keyword_color(E, control_flow_keyword_color);256}257258// Colorize built-ins like `COLOR` differently to make them easier259// to distinguish from keywords at a quick glance.260261List<String> built_ins;262263if (shader_inc.is_valid()) {264for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {265for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) {266for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {267built_ins.push_back(F.key);268}269}270271{272const Vector<ShaderLanguage::ModeInfo> &render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));273274for (const ShaderLanguage::ModeInfo &mode_info : render_modes) {275if (!mode_info.options.is_empty()) {276for (const StringName &option : mode_info.options) {277built_ins.push_back(String(mode_info.name) + "_" + String(option));278}279} else {280built_ins.push_back(String(mode_info.name));281}282}283}284285{286const Vector<ShaderLanguage::ModeInfo> &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(i));287288for (const ShaderLanguage::ModeInfo &mode_info : stencil_modes) {289if (!mode_info.options.is_empty()) {290for (const StringName &option : mode_info.options) {291built_ins.push_back(String(mode_info.name) + "_" + String(option));292}293} else {294built_ins.push_back(String(mode_info.name));295}296}297}298}299} else if (shader.is_valid()) {300for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) {301for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {302built_ins.push_back(F.key);303}304}305306{307const Vector<ShaderLanguage::ModeInfo> &shader_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));308309for (const ShaderLanguage::ModeInfo &mode_info : shader_modes) {310if (!mode_info.options.is_empty()) {311for (const StringName &option : mode_info.options) {312built_ins.push_back(String(mode_info.name) + "_" + String(option));313}314} else {315built_ins.push_back(String(mode_info.name));316}317}318}319320{321const Vector<ShaderLanguage::ModeInfo> &stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader->get_mode()));322323for (const ShaderLanguage::ModeInfo &mode_info : stencil_modes) {324if (!mode_info.options.is_empty()) {325for (const StringName &option : mode_info.options) {326built_ins.push_back(String(mode_info.name) + "_" + String(option));327}328} else {329built_ins.push_back(String(mode_info.name));330}331}332}333}334335const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");336337for (const String &E : built_ins) {338syntax_highlighter->add_keyword_color(E, user_type_color);339}340341// Colorize comments.342const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");343syntax_highlighter->clear_color_regions();344syntax_highlighter->add_color_region("/*", "*/", comment_color, false);345syntax_highlighter->add_color_region("//", "", comment_color, true);346347const Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color");348syntax_highlighter->add_color_region("/**", "*/", doc_comment_color, false);349// "/**/" will be treated as the start of the "/**" region, this line is guaranteed to end the color_region.350syntax_highlighter->add_color_region("/**/", "", comment_color, true);351352// Disabled preprocessor branches use translucent text color to be easier to distinguish from comments.353syntax_highlighter->set_disabled_branch_color(Color(EDITOR_GET("text_editor/theme/highlighting/text_color")) * Color(1, 1, 1, 0.5));354355te->clear_comment_delimiters();356te->add_comment_delimiter("/*", "*/", false);357te->add_comment_delimiter("//", "", true);358359if (!te->has_auto_brace_completion_open_key("/*")) {360te->add_auto_brace_completion_pair("/*", "*/");361}362363// Colorize preprocessor include strings.364const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");365syntax_highlighter->add_color_region("\"", "\"", string_color, false);366367if (warnings_panel) {368// Warnings panel.369warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_editor_theme()->get_font(SNAME("main"), EditorStringName(EditorFonts)));370warnings_panel->add_theme_font_size_override("normal_font_size", EditorNode::get_singleton()->get_editor_theme()->get_font_size(SNAME("main_size"), EditorStringName(EditorFonts)));371}372373syntax_highlighter->set_uint_suffix_enabled(true);374}375376void ShaderTextEditor::_check_shader_mode() {377String type = ShaderLanguage::get_shader_type(get_text_editor()->get_text());378379Shader::Mode mode;380381if (type == "canvas_item") {382mode = Shader::MODE_CANVAS_ITEM;383} else if (type == "particles") {384mode = Shader::MODE_PARTICLES;385} else if (type == "sky") {386mode = Shader::MODE_SKY;387} else if (type == "fog") {388mode = Shader::MODE_FOG;389} else {390mode = Shader::MODE_SPATIAL;391}392393if (shader->get_mode() != mode) {394set_block_shader_changed(true);395shader->set_code(get_text_editor()->get_text());396set_block_shader_changed(false);397_load_theme_settings();398}399}400401static ShaderLanguage::DataType _get_global_shader_uniform_type(const StringName &p_variable) {402RS::GlobalShaderParameterType gvt = RS::get_singleton()->global_shader_parameter_get_type(p_variable);403return (ShaderLanguage::DataType)RS::global_shader_uniform_type_get_shader_datatype(gvt);404}405406static String complete_from_path;407408static void _complete_include_paths_search(EditorFileSystemDirectory *p_efsd, List<ScriptLanguage::CodeCompletionOption> *r_options) {409if (!p_efsd) {410return;411}412for (int i = 0; i < p_efsd->get_file_count(); i++) {413if (p_efsd->get_file_type(i) == SNAME("ShaderInclude")) {414String path = p_efsd->get_file_path(i);415if (path.begins_with(complete_from_path)) {416path = path.replace_first(complete_from_path, "");417}418r_options->push_back(ScriptLanguage::CodeCompletionOption(path, ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH));419}420}421for (int j = 0; j < p_efsd->get_subdir_count(); j++) {422_complete_include_paths_search(p_efsd->get_subdir(j), r_options);423}424}425426static void _complete_include_paths(List<ScriptLanguage::CodeCompletionOption> *r_options) {427_complete_include_paths_search(EditorFileSystem::get_singleton()->get_filesystem(), r_options);428}429430void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) {431List<ScriptLanguage::CodeCompletionOption> pp_options;432List<ScriptLanguage::CodeCompletionOption> pp_defines;433ShaderPreprocessor preprocessor;434String code;435String resource_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path());436complete_from_path = resource_path.get_base_dir();437if (!complete_from_path.ends_with("/")) {438complete_from_path += "/";439}440preprocessor.preprocess(p_code, resource_path, code, nullptr, nullptr, nullptr, nullptr, &pp_options, &pp_defines, _complete_include_paths);441complete_from_path = String();442if (pp_options.size()) {443for (const ScriptLanguage::CodeCompletionOption &E : pp_options) {444r_options->push_back(E);445}446return;447}448for (const ScriptLanguage::CodeCompletionOption &E : pp_defines) {449r_options->push_back(E);450}451452ShaderLanguage sl;453String calltip;454ShaderLanguage::ShaderCompileInfo comp_info;455comp_info.global_shader_uniform_type_func = _get_global_shader_uniform_type;456457if (shader.is_null()) {458comp_info.is_include = true;459460sl.complete(code, comp_info, r_options, calltip);461get_text_editor()->set_code_hint(calltip);462return;463}464_check_shader_mode();465comp_info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()));466comp_info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));467comp_info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(shader->get_mode()));468comp_info.shader_types = ShaderTypes::get_singleton()->get_types();469470sl.complete(code, comp_info, r_options, calltip);471get_text_editor()->set_code_hint(calltip);472}473474void ShaderTextEditor::_validate_script() {475emit_signal(CoreStringName(script_changed)); // Ensure to notify that it changed, so it is applied476477String code;478479if (shader.is_valid()) {480_check_shader_mode();481code = shader->get_code();482} else {483code = shader_inc->get_code();484}485486ShaderPreprocessor preprocessor;487String code_pp;488String error_pp;489List<ShaderPreprocessor::FilePosition> err_positions;490List<ShaderPreprocessor::Region> regions;491String filename;492if (shader.is_valid()) {493filename = shader->get_path();494} else if (shader_inc.is_valid()) {495filename = shader_inc->get_path();496}497last_compile_result = preprocessor.preprocess(code, filename, code_pp, &error_pp, &err_positions, ®ions);498499for (int i = 0; i < get_text_editor()->get_line_count(); i++) {500get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));501}502503syntax_highlighter->clear_disabled_branch_regions();504for (const ShaderPreprocessor::Region ®ion : regions) {505if (!region.enabled) {506if (filename != region.file) {507continue;508}509syntax_highlighter->add_disabled_branch_region(Point2i(region.from_line, region.to_line));510}511}512513set_error("");514set_error_count(0);515516if (last_compile_result != OK) {517// Preprocessor error.518ERR_FAIL_COND(err_positions.is_empty());519520String err_text;521const int err_line = err_positions.front()->get().line;522if (err_positions.size() == 1) {523// Error in the main file.524const String message = error_pp.replace("[", "[lb]");525526err_text = vformat(TTR("Error at line %d:"), err_line) + " " + message;527} else {528// Error in an included file.529const String inc_file = err_positions.back()->get().file.get_file();530const int inc_line = err_positions.back()->get().line;531const String message = error_pp.replace("[", "[lb]");532533err_text = vformat(TTR("Error at line %d in include %s:%d:"), err_line, inc_file, inc_line) + " " + message;534set_error_count(err_positions.size() - 1);535}536537set_error(err_text);538set_error_pos(err_line - 1, 0);539540for (int i = 0; i < get_text_editor()->get_line_count(); i++) {541get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));542}543get_text_editor()->set_line_background_color(err_line - 1, marked_line_color);544545set_warning_count(0);546} else {547ShaderLanguage sl;548549sl.enable_warning_checking(saved_warnings_enabled);550uint32_t flags = saved_warning_flags;551if (shader.is_null()) {552if (flags & ShaderWarning::UNUSED_CONSTANT) {553flags &= ~(ShaderWarning::UNUSED_CONSTANT);554}555if (flags & ShaderWarning::UNUSED_FUNCTION) {556flags &= ~(ShaderWarning::UNUSED_FUNCTION);557}558if (flags & ShaderWarning::UNUSED_STRUCT) {559flags &= ~(ShaderWarning::UNUSED_STRUCT);560}561if (flags & ShaderWarning::UNUSED_UNIFORM) {562flags &= ~(ShaderWarning::UNUSED_UNIFORM);563}564if (flags & ShaderWarning::UNUSED_VARYING) {565flags &= ~(ShaderWarning::UNUSED_VARYING);566}567}568sl.set_warning_flags(flags);569570ShaderLanguage::ShaderCompileInfo comp_info;571comp_info.global_shader_uniform_type_func = _get_global_shader_uniform_type;572573if (shader.is_null()) {574comp_info.is_include = true;575} else {576Shader::Mode mode = shader->get_mode();577comp_info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode));578comp_info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode));579comp_info.stencil_modes = ShaderTypes::get_singleton()->get_stencil_modes(RenderingServer::ShaderMode(mode));580comp_info.shader_types = ShaderTypes::get_singleton()->get_types();581}582583code = code_pp;584//compiler error585last_compile_result = sl.compile(code, comp_info);586587if (last_compile_result != OK) {588Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions();589590String err_text;591int err_line;592if (include_positions.size() > 1) {593// Error in an included file.594err_line = include_positions[0].line;595596const String inc_file = include_positions[include_positions.size() - 1].file;597const int inc_line = include_positions[include_positions.size() - 1].line;598const String message = sl.get_error_text().replace("[", "[lb]");599600err_text = vformat(TTR("Error at line %d in include %s:%d:"), err_line, inc_file, inc_line) + " " + message;601set_error_count(include_positions.size() - 1);602} else {603// Error in the main file.604err_line = sl.get_error_line();605606const String message = sl.get_error_text().replace("[", "[lb]");607608err_text = vformat(TTR("Error at line %d:"), err_line) + " " + message;609set_error_count(0);610}611612set_error(err_text);613set_error_pos(err_line - 1, 0);614615get_text_editor()->set_line_background_color(err_line - 1, marked_line_color);616} else {617set_error("");618}619620if (warnings.size() > 0 || last_compile_result != OK) {621warnings_panel->clear();622}623warnings.clear();624for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) {625warnings.push_back(E->get());626}627if (warnings.size() > 0 && last_compile_result == OK) {628warnings.sort_custom<WarningsComparator>();629_update_warning_panel();630} else {631set_warning_count(0);632}633}634635emit_signal(SNAME("script_validated"), last_compile_result == OK); // Notify that validation finished, to update the list of scripts636}637638void ShaderTextEditor::_update_warning_panel() {639int warning_count = 0;640641warnings_panel->push_table(2);642for (const ShaderWarning &w : warnings) {643if (warning_count == 0) {644if (saved_treat_warning_as_errors) {645const String message = (w.get_message() + " " + TTR("Warnings should be fixed to prevent errors.")).replace("[", "[lb]");646const String error_text = vformat(TTR("Error at line %d:"), w.get_line()) + " " + message;647648set_error(error_text);649set_error_pos(w.get_line() - 1, 0);650651get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color);652}653}654655warning_count++;656int line = w.get_line();657658// First cell.659warnings_panel->push_cell();660warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));661if (line != -1) {662warnings_panel->push_meta(line - 1);663warnings_panel->add_text(vformat(TTR("Line %d (%s):"), line, w.get_name()));664warnings_panel->pop(); // Meta goto.665} else {666warnings_panel->add_text(w.get_name() + ":");667}668warnings_panel->pop(); // Color.669warnings_panel->pop(); // Cell.670671// Second cell.672warnings_panel->push_cell();673warnings_panel->add_text(w.get_message());674warnings_panel->pop(); // Cell.675}676warnings_panel->pop(); // Table.677678set_warning_count(warning_count);679}680681void ShaderTextEditor::_bind_methods() {682ADD_SIGNAL(MethodInfo("script_validated", PropertyInfo(Variant::BOOL, "valid")));683}684685ShaderTextEditor::ShaderTextEditor() {686syntax_highlighter.instantiate();687get_text_editor()->set_syntax_highlighter(syntax_highlighter);688}689690/*** SCRIPT EDITOR ******/691692void TextShaderEditor::_menu_option(int p_option) {693code_editor->get_text_editor()->apply_ime();694695switch (p_option) {696case EDIT_UNDO: {697code_editor->get_text_editor()->undo();698} break;699case EDIT_REDO: {700code_editor->get_text_editor()->redo();701} break;702case EDIT_CUT: {703code_editor->get_text_editor()->cut();704} break;705case EDIT_COPY: {706code_editor->get_text_editor()->copy();707} break;708case EDIT_PASTE: {709code_editor->get_text_editor()->paste();710} break;711case EDIT_SELECT_ALL: {712code_editor->get_text_editor()->select_all();713} break;714case EDIT_MOVE_LINE_UP: {715code_editor->get_text_editor()->move_lines_up();716} break;717case EDIT_MOVE_LINE_DOWN: {718code_editor->get_text_editor()->move_lines_down();719} break;720case EDIT_INDENT: {721if (shader.is_null() && shader_inc.is_null()) {722return;723}724code_editor->get_text_editor()->indent_lines();725} break;726case EDIT_UNINDENT: {727if (shader.is_null() && shader_inc.is_null()) {728return;729}730code_editor->get_text_editor()->unindent_lines();731} break;732case EDIT_DELETE_LINE: {733code_editor->get_text_editor()->delete_lines();734} break;735case EDIT_DUPLICATE_SELECTION: {736code_editor->get_text_editor()->duplicate_selection();737} break;738case EDIT_DUPLICATE_LINES: {739code_editor->get_text_editor()->duplicate_lines();740} break;741case EDIT_TOGGLE_WORD_WRAP: {742TextEdit::LineWrappingMode wrap = code_editor->get_text_editor()->get_line_wrapping_mode();743code_editor->get_text_editor()->set_line_wrapping_mode(wrap == TextEdit::LINE_WRAPPING_BOUNDARY ? TextEdit::LINE_WRAPPING_NONE : TextEdit::LINE_WRAPPING_BOUNDARY);744} break;745case EDIT_TOGGLE_COMMENT: {746if (shader.is_null() && shader_inc.is_null()) {747return;748}749code_editor->toggle_inline_comment("//");750} break;751case EDIT_COMPLETE: {752code_editor->get_text_editor()->request_code_completion();753} break;754case SEARCH_FIND: {755code_editor->get_find_replace_bar()->popup_search();756} break;757case SEARCH_FIND_NEXT: {758code_editor->get_find_replace_bar()->search_next();759} break;760case SEARCH_FIND_PREV: {761code_editor->get_find_replace_bar()->search_prev();762} break;763case SEARCH_REPLACE: {764code_editor->get_find_replace_bar()->popup_replace();765} break;766case SEARCH_GOTO_LINE: {767goto_line_popup->popup_find_line(code_editor);768} break;769case BOOKMARK_TOGGLE: {770code_editor->toggle_bookmark();771} break;772case BOOKMARK_GOTO_NEXT: {773code_editor->goto_next_bookmark();774} break;775case BOOKMARK_GOTO_PREV: {776code_editor->goto_prev_bookmark();777} break;778case BOOKMARK_REMOVE_ALL: {779code_editor->remove_all_bookmarks();780} break;781case HELP_DOCS: {782OS::get_singleton()->shell_open(vformat("%s/tutorials/shaders/shader_reference/index.html", GODOT_VERSION_DOCS_URL));783} break;784case EDIT_EMOJI_AND_SYMBOL: {785code_editor->get_text_editor()->show_emoji_and_symbol_picker();786} break;787}788if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {789callable_mp((Control *)code_editor->get_text_editor(), &Control::grab_focus).call_deferred();790}791}792793void TextShaderEditor::_prepare_edit_menu() {794const CodeEdit *tx = code_editor->get_text_editor();795PopupMenu *popup = edit_menu->get_popup();796popup->set_item_disabled(popup->get_item_index(EDIT_UNDO), !tx->has_undo());797popup->set_item_disabled(popup->get_item_index(EDIT_REDO), !tx->has_redo());798}799800void TextShaderEditor::_notification(int p_what) {801switch (p_what) {802case NOTIFICATION_THEME_CHANGED: {803site_search->set_button_icon(get_editor_theme_icon(SNAME("ExternalLink")));804} break;805806case NOTIFICATION_APPLICATION_FOCUS_IN: {807_check_for_external_edit();808} break;809}810}811812void TextShaderEditor::_editor_settings_changed() {813if (!EditorThemeManager::is_generated_theme_outdated() &&814!EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor") &&815!EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor")) {816return;817}818819_apply_editor_settings();820}821822void TextShaderEditor::_apply_editor_settings() {823code_editor->update_editor_settings();824825trim_trailing_whitespace_on_save = EDITOR_GET("text_editor/behavior/files/trim_trailing_whitespace_on_save");826trim_final_newlines_on_save = EDITOR_GET("text_editor/behavior/files/trim_final_newlines_on_save");827}828829void TextShaderEditor::_show_warnings_panel(bool p_show) {830warnings_panel->set_visible(p_show);831}832833void TextShaderEditor::_warning_clicked(const Variant &p_line) {834if (p_line.get_type() == Variant::INT) {835code_editor->goto_line_centered(p_line.operator int64_t());836}837}838839void TextShaderEditor::_bind_methods() {840ClassDB::bind_method("_show_warnings_panel", &TextShaderEditor::_show_warnings_panel);841ClassDB::bind_method("_warning_clicked", &TextShaderEditor::_warning_clicked);842843ADD_SIGNAL(MethodInfo("validation_changed"));844}845846void TextShaderEditor::ensure_select_current() {847}848849void TextShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) {850code_editor->goto_line_selection(p_line, p_begin, p_end);851}852853void TextShaderEditor::_project_settings_changed() {854_update_warnings(true);855}856857void TextShaderEditor::_update_warnings(bool p_validate) {858bool changed = false;859860bool warnings_enabled = GLOBAL_GET("debug/shader_language/warnings/enable").booleanize();861if (warnings_enabled != saved_warnings_enabled) {862saved_warnings_enabled = warnings_enabled;863changed = true;864}865866bool treat_warning_as_errors = GLOBAL_GET("debug/shader_language/warnings/treat_warnings_as_errors").booleanize();867if (treat_warning_as_errors != saved_treat_warning_as_errors) {868saved_treat_warning_as_errors = treat_warning_as_errors;869changed = true;870}871872bool update_flags = false;873874for (int i = 0; i < ShaderWarning::WARNING_MAX; i++) {875ShaderWarning::Code code = (ShaderWarning::Code)i;876bool value = GLOBAL_GET("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code(code).to_lower());877878if (saved_warnings[code] != value) {879saved_warnings[code] = value;880update_flags = true;881changed = true;882}883}884885if (update_flags) {886saved_warning_flags = (uint32_t)ShaderWarning::get_flags_from_codemap(saved_warnings);887}888889if (p_validate && changed && code_editor && code_editor->get_edited_shader().is_valid()) {890code_editor->validate_script();891}892}893894void TextShaderEditor::_check_for_external_edit() {895bool use_autoreload = bool(EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change"));896897if (shader_inc.is_valid()) {898if (shader_inc->get_last_modified_time() != FileAccess::get_modified_time(shader_inc->get_path())) {899if (use_autoreload) {900_reload_shader_include_from_disk();901} else {902callable_mp((Window *)disk_changed, &Window::popup_centered).call_deferred(Size2i());903}904}905return;906}907908if (shader.is_null() || shader->is_built_in()) {909return;910}911912if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) {913if (use_autoreload) {914_reload_shader_from_disk();915} else {916callable_mp((Window *)disk_changed, &Window::popup_centered).call_deferred(Size2i());917}918}919}920921void TextShaderEditor::_reload_shader_from_disk() {922Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);923ERR_FAIL_COND(rel_shader.is_null());924925code_editor->set_block_shader_changed(true);926shader->set_code(rel_shader->get_code());927code_editor->set_block_shader_changed(false);928shader->set_last_modified_time(rel_shader->get_last_modified_time());929code_editor->reload_text();930}931932void TextShaderEditor::_reload_shader_include_from_disk() {933Ref<ShaderInclude> rel_shader_include = ResourceLoader::load(shader_inc->get_path(), shader_inc->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);934ERR_FAIL_COND(rel_shader_include.is_null());935936code_editor->set_block_shader_changed(true);937shader_inc->set_code(rel_shader_include->get_code());938code_editor->set_block_shader_changed(false);939shader_inc->set_last_modified_time(rel_shader_include->get_last_modified_time());940code_editor->reload_text();941}942943void TextShaderEditor::_reload() {944if (shader.is_valid()) {945_reload_shader_from_disk();946} else if (shader_inc.is_valid()) {947_reload_shader_include_from_disk();948}949}950951void TextShaderEditor::edit_shader(const Ref<Shader> &p_shader) {952if (p_shader.is_null() || !p_shader->is_text_shader()) {953return;954}955956if (shader == p_shader) {957return;958}959960shader = p_shader;961shader_inc = Ref<ShaderInclude>();962963code_editor->set_edited_shader(shader);964}965966void TextShaderEditor::edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) {967if (p_shader_inc.is_null()) {968return;969}970971if (shader_inc == p_shader_inc) {972return;973}974975shader_inc = p_shader_inc;976shader = Ref<Shader>();977978code_editor->set_edited_shader_include(p_shader_inc);979}980981void TextShaderEditor::use_menu_bar_items(MenuButton *p_file_menu, Button *p_make_floating) {982p_file_menu->set_switch_on_hover(true);983menu_bar_hbox->add_child(p_file_menu);984menu_bar_hbox->move_child(p_file_menu, 0);985menu_bar_hbox->add_child(p_make_floating);986}987988void TextShaderEditor::save_external_data(const String &p_str) {989if (shader.is_null() && shader_inc.is_null()) {990disk_changed->hide();991return;992}993994if (trim_trailing_whitespace_on_save) {995trim_trailing_whitespace();996}997998if (trim_final_newlines_on_save) {999trim_final_newlines();1000}10011002apply_shaders();10031004Ref<Shader> edited_shader = code_editor->get_edited_shader();1005if (edited_shader.is_valid()) {1006ResourceSaver::save(edited_shader);1007}1008if (shader.is_valid() && shader != edited_shader) {1009ResourceSaver::save(shader);1010}10111012Ref<ShaderInclude> edited_shader_inc = code_editor->get_edited_shader_include();1013if (edited_shader_inc.is_valid()) {1014ResourceSaver::save(edited_shader_inc);1015}1016if (shader_inc.is_valid() && shader_inc != edited_shader_inc) {1017ResourceSaver::save(shader_inc);1018}1019code_editor->get_text_editor()->tag_saved_version();10201021disk_changed->hide();1022}10231024void TextShaderEditor::trim_trailing_whitespace() {1025code_editor->trim_trailing_whitespace();1026}10271028void TextShaderEditor::trim_final_newlines() {1029code_editor->trim_final_newlines();1030}10311032void TextShaderEditor::validate_script() {1033code_editor->_validate_script();1034}10351036bool TextShaderEditor::is_unsaved() const {1037return code_editor->get_text_editor()->get_saved_version() != code_editor->get_text_editor()->get_version();1038}10391040void TextShaderEditor::tag_saved_version() {1041code_editor->get_text_editor()->tag_saved_version();1042}10431044void TextShaderEditor::apply_shaders() {1045String editor_code = code_editor->get_text_editor()->get_text();1046if (shader.is_valid()) {1047String shader_code = shader->get_code();1048if (shader_code != editor_code || dependencies_version != code_editor->get_dependencies_version()) {1049code_editor->set_block_shader_changed(true);1050shader->set_code(editor_code);1051code_editor->set_block_shader_changed(false);1052shader->set_edited(true);1053}1054}1055if (shader_inc.is_valid()) {1056String shader_inc_code = shader_inc->get_code();1057if (shader_inc_code != editor_code || dependencies_version != code_editor->get_dependencies_version()) {1058code_editor->set_block_shader_changed(true);1059shader_inc->set_code(editor_code);1060code_editor->set_block_shader_changed(false);1061shader_inc->set_edited(true);1062}1063}10641065dependencies_version = code_editor->get_dependencies_version();1066}10671068void TextShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {1069Ref<InputEventMouseButton> mb = ev;10701071if (mb.is_valid()) {1072if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {1073CodeEdit *tx = code_editor->get_text_editor();10741075tx->apply_ime();10761077Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());1078int row = pos.y;1079int col = pos.x;1080tx->set_move_caret_on_right_click_enabled(EDITOR_GET("text_editor/behavior/navigation/move_caret_on_right_click"));10811082if (tx->is_move_caret_on_right_click_enabled()) {1083tx->remove_secondary_carets();1084if (tx->has_selection()) {1085int from_line = tx->get_selection_from_line();1086int to_line = tx->get_selection_to_line();1087int from_column = tx->get_selection_from_column();1088int to_column = tx->get_selection_to_column();10891090if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) {1091// Right click is outside the selected text1092tx->deselect();1093}1094}1095if (!tx->has_selection()) {1096tx->set_caret_line(row, true, false, -1);1097tx->set_caret_column(col);1098}1099}1100_make_context_menu(tx->has_selection(), get_local_mouse_position());1101}1102}11031104Ref<InputEventKey> k = ev;1105if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) {1106CodeEdit *tx = code_editor->get_text_editor();1107tx->adjust_viewport_to_caret();1108_make_context_menu(tx->has_selection(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos()));1109context_menu->grab_focus();1110}1111}11121113void TextShaderEditor::_update_bookmark_list() {1114bookmarks_menu->clear();11151116bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);1117bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL);1118bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);1119bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);11201121PackedInt32Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines();1122if (bookmark_list.is_empty()) {1123return;1124}11251126bookmarks_menu->add_separator();11271128for (int i = 0; i < bookmark_list.size(); i++) {1129String line = code_editor->get_text_editor()->get_line(bookmark_list[i]).strip_edges();1130// Limit the size of the line if too big.1131if (line.length() > 50) {1132line = line.substr(0, 50);1133}11341135bookmarks_menu->add_item(String::num_int64(bookmark_list[i] + 1) + " - \"" + line + "\"");1136bookmarks_menu->set_item_metadata(-1, bookmark_list[i]);1137}1138}11391140void TextShaderEditor::_bookmark_item_pressed(int p_idx) {1141if (p_idx < 4) { // Any item before the separator.1142_menu_option(bookmarks_menu->get_item_id(p_idx));1143} else {1144code_editor->goto_line(bookmarks_menu->get_item_metadata(p_idx));1145}1146}11471148void TextShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) {1149context_menu->clear();1150if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {1151context_menu->add_item(TTR("Emoji & Symbols"), EDIT_EMOJI_AND_SYMBOL);1152context_menu->add_separator();1153}1154if (p_selection) {1155context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);1156context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);1157}11581159context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE);1160context_menu->add_separator();1161context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);1162context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);1163context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);11641165context_menu->add_separator();1166context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT);1167context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT);1168context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);1169context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);11701171context_menu->set_item_disabled(context_menu->get_item_index(EDIT_UNDO), !code_editor->get_text_editor()->has_undo());1172context_menu->set_item_disabled(context_menu->get_item_index(EDIT_REDO), !code_editor->get_text_editor()->has_redo());11731174context_menu->set_position(get_screen_position() + p_position);1175context_menu->reset_size();1176context_menu->popup();1177}11781179TextShaderEditor::TextShaderEditor() {1180_update_warnings(false);11811182code_editor = memnew(ShaderTextEditor);11831184code_editor->connect("script_validated", callable_mp(this, &TextShaderEditor::_script_validated));11851186code_editor->set_v_size_flags(SIZE_EXPAND_FILL);1187code_editor->add_theme_constant_override("separation", 0);1188code_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);11891190code_editor->connect("show_warnings_panel", callable_mp(this, &TextShaderEditor::_show_warnings_panel));1191code_editor->connect(CoreStringName(script_changed), callable_mp(this, &TextShaderEditor::apply_shaders));1192EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextShaderEditor::_editor_settings_changed));1193ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextShaderEditor::_project_settings_changed));11941195code_editor->get_text_editor()->set_symbol_lookup_on_click_enabled(true);1196code_editor->get_text_editor()->set_context_menu_enabled(false);1197code_editor->get_text_editor()->set_draw_breakpoints_gutter(false);1198code_editor->get_text_editor()->set_draw_executing_lines_gutter(false);1199code_editor->get_text_editor()->connect(SceneStringName(gui_input), callable_mp(this, &TextShaderEditor::_text_edit_gui_input));12001201code_editor->update_editor_settings();12021203context_menu = memnew(PopupMenu);1204add_child(context_menu);1205context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option));12061207VBoxContainer *main_container = memnew(VBoxContainer);1208main_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);1209menu_bar_hbox = memnew(HBoxContainer);12101211edit_menu = memnew(MenuButton);1212edit_menu->set_flat(false);1213edit_menu->set_theme_type_variation("FlatMenuButton");1214edit_menu->set_shortcut_context(this);1215edit_menu->set_text(TTR("Edit"));1216edit_menu->set_switch_on_hover(true);1217edit_menu->connect("about_to_popup", callable_mp(this, &TextShaderEditor::_prepare_edit_menu));12181219edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);1220edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);1221edit_menu->get_popup()->add_separator();1222edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);1223edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);1224edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE);1225edit_menu->get_popup()->add_separator();1226edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);1227edit_menu->get_popup()->add_separator();1228edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP);1229edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN);1230edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT);1231edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT);1232edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);1233edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);1234edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION);1235edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_lines"), EDIT_DUPLICATE_LINES);1236edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_word_wrap"), EDIT_TOGGLE_WORD_WRAP);1237edit_menu->get_popup()->add_separator();1238edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE);1239edit_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option));12401241search_menu = memnew(MenuButton);1242search_menu->set_flat(false);1243search_menu->set_theme_type_variation("FlatMenuButton");1244search_menu->set_shortcut_context(this);1245search_menu->set_text(TTR("Search"));1246search_menu->set_switch_on_hover(true);12471248search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND);1249search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT);1250search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);1251search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);1252search_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option));12531254MenuButton *goto_menu = memnew(MenuButton);1255goto_menu->set_flat(false);1256goto_menu->set_theme_type_variation("FlatMenuButton");1257goto_menu->set_shortcut_context(this);1258goto_menu->set_text(TTR("Go To"));1259goto_menu->set_switch_on_hover(true);1260goto_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option));12611262goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);1263goto_menu->get_popup()->add_separator();12641265bookmarks_menu = memnew(PopupMenu);1266goto_menu->get_popup()->add_submenu_node_item(TTR("Bookmarks"), bookmarks_menu);1267_update_bookmark_list();1268bookmarks_menu->connect("about_to_popup", callable_mp(this, &TextShaderEditor::_update_bookmark_list));1269bookmarks_menu->connect("index_pressed", callable_mp(this, &TextShaderEditor::_bookmark_item_pressed));12701271add_child(main_container);1272main_container->add_child(menu_bar_hbox);1273menu_bar_hbox->add_child(edit_menu);1274menu_bar_hbox->add_child(search_menu);1275menu_bar_hbox->add_child(goto_menu);1276menu_bar_hbox->add_spacer();12771278site_search = memnew(Button);1279site_search->set_theme_type_variation(SceneStringName(FlatButton));1280site_search->connect(SceneStringName(pressed), callable_mp(this, &TextShaderEditor::_menu_option).bind(HELP_DOCS));1281site_search->set_text(TTR("Online Docs"));1282site_search->set_tooltip_text(TTR("Open Godot online documentation."));1283menu_bar_hbox->add_child(site_search);1284menu_bar_hbox->add_child(memnew(VSeparator));12851286menu_bar_hbox->add_theme_style_override(SceneStringName(panel), EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("ScriptEditorPanel"), EditorStringName(EditorStyles)));12871288VSplitContainer *editor_box = memnew(VSplitContainer);1289main_container->add_child(editor_box);1290editor_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);1291editor_box->set_v_size_flags(SIZE_EXPAND_FILL);1292editor_box->add_child(code_editor);12931294FindReplaceBar *bar = memnew(FindReplaceBar);1295main_container->add_child(bar);1296bar->hide();1297code_editor->set_find_replace_bar(bar);12981299warnings_panel = memnew(RichTextLabel);1300warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));1301warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);1302warnings_panel->set_meta_underline(true);1303warnings_panel->set_selection_enabled(true);1304warnings_panel->set_context_menu_enabled(true);1305warnings_panel->set_focus_mode(FOCUS_CLICK);1306warnings_panel->hide();1307warnings_panel->connect("meta_clicked", callable_mp(this, &TextShaderEditor::_warning_clicked));1308editor_box->add_child(warnings_panel);1309code_editor->set_warnings_panel(warnings_panel);13101311goto_line_popup = memnew(GotoLinePopup);1312add_child(goto_line_popup);13131314disk_changed = memnew(ConfirmationDialog);13151316VBoxContainer *vbc = memnew(VBoxContainer);1317disk_changed->add_child(vbc);13181319Label *dl = memnew(Label);1320dl->set_focus_mode(FOCUS_ACCESSIBILITY);1321dl->set_text(TTR("This shader has been modified on disk.\nWhat action should be taken?"));1322vbc->add_child(dl);13231324disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &TextShaderEditor::_reload));1325disk_changed->set_ok_button_text(TTR("Reload"));13261327disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");1328disk_changed->connect("custom_action", callable_mp(this, &TextShaderEditor::save_external_data));13291330add_child(disk_changed);13311332_editor_settings_changed();1333code_editor->show_toggle_files_button(); // TODO: Disabled for now, because it doesn't work properly.1334}133513361337