shader_editor_plugin.cpp 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625
  1. /*************************************************************************/
  2. /* shader_editor_plugin.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "shader_editor_plugin.h"
  31. #include "core/io/resource_loader.h"
  32. #include "core/io/resource_saver.h"
  33. #include "core/os/keyboard.h"
  34. #include "core/os/os.h"
  35. #include "core/version_generated.gen.h"
  36. #include "editor/editor_node.h"
  37. #include "editor/editor_scale.h"
  38. #include "editor/editor_settings.h"
  39. #include "editor/filesystem_dock.h"
  40. #include "editor/plugins/visual_shader_editor_plugin.h"
  41. #include "editor/project_settings_editor.h"
  42. #include "editor/shader_create_dialog.h"
  43. #include "scene/gui/split_container.h"
  44. #include "servers/display_server.h"
  45. #include "servers/rendering/shader_preprocessor.h"
  46. #include "servers/rendering/shader_types.h"
  47. /*** SHADER SYNTAX HIGHLIGHTER ****/
  48. Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
  49. Dictionary color_map;
  50. for (const Point2i &region : disabled_branch_regions) {
  51. if (p_line >= region.x && p_line <= region.y) {
  52. Dictionary highlighter_info;
  53. highlighter_info["color"] = disabled_branch_color;
  54. color_map[0] = highlighter_info;
  55. return color_map;
  56. }
  57. }
  58. return CodeHighlighter::_get_line_syntax_highlighting_impl(p_line);
  59. }
  60. void GDShaderSyntaxHighlighter::add_disabled_branch_region(const Point2i &p_region) {
  61. ERR_FAIL_COND(p_region.x < 0);
  62. ERR_FAIL_COND(p_region.y < 0);
  63. for (int i = 0; i < disabled_branch_regions.size(); i++) {
  64. ERR_FAIL_COND_MSG(disabled_branch_regions[i].x == p_region.x, "Branch region with a start line '" + itos(p_region.x) + "' already exists.");
  65. }
  66. Point2i disabled_branch_region;
  67. disabled_branch_region.x = p_region.x;
  68. disabled_branch_region.y = p_region.y;
  69. disabled_branch_regions.push_back(disabled_branch_region);
  70. clear_highlighting_cache();
  71. }
  72. void GDShaderSyntaxHighlighter::clear_disabled_branch_regions() {
  73. disabled_branch_regions.clear();
  74. clear_highlighting_cache();
  75. }
  76. void GDShaderSyntaxHighlighter::set_disabled_branch_color(const Color &p_color) {
  77. disabled_branch_color = p_color;
  78. clear_highlighting_cache();
  79. }
  80. /*** SHADER SCRIPT EDITOR ****/
  81. static bool saved_warnings_enabled = false;
  82. static bool saved_treat_warning_as_errors = false;
  83. static HashMap<ShaderWarning::Code, bool> saved_warnings;
  84. static uint32_t saved_warning_flags = 0U;
  85. void ShaderTextEditor::_notification(int p_what) {
  86. switch (p_what) {
  87. case NOTIFICATION_THEME_CHANGED: {
  88. if (is_visible_in_tree()) {
  89. _load_theme_settings();
  90. if (warnings.size() > 0 && last_compile_result == OK) {
  91. warnings_panel->clear();
  92. _update_warning_panel();
  93. }
  94. }
  95. } break;
  96. }
  97. }
  98. Ref<Shader> ShaderTextEditor::get_edited_shader() const {
  99. return shader;
  100. }
  101. Ref<ShaderInclude> ShaderTextEditor::get_edited_shader_include() const {
  102. return shader_inc;
  103. }
  104. void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) {
  105. set_edited_shader(p_shader, p_shader->get_code());
  106. }
  107. void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const String &p_code) {
  108. if (shader == p_shader) {
  109. return;
  110. }
  111. if (shader.is_valid()) {
  112. shader->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
  113. }
  114. shader = p_shader;
  115. shader_inc = Ref<ShaderInclude>();
  116. set_edited_code(p_code);
  117. if (shader.is_valid()) {
  118. shader->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
  119. }
  120. }
  121. void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc) {
  122. set_edited_shader_include(p_shader_inc, p_shader_inc->get_code());
  123. }
  124. void ShaderTextEditor::_shader_changed() {
  125. // This function is used for dependencies (include changing changes main shader and forces it to revalidate)
  126. if (block_shader_changed) {
  127. return;
  128. }
  129. dependencies_version++;
  130. _validate_script();
  131. }
  132. void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc, const String &p_code) {
  133. if (shader_inc == p_shader_inc) {
  134. return;
  135. }
  136. if (shader_inc.is_valid()) {
  137. shader_inc->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
  138. }
  139. shader_inc = p_shader_inc;
  140. shader = Ref<Shader>();
  141. set_edited_code(p_code);
  142. if (shader_inc.is_valid()) {
  143. shader_inc->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
  144. }
  145. }
  146. void ShaderTextEditor::set_edited_code(const String &p_code) {
  147. _load_theme_settings();
  148. get_text_editor()->set_text(p_code);
  149. get_text_editor()->clear_undo_history();
  150. get_text_editor()->call_deferred(SNAME("set_h_scroll"), 0);
  151. get_text_editor()->call_deferred(SNAME("set_v_scroll"), 0);
  152. get_text_editor()->tag_saved_version();
  153. _validate_script();
  154. _line_col_changed();
  155. }
  156. void ShaderTextEditor::reload_text() {
  157. ERR_FAIL_COND(shader.is_null());
  158. CodeEdit *te = get_text_editor();
  159. int column = te->get_caret_column();
  160. int row = te->get_caret_line();
  161. int h = te->get_h_scroll();
  162. int v = te->get_v_scroll();
  163. te->set_text(shader->get_code());
  164. te->set_caret_line(row);
  165. te->set_caret_column(column);
  166. te->set_h_scroll(h);
  167. te->set_v_scroll(v);
  168. te->tag_saved_version();
  169. update_line_and_column();
  170. }
  171. void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) {
  172. warnings_panel = p_warnings_panel;
  173. }
  174. void ShaderTextEditor::_load_theme_settings() {
  175. CodeEdit *text_editor = get_text_editor();
  176. Color updated_marked_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color");
  177. if (updated_marked_line_color != marked_line_color) {
  178. for (int i = 0; i < text_editor->get_line_count(); i++) {
  179. if (text_editor->get_line_background_color(i) == marked_line_color) {
  180. text_editor->set_line_background_color(i, updated_marked_line_color);
  181. }
  182. }
  183. marked_line_color = updated_marked_line_color;
  184. }
  185. syntax_highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color"));
  186. syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color"));
  187. syntax_highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color"));
  188. syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color"));
  189. syntax_highlighter->clear_keyword_colors();
  190. const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
  191. const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
  192. List<String> keywords;
  193. ShaderLanguage::get_keyword_list(&keywords);
  194. for (const String &E : keywords) {
  195. if (ShaderLanguage::is_control_flow_keyword(E)) {
  196. syntax_highlighter->add_keyword_color(E, control_flow_keyword_color);
  197. } else {
  198. syntax_highlighter->add_keyword_color(E, keyword_color);
  199. }
  200. }
  201. List<String> pp_keywords;
  202. ShaderPreprocessor::get_keyword_list(&pp_keywords, false);
  203. for (const String &E : pp_keywords) {
  204. syntax_highlighter->add_keyword_color(E, keyword_color);
  205. }
  206. // Colorize built-ins like `COLOR` differently to make them easier
  207. // to distinguish from keywords at a quick glance.
  208. List<String> built_ins;
  209. if (shader_inc.is_valid()) {
  210. for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
  211. for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) {
  212. for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
  213. built_ins.push_back(F.key);
  214. }
  215. }
  216. const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
  217. for (int j = 0; j < modes.size(); j++) {
  218. const ShaderLanguage::ModeInfo &info = modes[j];
  219. if (!info.options.is_empty()) {
  220. for (int k = 0; k < info.options.size(); k++) {
  221. built_ins.push_back(String(info.name) + "_" + String(info.options[k]));
  222. }
  223. } else {
  224. built_ins.push_back(String(info.name));
  225. }
  226. }
  227. }
  228. } else if (shader.is_valid()) {
  229. for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) {
  230. for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
  231. built_ins.push_back(F.key);
  232. }
  233. }
  234. const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
  235. for (int i = 0; i < modes.size(); i++) {
  236. const ShaderLanguage::ModeInfo &info = modes[i];
  237. if (!info.options.is_empty()) {
  238. for (int j = 0; j < info.options.size(); j++) {
  239. built_ins.push_back(String(info.name) + "_" + String(info.options[j]));
  240. }
  241. } else {
  242. built_ins.push_back(String(info.name));
  243. }
  244. }
  245. }
  246. const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
  247. for (const String &E : built_ins) {
  248. syntax_highlighter->add_keyword_color(E, user_type_color);
  249. }
  250. // Colorize comments.
  251. const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
  252. syntax_highlighter->clear_color_regions();
  253. syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
  254. syntax_highlighter->add_color_region("//", "", comment_color, true);
  255. syntax_highlighter->set_disabled_branch_color(comment_color);
  256. text_editor->clear_comment_delimiters();
  257. text_editor->add_comment_delimiter("/*", "*/", false);
  258. text_editor->add_comment_delimiter("//", "", true);
  259. if (!text_editor->has_auto_brace_completion_open_key("/*")) {
  260. text_editor->add_auto_brace_completion_pair("/*", "*/");
  261. }
  262. // Colorize preprocessor include strings.
  263. const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
  264. syntax_highlighter->add_color_region("\"", "\"", string_color, false);
  265. if (warnings_panel) {
  266. // Warnings panel.
  267. warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("main"), SNAME("EditorFonts")));
  268. warnings_panel->add_theme_font_size_override("normal_font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("main_size"), SNAME("EditorFonts")));
  269. }
  270. }
  271. void ShaderTextEditor::_check_shader_mode() {
  272. String type = ShaderLanguage::get_shader_type(get_text_editor()->get_text());
  273. Shader::Mode mode;
  274. if (type == "canvas_item") {
  275. mode = Shader::MODE_CANVAS_ITEM;
  276. } else if (type == "particles") {
  277. mode = Shader::MODE_PARTICLES;
  278. } else if (type == "sky") {
  279. mode = Shader::MODE_SKY;
  280. } else if (type == "fog") {
  281. mode = Shader::MODE_FOG;
  282. } else {
  283. mode = Shader::MODE_SPATIAL;
  284. }
  285. if (shader->get_mode() != mode) {
  286. set_block_shader_changed(true);
  287. shader->set_code(get_text_editor()->get_text());
  288. set_block_shader_changed(false);
  289. _load_theme_settings();
  290. }
  291. }
  292. static ShaderLanguage::DataType _get_global_shader_uniform_type(const StringName &p_variable) {
  293. RS::GlobalShaderUniformType gvt = RS::get_singleton()->global_shader_uniform_get_type(p_variable);
  294. return (ShaderLanguage::DataType)RS::global_shader_uniform_type_get_shader_datatype(gvt);
  295. }
  296. static String complete_from_path;
  297. static void _complete_include_paths_search(EditorFileSystemDirectory *p_efsd, List<ScriptLanguage::CodeCompletionOption> *r_options) {
  298. if (!p_efsd) {
  299. return;
  300. }
  301. for (int i = 0; i < p_efsd->get_file_count(); i++) {
  302. if (p_efsd->get_file_type(i) == SNAME("ShaderInclude")) {
  303. String path = p_efsd->get_file_path(i);
  304. if (path.begins_with(complete_from_path)) {
  305. path = path.replace_first(complete_from_path, "");
  306. }
  307. r_options->push_back(ScriptLanguage::CodeCompletionOption(path, ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH));
  308. }
  309. }
  310. for (int j = 0; j < p_efsd->get_subdir_count(); j++) {
  311. _complete_include_paths_search(p_efsd->get_subdir(j), r_options);
  312. }
  313. }
  314. static void _complete_include_paths(List<ScriptLanguage::CodeCompletionOption> *r_options) {
  315. _complete_include_paths_search(EditorFileSystem::get_singleton()->get_filesystem(), r_options);
  316. }
  317. void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) {
  318. List<ScriptLanguage::CodeCompletionOption> pp_options;
  319. ShaderPreprocessor preprocessor;
  320. String code;
  321. complete_from_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path()).get_base_dir();
  322. if (!complete_from_path.ends_with("/")) {
  323. complete_from_path += "/";
  324. }
  325. preprocessor.preprocess(p_code, "", code, nullptr, nullptr, nullptr, nullptr, &pp_options, _complete_include_paths);
  326. complete_from_path = String();
  327. if (pp_options.size()) {
  328. for (const ScriptLanguage::CodeCompletionOption &E : pp_options) {
  329. r_options->push_back(E);
  330. }
  331. return;
  332. }
  333. ShaderLanguage sl;
  334. String calltip;
  335. ShaderLanguage::ShaderCompileInfo info;
  336. info.global_shader_uniform_type_func = _get_global_shader_uniform_type;
  337. if (shader.is_null()) {
  338. info.is_include = true;
  339. sl.complete(code, info, r_options, calltip);
  340. get_text_editor()->set_code_hint(calltip);
  341. return;
  342. }
  343. _check_shader_mode();
  344. info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()));
  345. info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
  346. info.shader_types = ShaderTypes::get_singleton()->get_types();
  347. sl.complete(code, info, r_options, calltip);
  348. get_text_editor()->set_code_hint(calltip);
  349. }
  350. void ShaderTextEditor::_validate_script() {
  351. emit_signal(SNAME("script_changed")); // Ensure to notify that it changed, so it is applied
  352. String code;
  353. if (shader.is_valid()) {
  354. _check_shader_mode();
  355. code = shader->get_code();
  356. } else {
  357. code = shader_inc->get_code();
  358. }
  359. ShaderPreprocessor preprocessor;
  360. String code_pp;
  361. String error_pp;
  362. List<ShaderPreprocessor::FilePosition> err_positions;
  363. List<ShaderPreprocessor::Region> regions;
  364. String filename;
  365. if (shader.is_valid()) {
  366. filename = shader->get_path();
  367. } else if (shader_inc.is_valid()) {
  368. filename = shader_inc->get_path();
  369. }
  370. last_compile_result = preprocessor.preprocess(code, filename, code_pp, &error_pp, &err_positions, &regions);
  371. for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
  372. get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
  373. }
  374. syntax_highlighter->clear_disabled_branch_regions();
  375. for (const ShaderPreprocessor::Region &region : regions) {
  376. if (!region.enabled) {
  377. if (filename != region.file) {
  378. continue;
  379. }
  380. syntax_highlighter->add_disabled_branch_region(Point2i(region.from_line, region.to_line));
  381. }
  382. }
  383. set_error("");
  384. set_error_count(0);
  385. if (last_compile_result != OK) {
  386. //preprocessor error
  387. ERR_FAIL_COND(err_positions.size() == 0);
  388. String error_text = error_pp;
  389. int error_line = err_positions.front()->get().line;
  390. if (err_positions.size() == 1) {
  391. // Error in main file
  392. error_text = "error(" + itos(error_line) + "): " + error_text;
  393. } else {
  394. error_text = "error(" + itos(error_line) + ") in include " + err_positions.back()->get().file.get_file() + ":" + itos(err_positions.back()->get().line) + ": " + error_text;
  395. set_error_count(err_positions.size() - 1);
  396. }
  397. set_error(error_text);
  398. set_error_pos(error_line - 1, 0);
  399. for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
  400. get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
  401. }
  402. get_text_editor()->set_line_background_color(error_line - 1, marked_line_color);
  403. set_warning_count(0);
  404. } else {
  405. ShaderLanguage sl;
  406. sl.enable_warning_checking(saved_warnings_enabled);
  407. uint32_t flags = saved_warning_flags;
  408. if (shader.is_null()) {
  409. if (flags & ShaderWarning::UNUSED_CONSTANT) {
  410. flags &= ~(ShaderWarning::UNUSED_CONSTANT);
  411. }
  412. if (flags & ShaderWarning::UNUSED_FUNCTION) {
  413. flags &= ~(ShaderWarning::UNUSED_FUNCTION);
  414. }
  415. if (flags & ShaderWarning::UNUSED_STRUCT) {
  416. flags &= ~(ShaderWarning::UNUSED_STRUCT);
  417. }
  418. if (flags & ShaderWarning::UNUSED_UNIFORM) {
  419. flags &= ~(ShaderWarning::UNUSED_UNIFORM);
  420. }
  421. if (flags & ShaderWarning::UNUSED_VARYING) {
  422. flags &= ~(ShaderWarning::UNUSED_VARYING);
  423. }
  424. }
  425. sl.set_warning_flags(flags);
  426. ShaderLanguage::ShaderCompileInfo info;
  427. info.global_shader_uniform_type_func = _get_global_shader_uniform_type;
  428. if (shader.is_null()) {
  429. info.is_include = true;
  430. } else {
  431. Shader::Mode mode = shader->get_mode();
  432. info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode));
  433. info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode));
  434. info.shader_types = ShaderTypes::get_singleton()->get_types();
  435. }
  436. code = code_pp;
  437. //compiler error
  438. last_compile_result = sl.compile(code, info);
  439. if (last_compile_result != OK) {
  440. String error_text;
  441. int error_line;
  442. Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions();
  443. if (include_positions.size() > 1) {
  444. //error is in an include
  445. error_line = include_positions[0].line;
  446. error_text = "error(" + itos(error_line) + ") in include " + include_positions[include_positions.size() - 1].file + ":" + itos(include_positions[include_positions.size() - 1].line) + ": " + sl.get_error_text();
  447. set_error_count(include_positions.size() - 1);
  448. } else {
  449. error_line = sl.get_error_line();
  450. error_text = "error(" + itos(error_line) + "): " + sl.get_error_text();
  451. set_error_count(0);
  452. }
  453. set_error(error_text);
  454. set_error_pos(error_line - 1, 0);
  455. get_text_editor()->set_line_background_color(error_line - 1, marked_line_color);
  456. } else {
  457. set_error("");
  458. }
  459. if (warnings.size() > 0 || last_compile_result != OK) {
  460. warnings_panel->clear();
  461. }
  462. warnings.clear();
  463. for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) {
  464. warnings.push_back(E->get());
  465. }
  466. if (warnings.size() > 0 && last_compile_result == OK) {
  467. warnings.sort_custom<WarningsComparator>();
  468. _update_warning_panel();
  469. } else {
  470. set_warning_count(0);
  471. }
  472. }
  473. emit_signal(SNAME("script_validated"), last_compile_result == OK); // Notify that validation finished, to update the list of scripts
  474. }
  475. void ShaderTextEditor::_update_warning_panel() {
  476. int warning_count = 0;
  477. warnings_panel->push_table(2);
  478. for (int i = 0; i < warnings.size(); i++) {
  479. ShaderWarning &w = warnings[i];
  480. if (warning_count == 0) {
  481. if (saved_treat_warning_as_errors) {
  482. String error_text = "error(" + itos(w.get_line()) + "): " + w.get_message() + " " + TTR("Warnings should be fixed to prevent errors.");
  483. set_error_pos(w.get_line() - 1, 0);
  484. set_error(error_text);
  485. get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color);
  486. }
  487. }
  488. warning_count++;
  489. int line = w.get_line();
  490. // First cell.
  491. warnings_panel->push_cell();
  492. warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor")));
  493. if (line != -1) {
  494. warnings_panel->push_meta(line - 1);
  495. warnings_panel->add_text(TTR("Line") + " " + itos(line));
  496. warnings_panel->add_text(" (" + w.get_name() + "):");
  497. warnings_panel->pop(); // Meta goto.
  498. } else {
  499. warnings_panel->add_text(w.get_name() + ":");
  500. }
  501. warnings_panel->pop(); // Color.
  502. warnings_panel->pop(); // Cell.
  503. // Second cell.
  504. warnings_panel->push_cell();
  505. warnings_panel->add_text(w.get_message());
  506. warnings_panel->pop(); // Cell.
  507. }
  508. warnings_panel->pop(); // Table.
  509. set_warning_count(warning_count);
  510. }
  511. void ShaderTextEditor::_bind_methods() {
  512. ADD_SIGNAL(MethodInfo("script_validated", PropertyInfo(Variant::BOOL, "valid")));
  513. }
  514. ShaderTextEditor::ShaderTextEditor() {
  515. syntax_highlighter.instantiate();
  516. get_text_editor()->set_syntax_highlighter(syntax_highlighter);
  517. }
  518. /*** SCRIPT EDITOR ******/
  519. void ShaderEditor::_menu_option(int p_option) {
  520. switch (p_option) {
  521. case EDIT_UNDO: {
  522. shader_editor->get_text_editor()->undo();
  523. } break;
  524. case EDIT_REDO: {
  525. shader_editor->get_text_editor()->redo();
  526. } break;
  527. case EDIT_CUT: {
  528. shader_editor->get_text_editor()->cut();
  529. } break;
  530. case EDIT_COPY: {
  531. shader_editor->get_text_editor()->copy();
  532. } break;
  533. case EDIT_PASTE: {
  534. shader_editor->get_text_editor()->paste();
  535. } break;
  536. case EDIT_SELECT_ALL: {
  537. shader_editor->get_text_editor()->select_all();
  538. } break;
  539. case EDIT_MOVE_LINE_UP: {
  540. shader_editor->move_lines_up();
  541. } break;
  542. case EDIT_MOVE_LINE_DOWN: {
  543. shader_editor->move_lines_down();
  544. } break;
  545. case EDIT_INDENT_LEFT: {
  546. if (shader.is_null()) {
  547. return;
  548. }
  549. shader_editor->get_text_editor()->unindent_lines();
  550. } break;
  551. case EDIT_INDENT_RIGHT: {
  552. if (shader.is_null()) {
  553. return;
  554. }
  555. shader_editor->get_text_editor()->indent_lines();
  556. } break;
  557. case EDIT_DELETE_LINE: {
  558. shader_editor->delete_lines();
  559. } break;
  560. case EDIT_DUPLICATE_SELECTION: {
  561. shader_editor->duplicate_selection();
  562. } break;
  563. case EDIT_TOGGLE_COMMENT: {
  564. if (shader.is_null()) {
  565. return;
  566. }
  567. shader_editor->toggle_inline_comment("//");
  568. } break;
  569. case EDIT_COMPLETE: {
  570. shader_editor->get_text_editor()->request_code_completion();
  571. } break;
  572. case SEARCH_FIND: {
  573. shader_editor->get_find_replace_bar()->popup_search();
  574. } break;
  575. case SEARCH_FIND_NEXT: {
  576. shader_editor->get_find_replace_bar()->search_next();
  577. } break;
  578. case SEARCH_FIND_PREV: {
  579. shader_editor->get_find_replace_bar()->search_prev();
  580. } break;
  581. case SEARCH_REPLACE: {
  582. shader_editor->get_find_replace_bar()->popup_replace();
  583. } break;
  584. case SEARCH_GOTO_LINE: {
  585. goto_line_dialog->popup_find_line(shader_editor->get_text_editor());
  586. } break;
  587. case BOOKMARK_TOGGLE: {
  588. shader_editor->toggle_bookmark();
  589. } break;
  590. case BOOKMARK_GOTO_NEXT: {
  591. shader_editor->goto_next_bookmark();
  592. } break;
  593. case BOOKMARK_GOTO_PREV: {
  594. shader_editor->goto_prev_bookmark();
  595. } break;
  596. case BOOKMARK_REMOVE_ALL: {
  597. shader_editor->remove_all_bookmarks();
  598. } break;
  599. case HELP_DOCS: {
  600. OS::get_singleton()->shell_open(vformat("%s/tutorials/shaders/shader_reference/index.html", VERSION_DOCS_URL));
  601. } break;
  602. }
  603. if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {
  604. shader_editor->get_text_editor()->call_deferred(SNAME("grab_focus"));
  605. }
  606. }
  607. void ShaderEditor::_notification(int p_what) {
  608. switch (p_what) {
  609. case NOTIFICATION_ENTER_TREE:
  610. case NOTIFICATION_THEME_CHANGED: {
  611. PopupMenu *popup = help_menu->get_popup();
  612. popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
  613. } break;
  614. case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
  615. _check_for_external_edit();
  616. } break;
  617. }
  618. }
  619. void ShaderEditor::_editor_settings_changed() {
  620. shader_editor->update_editor_settings();
  621. shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/appearance/whitespace/line_spacing"));
  622. shader_editor->get_text_editor()->set_draw_breakpoints_gutter(false);
  623. shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false);
  624. }
  625. void ShaderEditor::_show_warnings_panel(bool p_show) {
  626. warnings_panel->set_visible(p_show);
  627. }
  628. void ShaderEditor::_warning_clicked(Variant p_line) {
  629. if (p_line.get_type() == Variant::INT) {
  630. shader_editor->get_text_editor()->set_caret_line(p_line.operator int64_t());
  631. }
  632. }
  633. void ShaderEditor::_bind_methods() {
  634. ClassDB::bind_method("_show_warnings_panel", &ShaderEditor::_show_warnings_panel);
  635. ClassDB::bind_method("_warning_clicked", &ShaderEditor::_warning_clicked);
  636. ADD_SIGNAL(MethodInfo("validation_changed"));
  637. }
  638. void ShaderEditor::ensure_select_current() {
  639. }
  640. void ShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
  641. shader_editor->goto_line_selection(p_line, p_begin, p_end);
  642. }
  643. void ShaderEditor::_project_settings_changed() {
  644. _update_warnings(true);
  645. }
  646. void ShaderEditor::_update_warnings(bool p_validate) {
  647. bool changed = false;
  648. bool warnings_enabled = GLOBAL_GET("debug/shader_language/warnings/enable").booleanize();
  649. if (warnings_enabled != saved_warnings_enabled) {
  650. saved_warnings_enabled = warnings_enabled;
  651. changed = true;
  652. }
  653. bool treat_warning_as_errors = GLOBAL_GET("debug/shader_language/warnings/treat_warnings_as_errors").booleanize();
  654. if (treat_warning_as_errors != saved_treat_warning_as_errors) {
  655. saved_treat_warning_as_errors = treat_warning_as_errors;
  656. changed = true;
  657. }
  658. bool update_flags = false;
  659. for (int i = 0; i < ShaderWarning::WARNING_MAX; i++) {
  660. ShaderWarning::Code code = (ShaderWarning::Code)i;
  661. bool value = GLOBAL_GET("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code(code).to_lower());
  662. if (saved_warnings[code] != value) {
  663. saved_warnings[code] = value;
  664. update_flags = true;
  665. changed = true;
  666. }
  667. }
  668. if (update_flags) {
  669. saved_warning_flags = (uint32_t)ShaderWarning::get_flags_from_codemap(saved_warnings);
  670. }
  671. if (p_validate && changed && shader_editor && shader_editor->get_edited_shader().is_valid()) {
  672. shader_editor->validate_script();
  673. }
  674. }
  675. void ShaderEditor::_check_for_external_edit() {
  676. bool use_autoreload = bool(EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change"));
  677. if (shader_inc.is_valid()) {
  678. if (shader_inc->get_last_modified_time() != FileAccess::get_modified_time(shader_inc->get_path())) {
  679. if (use_autoreload) {
  680. _reload_shader_include_from_disk();
  681. } else {
  682. disk_changed->call_deferred(SNAME("popup_centered"));
  683. }
  684. }
  685. return;
  686. }
  687. if (shader.is_null() || shader->is_built_in()) {
  688. return;
  689. }
  690. if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) {
  691. if (use_autoreload) {
  692. _reload_shader_from_disk();
  693. } else {
  694. disk_changed->call_deferred(SNAME("popup_centered"));
  695. }
  696. }
  697. }
  698. void ShaderEditor::_reload_shader_from_disk() {
  699. Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
  700. ERR_FAIL_COND(!rel_shader.is_valid());
  701. shader_editor->set_block_shader_changed(true);
  702. shader->set_code(rel_shader->get_code());
  703. shader_editor->set_block_shader_changed(false);
  704. shader->set_last_modified_time(rel_shader->get_last_modified_time());
  705. shader_editor->reload_text();
  706. }
  707. void ShaderEditor::_reload_shader_include_from_disk() {
  708. Ref<ShaderInclude> rel_shader_include = ResourceLoader::load(shader_inc->get_path(), shader_inc->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
  709. ERR_FAIL_COND(!rel_shader_include.is_valid());
  710. shader_editor->set_block_shader_changed(true);
  711. shader_inc->set_code(rel_shader_include->get_code());
  712. shader_editor->set_block_shader_changed(false);
  713. shader_inc->set_last_modified_time(rel_shader_include->get_last_modified_time());
  714. shader_editor->reload_text();
  715. }
  716. void ShaderEditor::_reload() {
  717. if (shader.is_valid()) {
  718. _reload_shader_from_disk();
  719. } else if (shader_inc.is_valid()) {
  720. _reload_shader_include_from_disk();
  721. }
  722. }
  723. void ShaderEditor::edit(const Ref<Shader> &p_shader) {
  724. if (p_shader.is_null() || !p_shader->is_text_shader()) {
  725. return;
  726. }
  727. if (shader == p_shader) {
  728. return;
  729. }
  730. shader = p_shader;
  731. shader_inc = Ref<ShaderInclude>();
  732. shader_editor->set_edited_shader(shader);
  733. }
  734. void ShaderEditor::edit(const Ref<ShaderInclude> &p_shader_inc) {
  735. if (p_shader_inc.is_null()) {
  736. return;
  737. }
  738. if (shader_inc == p_shader_inc) {
  739. return;
  740. }
  741. shader_inc = p_shader_inc;
  742. shader = Ref<Shader>();
  743. shader_editor->set_edited_shader_include(p_shader_inc);
  744. }
  745. void ShaderEditor::save_external_data(const String &p_str) {
  746. if (shader.is_null() && shader_inc.is_null()) {
  747. disk_changed->hide();
  748. return;
  749. }
  750. apply_shaders();
  751. Ref<Shader> edited_shader = shader_editor->get_edited_shader();
  752. if (edited_shader.is_valid()) {
  753. ResourceSaver::save(edited_shader);
  754. }
  755. if (shader.is_valid() && shader != edited_shader) {
  756. ResourceSaver::save(shader);
  757. }
  758. Ref<ShaderInclude> edited_shader_inc = shader_editor->get_edited_shader_include();
  759. if (edited_shader_inc.is_valid()) {
  760. ResourceSaver::save(edited_shader_inc);
  761. }
  762. if (shader_inc.is_valid() && shader_inc != edited_shader_inc) {
  763. ResourceSaver::save(shader_inc);
  764. }
  765. shader_editor->get_text_editor()->tag_saved_version();
  766. disk_changed->hide();
  767. }
  768. void ShaderEditor::validate_script() {
  769. shader_editor->_validate_script();
  770. }
  771. bool ShaderEditor::is_unsaved() const {
  772. return shader_editor->get_text_editor()->get_saved_version() != shader_editor->get_text_editor()->get_version();
  773. }
  774. void ShaderEditor::apply_shaders() {
  775. String editor_code = shader_editor->get_text_editor()->get_text();
  776. if (shader.is_valid()) {
  777. String shader_code = shader->get_code();
  778. if (shader_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) {
  779. shader_editor->set_block_shader_changed(true);
  780. shader->set_code(editor_code);
  781. shader_editor->set_block_shader_changed(false);
  782. shader->set_edited(true);
  783. }
  784. }
  785. if (shader_inc.is_valid()) {
  786. String shader_inc_code = shader_inc->get_code();
  787. if (shader_inc_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) {
  788. shader_editor->set_block_shader_changed(true);
  789. shader_inc->set_code(editor_code);
  790. shader_editor->set_block_shader_changed(false);
  791. shader_inc->set_edited(true);
  792. }
  793. }
  794. dependencies_version = shader_editor->get_dependencies_version();
  795. }
  796. void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
  797. Ref<InputEventMouseButton> mb = ev;
  798. if (mb.is_valid()) {
  799. if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
  800. CodeEdit *tx = shader_editor->get_text_editor();
  801. Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());
  802. int row = pos.y;
  803. int col = pos.x;
  804. tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click"));
  805. if (tx->is_move_caret_on_right_click_enabled()) {
  806. if (tx->has_selection()) {
  807. int from_line = tx->get_selection_from_line();
  808. int to_line = tx->get_selection_to_line();
  809. int from_column = tx->get_selection_from_column();
  810. int to_column = tx->get_selection_to_column();
  811. if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) {
  812. // Right click is outside the selected text
  813. tx->deselect();
  814. }
  815. }
  816. if (!tx->has_selection()) {
  817. tx->set_caret_line(row, true, false);
  818. tx->set_caret_column(col);
  819. }
  820. }
  821. _make_context_menu(tx->has_selection(), get_local_mouse_position());
  822. }
  823. }
  824. Ref<InputEventKey> k = ev;
  825. if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) {
  826. CodeEdit *tx = shader_editor->get_text_editor();
  827. tx->adjust_viewport_to_caret();
  828. _make_context_menu(tx->has_selection(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos()));
  829. context_menu->grab_focus();
  830. }
  831. }
  832. void ShaderEditor::_update_bookmark_list() {
  833. bookmarks_menu->clear();
  834. bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
  835. bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL);
  836. bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
  837. bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
  838. PackedInt32Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines();
  839. if (bookmark_list.size() == 0) {
  840. return;
  841. }
  842. bookmarks_menu->add_separator();
  843. for (int i = 0; i < bookmark_list.size(); i++) {
  844. String line = shader_editor->get_text_editor()->get_line(bookmark_list[i]).strip_edges();
  845. // Limit the size of the line if too big.
  846. if (line.length() > 50) {
  847. line = line.substr(0, 50);
  848. }
  849. bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - \"" + line + "\"");
  850. bookmarks_menu->set_item_metadata(-1, bookmark_list[i]);
  851. }
  852. }
  853. void ShaderEditor::_bookmark_item_pressed(int p_idx) {
  854. if (p_idx < 4) { // Any item before the separator.
  855. _menu_option(bookmarks_menu->get_item_id(p_idx));
  856. } else {
  857. shader_editor->goto_line(bookmarks_menu->get_item_metadata(p_idx));
  858. }
  859. }
  860. void ShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) {
  861. context_menu->clear();
  862. if (p_selection) {
  863. context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);
  864. context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);
  865. }
  866. context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE);
  867. context_menu->add_separator();
  868. context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);
  869. context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
  870. context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
  871. context_menu->add_separator();
  872. context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT);
  873. context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT);
  874. context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
  875. context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
  876. context_menu->set_position(get_screen_position() + p_position);
  877. context_menu->reset_size();
  878. context_menu->popup();
  879. }
  880. ShaderEditor::ShaderEditor() {
  881. GLOBAL_DEF("debug/shader_language/warnings/enable", true);
  882. GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false);
  883. for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) {
  884. GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true);
  885. }
  886. _update_warnings(false);
  887. shader_editor = memnew(ShaderTextEditor);
  888. shader_editor->connect("script_validated", callable_mp(this, &ShaderEditor::_script_validated));
  889. shader_editor->set_v_size_flags(SIZE_EXPAND_FILL);
  890. shader_editor->add_theme_constant_override("separation", 0);
  891. shader_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
  892. shader_editor->connect("show_warnings_panel", callable_mp(this, &ShaderEditor::_show_warnings_panel));
  893. shader_editor->connect("script_changed", callable_mp(this, &ShaderEditor::apply_shaders));
  894. EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ShaderEditor::_editor_settings_changed));
  895. ProjectSettingsEditor::get_singleton()->connect("confirmed", callable_mp(this, &ShaderEditor::_project_settings_changed));
  896. shader_editor->get_text_editor()->set_code_hint_draw_below(EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"));
  897. shader_editor->get_text_editor()->set_symbol_lookup_on_click_enabled(true);
  898. shader_editor->get_text_editor()->set_context_menu_enabled(false);
  899. shader_editor->get_text_editor()->connect("gui_input", callable_mp(this, &ShaderEditor::_text_edit_gui_input));
  900. shader_editor->update_editor_settings();
  901. context_menu = memnew(PopupMenu);
  902. add_child(context_menu);
  903. context_menu->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
  904. VBoxContainer *main_container = memnew(VBoxContainer);
  905. HBoxContainer *hbc = memnew(HBoxContainer);
  906. edit_menu = memnew(MenuButton);
  907. edit_menu->set_shortcut_context(this);
  908. edit_menu->set_text(TTR("Edit"));
  909. edit_menu->set_switch_on_hover(true);
  910. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
  911. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
  912. edit_menu->get_popup()->add_separator();
  913. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);
  914. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);
  915. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE);
  916. edit_menu->get_popup()->add_separator();
  917. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);
  918. edit_menu->get_popup()->add_separator();
  919. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP);
  920. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN);
  921. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT);
  922. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT);
  923. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);
  924. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
  925. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION);
  926. edit_menu->get_popup()->add_separator();
  927. edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE);
  928. edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
  929. search_menu = memnew(MenuButton);
  930. search_menu->set_shortcut_context(this);
  931. search_menu->set_text(TTR("Search"));
  932. search_menu->set_switch_on_hover(true);
  933. search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND);
  934. search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT);
  935. search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);
  936. search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);
  937. search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
  938. MenuButton *goto_menu = memnew(MenuButton);
  939. goto_menu->set_shortcut_context(this);
  940. goto_menu->set_text(TTR("Go To"));
  941. goto_menu->set_switch_on_hover(true);
  942. goto_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
  943. goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
  944. goto_menu->get_popup()->add_separator();
  945. bookmarks_menu = memnew(PopupMenu);
  946. bookmarks_menu->set_name("Bookmarks");
  947. goto_menu->get_popup()->add_child(bookmarks_menu);
  948. goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "Bookmarks");
  949. _update_bookmark_list();
  950. bookmarks_menu->connect("about_to_popup", callable_mp(this, &ShaderEditor::_update_bookmark_list));
  951. bookmarks_menu->connect("index_pressed", callable_mp(this, &ShaderEditor::_bookmark_item_pressed));
  952. help_menu = memnew(MenuButton);
  953. help_menu->set_text(TTR("Help"));
  954. help_menu->set_switch_on_hover(true);
  955. help_menu->get_popup()->add_item(TTR("Online Docs"), HELP_DOCS);
  956. help_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
  957. add_child(main_container);
  958. main_container->add_child(hbc);
  959. hbc->add_child(search_menu);
  960. hbc->add_child(edit_menu);
  961. hbc->add_child(goto_menu);
  962. hbc->add_child(help_menu);
  963. hbc->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("ScriptEditorPanel"), SNAME("EditorStyles")));
  964. VSplitContainer *editor_box = memnew(VSplitContainer);
  965. main_container->add_child(editor_box);
  966. editor_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
  967. editor_box->set_v_size_flags(SIZE_EXPAND_FILL);
  968. editor_box->add_child(shader_editor);
  969. FindReplaceBar *bar = memnew(FindReplaceBar);
  970. main_container->add_child(bar);
  971. bar->hide();
  972. shader_editor->set_find_replace_bar(bar);
  973. warnings_panel = memnew(RichTextLabel);
  974. warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));
  975. warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
  976. warnings_panel->set_meta_underline(true);
  977. warnings_panel->set_selection_enabled(true);
  978. warnings_panel->set_focus_mode(FOCUS_CLICK);
  979. warnings_panel->hide();
  980. warnings_panel->connect("meta_clicked", callable_mp(this, &ShaderEditor::_warning_clicked));
  981. editor_box->add_child(warnings_panel);
  982. shader_editor->set_warnings_panel(warnings_panel);
  983. goto_line_dialog = memnew(GotoLineDialog);
  984. add_child(goto_line_dialog);
  985. disk_changed = memnew(ConfirmationDialog);
  986. VBoxContainer *vbc = memnew(VBoxContainer);
  987. disk_changed->add_child(vbc);
  988. Label *dl = memnew(Label);
  989. dl->set_text(TTR("This shader has been modified on disk.\nWhat action should be taken?"));
  990. vbc->add_child(dl);
  991. disk_changed->connect("confirmed", callable_mp(this, &ShaderEditor::_reload));
  992. disk_changed->set_ok_button_text(TTR("Reload"));
  993. disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
  994. disk_changed->connect("custom_action", callable_mp(this, &ShaderEditor::save_external_data));
  995. add_child(disk_changed);
  996. _editor_settings_changed();
  997. }
  998. void ShaderEditorPlugin::_update_shader_list() {
  999. shader_list->clear();
  1000. for (uint32_t i = 0; i < edited_shaders.size(); i++) {
  1001. Ref<Resource> shader = edited_shaders[i].shader;
  1002. if (shader.is_null()) {
  1003. shader = edited_shaders[i].shader_inc;
  1004. }
  1005. String path = shader->get_path();
  1006. String text = path.get_file();
  1007. if (text.is_empty()) {
  1008. // This appears for newly created built-in shaders before saving the scene.
  1009. text = TTR("[unsaved]");
  1010. } else if (shader->is_built_in()) {
  1011. const String &shader_name = shader->get_name();
  1012. if (!shader_name.is_empty()) {
  1013. text = vformat("%s (%s)", shader_name, text.get_slice("::", 0));
  1014. }
  1015. }
  1016. bool unsaved = false;
  1017. if (edited_shaders[i].shader_editor) {
  1018. unsaved = edited_shaders[i].shader_editor->is_unsaved();
  1019. }
  1020. // TODO: Handle visual shaders too.
  1021. if (unsaved) {
  1022. text += "(*)";
  1023. }
  1024. String _class = shader->get_class();
  1025. if (!shader_list->has_theme_icon(_class, SNAME("EditorIcons"))) {
  1026. _class = "TextFile";
  1027. }
  1028. Ref<Texture2D> icon = shader_list->get_theme_icon(_class, SNAME("EditorIcons"));
  1029. shader_list->add_item(text, icon);
  1030. shader_list->set_item_tooltip(shader_list->get_item_count() - 1, path);
  1031. }
  1032. if (shader_tabs->get_tab_count()) {
  1033. shader_list->select(shader_tabs->get_current_tab());
  1034. }
  1035. for (int i = 1; i < FILE_MAX; i++) {
  1036. file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), edited_shaders.size() == 0);
  1037. }
  1038. _update_shader_list_status();
  1039. }
  1040. void ShaderEditorPlugin::_update_shader_list_status() {
  1041. for (int i = 0; i < shader_list->get_item_count(); i++) {
  1042. ShaderEditor *se = Object::cast_to<ShaderEditor>(shader_tabs->get_tab_control(i));
  1043. if (se) {
  1044. if (se->was_compilation_successful()) {
  1045. shader_list->set_item_tag_icon(i, Ref<Texture2D>());
  1046. } else {
  1047. shader_list->set_item_tag_icon(i, shader_list->get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
  1048. }
  1049. }
  1050. }
  1051. }
  1052. void ShaderEditorPlugin::_move_shader_tab(int p_from, int p_to) {
  1053. if (p_from == p_to) {
  1054. return;
  1055. }
  1056. EditedShader es = edited_shaders[p_from];
  1057. edited_shaders.remove_at(p_from);
  1058. edited_shaders.insert(p_to, es);
  1059. shader_tabs->move_child(shader_tabs->get_tab_control(p_from), p_to);
  1060. _update_shader_list();
  1061. }
  1062. void ShaderEditorPlugin::edit(Object *p_object) {
  1063. EditedShader es;
  1064. ShaderInclude *si = Object::cast_to<ShaderInclude>(p_object);
  1065. if (si != nullptr) {
  1066. for (uint32_t i = 0; i < edited_shaders.size(); i++) {
  1067. if (edited_shaders[i].shader_inc.ptr() == si) {
  1068. shader_tabs->set_current_tab(i);
  1069. shader_list->select(i);
  1070. return;
  1071. }
  1072. }
  1073. es.shader_inc = Ref<ShaderInclude>(si);
  1074. es.shader_editor = memnew(ShaderEditor);
  1075. es.shader_editor->edit(si);
  1076. shader_tabs->add_child(es.shader_editor);
  1077. es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
  1078. } else {
  1079. Shader *s = Object::cast_to<Shader>(p_object);
  1080. for (uint32_t i = 0; i < edited_shaders.size(); i++) {
  1081. if (edited_shaders[i].shader.ptr() == s) {
  1082. shader_tabs->set_current_tab(i);
  1083. shader_list->select(i);
  1084. return;
  1085. }
  1086. }
  1087. es.shader = Ref<Shader>(s);
  1088. Ref<VisualShader> vs = es.shader;
  1089. if (vs.is_valid()) {
  1090. es.visual_shader_editor = memnew(VisualShaderEditor);
  1091. shader_tabs->add_child(es.visual_shader_editor);
  1092. es.visual_shader_editor->edit(vs.ptr());
  1093. } else {
  1094. es.shader_editor = memnew(ShaderEditor);
  1095. shader_tabs->add_child(es.shader_editor);
  1096. es.shader_editor->edit(s);
  1097. es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
  1098. }
  1099. }
  1100. shader_tabs->set_current_tab(shader_tabs->get_tab_count() - 1);
  1101. edited_shaders.push_back(es);
  1102. _update_shader_list();
  1103. }
  1104. bool ShaderEditorPlugin::handles(Object *p_object) const {
  1105. return Object::cast_to<Shader>(p_object) != nullptr || Object::cast_to<ShaderInclude>(p_object) != nullptr;
  1106. }
  1107. void ShaderEditorPlugin::make_visible(bool p_visible) {
  1108. if (p_visible) {
  1109. EditorNode::get_singleton()->make_bottom_panel_item_visible(main_split);
  1110. }
  1111. }
  1112. void ShaderEditorPlugin::selected_notify() {
  1113. }
  1114. ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) {
  1115. for (uint32_t i = 0; i < edited_shaders.size(); i++) {
  1116. if (edited_shaders[i].shader == p_for_shader) {
  1117. return edited_shaders[i].shader_editor;
  1118. }
  1119. }
  1120. return nullptr;
  1121. }
  1122. VisualShaderEditor *ShaderEditorPlugin::get_visual_shader_editor(const Ref<Shader> &p_for_shader) {
  1123. for (uint32_t i = 0; i < edited_shaders.size(); i++) {
  1124. if (edited_shaders[i].shader == p_for_shader) {
  1125. return edited_shaders[i].visual_shader_editor;
  1126. }
  1127. }
  1128. return nullptr;
  1129. }
  1130. void ShaderEditorPlugin::save_external_data() {
  1131. for (uint32_t i = 0; i < edited_shaders.size(); i++) {
  1132. if (edited_shaders[i].shader_editor) {
  1133. edited_shaders[i].shader_editor->save_external_data();
  1134. }
  1135. }
  1136. _update_shader_list();
  1137. }
  1138. void ShaderEditorPlugin::apply_changes() {
  1139. for (uint32_t i = 0; i < edited_shaders.size(); i++) {
  1140. if (edited_shaders[i].shader_editor) {
  1141. edited_shaders[i].shader_editor->apply_shaders();
  1142. }
  1143. }
  1144. }
  1145. void ShaderEditorPlugin::_shader_selected(int p_index) {
  1146. if (edited_shaders[p_index].shader_editor) {
  1147. edited_shaders[p_index].shader_editor->validate_script();
  1148. }
  1149. shader_tabs->set_current_tab(p_index);
  1150. }
  1151. void ShaderEditorPlugin::_shader_list_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index) {
  1152. if (p_mouse_button_index == MouseButton::MIDDLE) {
  1153. _close_shader(p_item);
  1154. }
  1155. }
  1156. void ShaderEditorPlugin::_close_shader(int p_index) {
  1157. int index = shader_tabs->get_current_tab();
  1158. ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
  1159. Control *c = shader_tabs->get_tab_control(index);
  1160. memdelete(c);
  1161. edited_shaders.remove_at(index);
  1162. _update_shader_list();
  1163. EditorNode::get_singleton()->get_undo_redo()->clear_history(); // To prevent undo on deleted graphs.
  1164. }
  1165. void ShaderEditorPlugin::_resource_saved(Object *obj) {
  1166. // May have been renamed on save.
  1167. for (uint32_t i = 0; i < edited_shaders.size(); i++) {
  1168. if (edited_shaders[i].shader.ptr() == obj) {
  1169. _update_shader_list();
  1170. return;
  1171. }
  1172. }
  1173. }
  1174. void ShaderEditorPlugin::_menu_item_pressed(int p_index) {
  1175. switch (p_index) {
  1176. case FILE_NEW: {
  1177. String base_path = FileSystemDock::get_singleton()->get_current_path().get_base_dir();
  1178. shader_create_dialog->config(base_path.path_join("new_shader"), false, false, 0);
  1179. shader_create_dialog->popup_centered();
  1180. } break;
  1181. case FILE_NEW_INCLUDE: {
  1182. String base_path = FileSystemDock::get_singleton()->get_current_path().get_base_dir();
  1183. shader_create_dialog->config(base_path.path_join("new_shader"), false, false, 2);
  1184. shader_create_dialog->popup_centered();
  1185. } break;
  1186. case FILE_OPEN: {
  1187. InspectorDock::get_singleton()->open_resource("Shader");
  1188. } break;
  1189. case FILE_OPEN_INCLUDE: {
  1190. InspectorDock::get_singleton()->open_resource("ShaderInclude");
  1191. } break;
  1192. case FILE_SAVE: {
  1193. int index = shader_tabs->get_current_tab();
  1194. ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
  1195. if (edited_shaders[index].shader.is_valid()) {
  1196. EditorNode::get_singleton()->save_resource(edited_shaders[index].shader);
  1197. } else {
  1198. EditorNode::get_singleton()->save_resource(edited_shaders[index].shader_inc);
  1199. }
  1200. } break;
  1201. case FILE_SAVE_AS: {
  1202. int index = shader_tabs->get_current_tab();
  1203. ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
  1204. String path;
  1205. if (edited_shaders[index].shader.is_valid()) {
  1206. path = edited_shaders[index].shader->get_path();
  1207. if (!path.is_resource_file()) {
  1208. path = "";
  1209. }
  1210. EditorNode::get_singleton()->save_resource_as(edited_shaders[index].shader, path);
  1211. } else {
  1212. path = edited_shaders[index].shader_inc->get_path();
  1213. if (!path.is_resource_file()) {
  1214. path = "";
  1215. }
  1216. EditorNode::get_singleton()->save_resource_as(edited_shaders[index].shader_inc, path);
  1217. }
  1218. } break;
  1219. case FILE_INSPECT: {
  1220. int index = shader_tabs->get_current_tab();
  1221. ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
  1222. if (edited_shaders[index].shader.is_valid()) {
  1223. EditorNode::get_singleton()->push_item(edited_shaders[index].shader.ptr());
  1224. } else {
  1225. EditorNode::get_singleton()->push_item(edited_shaders[index].shader_inc.ptr());
  1226. }
  1227. } break;
  1228. case FILE_CLOSE: {
  1229. _close_shader(shader_tabs->get_current_tab());
  1230. } break;
  1231. }
  1232. }
  1233. void ShaderEditorPlugin::_shader_created(Ref<Shader> p_shader) {
  1234. EditorNode::get_singleton()->push_item(p_shader.ptr());
  1235. }
  1236. void ShaderEditorPlugin::_shader_include_created(Ref<ShaderInclude> p_shader_inc) {
  1237. EditorNode::get_singleton()->push_item(p_shader_inc.ptr());
  1238. }
  1239. Variant ShaderEditorPlugin::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
  1240. if (shader_list->get_item_count() == 0) {
  1241. return Variant();
  1242. }
  1243. int idx = shader_list->get_item_at_position(p_point);
  1244. if (idx < 0) {
  1245. return Variant();
  1246. }
  1247. HBoxContainer *drag_preview = memnew(HBoxContainer);
  1248. String preview_name = shader_list->get_item_text(idx);
  1249. Ref<Texture2D> preview_icon = shader_list->get_item_icon(idx);
  1250. if (!preview_icon.is_null()) {
  1251. TextureRect *tf = memnew(TextureRect);
  1252. tf->set_texture(preview_icon);
  1253. tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
  1254. drag_preview->add_child(tf);
  1255. }
  1256. Label *label = memnew(Label(preview_name));
  1257. drag_preview->add_child(label);
  1258. main_split->set_drag_preview(drag_preview);
  1259. Dictionary drag_data;
  1260. drag_data["type"] = "shader_list_element";
  1261. drag_data["shader_list_element"] = idx;
  1262. return drag_data;
  1263. }
  1264. bool ShaderEditorPlugin::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
  1265. Dictionary d = p_data;
  1266. if (!d.has("type")) {
  1267. return false;
  1268. }
  1269. if (String(d["type"]) == "shader_list_element") {
  1270. return true;
  1271. }
  1272. if (String(d["type"]) == "files") {
  1273. Vector<String> files = d["files"];
  1274. if (files.size() == 0) {
  1275. return false;
  1276. }
  1277. for (int i = 0; i < files.size(); i++) {
  1278. String file = files[i];
  1279. if (ResourceLoader::exists(file, "Shader")) {
  1280. Ref<Shader> shader = ResourceLoader::load(file);
  1281. if (shader.is_valid()) {
  1282. return true;
  1283. }
  1284. }
  1285. }
  1286. return false;
  1287. }
  1288. return false;
  1289. }
  1290. void ShaderEditorPlugin::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
  1291. if (!can_drop_data_fw(p_point, p_data, p_from)) {
  1292. return;
  1293. }
  1294. Dictionary d = p_data;
  1295. if (!d.has("type")) {
  1296. return;
  1297. }
  1298. if (String(d["type"]) == "shader_list_element") {
  1299. int idx = d["shader_list_element"];
  1300. int new_idx = shader_list->get_item_at_position(p_point);
  1301. _move_shader_tab(idx, new_idx);
  1302. return;
  1303. }
  1304. if (String(d["type"]) == "files") {
  1305. Vector<String> files = d["files"];
  1306. for (int i = 0; i < files.size(); i++) {
  1307. String file = files[i];
  1308. if (!ResourceLoader::exists(file, "Shader")) {
  1309. continue;
  1310. }
  1311. Ref<Resource> res = ResourceLoader::load(file);
  1312. if (res.is_valid()) {
  1313. edit(res.ptr());
  1314. }
  1315. }
  1316. }
  1317. }
  1318. void ShaderEditorPlugin::_bind_methods() {
  1319. ClassDB::bind_method(D_METHOD("_get_drag_data_fw", "point", "from"), &ShaderEditorPlugin::get_drag_data_fw);
  1320. ClassDB::bind_method(D_METHOD("_can_drop_data_fw", "point", "data", "from"), &ShaderEditorPlugin::can_drop_data_fw);
  1321. ClassDB::bind_method(D_METHOD("_drop_data_fw", "point", "data", "from"), &ShaderEditorPlugin::drop_data_fw);
  1322. }
  1323. ShaderEditorPlugin::ShaderEditorPlugin() {
  1324. main_split = memnew(HSplitContainer);
  1325. VBoxContainer *vb = memnew(VBoxContainer);
  1326. HBoxContainer *file_hb = memnew(HBoxContainer);
  1327. vb->add_child(file_hb);
  1328. file_menu = memnew(MenuButton);
  1329. file_menu->set_text(TTR("File"));
  1330. file_menu->get_popup()->add_item(TTR("New Shader"), FILE_NEW);
  1331. file_menu->get_popup()->add_item(TTR("New Shader Include"), FILE_NEW_INCLUDE);
  1332. file_menu->get_popup()->add_separator();
  1333. file_menu->get_popup()->add_item(TTR("Load Shader File"), FILE_OPEN);
  1334. file_menu->get_popup()->add_item(TTR("Load Shader Include File"), FILE_OPEN_INCLUDE);
  1335. file_menu->get_popup()->add_item(TTR("Save File"), FILE_SAVE);
  1336. file_menu->get_popup()->add_item(TTR("Save File As"), FILE_SAVE_AS);
  1337. file_menu->get_popup()->add_separator();
  1338. file_menu->get_popup()->add_item(TTR("Open File in Inspector"), FILE_INSPECT);
  1339. file_menu->get_popup()->add_separator();
  1340. file_menu->get_popup()->add_item(TTR("Close File"), FILE_CLOSE);
  1341. file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed));
  1342. file_hb->add_child(file_menu);
  1343. for (int i = 2; i < FILE_MAX; i++) {
  1344. file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), true);
  1345. }
  1346. shader_list = memnew(ItemList);
  1347. shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1348. vb->add_child(shader_list);
  1349. shader_list->connect("item_selected", callable_mp(this, &ShaderEditorPlugin::_shader_selected));
  1350. shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked));
  1351. shader_list->set_drag_forwarding(this);
  1352. main_split->add_child(vb);
  1353. vb->set_custom_minimum_size(Size2(200, 300) * EDSCALE);
  1354. shader_tabs = memnew(TabContainer);
  1355. shader_tabs->set_tabs_visible(false);
  1356. shader_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  1357. main_split->add_child(shader_tabs);
  1358. Ref<StyleBoxEmpty> empty;
  1359. empty.instantiate();
  1360. shader_tabs->add_theme_style_override("panel", empty);
  1361. button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader Editor"), main_split);
  1362. // Defer connect because Editor class is not in the binding system yet.
  1363. EditorNode::get_singleton()->call_deferred("connect", "resource_saved", callable_mp(this, &ShaderEditorPlugin::_resource_saved), CONNECT_DEFERRED);
  1364. shader_create_dialog = memnew(ShaderCreateDialog);
  1365. vb->add_child(shader_create_dialog);
  1366. shader_create_dialog->connect("shader_created", callable_mp(this, &ShaderEditorPlugin::_shader_created));
  1367. shader_create_dialog->connect("shader_include_created", callable_mp(this, &ShaderEditorPlugin::_shader_include_created));
  1368. }
  1369. ShaderEditorPlugin::~ShaderEditorPlugin() {
  1370. }