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