summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/object/script_language.h23
-rw-r--r--doc/classes/CodeEdit.xml6
-rw-r--r--doc/classes/CodeHighlighter.xml6
-rw-r--r--doc/classes/Control.xml2
-rw-r--r--doc/classes/CryptoKey.xml2
-rw-r--r--doc/classes/DisplayServer.xml4
-rw-r--r--doc/classes/EditorImportPlugin.xml2
-rw-r--r--doc/classes/Font.xml6
-rw-r--r--doc/classes/FontData.xml4
-rw-r--r--doc/classes/ItemList.xml2
-rw-r--r--doc/classes/ParticlesMaterial.xml4
-rw-r--r--doc/classes/RichTextLabel.xml2
-rw-r--r--doc/classes/ScrollContainer.xml4
-rw-r--r--doc/classes/String.xml2
-rw-r--r--doc/classes/TextEdit.xml2
-rw-r--r--doc/classes/TextParagraph.xml2
-rw-r--r--doc/classes/TextServer.xml6
-rw-r--r--doc/classes/UndoRedo.xml2
-rw-r--r--editor/animation_track_editor.cpp8
-rw-r--r--editor/editor_file_dialog.cpp2
-rw-r--r--editor/editor_help.cpp4
-rw-r--r--editor/editor_inspector.cpp4
-rw-r--r--editor/editor_settings.cpp40
-rw-r--r--editor/filesystem_dock.cpp2
-rw-r--r--editor/plugin_config_dialog.cpp54
-rw-r--r--editor/plugins/shader_editor_plugin.cpp13
-rw-r--r--editor/plugins/theme_editor_plugin.cpp520
-rw-r--r--editor/plugins/theme_editor_plugin.h14
-rw-r--r--editor/plugins/theme_editor_preview.cpp6
-rw-r--r--editor/project_manager.cpp4
-rw-r--r--editor/property_editor.cpp2
-rw-r--r--editor/script_create_dialog.cpp575
-rw-r--r--editor/script_create_dialog.h33
-rw-r--r--editor/template_builders.py95
-rw-r--r--main/main.cpp2
-rw-r--r--modules/enet/doc_classes/ENetMultiplayerPeer.xml2
-rw-r--r--modules/gdscript/SCsub2
-rw-r--r--modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd29
-rw-r--r--modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd32
-rw-r--r--modules/gdscript/editor_templates/EditorPlugin/plugin.gd11
-rw-r--r--modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd7
-rw-r--r--modules/gdscript/editor_templates/Node/default.gd11
-rw-r--r--modules/gdscript/editor_templates/Object/empty.gd3
-rw-r--r--modules/gdscript/editor_templates/SCsub16
-rw-r--r--modules/gdscript/gdscript.cpp14
-rw-r--r--modules/gdscript/gdscript.h101
-rw-r--r--modules/gdscript/gdscript_editor.cpp91
-rw-r--r--modules/mono/SCsub2
-rw-r--r--modules/mono/csharp_script.cpp65
-rw-r--r--modules/mono/csharp_script.h4
-rw-r--r--modules/mono/editor_templates/CharacterBody2D/basic_movement.cs41
-rw-r--r--modules/mono/editor_templates/CharacterBody3D/basic_movement.cs44
-rw-r--r--modules/mono/editor_templates/EditorPlugin/plugin.cs19
-rw-r--r--modules/mono/editor_templates/EditorScript/basic_editor_script.cs14
-rw-r--r--modules/mono/editor_templates/Node/default.cs19
-rw-r--r--modules/mono/editor_templates/Object/empty.cs9
-rw-r--r--modules/mono/editor_templates/SCsub16
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml78
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNode.xml34
-rw-r--r--modules/visual_script/visual_script.cpp15
-rw-r--r--modules/visual_script/visual_script.h79
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml2
-rw-r--r--modules/websocket/doc_classes/WebSocketClient.xml4
-rw-r--r--platform/android/java/build.gradle12
-rw-r--r--platform/windows/display_server_windows.cpp34
-rw-r--r--scene/gui/item_list.cpp2
-rw-r--r--scene/gui/item_list.h2
-rw-r--r--scene/gui/popup_menu.cpp10
-rw-r--r--scene/gui/rich_text_label.cpp2
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/gui/scroll_container.cpp8
-rw-r--r--scene/gui/scroll_container.h4
-rw-r--r--servers/audio_server.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp15
-rw-r--r--servers/rendering/renderer_rd/shader_compiler_rd.cpp81
-rw-r--r--servers/rendering/shader_language.cpp256
-rw-r--r--servers/rendering/shader_language.h22
-rw-r--r--servers/rendering/shader_warnings.cpp12
-rw-r--r--servers/rendering/shader_warnings.h7
79 files changed, 1702 insertions, 992 deletions
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 8d76cbf479..4754b01a07 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -274,13 +274,32 @@ public:
String message;
};
+ enum TemplateLocation {
+ TEMPLATE_BUILT_IN,
+ TEMPLATE_EDITOR,
+ TEMPLATE_PROJECT
+ };
+
+ struct ScriptTemplate {
+ String inherit = "Object";
+ String name;
+ String description;
+ String content;
+ int id = 0;
+ TemplateLocation origin = TemplateLocation::TEMPLATE_BUILT_IN;
+
+ String get_hash() const {
+ return itos(origin) + inherit + name;
+ }
+ };
+
void get_core_type_words(List<String> *p_core_type_words) const;
virtual void get_reserved_words(List<String> *p_words) const = 0;
virtual bool is_control_flow_keyword(String p_string) const = 0;
virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0;
virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;
- virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0;
- virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {}
+ virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); }
+ virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); }
virtual bool is_using_templates() { return false; }
virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const = 0;
virtual String validate_path(const String &p_path) const { return ""; }
diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml
index 59da64314b..697b0443f1 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -234,7 +234,7 @@
<method name="get_folded_lines" qualifiers="const">
<return type="int[]" />
<description>
- Return all lines that are current folded.
+ Returns all lines that are current folded.
</description>
</method>
<method name="get_text_for_code_completion" qualifiers="const">
@@ -288,7 +288,7 @@
<argument index="0" name="line" type="int" />
<argument index="1" name="column" type="int" default="-1" />
<description>
- Return delimiter index if [code]line[/code] [code]column[/code] is in a comment. If [code]column[/code] is not provided, will return delimiter index if the entire [code]line[/code] is a comment. Otherwise [code]-1[/code].
+ Returns delimiter index if [code]line[/code] [code]column[/code] is in a comment. If [code]column[/code] is not provided, will return delimiter index if the entire [code]line[/code] is a comment. Otherwise [code]-1[/code].
</description>
</method>
<method name="is_in_string" qualifiers="const">
@@ -296,7 +296,7 @@
<argument index="0" name="line" type="int" />
<argument index="1" name="column" type="int" default="-1" />
<description>
- Return the delimiter index if [code]line[/code] [code]column[/code] is in a string. If [code]column[/code] is not provided, will return the delimiter index if the entire [code]line[/code] is a string. Otherwise [code]-1[/code].
+ Returns the delimiter index if [code]line[/code] [code]column[/code] is in a string. If [code]column[/code] is not provided, will return the delimiter index if the entire [code]line[/code] is a string. Otherwise [code]-1[/code].
</description>
</method>
<method name="is_line_bookmarked" qualifiers="const">
diff --git a/doc/classes/CodeHighlighter.xml b/doc/classes/CodeHighlighter.xml
index 064be0c29d..efd8f618e7 100644
--- a/doc/classes/CodeHighlighter.xml
+++ b/doc/classes/CodeHighlighter.xml
@@ -76,21 +76,21 @@
<return type="bool" />
<argument index="0" name="start_key" type="String" />
<description>
- Return [code]true[/code] if the start key exists, else [code]false[/code].
+ Returns [code]true[/code] if the start key exists, else [code]false[/code].
</description>
</method>
<method name="has_keyword_color" qualifiers="const">
<return type="bool" />
<argument index="0" name="keyword" type="String" />
<description>
- Return [code]true[/code] if the keyword exists, else [code]false[/code].
+ Returns [code]true[/code] if the keyword exists, else [code]false[/code].
</description>
</method>
<method name="has_member_keyword_color" qualifiers="const">
<return type="bool" />
<argument index="0" name="member_keyword" type="String" />
<description>
- Return [code]true[/code] if the member keyword exists, else [code]false[/code].
+ Returns [code]true[/code] if the member keyword exists, else [code]false[/code].
</description>
</method>
<method name="remove_color_region">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 39f8980b2c..3d8199831d 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -201,7 +201,7 @@
<argument index="1" name="text" type="String" />
<description>
User defined BiDi algorithm override function.
- Return [code]Array[/code] of [code]Vector2i[/code] text ranges, in the left-to-right order. Ranges should cover full source [code]text[/code] without overlaps. BiDi algorithm will be used on each range separately.
+ Returns [code]Array[/code] of [code]Vector2i[/code] text ranges, in the left-to-right order. Ranges should cover full source [code]text[/code] without overlaps. BiDi algorithm will be used on each range separately.
</description>
</method>
<method name="accept_event">
diff --git a/doc/classes/CryptoKey.xml b/doc/classes/CryptoKey.xml
index 111da858ea..9e56a41fee 100644
--- a/doc/classes/CryptoKey.xml
+++ b/doc/classes/CryptoKey.xml
@@ -14,7 +14,7 @@
<method name="is_public_only" qualifiers="const">
<return type="bool" />
<description>
- Return [code]true[/code] if this CryptoKey only has the public part, and not the private one.
+ Returns [code]true[/code] if this CryptoKey only has the public part, and not the private one.
</description>
</method>
<method name="load">
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 1ca69057b4..a41875385c 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -392,7 +392,7 @@
<method name="screen_get_max_scale" qualifiers="const">
<return type="float" />
<description>
- Return the greatest scale factor of all screens.
+ Returns the greatest scale factor of all screens.
[b]Note:[/b] On macOS returned value is [code]2.0[/code] if there is at least one hiDPI (Retina) screen in the system, and [code]1.0[/code] in all other cases.
[b]Note:[/b] This method is implemented on macOS.
</description>
@@ -413,7 +413,7 @@
<return type="float" />
<argument index="0" name="screen" type="int" default="-1" />
<description>
- Return the scale factor of the specified screen by index.
+ Returns the scale factor of the specified screen by index.
[b]Note:[/b] On macOS returned value is [code]2.0[/code] for hiDPI (Retina) screen, and [code]1.0[/code] for all other cases.
[b]Note:[/b] This method is implemented on macOS.
</description>
diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml
index 1f98781bbd..50c227dc0a 100644
--- a/doc/classes/EditorImportPlugin.xml
+++ b/doc/classes/EditorImportPlugin.xml
@@ -163,7 +163,7 @@
}
[/csharp]
[/codeblocks]
- Return [code]true[/code] to make all options always visible.
+ Returns [code]true[/code] to make all options always visible.
</description>
</method>
<method name="_get_preset_count" qualifiers="virtual const">
diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml
index 275d6b6577..3537fc0b66 100644
--- a/doc/classes/Font.xml
+++ b/doc/classes/Font.xml
@@ -222,7 +222,7 @@
<return type="float" />
<argument index="0" name="size" type="int" default="16" />
<description>
- Return average pixel offset of the underline below the baseline.
+ Returns average pixel offset of the underline below the baseline.
[b]Note:[/b] Real underline position of the string is context-dependent and can be significantly different from the value returned by this function. Use it only as rough estimate.
</description>
</method>
@@ -230,7 +230,7 @@
<return type="float" />
<argument index="0" name="size" type="int" default="16" />
<description>
- Return average thickness of the underline.
+ Returns average thickness of the underline.
[b]Note:[/b] Real underline thickness of the string is context-dependent and can be significantly different from the value returned by this function. Use it only as rough estimate.
</description>
</method>
@@ -238,7 +238,7 @@
<return type="bool" />
<argument index="0" name="char" type="int" />
<description>
- Return [code]true[/code] if a Unicode [code]char[/code] is available in the font.
+ Returns [code]true[/code] if a Unicode [code]char[/code] is available in the font.
</description>
</method>
<method name="remove_data">
diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml
index 9889b8945a..0c8386784e 100644
--- a/doc/classes/FontData.xml
+++ b/doc/classes/FontData.xml
@@ -259,7 +259,7 @@
<return type="Array" />
<argument index="0" name="cache_index" type="int" />
<description>
- Return list of the font sizes in the cache. Each size is [code]Vector2i[/code] with font size and outline size.
+ Returns list of the font sizes in the cache. Each size is [code]Vector2i[/code] with font size and outline size.
</description>
</method>
<method name="get_spacing" qualifiers="const">
@@ -343,7 +343,7 @@
<return type="bool" />
<argument index="0" name="char" type="int" />
<description>
- Return [code]true[/code] if a Unicode [code]char[/code] is available in the font.
+ Returns [code]true[/code] if a Unicode [code]char[/code] is available in the font.
</description>
</method>
<method name="is_antialiased" qualifiers="const">
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index 7263687ea8..7af19301f6 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -155,7 +155,7 @@
Returns an array with the indexes of the selected items.
</description>
</method>
- <method name="get_v_scroll">
+ <method name="get_v_scroll_bar">
<return type="VScrollBar" />
<description>
Returns the [Object] ID associated with the list.
diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml
index 718a1ac80d..6cf0ea2a93 100644
--- a/doc/classes/ParticlesMaterial.xml
+++ b/doc/classes/ParticlesMaterial.xml
@@ -15,14 +15,14 @@
<return type="float" />
<argument index="0" name="param" type="int" enum="ParticlesMaterial.Parameter" />
<description>
- Return the maximum value range for the given parameter.
+ Returns the maximum value range for the given parameter.
</description>
</method>
<method name="get_param_min" qualifiers="const">
<return type="float" />
<argument index="0" name="param" type="int" enum="ParticlesMaterial.Parameter" />
<description>
- Return the minimum value range for the given parameter.
+ Returns the minimum value range for the given parameter.
</description>
</method>
<method name="get_param_texture" qualifiers="const">
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 132ede7a0a..c8ed373271 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -97,7 +97,7 @@
Returns the total number of characters from text tags. Does not include BBCodes.
</description>
</method>
- <method name="get_v_scroll">
+ <method name="get_v_scroll_bar">
<return type="VScrollBar" />
<description>
Returns the vertical scrollbar.
diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml
index 676c62c93c..948c052a20 100644
--- a/doc/classes/ScrollContainer.xml
+++ b/doc/classes/ScrollContainer.xml
@@ -24,14 +24,14 @@
[/codeblock]
</description>
</method>
- <method name="get_h_scrollbar">
+ <method name="get_h_scroll_bar">
<return type="HScrollBar" />
<description>
Returns the horizontal scrollbar [HScrollBar] of this [ScrollContainer].
[b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to disable or hide a scrollbar, you can use [member horizontal_scroll_mode].
</description>
</method>
- <method name="get_v_scrollbar">
+ <method name="get_v_scroll_bar">
<return type="VScrollBar" />
<description>
Returns the vertical scrollbar [VScrollBar] of this [ScrollContainer].
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index cd6fc10931..d86c577e5e 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -340,7 +340,7 @@
<return type="String" />
<argument index="0" name="parts" type="PackedStringArray" />
<description>
- Return a [String] which is the concatenation of the [code]parts[/code]. The separator between elements is the string providing this method.
+ Returns a [String] which is the concatenation of the [code]parts[/code]. The separator between elements is the string providing this method.
Example:
[codeblocks]
[gdscript]
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index d6738bfb89..26ad20e85b 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -201,7 +201,7 @@
<method name="get_last_full_visible_line" qualifiers="const">
<return type="int" />
<description>
- Return the last visible line. Use [method get_last_full_visible_line_wrap_index] for the wrap index.
+ Returns the last visible line. Use [method get_last_full_visible_line_wrap_index] for the wrap index.
</description>
</method>
<method name="get_last_full_visible_line_wrap_index" qualifiers="const">
diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml
index 6a510e4a74..ff193a0e4b 100644
--- a/doc/classes/TextParagraph.xml
+++ b/doc/classes/TextParagraph.xml
@@ -113,7 +113,7 @@
<method name="get_dropcap_rid" qualifiers="const">
<return type="RID" />
<description>
- Return drop cap text buffer RID.
+ Returns drop cap text buffer RID.
</description>
</method>
<method name="get_dropcap_size" qualifiers="const">
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index d7447fcaa2..443716435a 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -244,7 +244,7 @@
<return type="int" />
<argument index="0" name="font_rid" type="RID" />
<description>
- Return the width of the range around the shape between the minimum and maximum representable signed distance.
+ Returns the width of the range around the shape between the minimum and maximum representable signed distance.
</description>
</method>
<method name="font_get_msdf_size" qualifiers="const">
@@ -295,7 +295,7 @@
<return type="Array" />
<argument index="0" name="font_rid" type="RID" />
<description>
- Return list of the font sizes in the cache. Each size is [code]Vector2i[/code] with font size and outline size.
+ Returns list of the font sizes in the cache. Each size is [code]Vector2i[/code] with font size and outline size.
</description>
</method>
<method name="font_get_spacing" qualifiers="const">
@@ -382,7 +382,7 @@
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="char" type="int" />
<description>
- Return [code]true[/code] if a Unicode [code]char[/code] is available in the font.
+ Returns [code]true[/code] if a Unicode [code]char[/code] is available in the font.
</description>
</method>
<method name="font_is_antialiased" qualifiers="const">
diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml
index 044460f569..0bb64fb845 100644
--- a/doc/classes/UndoRedo.xml
+++ b/doc/classes/UndoRedo.xml
@@ -162,7 +162,7 @@
<method name="get_history_count">
<return type="int" />
<description>
- Return how many elements are in the history.
+ Returns how many elements are in the history.
</description>
</method>
<method name="get_version" qualifiers="const">
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index e7251662b7..be4a56c5cd 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -3390,7 +3390,7 @@ Dictionary AnimationTrackEditor::get_state() const {
state["fps_mode"] = timeline->is_using_fps();
state["zoom"] = zoom->get_value();
state["offset"] = timeline->get_value();
- state["v_scroll"] = scroll->get_v_scrollbar()->get_value();
+ state["v_scroll"] = scroll->get_v_scroll_bar()->get_value();
return state;
}
@@ -3418,9 +3418,9 @@ void AnimationTrackEditor::set_state(const Dictionary &p_state) {
timeline->set_value(0);
}
if (p_state.has("v_scroll")) {
- scroll->get_v_scrollbar()->set_value(p_state["v_scroll"]);
+ scroll->get_v_scroll_bar()->set_value(p_state["v_scroll"]);
} else {
- scroll->get_v_scrollbar()->set_value(0);
+ scroll->get_v_scroll_bar()->set_value(0);
}
}
@@ -6091,7 +6091,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
scroll = memnew(ScrollContainer);
timeline_vbox->add_child(scroll);
scroll->set_v_size_flags(SIZE_EXPAND_FILL);
- VScrollBar *sb = scroll->get_v_scrollbar();
+ VScrollBar *sb = scroll->get_v_scroll_bar();
scroll->remove_child(sb);
timeline_scroll->add_child(sb); // Move here so timeline and tracks are always aligned.
scroll->connect("gui_input", callable_mp(this, &AnimationTrackEditor::_scroll_input));
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index a35458d284..eb14c68d72 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -723,7 +723,7 @@ void EditorFileDialog::update_file_list() {
item_list->clear();
// Scroll back to the top after opening a directory
- item_list->get_v_scroll()->set_value(0);
+ item_list->get_v_scroll_bar()->set_value(0);
if (display_mode == DISPLAY_THUMBNAILS) {
item_list->set_max_columns(0);
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index fc1e7ef389..adee4a2ed2 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1798,11 +1798,11 @@ void EditorHelp::search_again(bool p_search_previous) {
}
int EditorHelp::get_scroll() const {
- return class_desc->get_v_scroll()->get_value();
+ return class_desc->get_v_scroll_bar()->get_value();
}
void EditorHelp::set_scroll(int p_scroll) {
- class_desc->get_v_scroll()->set_value(p_scroll);
+ class_desc->get_v_scroll_bar()->set_value(p_scroll);
}
void EditorHelp::update_toggle_scripts_button() {
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 2643f6e31c..1108d8246d 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3349,7 +3349,7 @@ void EditorInspector::_notification(int p_what) {
if (p_what == NOTIFICATION_PROCESS) {
if (update_scroll_request >= 0) {
- get_v_scrollbar()->call_deferred(SNAME("set_value"), update_scroll_request);
+ get_v_scroll_bar()->call_deferred(SNAME("set_value"), update_scroll_request);
update_scroll_request = -1;
}
if (refresh_countdown > 0) {
@@ -3570,7 +3570,7 @@ EditorInspector::EditorInspector() {
deletable_properties = false;
property_clipboard = Variant();
- get_v_scrollbar()->connect("value_changed", callable_mp(this, &EditorInspector::_vscroll_changed));
+ get_v_scroll_bar()->connect("value_changed", callable_mp(this, &EditorInspector::_vscroll_changed));
update_scroll_request = -1;
if (EditorSettings::get_singleton()) {
refresh_countdown = float(EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval"));
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 402df52784..0e37abbc10 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -781,43 +781,6 @@ bool EditorSettings::_is_default_text_editor_theme(String p_theme_name) {
return p_theme_name == "default" || p_theme_name == "godot 2" || p_theme_name == "custom";
}
-static Dictionary _get_builtin_script_templates() {
- Dictionary templates;
-
- // No Comments
- templates["no_comments.gd"] =
- "extends %BASE%\n"
- "\n"
- "\n"
- "func _ready()%VOID_RETURN%:\n"
- "%TS%pass\n";
-
- // Empty
- templates["empty.gd"] =
- "extends %BASE%"
- "\n"
- "\n";
-
- return templates;
-}
-
-static void _create_script_templates(const String &p_path) {
- Dictionary templates = _get_builtin_script_templates();
- List<Variant> keys;
- templates.get_key_list(&keys);
- FileAccessRef file = FileAccess::create(FileAccess::ACCESS_FILESYSTEM);
- DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- dir->change_dir(p_path);
- for (int i = 0; i < keys.size(); i++) {
- if (!dir->file_exists(keys[i])) {
- Error err = file->reopen(p_path.plus_file((String)keys[i]), FileAccess::WRITE);
- ERR_FAIL_COND(err != OK);
- file->store_string(templates[keys[i]]);
- file->close();
- }
- }
-}
-
// PUBLIC METHODS
EditorSettings *EditorSettings::get_singleton() {
@@ -852,10 +815,7 @@ void EditorSettings::create() {
}
if (EditorPaths::get_singleton()->are_paths_valid()) {
- _create_script_templates(EditorPaths::get_singleton()->get_config_dir().plus_file("script_templates"));
-
// Validate editor config file.
-
DirAccessRef dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir());
String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres";
config_file_path = EditorPaths::get_singleton()->get_config_dir().plus_file(config_file_name);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 72fc9fbc6e..f0d36eb26c 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -529,7 +529,7 @@ void FileSystemDock::_navigate_to_path(const String &p_path, bool p_select_in_fa
_update_tree(_compute_uncollapsed_paths(), false, p_select_in_favorites, true);
if (display_mode == DISPLAY_MODE_SPLIT) {
_update_file_list(false);
- files->get_v_scroll()->set_value(0);
+ files->get_v_scroll_bar()->set_value(0);
}
String file_name = p_path.get_file();
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index ad22aafb2b..e6e53d2e29 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -36,12 +36,6 @@
#include "editor/editor_plugin.h"
#include "editor/editor_scale.h"
#include "editor/project_settings_editor.h"
-#include "scene/gui/grid_container.h"
-
-#include "modules/modules_enabled.gen.h" // For gdscript.
-#ifdef MODULE_GDSCRIPT_ENABLED
-#include "modules/gdscript/gdscript.h"
-#endif
void PluginConfigDialog::_clear_fields() {
name_edit->set_text("");
@@ -76,42 +70,16 @@ void PluginConfigDialog::_on_confirmed() {
String lang_name = ScriptServer::get_language(lang_idx)->get_name();
Ref<Script> script;
-
- // TODO Use script templates. Right now, this code won't add the 'tool' annotation to other languages.
- // TODO Better support script languages with named classes (has_named_classes).
-
- // FIXME: It's hacky to have hardcoded access to the GDScript module here.
- // The editor code should not have to know what languages are enabled.
-#ifdef MODULE_GDSCRIPT_ENABLED
- if (lang_name == GDScriptLanguage::get_singleton()->get_name()) {
- // Hard-coded GDScript template to keep usability until we use script templates.
- Ref<Script> gdscript = memnew(GDScript);
- gdscript->set_source_code(
- "@tool\n"
- "extends EditorPlugin\n"
- "\n"
- "\n"
- "func _enter_tree()%VOID_RETURN%:\n"
- "%TS%pass\n"
- "\n"
- "\n"
- "func _exit_tree()%VOID_RETURN%:\n"
- "%TS%pass\n");
- GDScriptLanguage::get_singleton()->make_template("", "", gdscript);
- String script_path = path.plus_file(script_edit->get_text());
- gdscript->set_path(script_path);
- ResourceSaver::save(script_path, gdscript);
- script = gdscript;
- } else {
-#endif
- String script_path = path.plus_file(script_edit->get_text());
- String class_name = script_path.get_file().get_basename();
- script = ScriptServer::get_language(lang_idx)->get_template(class_name, "EditorPlugin");
- script->set_path(script_path);
- ResourceSaver::save(script_path, script);
-#ifdef MODULE_GDSCRIPT_ENABLED
+ String script_path = path.plus_file(script_edit->get_text());
+ String class_name = script_path.get_file().get_basename();
+ String template_content = "";
+ Vector<ScriptLanguage::ScriptTemplate> templates = ScriptServer::get_language(lang_idx)->get_built_in_templates("EditorPlugin");
+ if (templates.size() > 0) {
+ template_content = templates.get(0).content;
}
-#endif
+ script = ScriptServer::get_language(lang_idx)->make_template(template_content, class_name, "EditorPlugin");
+ script->set_path(script_path);
+ ResourceSaver::save(script_path, script);
emit_signal(SNAME("plugin_ready"), script.operator->(), active_edit->is_pressed() ? _to_absolute_plugin_path(subfolder_edit->get_text()) : "");
} else {
@@ -331,11 +299,9 @@ PluginConfigDialog::PluginConfigDialog() {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptLanguage *lang = ScriptServer::get_language(i);
script_option_edit->add_item(lang->get_name());
-#ifdef MODULE_GDSCRIPT_ENABLED
- if (lang == GDScriptLanguage::get_singleton()) {
+ if (lang->get_name() == "GDScript") {
default_lang = i;
}
-#endif
}
script_option_edit->select(default_lang);
grid->add_child(script_option_edit);
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index d5ee52dfab..26c8b02a64 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -293,15 +293,20 @@ void ShaderTextEditor::_update_warning_panel() {
}
warning_count++;
+ int line = w.get_line();
// First cell.
warnings_panel->push_cell();
- warnings_panel->push_meta(w.get_line() - 1);
warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- warnings_panel->add_text(TTR("Line") + " " + itos(w.get_line()));
- warnings_panel->add_text(" (" + w.get_name() + "):");
+ if (line != -1) {
+ warnings_panel->push_meta(line - 1);
+ warnings_panel->add_text(TTR("Line") + " " + itos(line));
+ warnings_panel->add_text(" (" + w.get_name() + "):");
+ warnings_panel->pop(); // Meta goto.
+ } else {
+ warnings_panel->add_text(w.get_name() + ":");
+ }
warnings_panel->pop(); // Color.
- warnings_panel->pop(); // Meta goto.
warnings_panel->pop(); // Cell.
// Second cell.
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index b5d2b571f5..9f10f76dc9 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -754,8 +754,9 @@ void ThemeItemImportTree::_import_selected() {
return;
}
- // Prevent changes from immediately being reported while the operation is still ongoing.
- edited_theme->_freeze_change_propagation();
+ Ref<Theme> old_snapshot = edited_theme->duplicate();
+ Ref<Theme> new_snapshot = edited_theme->duplicate();
+
ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size() + 2);
int idx = 0;
@@ -808,7 +809,7 @@ void ThemeItemImportTree::_import_selected() {
}
}
- edited_theme->set_theme_item(ti.data_type, ti.item_name, ti.type_name, item_value);
+ new_snapshot->set_theme_item(ti.data_type, ti.item_name, ti.type_name, item_value);
}
idx++;
@@ -816,12 +817,24 @@ void ThemeItemImportTree::_import_selected() {
// Allow changes to be reported now that the operation is finished.
ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Updating the editor"), idx++);
- edited_theme->_unfreeze_and_propagate_changes();
+
// Make sure the task is not ended before the editor freezes to update the Inspector.
ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Finalizing"), idx++);
ProgressDialog::get_singleton()->end_task("import_theme_items");
- emit_signal(SNAME("items_imported"));
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Import Theme Items"));
+
+ ur->add_do_method(*edited_theme, "clear");
+ ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
+ ur->add_undo_method(*edited_theme, "clear");
+ ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
+
+ ur->add_do_method(this, "emit_signal", SNAME("items_imported"));
+ ur->add_undo_method(this, "emit_signal", SNAME("items_imported"));
+
+ ur->commit_action();
}
void ThemeItemImportTree::set_edited_theme(const Ref<Theme> &p_theme) {
@@ -1296,6 +1309,7 @@ void ThemeItemEditorDialog::_update_edit_types() {
edit_items_message->set_text(TTR("Select a theme type from the list to edit its items.\nYou can add a custom type or import a type with its items from another theme."));
edit_items_message->show();
}
+
_update_edit_item_tree(selected_type);
}
@@ -1475,19 +1489,25 @@ void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_colu
String item_name = item->get_text(0);
int data_type = item->get_parent()->get_metadata(0);
_open_rename_theme_item_dialog((Theme::DataType)data_type, item_name);
+ _update_edit_item_tree(edited_item_type);
} break;
case ITEMS_TREE_REMOVE_ITEM: {
String item_name = item->get_text(0);
int data_type = item->get_parent()->get_metadata(0);
- edited_theme->clear_theme_item((Theme::DataType)data_type, item_name, edited_item_type);
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Remove Theme Item"));
+ ur->add_do_method(*edited_theme, "clear_theme_item", (Theme::DataType)data_type, item_name, edited_item_type);
+ ur->add_undo_method(*edited_theme, "set_theme_item", (Theme::DataType)data_type, item_name, edited_item_type, edited_theme->get_theme_item((Theme::DataType)data_type, item_name, edited_item_type));
+ ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
+ ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
+ ur->commit_action();
} break;
case ITEMS_TREE_REMOVE_DATA_TYPE: {
int data_type = item->get_metadata(0);
_remove_data_type_items((Theme::DataType)data_type, edited_item_type);
} break;
}
-
- _update_edit_item_tree(edited_item_type);
}
void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) {
@@ -1500,57 +1520,91 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) {
edited_theme->add_font_size_type(new_type);
edited_theme->add_color_type(new_type);
edited_theme->add_constant_type(new_type);
+
_update_edit_types();
- // Force emit a change so that other parts of the editor can update.
edited_theme->emit_changed();
}
void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Create Theme Item"));
+
switch (p_data_type) {
case Theme::DATA_TYPE_ICON:
- edited_theme->set_icon(p_item_name, p_item_type, Ref<Texture2D>());
+ ur->add_do_method(*edited_theme, "set_icon", p_item_name, p_item_type, Ref<Texture2D>());
+ ur->add_undo_method(*edited_theme, "clear_icon", p_item_name, p_item_type);
break;
case Theme::DATA_TYPE_STYLEBOX:
- edited_theme->set_stylebox(p_item_name, p_item_type, Ref<StyleBox>());
+ ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, p_item_type, Ref<StyleBox>());
+ ur->add_undo_method(*edited_theme, "clear_stylebox", p_item_name, p_item_type);
+
+ if (theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(p_item_name, p_item_type))) {
+ ur->add_undo_method(theme_type_editor, "_unpin_leading_stylebox");
+ }
break;
case Theme::DATA_TYPE_FONT:
- edited_theme->set_font(p_item_name, p_item_type, Ref<Font>());
+ ur->add_do_method(*edited_theme, "set_font", p_item_name, p_item_type, Ref<Font>());
+ ur->add_undo_method(*edited_theme, "clear_font", p_item_name, p_item_type);
break;
case Theme::DATA_TYPE_FONT_SIZE:
- edited_theme->set_font_size(p_item_name, p_item_type, -1);
+ ur->add_do_method(*edited_theme, "set_font_size", p_item_name, p_item_type, -1);
+ ur->add_undo_method(*edited_theme, "clear_font_size", p_item_name, p_item_type);
break;
case Theme::DATA_TYPE_COLOR:
- edited_theme->set_color(p_item_name, p_item_type, Color());
+ ur->add_do_method(*edited_theme, "set_color", p_item_name, p_item_type, Color());
+ ur->add_undo_method(*edited_theme, "clear_color", p_item_name, p_item_type);
break;
case Theme::DATA_TYPE_CONSTANT:
- edited_theme->set_constant(p_item_name, p_item_type, 0);
+ ur->add_do_method(*edited_theme, "set_constant", p_item_name, p_item_type, 0);
+ ur->add_undo_method(*edited_theme, "clear_constant", p_item_name, p_item_type);
break;
case Theme::DATA_TYPE_MAX:
break; // Can't happen, but silences warning.
}
+
+ ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
+ ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
+ ur->commit_action();
}
void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type, String p_item_type) {
List<StringName> names;
- // Prevent changes from immediately being reported while the operation is still ongoing.
- edited_theme->_freeze_change_propagation();
+ Ref<Theme> old_snapshot = edited_theme->duplicate();
+ Ref<Theme> new_snapshot = edited_theme->duplicate();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Remove Data Type Items From Theme"));
- edited_theme->get_theme_item_list(p_data_type, p_item_type, &names);
+ new_snapshot->get_theme_item_list(p_data_type, p_item_type, &names);
for (const StringName &E : names) {
- edited_theme->clear_theme_item(p_data_type, E, p_item_type);
+ new_snapshot->clear_theme_item(p_data_type, E, edited_item_type);
+
+ if (p_data_type == Theme::DATA_TYPE_STYLEBOX && theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(E, p_item_type))) {
+ ur->add_do_method(theme_type_editor, "_unpin_leading_stylebox");
+ ur->add_undo_method(theme_type_editor, "_pin_leading_stylebox", E, edited_theme->get_stylebox(E, p_item_type));
+ }
}
- // Allow changes to be reported now that the operation is finished.
- edited_theme->_unfreeze_and_propagate_changes();
+ ur->add_do_method(*edited_theme, "clear");
+ ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
+ ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
+
+ ur->add_do_method(theme_type_editor, "_update_edit_item_tree", edited_item_type);
+ ur->add_undo_method(theme_type_editor, "_update_edit_item_tree", edited_item_type);
+
+ ur->commit_action();
}
void ThemeItemEditorDialog::_remove_class_items() {
List<StringName> names;
- // Prevent changes from immediately being reported while the operation is still ongoing.
- edited_theme->_freeze_change_propagation();
+ Ref<Theme> old_snapshot = edited_theme->duplicate();
+ Ref<Theme> new_snapshot = edited_theme->duplicate();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Remove Class Items From Theme"));
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
Theme::DataType data_type = (Theme::DataType)dt;
@@ -1558,62 +1612,95 @@ void ThemeItemEditorDialog::_remove_class_items() {
names.clear();
Theme::get_default()->get_theme_item_list(data_type, edited_item_type, &names);
for (const StringName &E : names) {
- if (edited_theme->has_theme_item_nocheck(data_type, E, edited_item_type)) {
- edited_theme->clear_theme_item(data_type, E, edited_item_type);
+ if (new_snapshot->has_theme_item_nocheck(data_type, E, edited_item_type)) {
+ new_snapshot->clear_theme_item(data_type, E, edited_item_type);
+
+ if (dt == Theme::DATA_TYPE_STYLEBOX && theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(E, edited_item_type))) {
+ ur->add_do_method(theme_type_editor, "_unpin_leading_stylebox");
+ ur->add_undo_method(theme_type_editor, "_pin_leading_stylebox", E, edited_theme->get_stylebox(E, edited_item_type));
+ }
}
}
}
- // Allow changes to be reported now that the operation is finished.
- edited_theme->_unfreeze_and_propagate_changes();
+ ur->add_do_method(*edited_theme, "clear");
+ ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
+ ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
+
+ ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
+ ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
- _update_edit_item_tree(edited_item_type);
+ ur->commit_action();
}
void ThemeItemEditorDialog::_remove_custom_items() {
List<StringName> names;
- // Prevent changes from immediately being reported while the operation is still ongoing.
- edited_theme->_freeze_change_propagation();
+ Ref<Theme> old_snapshot = edited_theme->duplicate();
+ Ref<Theme> new_snapshot = edited_theme->duplicate();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Remove Custom Items From Theme"));
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
Theme::DataType data_type = (Theme::DataType)dt;
names.clear();
- edited_theme->get_theme_item_list(data_type, edited_item_type, &names);
+ new_snapshot->get_theme_item_list(data_type, edited_item_type, &names);
for (const StringName &E : names) {
if (!Theme::get_default()->has_theme_item_nocheck(data_type, E, edited_item_type)) {
- edited_theme->clear_theme_item(data_type, E, edited_item_type);
+ new_snapshot->clear_theme_item(data_type, E, edited_item_type);
+
+ if (dt == Theme::DATA_TYPE_STYLEBOX && theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(E, edited_item_type))) {
+ ur->add_do_method(theme_type_editor, "_unpin_leading_stylebox");
+ ur->add_undo_method(theme_type_editor, "_pin_leading_stylebox", E, edited_theme->get_stylebox(E, edited_item_type));
+ }
}
}
}
- // Allow changes to be reported now that the operation is finished.
- edited_theme->_unfreeze_and_propagate_changes();
+ ur->add_do_method(*edited_theme, "clear");
+ ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
+ ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
- _update_edit_item_tree(edited_item_type);
+ ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
+ ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
+
+ ur->commit_action();
}
void ThemeItemEditorDialog::_remove_all_items() {
List<StringName> names;
- // Prevent changes from immediately being reported while the operation is still ongoing.
- edited_theme->_freeze_change_propagation();
+ Ref<Theme> old_snapshot = edited_theme->duplicate();
+ Ref<Theme> new_snapshot = edited_theme->duplicate();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Remove All Items From Theme"));
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
Theme::DataType data_type = (Theme::DataType)dt;
names.clear();
- edited_theme->get_theme_item_list(data_type, edited_item_type, &names);
+ new_snapshot->get_theme_item_list(data_type, edited_item_type, &names);
for (const StringName &E : names) {
- edited_theme->clear_theme_item(data_type, E, edited_item_type);
+ new_snapshot->clear_theme_item(data_type, E, edited_item_type);
+
+ if (dt == Theme::DATA_TYPE_STYLEBOX && theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(E, edited_item_type))) {
+ ur->add_do_method(theme_type_editor, "_unpin_leading_stylebox");
+ ur->add_undo_method(theme_type_editor, "_pin_leading_stylebox", E, edited_theme->get_stylebox(E, edited_item_type));
+ }
}
}
- // Allow changes to be reported now that the operation is finished.
- edited_theme->_unfreeze_and_propagate_changes();
+ ur->add_do_method(*edited_theme, "clear");
+ ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
+ ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
- _update_edit_item_tree(edited_item_type);
+ ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
+ ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
+
+ ur->commit_action();
}
void ThemeItemEditorDialog::_open_add_theme_item_dialog(int p_data_type) {
@@ -1692,14 +1779,21 @@ void ThemeItemEditorDialog::_confirm_edit_theme_item() {
if (item_popup_mode == CREATE_THEME_ITEM) {
_add_theme_item(edit_item_data_type, theme_item_name->get_text(), edited_item_type);
} else if (item_popup_mode == RENAME_THEME_ITEM) {
- edited_theme->rename_theme_item(edit_item_data_type, edit_item_old_name, theme_item_name->get_text(), edited_item_type);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Rename Theme Item"));
+
+ ur->add_do_method(*edited_theme, "rename_theme_item", edit_item_data_type, edit_item_old_name, theme_item_name->get_text(), edited_item_type);
+ ur->add_undo_method(*edited_theme, "rename_theme_item", edit_item_data_type, theme_item_name->get_text(), edit_item_old_name, edited_item_type);
+
+ ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
+ ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
+
+ ur->commit_action();
}
item_popup_mode = ITEM_POPUP_MODE_MAX;
edit_item_data_type = Theme::DATA_TYPE_MAX;
edit_item_old_name = "";
-
- _update_edit_item_tree(edited_item_type);
}
void ThemeItemEditorDialog::_edit_theme_item_gui_input(const Ref<InputEvent> &p_event) {
@@ -1773,15 +1867,22 @@ void ThemeItemEditorDialog::_notification(int p_what) {
}
}
+void ThemeItemEditorDialog::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_update_edit_types"), &ThemeItemEditorDialog::_update_edit_types);
+ ClassDB::bind_method(D_METHOD("_update_edit_item_tree"), &ThemeItemEditorDialog::_update_edit_item_tree);
+}
+
void ThemeItemEditorDialog::set_edited_theme(const Ref<Theme> &p_theme) {
edited_theme = p_theme;
}
-ThemeItemEditorDialog::ThemeItemEditorDialog() {
+ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_editor) {
set_title(TTR("Manage Theme Items"));
get_ok_button()->set_text(TTR("Close"));
set_hide_on_ok(false); // Closing may require a confirmation in some cases.
+ theme_type_editor = p_theme_type_editor;
+
tc = memnew(TabContainer);
tc->set_tab_alignment(TabContainer::ALIGNMENT_LEFT);
add_child(tc);
@@ -2540,11 +2641,11 @@ void ThemeTypeEditor::_update_type_items() {
pin_leader_button->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")));
pin_leader_button->set_tooltip(TTR("Unpin this StyleBox as a main style."));
item_control->add_child(pin_leader_button);
- pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_unpin_leading_stylebox));
+ pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_on_unpin_leader_button_pressed));
item_control->add_child(item_editor);
- if (leading_stylebox.stylebox.is_valid()) {
+ if (edited_theme->has_stylebox(leading_stylebox.item_name, edited_type)) {
item_editor->set_edited_resource(leading_stylebox.stylebox);
} else {
item_editor->set_edited_resource(RES());
@@ -2569,10 +2670,8 @@ void ThemeTypeEditor::_update_type_items() {
item_editor->set_base_type("StyleBox");
if (E.get()) {
- Ref<StyleBox> stylebox_value;
if (edited_theme->has_stylebox(E.key(), edited_type)) {
- stylebox_value = edited_theme->get_stylebox(E.key(), edited_type);
- item_editor->set_edited_resource(stylebox_value);
+ item_editor->set_edited_resource(edited_theme->get_stylebox(E.key(), edited_type));
} else {
item_editor->set_edited_resource(RES());
}
@@ -2585,7 +2684,7 @@ void ThemeTypeEditor::_update_type_items() {
pin_leader_button->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")));
pin_leader_button->set_tooltip(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type."));
item_control->add_child(pin_leader_button);
- pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_pin_leading_stylebox), varray(item_editor, E.key()));
+ pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_on_pin_leader_button_pressed), varray(item_editor, E.key()));
} else {
if (Theme::get_default()->has_stylebox(E.key(), edited_type)) {
item_editor->set_edited_resource(Theme::get_default()->get_stylebox(E.key(), edited_type));
@@ -2636,16 +2735,17 @@ void ThemeTypeEditor::_add_default_type_items() {
default_type = edited_theme->get_type_variation_base(edited_type);
}
+ Ref<Theme> old_snapshot = edited_theme->duplicate();
+ Ref<Theme> new_snapshot = edited_theme->duplicate();
+
updating = true;
- // Prevent changes from immediately being reported while the operation is still ongoing.
- edited_theme->_freeze_change_propagation();
{
names.clear();
Theme::get_default()->get_icon_list(default_type, &names);
for (const StringName &E : names) {
- if (!edited_theme->has_icon(E, edited_type)) {
- edited_theme->set_icon(E, edited_type, Ref<Texture2D>());
+ if (!new_snapshot->has_icon(E, edited_type)) {
+ new_snapshot->set_icon(E, edited_type, Theme::get_default()->get_icon(E, edited_type));
}
}
}
@@ -2653,8 +2753,8 @@ void ThemeTypeEditor::_add_default_type_items() {
names.clear();
Theme::get_default()->get_stylebox_list(default_type, &names);
for (const StringName &E : names) {
- if (!edited_theme->has_stylebox(E, edited_type)) {
- edited_theme->set_stylebox(E, edited_type, Ref<StyleBox>());
+ if (!new_snapshot->has_stylebox(E, edited_type)) {
+ new_snapshot->set_stylebox(E, edited_type, Theme::get_default()->get_stylebox(E, edited_type));
}
}
}
@@ -2662,8 +2762,8 @@ void ThemeTypeEditor::_add_default_type_items() {
names.clear();
Theme::get_default()->get_font_list(default_type, &names);
for (const StringName &E : names) {
- if (!edited_theme->has_font(E, edited_type)) {
- edited_theme->set_font(E, edited_type, Ref<Font>());
+ if (!new_snapshot->has_font(E, edited_type)) {
+ new_snapshot->set_font(E, edited_type, Theme::get_default()->get_font(E, edited_type));
}
}
}
@@ -2671,8 +2771,8 @@ void ThemeTypeEditor::_add_default_type_items() {
names.clear();
Theme::get_default()->get_font_size_list(default_type, &names);
for (const StringName &E : names) {
- if (!edited_theme->has_font_size(E, edited_type)) {
- edited_theme->set_font_size(E, edited_type, Theme::get_default()->get_font_size(E, default_type));
+ if (!new_snapshot->has_font_size(E, edited_type)) {
+ new_snapshot->set_font_size(E, edited_type, Theme::get_default()->get_font_size(E, edited_type));
}
}
}
@@ -2680,8 +2780,8 @@ void ThemeTypeEditor::_add_default_type_items() {
names.clear();
Theme::get_default()->get_color_list(default_type, &names);
for (const StringName &E : names) {
- if (!edited_theme->has_color(E, edited_type)) {
- edited_theme->set_color(E, edited_type, Theme::get_default()->get_color(E, default_type));
+ if (!new_snapshot->has_color(E, edited_type)) {
+ new_snapshot->set_color(E, edited_type, Theme::get_default()->get_color(E, edited_type));
}
}
}
@@ -2689,17 +2789,25 @@ void ThemeTypeEditor::_add_default_type_items() {
names.clear();
Theme::get_default()->get_constant_list(default_type, &names);
for (const StringName &E : names) {
- if (!edited_theme->has_constant(E, edited_type)) {
- edited_theme->set_constant(E, edited_type, Theme::get_default()->get_constant(E, default_type));
+ if (!new_snapshot->has_constant(E, edited_type)) {
+ new_snapshot->set_constant(E, edited_type, Theme::get_default()->get_constant(E, edited_type));
}
}
}
- // Allow changes to be reported now that the operation is finished.
- edited_theme->_unfreeze_and_propagate_changes();
updating = false;
- _update_type_items();
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Override All Default Theme Items"));
+
+ ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
+ ur->add_undo_method(*edited_theme, "clear");
+ ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
+
+ ur->add_do_method(this, "_update_type_items");
+ ur->add_undo_method(this, "_update_type_items");
+
+ ur->commit_action();
}
void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) {
@@ -2709,27 +2817,43 @@ void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) {
}
String item_name = le->get_text().strip_edges();
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Add Theme Item"));
+
switch (p_data_type) {
case Theme::DATA_TYPE_COLOR: {
- edited_theme->set_color(item_name, edited_type, Color());
+ ur->add_do_method(*edited_theme, "set_color", item_name, edited_type, Color());
+ ur->add_undo_method(*edited_theme, "clear_color", item_name, edited_type);
} break;
case Theme::DATA_TYPE_CONSTANT: {
- edited_theme->set_constant(item_name, edited_type, 0);
+ ur->add_do_method(*edited_theme, "set_constant", item_name, edited_type, 0);
+ ur->add_undo_method(*edited_theme, "clear_constant", item_name, edited_type);
} break;
case Theme::DATA_TYPE_FONT: {
- edited_theme->set_font(item_name, edited_type, Ref<Font>());
+ ur->add_do_method(*edited_theme, "set_font", item_name, edited_type, Ref<Font>());
+ ur->add_undo_method(*edited_theme, "clear_font", item_name, edited_type);
} break;
case Theme::DATA_TYPE_FONT_SIZE: {
- edited_theme->set_font_size(item_name, edited_type, -1);
+ ur->add_do_method(*edited_theme, "set_font_size", item_name, edited_type, -1);
+ ur->add_undo_method(*edited_theme, "clear_font_size", item_name, edited_type);
} break;
case Theme::DATA_TYPE_ICON: {
- edited_theme->set_icon(item_name, edited_type, Ref<Texture2D>());
+ ur->add_do_method(*edited_theme, "set_icon", item_name, edited_type, Ref<Texture2D>());
+ ur->add_undo_method(*edited_theme, "clear_icon", item_name, edited_type);
} break;
case Theme::DATA_TYPE_STYLEBOX: {
- edited_theme->set_stylebox(item_name, edited_type, Ref<StyleBox>());
+ Ref<StyleBox> sb;
+ ur->add_do_method(*edited_theme, "set_stylebox", item_name, edited_type, sb);
+ ur->add_undo_method(*edited_theme, "clear_stylebox", item_name, edited_type);
+
+ if (is_stylebox_pinned(sb)) {
+ ur->add_undo_method(this, "_unpin_leading_stylebox");
+ }
} break;
}
+ ur->commit_action();
+
le->set_text("");
}
@@ -2738,53 +2862,94 @@ void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Co
}
void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Override Theme Item"));
+
switch (p_data_type) {
case Theme::DATA_TYPE_COLOR: {
- edited_theme->set_color(p_item_name, edited_type, Theme::get_default()->get_color(p_item_name, edited_type));
+ ur->add_do_method(*edited_theme, "set_color", p_item_name, edited_type, Theme::get_default()->get_color(p_item_name, edited_type));
+ ur->add_undo_method(*edited_theme, "clear_color", p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_CONSTANT: {
- edited_theme->set_constant(p_item_name, edited_type, Theme::get_default()->get_constant(p_item_name, edited_type));
+ ur->add_do_method(*edited_theme, "set_constant", p_item_name, edited_type, Theme::get_default()->get_constant(p_item_name, edited_type));
+ ur->add_undo_method(*edited_theme, "clear_constant", p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_FONT: {
- edited_theme->set_font(p_item_name, edited_type, Ref<Font>());
+ ur->add_do_method(*edited_theme, "set_font", p_item_name, edited_type, Ref<Font>());
+ ur->add_undo_method(*edited_theme, "clear_font", p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_FONT_SIZE: {
- edited_theme->set_font_size(p_item_name, edited_type, Theme::get_default()->get_font_size(p_item_name, edited_type));
+ ur->add_do_method(*edited_theme, "set_font_size", p_item_name, edited_type, Theme::get_default()->get_font_size(p_item_name, edited_type));
+ ur->add_undo_method(*edited_theme, "clear_font_size", p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_ICON: {
- edited_theme->set_icon(p_item_name, edited_type, Ref<Texture2D>());
+ ur->add_do_method(*edited_theme, "set_icon", p_item_name, edited_type, Ref<Texture2D>());
+ ur->add_undo_method(*edited_theme, "clear_icon", p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_STYLEBOX: {
- edited_theme->set_stylebox(p_item_name, edited_type, Ref<StyleBox>());
+ Ref<StyleBox> sb;
+ ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, edited_type, sb);
+ ur->add_undo_method(*edited_theme, "clear_stylebox", p_item_name, edited_type);
+
+ if (is_stylebox_pinned(sb)) {
+ ur->add_undo_method(this, "_unpin_leading_stylebox");
+ }
} break;
}
+
+ ur->commit_action();
}
void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Remove Theme Item"));
+
switch (p_data_type) {
case Theme::DATA_TYPE_COLOR: {
- edited_theme->clear_color(p_item_name, edited_type);
+ ur->add_do_method(*edited_theme, "clear_color", p_item_name, edited_type);
+ ur->add_undo_method(*edited_theme, "set_color", p_item_name, edited_type, edited_theme->get_color(p_item_name, edited_type));
} break;
case Theme::DATA_TYPE_CONSTANT: {
- edited_theme->clear_constant(p_item_name, edited_type);
+ ur->add_do_method(*edited_theme, "clear_constant", p_item_name, edited_type);
+ ur->add_undo_method(*edited_theme, "set_constant", p_item_name, edited_type, edited_theme->get_constant(p_item_name, edited_type));
} break;
case Theme::DATA_TYPE_FONT: {
- edited_theme->clear_font(p_item_name, edited_type);
+ ur->add_do_method(*edited_theme, "clear_font", p_item_name, edited_type);
+ if (edited_theme->has_font(p_item_name, edited_type)) {
+ ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, edited_theme->get_font(p_item_name, edited_type));
+ } else {
+ ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, Ref<Font>());
+ }
} break;
case Theme::DATA_TYPE_FONT_SIZE: {
- edited_theme->clear_font_size(p_item_name, edited_type);
+ ur->add_do_method(*edited_theme, "clear_font_size", p_item_name, edited_type);
+ ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type));
} break;
case Theme::DATA_TYPE_ICON: {
- edited_theme->clear_icon(p_item_name, edited_type);
+ ur->add_do_method(*edited_theme, "clear_icon", p_item_name, edited_type);
+ if (edited_theme->has_icon(p_item_name, edited_type)) {
+ ur->add_undo_method(*edited_theme, "set_icon", p_item_name, edited_type, edited_theme->get_icon(p_item_name, edited_type));
+ } else {
+ ur->add_undo_method(*edited_theme, "set_icon", p_item_name, edited_type, Ref<Texture2D>());
+ }
} break;
case Theme::DATA_TYPE_STYLEBOX: {
- edited_theme->clear_stylebox(p_item_name, edited_type);
+ Ref<StyleBox> sb = edited_theme->get_stylebox(p_item_name, edited_type);
+ ur->add_do_method(*edited_theme, "clear_stylebox", p_item_name, edited_type);
+ if (edited_theme->has_stylebox(p_item_name, edited_type)) {
+ ur->add_undo_method(*edited_theme, "set_stylebox", p_item_name, edited_type, sb);
+ } else {
+ ur->add_undo_method(*edited_theme, "set_stylebox", p_item_name, edited_type, Ref<StyleBox>());
+ }
- if (leading_stylebox.pinned && leading_stylebox.item_name == p_item_name) {
- _unpin_leading_stylebox();
+ if (is_stylebox_pinned(sb)) {
+ ur->add_do_method(this, "_unpin_leading_stylebox");
+ ur->add_undo_method(this, "_pin_leading_stylebox", p_item_name, sb);
}
} break;
}
+
+ ur->commit_action();
}
void ThemeTypeEditor::_item_rename_cbk(int p_data_type, String p_item_name, Control *p_control) {
@@ -2814,30 +2979,41 @@ void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name
return;
}
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Rename Theme Item"));
+
switch (p_data_type) {
case Theme::DATA_TYPE_COLOR: {
- edited_theme->rename_color(p_item_name, new_name, edited_type);
+ ur->add_do_method(*edited_theme, "rename_color", p_item_name, new_name, edited_type);
+ ur->add_undo_method(*edited_theme, "rename_color", new_name, p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_CONSTANT: {
- edited_theme->rename_constant(p_item_name, new_name, edited_type);
+ ur->add_do_method(*edited_theme, "rename_constant", p_item_name, new_name, edited_type);
+ ur->add_undo_method(*edited_theme, "rename_constant", new_name, p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_FONT: {
- edited_theme->rename_font(p_item_name, new_name, edited_type);
+ ur->add_do_method(*edited_theme, "rename_font", p_item_name, new_name, edited_type);
+ ur->add_undo_method(*edited_theme, "rename_font", new_name, p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_FONT_SIZE: {
- edited_theme->rename_font_size(p_item_name, new_name, edited_type);
+ ur->add_do_method(*edited_theme, "rename_font_size", p_item_name, new_name, edited_type);
+ ur->add_undo_method(*edited_theme, "rename_font_size", new_name, p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_ICON: {
- edited_theme->rename_icon(p_item_name, new_name, edited_type);
+ ur->add_do_method(*edited_theme, "rename_icon", p_item_name, new_name, edited_type);
+ ur->add_undo_method(*edited_theme, "rename_icon", new_name, p_item_name, edited_type);
} break;
case Theme::DATA_TYPE_STYLEBOX: {
- edited_theme->rename_stylebox(p_item_name, new_name, edited_type);
+ ur->add_do_method(*edited_theme, "rename_stylebox", p_item_name, new_name, edited_type);
+ ur->add_undo_method(*edited_theme, "rename_stylebox", new_name, p_item_name, edited_type);
if (leading_stylebox.pinned && leading_stylebox.item_name == p_item_name) {
leading_stylebox.item_name = new_name;
}
} break;
}
+
+ ur->commit_action();
}
void ThemeTypeEditor::_item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control) {
@@ -2859,15 +3035,27 @@ void ThemeTypeEditor::_item_rename_canceled(int p_data_type, String p_item_name,
}
void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) {
- edited_theme->set_color(p_item_name, edited_type, p_value);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Color Item in Theme"), UndoRedo::MERGE_ENDS);
+ ur->add_do_method(*edited_theme, "set_color", p_item_name, edited_type, p_value);
+ ur->add_undo_method(*edited_theme, "set_color", p_item_name, edited_type, edited_theme->get_color(p_item_name, edited_type));
+ ur->commit_action();
}
void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) {
- edited_theme->set_constant(p_item_name, edited_type, int(p_value));
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Constant Item in Theme"));
+ ur->add_do_method(*edited_theme, "set_constant", p_item_name, edited_type, p_value);
+ ur->add_undo_method(*edited_theme, "set_constant", p_item_name, edited_type, edited_theme->get_constant(p_item_name, edited_type));
+ ur->commit_action();
}
void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) {
- edited_theme->set_font_size(p_item_name, edited_type, int(p_value));
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Font Size Item in Theme"));
+ ur->add_do_method(*edited_theme, "set_font_size", p_item_name, edited_type, p_value);
+ ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type));
+ ur->commit_action();
}
void ThemeTypeEditor::_edit_resource_item(RES p_resource, bool p_edit) {
@@ -2875,53 +3063,123 @@ void ThemeTypeEditor::_edit_resource_item(RES p_resource, bool p_edit) {
}
void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) {
- edited_theme->set_font(p_item_name, edited_type, p_value);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Font Item in Theme"));
+
+ ur->add_do_method(*edited_theme, "set_font", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Font>());
+ if (edited_theme->has_font(p_item_name, edited_type)) {
+ ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, edited_theme->get_font(p_item_name, edited_type));
+ } else {
+ ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, Ref<Font>());
+ }
+
+ ur->add_do_method(this, "call_deferred", "_update_type_items");
+ ur->add_undo_method(this, "call_deferred", "_update_type_items");
+
+ ur->commit_action();
}
void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) {
- edited_theme->set_icon(p_item_name, edited_type, p_value);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Icon Item in Theme"));
+
+ ur->add_do_method(*edited_theme, "set_icon", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Texture2D>());
+ if (edited_theme->has_icon(p_item_name, edited_type)) {
+ ur->add_undo_method(*edited_theme, "set_icon", p_item_name, edited_type, edited_theme->get_icon(p_item_name, edited_type));
+ } else {
+ ur->add_undo_method(*edited_theme, "set_icon", p_item_name, edited_type, Ref<Texture2D>());
+ }
+
+ ur->add_do_method(this, "call_deferred", "_update_type_items");
+ ur->add_undo_method(this, "call_deferred", "_update_type_items");
+
+ ur->commit_action();
}
void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) {
- edited_theme->set_stylebox(p_item_name, edited_type, p_value);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Stylebox Item in Theme"));
+
+ ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<StyleBox>());
+ if (edited_theme->has_stylebox(p_item_name, edited_type)) {
+ ur->add_undo_method(*edited_theme, "set_stylebox", p_item_name, edited_type, edited_theme->get_stylebox(p_item_name, edited_type));
+ } else {
+ ur->add_undo_method(*edited_theme, "set_stylebox", p_item_name, edited_type, Ref<StyleBox>());
+ }
+
+ ur->add_do_method(this, "_change_pinned_stylebox");
+ ur->add_undo_method(this, "_change_pinned_stylebox");
+
+ ur->add_do_method(this, "call_deferred", "_update_type_items");
+ ur->add_undo_method(this, "call_deferred", "_update_type_items");
+
+ ur->commit_action();
+}
- if (leading_stylebox.pinned && leading_stylebox.item_name == p_item_name) {
+void ThemeTypeEditor::_change_pinned_stylebox() {
+ if (leading_stylebox.pinned) {
if (leading_stylebox.stylebox.is_valid()) {
leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
- leading_stylebox.stylebox = p_value;
- leading_stylebox.ref_stylebox = (p_value.is_valid() ? p_value->duplicate() : RES());
- if (p_value.is_valid()) {
- leading_stylebox.stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
- }
- }
-}
+ Ref<StyleBox> new_stylebox = edited_theme->get_stylebox(leading_stylebox.item_name, edited_type);
+ leading_stylebox.stylebox = new_stylebox;
+ leading_stylebox.ref_stylebox = (new_stylebox.is_valid() ? new_stylebox->duplicate() : RES());
-void ThemeTypeEditor::_pin_leading_stylebox(Control *p_editor, String p_item_name) {
- if (leading_stylebox.stylebox.is_valid()) {
+ if (leading_stylebox.stylebox.is_valid()) {
+ new_stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ }
+ } else if (leading_stylebox.stylebox.is_valid()) {
leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
+}
+void ThemeTypeEditor::_on_pin_leader_button_pressed(Control *p_editor, String p_item_name) {
Ref<StyleBox> stylebox;
if (Object::cast_to<EditorResourcePicker>(p_editor)) {
stylebox = Object::cast_to<EditorResourcePicker>(p_editor)->get_edited_resource();
}
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Pin Stylebox"));
+ ur->add_do_method(this, "_pin_leading_stylebox", p_item_name, stylebox);
+
+ if (leading_stylebox.pinned) {
+ ur->add_undo_method(this, "_pin_leading_stylebox", leading_stylebox.item_name, leading_stylebox.stylebox);
+ } else {
+ ur->add_undo_method(this, "_unpin_leading_stylebox");
+ }
+
+ ur->commit_action();
+}
+
+void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_stylebox) {
+ if (leading_stylebox.stylebox.is_valid()) {
+ leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ }
+
LeadingStylebox leader;
leader.pinned = true;
leader.item_name = p_item_name;
- leader.stylebox = stylebox;
- leader.ref_stylebox = (stylebox.is_valid() ? stylebox->duplicate() : RES());
+ leader.stylebox = p_stylebox;
+ leader.ref_stylebox = (p_stylebox.is_valid() ? p_stylebox->duplicate() : RES());
leading_stylebox = leader;
- if (leading_stylebox.stylebox.is_valid()) {
- leading_stylebox.stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ if (p_stylebox.is_valid()) {
+ p_stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
_update_type_items();
}
+void ThemeTypeEditor::_on_unpin_leader_button_pressed() {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Unpin Stylebox"));
+ ur->add_do_method(this, "_unpin_leading_stylebox");
+ ur->add_undo_method(this, "_pin_leading_stylebox", leading_stylebox.item_name, leading_stylebox.stylebox);
+ ur->commit_action();
+}
+
void ThemeTypeEditor::_unpin_leading_stylebox() {
if (leading_stylebox.stylebox.is_valid()) {
leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
@@ -2982,11 +3240,22 @@ void ThemeTypeEditor::_update_stylebox_from_leading() {
}
void ThemeTypeEditor::_type_variation_changed(const String p_value) {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Theme Type Variation"));
+
if (p_value.is_empty()) {
- edited_theme->clear_type_variation(edited_type);
+ ur->add_do_method(*edited_theme, "clear_type_variation", edited_type);
} else {
- edited_theme->set_type_variation(edited_type, StringName(p_value));
+ ur->add_do_method(*edited_theme, "set_type_variation", edited_type, StringName(p_value));
}
+
+ if (edited_theme->get_type_variation_base(edited_type) == "") {
+ ur->add_undo_method(*edited_theme, "clear_type_variation", edited_type);
+ } else {
+ ur->add_undo_method(*edited_theme, "set_type_variation", edited_type, edited_theme->get_type_variation_base(edited_type));
+ }
+
+ ur->commit_action();
}
void ThemeTypeEditor::_add_type_variation_cbk() {
@@ -3002,7 +3271,6 @@ void ThemeTypeEditor::_add_type_dialog_selected(const String p_type_name) {
select_type(p_type_name);
} else if (add_type_mode == ADD_VARIATION_BASE) {
_type_variation_changed(p_type_name);
- _update_type_items();
}
}
@@ -3028,6 +3296,13 @@ void ThemeTypeEditor::_notification(int p_what) {
}
}
+void ThemeTypeEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_update_type_items"), &ThemeTypeEditor::_update_type_items);
+ ClassDB::bind_method(D_METHOD("_pin_leading_stylebox"), &ThemeTypeEditor::_pin_leading_stylebox);
+ ClassDB::bind_method(D_METHOD("_unpin_leading_stylebox"), &ThemeTypeEditor::_unpin_leading_stylebox);
+ ClassDB::bind_method(D_METHOD("_change_pinned_stylebox"), &ThemeTypeEditor::_change_pinned_stylebox);
+}
+
void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) {
if (edited_theme.is_valid()) {
edited_theme->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
@@ -3067,6 +3342,10 @@ void ThemeTypeEditor::select_type(String p_type_name) {
}
}
+bool ThemeTypeEditor::is_stylebox_pinned(Ref<StyleBox> p_stylebox) {
+ return leading_stylebox.pinned && leading_stylebox.stylebox == p_stylebox;
+}
+
ThemeTypeEditor::ThemeTypeEditor() {
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
@@ -3331,7 +3610,9 @@ ThemeEditor::ThemeEditor() {
theme_edit_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_edit_button_cbk));
top_menu->add_child(theme_edit_button);
- theme_edit_dialog = memnew(ThemeItemEditorDialog);
+ theme_type_editor = memnew(ThemeTypeEditor);
+
+ theme_edit_dialog = memnew(ThemeItemEditorDialog(theme_type_editor));
theme_edit_dialog->hide();
top_menu->add_child(theme_edit_dialog);
@@ -3381,7 +3662,6 @@ ThemeEditor::ThemeEditor() {
main_hs->add_child(preview_scene_dialog);
preview_scene_dialog->connect("file_selected", callable_mp(this, &ThemeEditor::_preview_scene_dialog_cbk));
- theme_type_editor = memnew(ThemeTypeEditor);
main_hs->add_child(theme_type_editor);
theme_type_editor->set_custom_minimum_size(Size2(280, 0) * EDSCALE);
}
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index bd1bf216e1..ef2a2b6fce 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -177,9 +177,13 @@ public:
ThemeItemImportTree();
};
+class ThemeTypeEditor;
+
class ThemeItemEditorDialog : public AcceptDialog {
GDCLASS(ThemeItemEditorDialog, AcceptDialog);
+ ThemeTypeEditor *theme_type_editor;
+
Ref<Theme> edited_theme;
TabContainer *tc;
@@ -258,11 +262,12 @@ class ThemeItemEditorDialog : public AcceptDialog {
protected:
void _notification(int p_what);
+ static void _bind_methods();
public:
void set_edited_theme(const Ref<Theme> &p_theme);
- ThemeItemEditorDialog();
+ ThemeItemEditorDialog(ThemeTypeEditor *p_theme_editor);
};
class ThemeTypeDialog : public ConfirmationDialog {
@@ -373,7 +378,10 @@ class ThemeTypeEditor : public MarginContainer {
void _font_item_changed(Ref<Font> p_value, String p_item_name);
void _icon_item_changed(Ref<Texture2D> p_value, String p_item_name);
void _stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name);
- void _pin_leading_stylebox(Control *p_editor, String p_item_name);
+ void _change_pinned_stylebox();
+ void _on_pin_leader_button_pressed(Control *p_editor, String p_item_name);
+ void _pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_stylebox);
+ void _on_unpin_leader_button_pressed();
void _unpin_leading_stylebox();
void _update_stylebox_from_leading();
@@ -384,10 +392,12 @@ class ThemeTypeEditor : public MarginContainer {
protected:
void _notification(int p_what);
+ static void _bind_methods();
public:
void set_edited_theme(const Ref<Theme> &p_theme);
void select_type(String p_type_name);
+ bool is_stylebox_pinned(Ref<StyleBox> p_stylebox);
ThemeTypeEditor();
};
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index 9a5ecd3802..2aec7b7145 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -36,6 +36,8 @@
#include "editor/editor_scale.h"
+constexpr double REFRESH_TIMER = 1.5;
+
void ThemeEditorPreview::set_preview_theme(const Ref<Theme> &p_theme) {
preview_content->set_theme(p_theme);
}
@@ -66,7 +68,7 @@ void ThemeEditorPreview::_refresh_interval() {
}
void ThemeEditorPreview::_preview_visibility_changed() {
- set_process(is_visible());
+ set_process(is_visible_in_tree());
}
void ThemeEditorPreview::_picker_button_cbk() {
@@ -200,7 +202,7 @@ void ThemeEditorPreview::_notification(int p_what) {
case NOTIFICATION_PROCESS: {
time_left -= get_process_delta_time();
if (time_left < 0) {
- time_left = 1.5;
+ time_left = REFRESH_TIMER;
_refresh_interval();
}
} break;
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 134f238bb6..469c4335e2 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1765,8 +1765,8 @@ void ProjectList::erase_selected_projects(bool p_delete_project_contents) {
void ProjectList::_panel_draw(Node *p_hb) {
Control *hb = Object::cast_to<Control>(p_hb);
- if (is_layout_rtl() && get_v_scrollbar()->is_visible_in_tree()) {
- hb->draw_line(Point2(get_v_scrollbar()->get_minimum_size().x, hb->get_size().y + 1), Point2(hb->get_size().x, hb->get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
+ if (is_layout_rtl() && get_v_scroll_bar()->is_visible_in_tree()) {
+ hb->draw_line(Point2(get_v_scroll_bar()->get_minimum_size().x, hb->get_size().y + 1), Point2(hb->get_size().x, hb->get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
} else {
hb->draw_line(Point2(0, hb->get_size().y + 1), Point2(hb->get_size().x, hb->get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
}
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index abe1bcf9e5..7891c6f440 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -810,7 +810,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
//late init for performance
color_picker = memnew(ColorPicker);
color_picker->set_deferred_mode(true);
- add_child(color_picker);
+ value_vbox->add_child(color_picker);
color_picker->hide();
color_picker->connect("color_changed", callable_mp(this, &CustomPropertyEditor::_color_changed));
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 0e96a1d247..a1e3f5aabe 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -33,7 +33,6 @@
#include "core/config/project_settings.h"
#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
-#include "core/object/script_language.h"
#include "core/string/string_builder.h"
#include "editor/create_dialog.h"
#include "editor/editor_node.h"
@@ -45,17 +44,16 @@ void ScriptCreateDialog::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- String lang = ScriptServer::get_language(i)->get_type();
- Ref<Texture2D> lang_icon = get_theme_icon(lang, SNAME("EditorIcons"));
- if (lang_icon.is_valid()) {
- language_menu->set_item_icon(i, lang_icon);
+ Ref<Texture2D> language_icon = get_theme_icon(ScriptServer::get_language(i)->get_type(), SNAME("EditorIcons"));
+ if (language_icon.is_valid()) {
+ language_menu->set_item_icon(i, language_icon);
}
}
- String last_lang = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", "");
- if (!last_lang.is_empty()) {
+ String last_language = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", "");
+ if (!last_language.is_empty()) {
for (int i = 0; i < language_menu->get_item_count(); i++) {
- if (language_menu->get_item_text(i) == last_lang) {
+ if (language_menu->get_item_text(i) == last_language) {
language_menu->select(i);
current_language = i;
break;
@@ -64,6 +62,10 @@ void ScriptCreateDialog::_notification(int p_what) {
} else {
language_menu->select(default_language);
}
+ if (EditorSettings::get_singleton()->has_meta("script_setup/use_script_templates")) {
+ is_using_templates = bool(EditorSettings::get_singleton()->get_meta("script_setup/use_script_templates"));
+ use_templates->set_pressed(is_using_templates);
+ }
path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
parent_browse_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
@@ -114,7 +116,7 @@ void ScriptCreateDialog::config(const String &p_base_name, const String &p_base_
built_in_enabled = p_built_in_enabled;
load_enabled = p_load_enabled;
- _lang_changed(current_language);
+ _language_changed(current_language);
_class_name_changed("");
_path_changed(file_path->get_text());
}
@@ -145,8 +147,9 @@ bool ScriptCreateDialog::_validate_class(const String &p_string) {
for (int i = 0; i < p_string.length(); i++) {
if (i == 0) {
+ // Cannot start with a number.
if (p_string[0] >= '0' && p_string[0] <= '9') {
- return false; // no start with number plz
+ return false;
}
}
@@ -170,6 +173,10 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
return TTR("Filename is empty.");
}
+ if (!p.get_file().get_basename().is_valid_filename()) {
+ return TTR("Filename is invalid.");
+ }
+
p = ProjectSettings::get_singleton()->localize_path(p);
if (!p.begins_with("res://")) {
return TTR("Path is not local.");
@@ -178,11 +185,11 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
DirAccess *d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (d->change_dir(p.get_base_dir()) != OK) {
memdelete(d);
- return TTR("Invalid base path.");
+ return TTR("Base path is invalid.");
}
memdelete(d);
- /* Does file already exist */
+ // Check if file exists.
DirAccess *f = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (f->dir_exists(p)) {
memdelete(f);
@@ -193,11 +200,11 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
}
memdelete(f);
- /* Check file extension */
+ // Check file extension.
String extension = p.get_extension();
List<String> extensions;
- // get all possible extensions for script
+ // Get all possible extensions for script.
for (int l = 0; l < language_menu->get_item_count(); l++) {
ScriptServer::get_language(l)->get_recognized_extensions(&extensions);
}
@@ -207,8 +214,6 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
int index = 0;
for (const String &E : extensions) {
if (E.nocasecmp_to(extension) == 0) {
- //FIXME (?) - changing language this way doesn't update controls, needs rework
- //language_menu->select(index); // change Language option by extension
found = true;
if (E == ScriptServer::get_language(language_menu->get_selected())->get_extension()) {
match = true;
@@ -222,16 +227,16 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
return TTR("Invalid extension.");
}
if (!match) {
- return TTR("Wrong extension chosen.");
+ return TTR("Extension doesn't match chosen language.");
}
- /* Let ScriptLanguage do custom validation */
+ // Let ScriptLanguage do custom validation.
String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p);
if (!path_error.is_empty()) {
return path_error;
}
- /* All checks passed */
+ // All checks passed.
return "";
}
@@ -244,40 +249,49 @@ String ScriptCreateDialog::_get_class_name() const {
}
void ScriptCreateDialog::_class_name_changed(const String &p_name) {
- if (_validate_class(class_name->get_text())) {
- is_class_name_valid = true;
- } else {
- is_class_name_valid = false;
- }
+ is_class_name_valid = _validate_class(class_name->get_text());
_update_dialog();
}
void ScriptCreateDialog::_parent_name_changed(const String &p_parent) {
- if (_validate_parent(parent_name->get_text())) {
- is_parent_name_valid = true;
- } else {
- is_parent_name_valid = false;
- }
+ is_parent_name_valid = _validate_parent(parent_name->get_text());
_update_dialog();
}
void ScriptCreateDialog::_template_changed(int p_template) {
- String selected_template = p_template == 0 ? "" : template_menu->get_item_text(p_template);
- EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_template", selected_template);
- if (p_template == 0) {
- //default
- script_template = "";
- return;
- }
- int selected_id = template_menu->get_selected_id();
-
- for (int i = 0; i < template_list.size(); i++) {
- const ScriptTemplateInfo &sinfo = template_list[i];
- if (sinfo.id == selected_id) {
- script_template = sinfo.dir.plus_file(sinfo.name + "." + sinfo.extension);
- break;
+ const ScriptLanguage::ScriptTemplate &sinfo = _get_current_template();
+ // Update last used dictionaries
+ if (is_using_templates && !parent_name->get_text().begins_with("\"res:")) {
+ if (sinfo.origin == ScriptLanguage::TemplateLocation::TEMPLATE_PROJECT) {
+ // Save the last used template for this node into the project dictionary.
+ Dictionary dic_templates_project = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary());
+ dic_templates_project[parent_name->get_text()] = sinfo.get_hash();
+ EditorSettings::get_singleton()->set_project_metadata("script_setup", "templates_dictionary", dic_templates_project);
+ } else {
+ // Save template into to editor dictionary (not a project template).
+ Dictionary dic_templates;
+ if (EditorSettings::get_singleton()->has_meta("script_setup/templates_dictionary")) {
+ dic_templates = (Dictionary)EditorSettings::get_singleton()->get_meta("script_setup/templates_dictionary");
+ }
+ dic_templates[parent_name->get_text()] = sinfo.get_hash();
+ EditorSettings::get_singleton()->set_meta("script_setup/templates_dictionary", dic_templates);
+ // Remove template from project dictionary as we last used an editor level template.
+ Dictionary dic_templates_project = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary());
+ if (dic_templates_project.has(parent_name->get_text())) {
+ dic_templates_project.erase(parent_name->get_text());
+ EditorSettings::get_singleton()->set_project_metadata("script_setup", "templates_dictionary", dic_templates_project);
+ }
}
}
+ // Update template label information.
+ String template_info = String::utf8("• ");
+ template_info += TTR("Template:");
+ template_info += " " + sinfo.name;
+ if (!sinfo.description.is_empty()) {
+ template_info += " - " + sinfo.description;
+ }
+ template_info_label->set_text(template_info);
+ template_info_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor")));
}
void ScriptCreateDialog::ok_pressed() {
@@ -287,6 +301,7 @@ void ScriptCreateDialog::ok_pressed() {
_load_exist();
}
+ EditorSettings::get_singleton()->save();
is_new_script_created = true;
_update_dialog();
}
@@ -295,18 +310,10 @@ void ScriptCreateDialog::_create_new() {
String cname_param = _get_class_name();
Ref<Script> scr;
- if (!script_template.is_empty()) {
- scr = ResourceLoader::load(script_template);
- if (scr.is_null()) {
- alert->set_text(vformat(TTR("Error loading template '%s'"), script_template));
- alert->popup_centered();
- return;
- }
- scr = scr->duplicate();
- ScriptServer::get_language(language_menu->get_selected())->make_template(cname_param, parent_name->get_text(), scr);
- } else {
- scr = ScriptServer::get_language(language_menu->get_selected())->get_template(cname_param, parent_name->get_text());
- }
+
+ const ScriptLanguage::ScriptTemplate sinfo = _get_current_template();
+
+ scr = ScriptServer::get_language(language_menu->get_selected())->make_template(sinfo.content, cname_param, parent_name->get_text());
if (has_named_classes) {
String cname = class_name->get_text();
@@ -345,8 +352,20 @@ void ScriptCreateDialog::_load_exist() {
hide();
}
-void ScriptCreateDialog::_lang_changed(int l) {
- ScriptLanguage *language = ScriptServer::get_language(l);
+Vector<String> ScriptCreateDialog::get_hierarchy(String p_object) const {
+ Vector<String> hierachy;
+ hierachy.append(p_object);
+
+ String parent_class = ClassDB::get_parent_class(p_object);
+ while (parent_class.is_valid_identifier()) {
+ hierachy.append(parent_class);
+ parent_class = ClassDB::get_parent_class(parent_class);
+ }
+ return hierachy;
+}
+
+void ScriptCreateDialog::_language_changed(int l) {
+ language = ScriptServer::get_language(l);
has_named_classes = language->has_named_classes();
can_inherit_from_file = language->can_inherit_from_file();
@@ -364,13 +383,13 @@ void ScriptCreateDialog::_lang_changed(int l) {
}
if (extension.length() == 0) {
- // add extension if none
+ // Add extension if none.
path += selected_ext;
_path_changed(path);
} else {
- // change extension by selected language
+ // Change extension by selected language.
List<String> extensions;
- // get all possible extensions for script
+ // Get all possible extensions for script.
for (int m = 0; m < language_menu->get_item_count(); m++) {
ScriptServer::get_language(m)->get_recognized_extensions(&extensions);
}
@@ -389,123 +408,12 @@ void ScriptCreateDialog::_lang_changed(int l) {
}
file_path->set_text(path);
- bool use_templates = language->is_using_templates();
- template_menu->set_disabled(!use_templates);
- template_menu->clear();
-
- if (use_templates) {
- _update_script_templates(language->get_extension());
-
- String last_lang = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", "");
- String last_template = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_template", "");
-
- template_menu->add_item(TTR("Default"));
-
- ScriptTemplateInfo *templates = template_list.ptrw();
-
- Vector<String> origin_names;
- origin_names.push_back(TTR("Project"));
- origin_names.push_back(TTR("Editor"));
- int cur_origin = -1;
-
- // Populate script template items previously sorted and now grouped by origin
- for (int i = 0; i < template_list.size(); i++) {
- if (int(templates[i].origin) != cur_origin) {
- template_menu->add_separator();
-
- String origin_name = origin_names[templates[i].origin];
-
- int last_index = template_menu->get_item_count() - 1;
- template_menu->set_item_text(last_index, origin_name);
-
- cur_origin = templates[i].origin;
- }
- String item_name = templates[i].name.capitalize();
- template_menu->add_item(item_name);
-
- int new_id = template_menu->get_item_count() - 1;
- templates[i].id = new_id;
- }
- // Disable overridden
- for (const KeyValue<String, Vector<int>> &E : template_overrides) {
- const Vector<int> &overrides = E.value;
-
- if (overrides.size() == 1) {
- continue; // doesn't override anything
- }
- const ScriptTemplateInfo &extended = template_list[overrides[0]];
-
- StringBuilder override_info;
- override_info += TTR("Overrides");
- override_info += ": ";
-
- for (int i = 1; i < overrides.size(); i++) {
- const ScriptTemplateInfo &overridden = template_list[overrides[i]];
-
- int disable_index = template_menu->get_item_index(overridden.id);
- template_menu->set_item_disabled(disable_index, true);
-
- override_info += origin_names[overridden.origin];
- if (i < overrides.size() - 1) {
- override_info += ", ";
- }
- }
- template_menu->set_item_icon(extended.id, get_theme_icon(SNAME("Override"), SNAME("EditorIcons")));
- template_menu->get_popup()->set_item_tooltip(extended.id, override_info.as_string());
- }
- // Reselect last selected template
- for (int i = 0; i < template_menu->get_item_count(); i++) {
- const String &ti = template_menu->get_item_text(i);
- if (language_menu->get_item_text(language_menu->get_selected()) == last_lang && last_template == ti) {
- template_menu->select(i);
- break;
- }
- }
- } else {
- template_menu->add_item(TTR("N/A"));
- script_template = "";
- }
-
- _template_changed(template_menu->get_selected());
EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected()));
_parent_name_changed(parent_name->get_text());
_update_dialog();
}
-void ScriptCreateDialog::_update_script_templates(const String &p_extension) {
- template_list.clear();
- template_overrides.clear();
-
- Vector<String> dirs;
-
- // Ordered from local to global for correct override mechanism
- dirs.push_back(EditorSettings::get_singleton()->get_project_script_templates_dir());
- dirs.push_back(EditorSettings::get_singleton()->get_script_templates_dir());
-
- for (int i = 0; i < dirs.size(); i++) {
- Vector<String> list = EditorSettings::get_singleton()->get_script_templates(p_extension, dirs[i]);
-
- for (int j = 0; j < list.size(); j++) {
- ScriptTemplateInfo sinfo;
- sinfo.origin = ScriptOrigin(i);
- sinfo.dir = dirs[i];
- sinfo.name = list[j];
- sinfo.extension = p_extension;
- template_list.push_back(sinfo);
-
- if (!template_overrides.has(sinfo.name)) {
- Vector<int> overrides;
- overrides.push_back(template_list.size() - 1); // first one
- template_overrides.insert(sinfo.name, overrides);
- } else {
- Vector<int> &overrides = template_overrides[sinfo.name];
- overrides.push_back(template_list.size() - 1);
- }
- }
- }
-}
-
void ScriptCreateDialog::_built_in_pressed() {
if (internal->is_pressed()) {
is_built_in = true;
@@ -517,6 +425,12 @@ void ScriptCreateDialog::_built_in_pressed() {
_update_dialog();
}
+void ScriptCreateDialog::_use_template_pressed() {
+ is_using_templates = use_templates->is_pressed();
+ EditorSettings::get_singleton()->set_meta("script_setup/use_script_templates", is_using_templates);
+ _update_dialog();
+}
+
void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) {
is_browsing_parent = browse_parent;
@@ -545,16 +459,16 @@ void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) {
}
void ScriptCreateDialog::_file_selected(const String &p_file) {
- String p = ProjectSettings::get_singleton()->localize_path(p_file);
+ String path = ProjectSettings::get_singleton()->localize_path(p_file);
if (is_browsing_parent) {
- parent_name->set_text("\"" + p + "\"");
+ parent_name->set_text("\"" + path + "\"");
_parent_name_changed(parent_name->get_text());
} else {
- file_path->set_text(p);
- _path_changed(p);
+ file_path->set_text(path);
+ _path_changed(path);
- String filename = p.get_file().get_basename();
- int select_start = p.rfind(filename);
+ String filename = path.get_file().get_basename();
+ int select_start = path.rfind(filename);
file_path->select(select_start, select_start + filename.length());
file_path->set_caret_column(select_start + filename.length());
file_path->grab_focus();
@@ -588,7 +502,7 @@ void ScriptCreateDialog::_path_changed(const String &p_path) {
return;
}
- /* Does file already exist */
+ // Check if file exists.
DirAccess *f = DirAccess::create(DirAccess::ACCESS_RESOURCES);
String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges());
if (f->file_exists(p)) {
@@ -623,9 +537,98 @@ void ScriptCreateDialog::_msg_path_valid(bool valid, const String &p_msg) {
}
}
-void ScriptCreateDialog::_update_dialog() {
- /* "Add Script Dialog" GUI logic and script checks. */
+void ScriptCreateDialog::_update_template_menu() {
+ bool is_language_using_templates = language->is_using_templates();
+ template_menu->set_disabled(false);
+ template_menu->clear();
+ template_list.clear();
+ if (is_language_using_templates) {
+ // Get the lastest templates used for each type of node from project settings then global settings.
+ Dictionary last_local_templates = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary());
+ Dictionary last_global_templates;
+ if (EditorSettings::get_singleton()->has_meta("script_setup/templates_dictionary")) {
+ last_global_templates = (Dictionary)EditorSettings::get_singleton()->get_meta("script_setup/templates_dictionary");
+ }
+ String inherits_base_type = parent_name->get_text();
+
+ // If it inherits from a script, select Object instead.
+ if (inherits_base_type[0] == '"') {
+ inherits_base_type = "Object";
+ }
+
+ // Get all ancestor node for selected base node.
+ // There templates will also fit the base node.
+ Vector<String> hierarchy = get_hierarchy(inherits_base_type);
+ int last_used_template = -1;
+ int preselected_template = -1;
+ int previous_ancestor_level = -1;
+
+ // Templates can be stored in tree different locations.
+ Vector<ScriptLanguage::TemplateLocation> template_locations;
+ template_locations.append(ScriptLanguage::TEMPLATE_PROJECT);
+ template_locations.append(ScriptLanguage::TEMPLATE_EDITOR);
+ template_locations.append(ScriptLanguage::TEMPLATE_BUILT_IN);
+
+ for (const ScriptLanguage::TemplateLocation &template_location : template_locations) {
+ String display_name = _get_script_origin_label(template_location);
+ bool separator = false;
+ int ancestor_level = 0;
+ for (const String &current_node : hierarchy) {
+ Vector<ScriptLanguage::ScriptTemplate> templates_found;
+ if (template_location == ScriptLanguage::TEMPLATE_BUILT_IN) {
+ templates_found = language->get_built_in_templates(current_node);
+ } else {
+ String template_directory;
+ if (template_location == ScriptLanguage::TEMPLATE_PROJECT) {
+ template_directory = EditorSettings::get_singleton()->get_project_script_templates_dir();
+ } else {
+ template_directory = EditorSettings::get_singleton()->get_script_templates_dir();
+ }
+ templates_found = _get_user_templates(language, current_node, template_directory, template_location);
+ }
+ if (!templates_found.is_empty()) {
+ if (!separator) {
+ template_menu->add_separator();
+ template_menu->set_item_text(template_menu->get_item_count() - 1, display_name);
+ separator = true;
+ }
+ for (ScriptLanguage::ScriptTemplate &t : templates_found) {
+ template_menu->add_item(t.inherit + ": " + t.name);
+ int id = template_menu->get_item_count() - 1;
+ // Check if this template should be preselected if node isn't in the last used dictionary.
+ if (ancestor_level < previous_ancestor_level || previous_ancestor_level == -1) {
+ previous_ancestor_level = ancestor_level;
+ preselected_template = id;
+ }
+ // Check for last used template for this node in project settings then in global settings.
+ if (last_local_templates.has(parent_name->get_text()) && t.get_hash() == String(last_local_templates[parent_name->get_text()])) {
+ last_used_template = id;
+ } else if (last_used_template == -1 && last_global_templates.has(parent_name->get_text()) && t.get_hash() == String(last_global_templates[parent_name->get_text()])) {
+ last_used_template = id;
+ }
+ t.id = id;
+ template_list.push_back(t);
+ String icon = has_theme_icon(t.inherit, SNAME("EditorIcons")) ? t.inherit : "Object";
+ template_menu->set_item_icon(id, get_theme_icon(icon, SNAME("EditorIcons")));
+ }
+ }
+ ancestor_level++;
+ }
+ }
+
+ if (last_used_template != -1) {
+ template_menu->select(last_used_template);
+ } else if (preselected_template != -1) {
+ template_menu->select(preselected_template);
+ }
+ }
+ _template_changed(template_menu->get_selected());
+}
+
+void ScriptCreateDialog::_update_dialog() {
+ // "Add Script Dialog" GUI logic and script checks.
+ _update_template_menu();
bool script_ok = true;
// Is script path/name valid (order from top to bottom)?
@@ -697,41 +700,51 @@ void ScriptCreateDialog::_update_dialog() {
// This warning isn't relevant if the script is built-in.
script_name_warning_label->set_visible(!is_built_in && _get_class_name() == parent_name->get_text());
- if (is_built_in) {
- get_ok_button()->set_text(TTR("Create"));
- parent_name->set_editable(true);
- parent_search_button->set_disabled(false);
- parent_browse_button->set_disabled(!can_inherit_from_file);
- _msg_path_valid(true, TTR("Built-in script (into scene file)."));
- } else if (is_new_script_created) {
- // New script created.
-
- get_ok_button()->set_text(TTR("Create"));
- parent_name->set_editable(true);
- parent_search_button->set_disabled(false);
- parent_browse_button->set_disabled(!can_inherit_from_file);
- if (is_path_valid) {
+ bool is_new_file = is_built_in || is_new_script_created;
+
+ parent_name->set_editable(is_new_file);
+ parent_search_button->set_disabled(!is_new_file);
+ parent_browse_button->set_disabled(!is_new_file || !can_inherit_from_file);
+ template_inactive_message = "";
+ String button_text = is_new_file ? TTR("Create") : TTR("Load");
+ get_ok_button()->set_text(button_text);
+
+ if (is_new_file) {
+ if (is_built_in) {
+ _msg_path_valid(true, TTR("Built-in script (into scene file)."));
+ }
+ if (is_new_script_created && is_path_valid) {
_msg_path_valid(true, TTR("Will create a new script file."));
}
- } else if (load_enabled) {
- // Script loaded.
-
- get_ok_button()->set_text(TTR("Load"));
- parent_name->set_editable(false);
- parent_search_button->set_disabled(true);
- parent_browse_button->set_disabled(true);
- if (is_path_valid) {
- _msg_path_valid(true, TTR("Will load an existing script file."));
+ } else {
+ if (load_enabled) {
+ template_inactive_message = TTR("Using existing script file.");
+ if (is_path_valid) {
+ _msg_path_valid(true, TTR("Will load an existing script file."));
+ }
+ } else {
+ template_inactive_message = TTR("Using existing script file.");
+ _msg_path_valid(false, TTR("Script file already exists."));
+ script_ok = false;
+ }
+ }
+
+ // Show templates list if needed.
+ if (is_using_templates) {
+ // Check if at least one suitable template has been found.
+ if (template_menu->get_item_count() == 0 && template_inactive_message.is_empty()) {
+ template_inactive_message = TTR("No suitable template.");
}
} else {
- get_ok_button()->set_text(TTR("Create"));
- parent_name->set_editable(true);
- parent_search_button->set_disabled(false);
- parent_browse_button->set_disabled(!can_inherit_from_file);
- _msg_path_valid(false, TTR("Script file already exists."));
+ template_inactive_message = TTR("Empty");
+ }
- script_ok = false;
+ if (!template_inactive_message.is_empty()) {
+ template_menu->set_disabled(true);
+ template_menu->clear();
+ template_menu->add_item(template_inactive_message);
}
+ template_info_label->set_visible(!template_menu->is_disabled());
get_ok_button()->set_disabled(!script_ok);
@@ -745,6 +758,122 @@ void ScriptCreateDialog::_update_dialog() {
}
}
+ScriptLanguage::ScriptTemplate ScriptCreateDialog::_get_current_template() const {
+ int selected_id = template_menu->get_selected_id();
+ for (const ScriptLanguage::ScriptTemplate &t : template_list) {
+ if (is_using_templates) {
+ if (t.id == selected_id) {
+ return t;
+ }
+ } else {
+ // Using empty built-in template if templates are disabled.
+ if (t.origin == ScriptLanguage::TemplateLocation::TEMPLATE_BUILT_IN && t.name == "Empty") {
+ return t;
+ }
+ }
+ }
+ return ScriptLanguage::ScriptTemplate();
+}
+
+Vector<ScriptLanguage::ScriptTemplate> ScriptCreateDialog::_get_user_templates(const ScriptLanguage *language, const StringName &p_object, const String &p_dir, const ScriptLanguage::TemplateLocation &p_origin) const {
+ Vector<ScriptLanguage::ScriptTemplate> user_templates;
+ String extension = language->get_extension();
+
+ String dir_path = p_dir.plus_file(p_object);
+
+ DirAccess *d = DirAccess::open(dir_path);
+ if (d) {
+ d->list_dir_begin();
+ String file = d->get_next();
+ while (file != String()) {
+ if (file.get_extension() == extension) {
+ user_templates.append(_parse_template(language, dir_path, file, p_origin, p_object));
+ }
+ file = d->get_next();
+ }
+ d->list_dir_end();
+ memdelete(d);
+ }
+ return user_templates;
+}
+
+ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptLanguage *language, const String &p_path, const String &p_filename, const ScriptLanguage::TemplateLocation &p_origin, const String &p_inherits) const {
+ ScriptLanguage::ScriptTemplate script_template = ScriptLanguage::ScriptTemplate();
+ script_template.origin = p_origin;
+ script_template.inherit = p_inherits;
+ String space_indent = " ";
+ // Get meta delimiter
+ String meta_delimiter = String();
+ List<String> comment_delimiters;
+ language->get_comment_delimiters(&comment_delimiters);
+ for (const String &script_delimiter : comment_delimiters) {
+ if (script_delimiter.find(" ") == -1) {
+ meta_delimiter = script_delimiter;
+ break;
+ }
+ }
+ String meta_prefix = meta_delimiter + " meta-";
+
+ // Parse file for meta-information and script content
+ Error err;
+ FileAccess *file = FileAccess::open(p_path.plus_file(p_filename), FileAccess::READ, &err);
+ if (!err) {
+ while (!file->eof_reached()) {
+ String line = file->get_line();
+ if (line.begins_with(meta_prefix)) {
+ // Store meta information
+ line = line.substr(meta_prefix.length(), -1);
+ if (line.begins_with("name")) {
+ script_template.name = line.substr(5, -1).strip_edges();
+ }
+ if (line.begins_with("description")) {
+ script_template.description = line.substr(12, -1).strip_edges();
+ }
+ if (line.begins_with("space-indent")) {
+ String indent_value = line.substr(17, -1).strip_edges();
+ if (indent_value.is_valid_int()) {
+ space_indent = "";
+ for (int i = 0; i < indent_value.to_int(); i++) {
+ space_indent += " ";
+ }
+ } else {
+ WARN_PRINT(vformat("Template meta-use_space_indent need to be a valid integer value. Found %s.", indent_value));
+ }
+ }
+ } else {
+ // Store script
+ if (space_indent != "") {
+ line = line.replace(space_indent, "_TS_");
+ }
+ script_template.content += line.replace("\t", "_TS_") + "\n";
+ }
+ }
+ file->close();
+ memdelete(file);
+ }
+
+ script_template.content = script_template.content.lstrip("\n");
+
+ // Get name from file name if no name in meta information
+ if (script_template.name == String()) {
+ script_template.name = p_filename.get_basename().replace("_", " ").capitalize();
+ }
+
+ return script_template;
+}
+
+String ScriptCreateDialog::_get_script_origin_label(const ScriptLanguage::TemplateLocation &p_origin) const {
+ switch (p_origin) {
+ case ScriptLanguage::TEMPLATE_BUILT_IN:
+ return TTR("Built-in");
+ case ScriptLanguage::TEMPLATE_EDITOR:
+ return TTR("Editor");
+ case ScriptLanguage::TEMPLATE_PROJECT:
+ return TTR("Project");
+ }
+ return "";
+}
+
void ScriptCreateDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("config", "inherits", "path", "built_in_enabled", "load_enabled"), &ScriptCreateDialog::config, DEFVAL(true), DEFVAL(true));
@@ -757,7 +886,7 @@ ScriptCreateDialog::ScriptCreateDialog() {
GridContainer *gc = memnew(GridContainer);
gc->set_columns(2);
- /* Error Messages Field */
+ /* Information Messages Field */
VBoxContainer *vb = memnew(VBoxContainer);
@@ -782,6 +911,10 @@ ScriptCreateDialog::ScriptCreateDialog() {
script_name_warning_label->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
script_name_warning_label->hide();
+ template_info_label = memnew(Label);
+ vb->add_child(template_info_label);
+ template_info_label->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+
status_panel = memnew(PanelContainer);
status_panel->set_h_size_flags(Control::SIZE_FILL);
status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -801,7 +934,7 @@ ScriptCreateDialog::ScriptCreateDialog() {
/* Language */
language_menu = memnew(OptionButton);
- language_menu->set_custom_minimum_size(Size2(250, 0) * EDSCALE);
+ language_menu->set_custom_minimum_size(Size2(350, 0) * EDSCALE);
language_menu->set_h_size_flags(Control::SIZE_EXPAND_FILL);
gc->add_child(memnew(Label(TTR("Language:"))));
gc->add_child(language_menu);
@@ -819,7 +952,7 @@ ScriptCreateDialog::ScriptCreateDialog() {
}
current_language = default_language;
- language_menu->connect("item_selected", callable_mp(this, &ScriptCreateDialog::_lang_changed));
+ language_menu->connect("item_selected", callable_mp(this, &ScriptCreateDialog::_language_changed));
/* Inherits */
@@ -851,10 +984,24 @@ ScriptCreateDialog::ScriptCreateDialog() {
/* Templates */
- template_menu = memnew(OptionButton);
+ is_using_templates = true;
gc->add_child(memnew(Label(TTR("Template:"))));
- gc->add_child(template_menu);
+ HBoxContainer *template_hb = memnew(HBoxContainer);
+ template_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ use_templates = memnew(CheckBox);
+ use_templates->set_pressed(is_using_templates);
+ use_templates->connect("pressed", callable_mp(this, &ScriptCreateDialog::_use_template_pressed));
+ template_hb->add_child(use_templates);
+
+ template_inactive_message = "";
+
+ template_menu = memnew(OptionButton);
+ template_menu->set_h_size_flags(Control::SIZE_EXPAND_FILL);
template_menu->connect("item_selected", callable_mp(this, &ScriptCreateDialog::_template_changed));
+ template_hb->add_child(template_menu);
+
+ gc->add_child(template_hb);
/* Built-in Script */
diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h
index dba798eea7..2c980f6e6e 100644
--- a/editor/script_create_dialog.h
+++ b/editor/script_create_dialog.h
@@ -31,6 +31,7 @@
#ifndef SCRIPT_CREATE_DIALOG_H
#define SCRIPT_CREATE_DIALOG_H
+#include "core/object/script_language.h"
#include "editor/editor_file_dialog.h"
#include "editor/editor_settings.h"
#include "scene/gui/check_box.h"
@@ -50,6 +51,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
Label *path_error_label;
Label *builtin_warning_label;
Label *script_name_warning_label;
+ Label *template_info_label;
PanelContainer *status_panel;
LineEdit *parent_name;
Button *parent_browse_button;
@@ -61,12 +63,14 @@ class ScriptCreateDialog : public ConfirmationDialog {
Button *path_button;
EditorFileDialog *file_browse;
CheckBox *internal;
+ CheckBox *use_templates;
VBoxContainer *path_vb;
AcceptDialog *alert;
CreateDialog *select_class;
bool path_valid;
bool create_new;
bool is_browsing_parent;
+ String template_inactive_message;
String initial_bp;
bool is_new_script_created;
bool is_path_valid;
@@ -76,6 +80,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
bool is_parent_name_valid;
bool is_class_name_valid;
bool is_built_in;
+ bool is_using_templates;
bool built_in_enabled;
bool load_enabled;
int current_language;
@@ -85,23 +90,8 @@ class ScriptCreateDialog : public ConfirmationDialog {
Control *path_controls[2];
Control *name_controls[2];
- enum ScriptOrigin {
- SCRIPT_ORIGIN_PROJECT,
- SCRIPT_ORIGIN_EDITOR,
- };
- struct ScriptTemplateInfo {
- int id = 0;
- ScriptOrigin origin = ScriptOrigin::SCRIPT_ORIGIN_EDITOR;
- String dir;
- String name;
- String extension;
- };
-
- String script_template;
- Vector<ScriptTemplateInfo> template_list;
- Map<String, Vector<int>> template_overrides; // name : indices
-
- void _update_script_templates(const String &p_extension);
+ Vector<ScriptLanguage::ScriptTemplate> template_list;
+ ScriptLanguage *language;
String base_type;
@@ -109,8 +99,9 @@ class ScriptCreateDialog : public ConfirmationDialog {
bool _can_be_built_in();
void _path_changed(const String &p_path = String());
void _path_submitted(const String &p_path = String());
- void _lang_changed(int l = 0);
+ void _language_changed(int l = 0);
void _built_in_pressed();
+ void _use_template_pressed();
bool _validate_parent(const String &p_string);
bool _validate_class(const String &p_string);
String _validate_path(const String &p_path, bool p_file_must_exist);
@@ -125,9 +116,15 @@ class ScriptCreateDialog : public ConfirmationDialog {
virtual void ok_pressed() override;
void _create_new();
void _load_exist();
+ Vector<String> get_hierarchy(String p_object) const;
void _msg_script_valid(bool valid, const String &p_msg = String());
void _msg_path_valid(bool valid, const String &p_msg = String());
+ void _update_template_menu();
void _update_dialog();
+ ScriptLanguage::ScriptTemplate _get_current_template() const;
+ Vector<ScriptLanguage::ScriptTemplate> _get_user_templates(const ScriptLanguage *language, const StringName &p_object, const String &p_dir, const ScriptLanguage::TemplateLocation &p_origin) const;
+ ScriptLanguage::ScriptTemplate _parse_template(const ScriptLanguage *language, const String &p_path, const String &p_filename, const ScriptLanguage::TemplateLocation &p_origin, const String &p_inherits) const;
+ String _get_script_origin_label(const ScriptLanguage::TemplateLocation &p_origin) const;
protected:
void _notification(int p_what);
diff --git a/editor/template_builders.py b/editor/template_builders.py
new file mode 100644
index 0000000000..efed567d46
--- /dev/null
+++ b/editor/template_builders.py
@@ -0,0 +1,95 @@
+"""Functions used to generate source files during build time
+All such functions are invoked in a subprocess on Windows to prevent build flakiness.
+"""
+
+import os
+from io import StringIO
+from platform_methods import subprocess_main
+
+
+def parse_template(inherits, source, delimiter):
+ script_template = {
+ "inherits": inherits,
+ "name": "",
+ "description": "",
+ "version": "",
+ "script": "",
+ "space-indent": "4",
+ }
+ meta_prefix = delimiter + " meta-"
+ meta = ["name", "description", "version", "space-indent"]
+
+ with open(source) as f:
+ lines = f.readlines()
+ for line in lines:
+ if line.startswith(meta_prefix):
+ line = line[len(meta_prefix) :]
+ for m in meta:
+ if line.startswith(m):
+ strip_lenght = len(m) + 1
+ script_template[m] = line[strip_lenght:].strip()
+ else:
+ script_template["script"] += line
+ if script_template["space-indent"] != "":
+ indent = " " * int(script_template["space-indent"])
+ script_template["script"] = script_template["script"].replace(indent, "_TS_")
+ if script_template["name"] == "":
+ script_template["name"] = os.path.splitext(os.path.basename(source))[0].replace("_", " ").title()
+ script_template["script"] = (
+ script_template["script"].replace('"', '\\"').lstrip().replace("\n", "\\n").replace("\t", "_TS_")
+ )
+ return (
+ '{ String("'
+ + script_template["inherits"]
+ + '"), String("'
+ + script_template["name"]
+ + '"), String("'
+ + script_template["description"]
+ + '"), String("'
+ + script_template["script"]
+ + '")'
+ + " },\n"
+ )
+
+
+def make_templates(target, source, env):
+ dst = target[0]
+ s = StringIO()
+ s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n\n")
+ s.write("#ifndef _CODE_TEMPLATES_H\n")
+ s.write("#define _CODE_TEMPLATES_H\n\n")
+ s.write('#include "core/object/object.h"\n')
+ s.write('#include "core/object/script_language.h"\n')
+
+ delimiter = "#" # GDScript single line comment delimiter by default.
+ if source:
+ ext = os.path.splitext(source[0])[1]
+ if ext == ".cs":
+ delimiter = "//"
+
+ parsed_template_string = ""
+ number_of_templates = 0
+
+ for filepath in source:
+ node_name = os.path.basename(os.path.dirname(filepath))
+ parsed_template = parse_template(node_name, filepath, delimiter)
+ parsed_template_string += "\t" + parsed_template
+ number_of_templates += 1
+
+ s.write("\nstatic const int TEMPLATES_ARRAY_SIZE = " + str(number_of_templates) + ";\n")
+ s.write("\nstatic const struct ScriptLanguage::ScriptTemplate TEMPLATES[" + str(number_of_templates) + "] = {\n")
+
+ s.write(parsed_template_string)
+
+ s.write("};\n")
+
+ s.write("\n#endif\n")
+
+ with open(dst, "w") as f:
+ f.write(s.getvalue())
+
+ s.close()
+
+
+if __name__ == "__main__":
+ subprocess_main(globals())
diff --git a/main/main.cpp b/main/main.cpp
index 9b7f960660..bd9eba3d28 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -350,7 +350,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
OS::get_singleton()->print(" --profiling Enable profiling in the script debugger.\n");
OS::get_singleton()->print(" --vk-layers Enable Vulkan Validation layers for debugging.\n");
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
OS::get_singleton()->print(" --gpu-abort Abort on GPU errors (usually validation layer errors), may help see the problem if your system freezes.\n");
#endif
OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
index d2456d3360..723149843a 100644
--- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml
+++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
@@ -62,7 +62,7 @@
<return type="ENetPacketPeer" />
<argument index="0" name="id" type="int" />
<description>
- Return the [ENetPacketPeer] associated to the given [code]id[/code].
+ Returns the [ENetPacketPeer] associated to the given [code]id[/code].
</description>
</method>
<method name="set_bind_ip">
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 5c8cbdf869..c6121ec7fe 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -21,3 +21,5 @@ if env["tools"]:
if env["tests"]:
env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"])
env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp")
+
+SConscript("editor_templates/SCsub")
diff --git a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
new file mode 100644
index 0000000000..0824d894c5
--- /dev/null
+++ b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
@@ -0,0 +1,29 @@
+# meta-description: Classic movement for gravity games (platformer, ...)
+
+extends _BASE_
+
+const SPEED: float = 300.0
+const JUMP_FORCE: float = -400.0
+
+# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
+var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")
+
+
+func _physics_process(delta: float) -> void:
+ # Add the gravity.
+ if not is_on_floor():
+ motion_velocity.y += gravity * delta
+
+ # Handle Jump.
+ if Input.is_action_just_pressed("ui_accept") and is_on_floor():
+ motion_velocity.y = JUMP_FORCE
+
+ # Get the input direction and handle the movement/deceleration.
+ # As good practice, you should replace UI actions with custom gameplay actions.
+ var direction := Input.get_axis("ui_left", "ui_right")
+ if direction:
+ motion_velocity.x = direction * SPEED
+ else:
+ motion_velocity.x = move_toward(motion_velocity.x, 0, SPEED)
+
+ move_and_slide()
diff --git a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
new file mode 100644
index 0000000000..ce6d67ae84
--- /dev/null
+++ b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
@@ -0,0 +1,32 @@
+# meta-description: Classic movement for gravity games (FPS, TPS, ...)
+
+extends _BASE_
+
+const SPEED: float = 5.0
+const JUMP_FORCE: float = 4.5
+
+# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
+var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
+
+
+func _physics_process(delta: float) -> void:
+ # Add the gravity.
+ if not is_on_floor():
+ motion_velocity.y -= gravity * delta
+
+ # Handle Jump.
+ if Input.is_action_just_pressed("ui_accept") and is_on_floor():
+ motion_velocity.y = JUMP_FORCE
+
+ # Get the input direction and handle the movement/deceleration.
+ # As good practice, you should replace UI actions with custom gameplay actions.
+ var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
+ var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
+ if direction:
+ motion_velocity.x = direction.x * SPEED
+ motion_velocity.z = direction.z * SPEED
+ else:
+ motion_velocity.x = move_toward(motion_velocity.x, 0, SPEED)
+ motion_velocity.z = move_toward(motion_velocity.z, 0, SPEED)
+
+ move_and_slide()
diff --git a/modules/gdscript/editor_templates/EditorPlugin/plugin.gd b/modules/gdscript/editor_templates/EditorPlugin/plugin.gd
new file mode 100644
index 0000000000..8614bb8b17
--- /dev/null
+++ b/modules/gdscript/editor_templates/EditorPlugin/plugin.gd
@@ -0,0 +1,11 @@
+# meta-description: Basic plugin template
+@tool
+extends EditorPlugin
+
+func _enter_tree() -> void:
+ # Initialization of the plugin goes here.
+ pass
+
+func _exit_tree() -> void:
+ # Clean-up of the plugin goes here.
+ pass
diff --git a/modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd b/modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd
new file mode 100644
index 0000000000..fdb174c7ed
--- /dev/null
+++ b/modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd
@@ -0,0 +1,7 @@
+# meta-description: Basic editor script template
+@tool
+extends EditorScript
+
+func _run() -> void:
+ # Called when the script is executed (using File -> Run in Script Editor).
+ pass
diff --git a/modules/gdscript/editor_templates/Node/default.gd b/modules/gdscript/editor_templates/Node/default.gd
new file mode 100644
index 0000000000..ee5c0b99cc
--- /dev/null
+++ b/modules/gdscript/editor_templates/Node/default.gd
@@ -0,0 +1,11 @@
+# meta-description: Base template for Node with default Godot cycle methods
+
+extends _BASE_
+
+# Called when the node enters the scene tree for the first time.
+func _ready() -> void:
+ pass # Replace with function body.
+
+# Called every frame. 'delta' is the elapsed time since the previous frame.
+func _process(delta: float) -> void:
+ pass
diff --git a/modules/gdscript/editor_templates/Object/empty.gd b/modules/gdscript/editor_templates/Object/empty.gd
new file mode 100644
index 0000000000..387786b0a4
--- /dev/null
+++ b/modules/gdscript/editor_templates/Object/empty.gd
@@ -0,0 +1,3 @@
+# meta-description: Empty template suitable for all Objects
+
+extends _BASE_
diff --git a/modules/gdscript/editor_templates/SCsub b/modules/gdscript/editor_templates/SCsub
new file mode 100644
index 0000000000..2266ef2d01
--- /dev/null
+++ b/modules/gdscript/editor_templates/SCsub
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+Import("env")
+
+import editor.template_builders as build_template_gd
+
+env["BUILDERS"]["MakeGDTemplateBuilder"] = Builder(
+ action=env.Run(build_template_gd.make_templates, "Generating GDScript templates header."),
+ suffix=".h",
+ src_suffix=".gd",
+)
+
+# Template files
+templates_sources = Glob("*/*.gd")
+
+env.Alias("editor_template_gd", [env.MakeGDTemplateBuilder("templates.gen.h", templates_sources)])
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 4822e411ce..02c1bfb40c 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -49,6 +49,10 @@
#include "tests/gdscript_test_runner.h"
#endif
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif
+
///////////////////////////
GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) {
@@ -817,10 +821,16 @@ Error GDScript::reload(bool p_keep_state) {
basedir = basedir.get_base_dir();
}
- if (source.find("%BASE%") != -1) {
- //loading a template, don't parse
+// Loading a template, don't parse.
+#ifdef TOOLS_ENABLED
+ if (basedir.begins_with(EditorSettings::get_singleton()->get_project_script_templates_dir())) {
return OK;
}
+#else
+ if (source.find("_BASE_") != -1) {
+ return OK;
+ }
+#endif
{
String source_path = path;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index ade4f247c9..2f1187e5b1 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -396,7 +396,7 @@ public:
_debug_call_stack_pos--;
}
- virtual Vector<StackInfo> debug_get_current_stack_info() {
+ virtual Vector<StackInfo> debug_get_current_stack_info() override {
if (Thread::get_main_id() != Thread::get_caller_id()) {
return Vector<StackInfo>();
}
@@ -430,77 +430,76 @@ public:
_FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; }
- virtual String get_name() const;
+ virtual String get_name() const override;
/* LANGUAGE FUNCTIONS */
- virtual void init();
- virtual String get_type() const;
- virtual String get_extension() const;
- virtual Error execute_file(const String &p_path);
- virtual void finish();
+ virtual void init() override;
+ virtual String get_type() const override;
+ virtual String get_extension() const override;
+ virtual Error execute_file(const String &p_path) override;
+ virtual void finish() override;
/* EDITOR FUNCTIONS */
- virtual void get_reserved_words(List<String> *p_words) const;
- virtual bool is_control_flow_keyword(String p_keywords) const;
- virtual void get_comment_delimiters(List<String> *p_delimiters) const;
- virtual void get_string_delimiters(List<String> *p_delimiters) const;
- virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const;
- virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool is_using_templates();
- virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
- virtual Script *create_script() const;
- virtual bool has_named_classes() const;
- virtual bool supports_builtin_mode() const;
- virtual bool supports_documentation() const;
- virtual bool can_inherit_from_file() const { return true; }
- virtual int find_function(const String &p_function, const String &p_code) const;
- virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
- virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint);
+ virtual void get_reserved_words(List<String> *p_words) const override;
+ virtual bool is_control_flow_keyword(String p_keywords) const override;
+ virtual void get_comment_delimiters(List<String> *p_delimiters) const override;
+ virtual void get_string_delimiters(List<String> *p_delimiters) const override;
+ virtual bool is_using_templates() override;
+ virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
+ virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override;
+ virtual Script *create_script() const override;
+ virtual bool has_named_classes() const override;
+ virtual bool supports_builtin_mode() const override;
+ virtual bool supports_documentation() const override;
+ virtual bool can_inherit_from_file() const override { return true; }
+ virtual int find_function(const String &p_function, const String &p_code) const override;
+ virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override;
+ virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) override;
#ifdef TOOLS_ENABLED
- virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result);
+ virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) override;
#endif
virtual String _get_indentation() const;
- virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const;
- virtual void add_global_constant(const StringName &p_variable, const Variant &p_value);
- virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value);
- virtual void remove_named_global_constant(const StringName &p_name);
+ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override;
+ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) override;
+ virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) override;
+ virtual void remove_named_global_constant(const StringName &p_name) override;
/* DEBUGGER FUNCTIONS */
- virtual String debug_get_error() const;
- virtual int debug_get_stack_level_count() const;
- virtual int debug_get_stack_level_line(int p_level) const;
- virtual String debug_get_stack_level_function(int p_level) const;
- virtual String debug_get_stack_level_source(int p_level) const;
- virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
- virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
- virtual ScriptInstance *debug_get_stack_level_instance(int p_level);
- virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
- virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1);
+ virtual String debug_get_error() const override;
+ virtual int debug_get_stack_level_count() const override;
+ virtual int debug_get_stack_level_line(int p_level) const override;
+ virtual String debug_get_stack_level_function(int p_level) const override;
+ virtual String debug_get_stack_level_source(int p_level) const override;
+ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
+ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
+ virtual ScriptInstance *debug_get_stack_level_instance(int p_level) override;
+ virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
+ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) override;
- virtual void reload_all_scripts();
- virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload);
+ virtual void reload_all_scripts() override;
+ virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;
- virtual void frame();
+ virtual void frame() override;
- virtual void get_public_functions(List<MethodInfo> *p_functions) const;
- virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const;
+ virtual void get_public_functions(List<MethodInfo> *p_functions) const override;
+ virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override;
- virtual void profiling_start();
- virtual void profiling_stop();
+ virtual void profiling_start() override;
+ virtual void profiling_stop() override;
- virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max);
- virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max);
+ virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override;
+ virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override;
/* LOADER FUNCTIONS */
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
/* GLOBAL CLASSES */
- virtual bool handles_global_class_type(const String &p_type) const;
- virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const;
+ virtual bool handles_global_class_type(const String &p_type) const override;
+ virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override;
void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass);
Ref<GDScript> get_orphan_subclass(const String &p_qualified_name);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index ec01c19295..3fb834590b 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -33,6 +33,7 @@
#include "core/config/engine.h"
#include "core/core_constants.h"
#include "core/io/file_access.h"
+#include "editor_templates/templates.gen.h"
#include "gdscript_analyzer.h"
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
@@ -55,68 +56,44 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("\"\"\" \"\"\"");
}
-String GDScriptLanguage::_get_processed_template(const String &p_template, const String &p_base_class_name) const {
- String processed_template = p_template;
+bool GDScriptLanguage::is_using_templates() {
+ return true;
+}
+Ref<Script> GDScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {
+ Ref<GDScript> script;
+ script.instantiate();
+ String processed_template = p_template;
#ifdef TOOLS_ENABLED
- if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) {
- processed_template = processed_template.replace("%INT_TYPE%", ": int");
- processed_template = processed_template.replace("%STRING_TYPE%", ": String");
- processed_template = processed_template.replace("%FLOAT_TYPE%", ": float");
- processed_template = processed_template.replace("%VOID_RETURN%", " -> void");
- } else {
- processed_template = processed_template.replace("%INT_TYPE%", "");
- processed_template = processed_template.replace("%STRING_TYPE%", "");
- processed_template = processed_template.replace("%FLOAT_TYPE%", "");
- processed_template = processed_template.replace("%VOID_RETURN%", "");
+ if (!EDITOR_DEF("text_editor/completion/add_type_hints", false)) {
+ processed_template = processed_template.replace(": int", "")
+ .replace(": String", "")
+ .replace(": float", "")
+ .replace(":=", "=")
+ .replace(" -> void", "");
}
#else
- processed_template = processed_template.replace("%INT_TYPE%", "");
- processed_template = processed_template.replace("%STRING_TYPE%", "");
- processed_template = processed_template.replace("%FLOAT_TYPE%", "");
- processed_template = processed_template.replace("%VOID_RETURN%", "");
+ processed_template = processed_template.replace(": int", "")
+ .replace(": String", "")
+ .replace(": float", "")
+ .replace(" -> void", "");
#endif
- processed_template = processed_template.replace("%BASE%", p_base_class_name);
- processed_template = processed_template.replace("%TS%", _get_indentation());
-
- return processed_template;
-}
-
-Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
- String _template = "extends %BASE%\n"
- "\n"
- "\n"
- "# Declare member variables here. Examples:\n"
- "# var a%INT_TYPE% = 2\n"
- "# var b%STRING_TYPE% = \"text\"\n"
- "\n"
- "\n"
- "# Called when the node enters the scene tree for the first time.\n"
- "func _ready()%VOID_RETURN%:\n"
- "%TS%pass # Replace with function body.\n"
- "\n"
- "\n"
- "# Called every frame. 'delta' is the elapsed time since the previous frame.\n"
- "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n"
- "#%TS%pass\n";
-
- _template = _get_processed_template(_template, p_base_class_name);
-
- Ref<GDScript> script;
- script.instantiate();
- script->set_source_code(_template);
-
+ processed_template = processed_template.replace("_BASE_", p_base_class_name)
+ .replace("_CLASS_", p_class_name)
+ .replace("_TS_", _get_indentation());
+ script->set_source_code(processed_template);
return script;
}
-bool GDScriptLanguage::is_using_templates() {
- return true;
-}
-
-void GDScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {
- String _template = _get_processed_template(p_script->get_source_code(), p_base_class_name);
- p_script->set_source_code(_template);
+Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(StringName p_object) {
+ Vector<ScriptLanguage::ScriptTemplate> templates;
+ for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) {
+ if (TEMPLATES[i].inherit == p_object) {
+ templates.append(TEMPLATES[i]);
+ }
+ }
+ return templates;
}
static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, Map<int, String> &r_funcs) {
@@ -236,7 +213,7 @@ Script *GDScriptLanguage::create_script() const {
/* DEBUGGER FUNCTIONS */
bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
- //break because of parse error
+ // break because of parse error
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
_debug_parse_err_line = p_line;
@@ -1383,8 +1360,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
}
if (!script.ends_with(".gd")) {
- //not a script, try find the script anyway,
- //may have some success
+ // not a script, try find the script anyway,
+ // may have some success
script = script.get_basename() + ".gd";
}
@@ -2770,7 +2747,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
}
if (indent_stack.size() && indent_stack.back()->get() != tc) {
- indent_stack.push_back(tc); //this is not right but gets the job done
+ indent_stack.push_back(tc); // this is not right but gets the job done
}
}
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 95c959235c..db35e804aa 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -63,3 +63,5 @@ elif env["platform"] == "android":
if env["tools"]:
env_mono.add_source_files(env.modules_sources, "editor/*.cpp")
+
+SConscript("editor_templates/SCsub")
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index aa24f59fdb..4dcf975a9a 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -55,6 +55,7 @@
#endif
#include "editor/editor_internal_calls.h"
+#include "editor_templates/templates.gen.h"
#include "godotsharp_dirs.h"
#include "mono_gd/gd_mono_cache.h"
#include "mono_gd/gd_mono_class.h"
@@ -351,57 +352,33 @@ static String get_base_class_name(const String &p_base_class_name, const String
return base_class;
}
-Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
- String script_template = "using " BINDINGS_NAMESPACE ";\n"
- "using System;\n"
- "\n"
- "public partial class %CLASS% : %BASE%\n"
- "{\n"
- " // Declare member variables here. Examples:\n"
- " // private int a = 2;\n"
- " // private string b = \"text\";\n"
- "\n"
- " // Called when the node enters the scene tree for the first time.\n"
- " public override void _Ready()\n"
- " {\n"
- " \n"
- " }\n"
- "\n"
- "// // Called every frame. 'delta' is the elapsed time since the previous frame.\n"
- "// public override void _Process(float delta)\n"
- "// {\n"
- "// \n"
- "// }\n"
- "}\n";
-
- // Replaces all spaces in p_class_name with underscores to prevent
- // invalid C# Script templates from being generated when the object name
- // has spaces in it.
- String class_name_no_spaces = p_class_name.replace(" ", "_");
- String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces);
- script_template = script_template.replace("%BASE%", base_class_name)
- .replace("%CLASS%", class_name_no_spaces);
+bool CSharpLanguage::is_using_templates() {
+ return true;
+}
+Ref<Script> CSharpLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {
Ref<CSharpScript> script;
script.instantiate();
- script->set_source_code(script_template);
- script->set_name(class_name_no_spaces);
+ String class_name_no_spaces = p_class_name.replace(" ", "_");
+ String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces);
+ String processed_template = p_template;
+ processed_template = processed_template.replace("_BINDINGS_NAMESPACE_", BINDINGS_NAMESPACE)
+ .replace("_BASE_", base_class_name)
+ .replace("_CLASS_", class_name_no_spaces)
+ .replace("_TS_", _get_indentation());
+ script->set_source_code(processed_template);
return script;
}
-bool CSharpLanguage::is_using_templates() {
- return true;
-}
-
-void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {
- String src = p_script->get_source_code();
- String class_name_no_spaces = p_class_name.replace(" ", "_");
- String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces);
- src = src.replace("%BASE%", base_class_name)
- .replace("%CLASS%", class_name_no_spaces)
- .replace("%TS%", _get_indentation());
- p_script->set_source_code(src);
+Vector<ScriptLanguage::ScriptTemplate> CSharpLanguage::get_built_in_templates(StringName p_object) {
+ Vector<ScriptLanguage::ScriptTemplate> templates;
+ for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) {
+ if (TEMPLATES[i].inherit == p_object) {
+ templates.append(TEMPLATES[i]);
+ }
+ }
+ return templates;
}
String CSharpLanguage::validate_path(const String &p_path) const {
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index c998d9c1e4..15a0326383 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -463,9 +463,9 @@ public:
bool is_control_flow_keyword(String p_keyword) const override;
void get_comment_delimiters(List<String> *p_delimiters) const override;
void get_string_delimiters(List<String> *p_delimiters) const override;
- Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const override;
bool is_using_templates() override;
- void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) override;
+ virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
+ virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override;
/* TODO */ bool validate(const String &p_script, const String &p_path, List<String> *r_functions,
List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override {
return true;
diff --git a/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs
new file mode 100644
index 0000000000..6e9547ce9b
--- /dev/null
+++ b/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs
@@ -0,0 +1,41 @@
+// meta-description: Classic movement for gravity games (platformer, ...)
+
+using _BINDINGS_NAMESPACE_;
+using System;
+
+public partial class _CLASS_ : _BASE_
+{
+ public const float Speed = 300.0f;
+ public const float JumpForce = -400.0f;
+
+ // Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
+ public float gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
+
+ public override void _PhysicsProcess(float delta)
+ {
+ Vector2 motionVelocity = MotionVelocity;
+
+ // Add the gravity.
+ if (!IsOnFloor())
+ motionVelocity.y += gravity * delta;
+
+ // Handle Jump.
+ if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
+ motionVelocity.y = JumpForce;
+
+ // Get the input direction and handle the movement/deceleration.
+ // As good practice, you should replace UI actions with custom gameplay actions.
+ Vector2 direction = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
+ if (direction != Vector2.Zero)
+ {
+ motionVelocity.x = direction.x * Speed;
+ }
+ else
+ {
+ motionVelocity.x = Mathf.MoveToward(MotionVelocity.x, 0, Speed);
+ }
+
+ MotionVelocity = motionVelocity;
+ MoveAndSlide();
+ }
+}
diff --git a/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs
new file mode 100644
index 0000000000..13be4bbcb1
--- /dev/null
+++ b/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs
@@ -0,0 +1,44 @@
+// meta-description: Classic movement for gravity games (FPS, TPS, ...)
+
+using _BINDINGS_NAMESPACE_;
+using System;
+
+public partial class _CLASS_ : _BASE_
+{
+ public const float Speed = 5.0f;
+ public const float JumpForce = 4.5f;
+
+ // Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
+ public float gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
+
+ public override void _PhysicsProcess(float delta)
+ {
+ Vector3 motionVelocity = MotionVelocity;
+
+ // Add the gravity.
+ if (!IsOnFloor())
+ motionVelocity.y -= gravity * delta;
+
+ // Handle Jump.
+ if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
+ motionVelocity.y = JumpForce;
+
+ // Get the input direction and handle the movement/deceleration.
+ // As good practice, you should replace UI actions with custom gameplay actions.
+ Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
+ Vector3 direction = Transform.basis.Xform(new Vector3(inputDir.x, 0, inputDir.y)).Normalized();
+ if (direction != Vector3.Zero)
+ {
+ motionVelocity.x = direction.x * Speed;
+ motionVelocity.z = direction.z * Speed;
+ }
+ else
+ {
+ motionVelocity.x = Mathf.MoveToward(MotionVelocity.x, 0, Speed);
+ motionVelocity.z = Mathf.MoveToward(MotionVelocity.z, 0, Speed);
+ }
+
+ MotionVelocity = motionVelocity;
+ MoveAndSlide();
+ }
+}
diff --git a/modules/mono/editor_templates/EditorPlugin/plugin.cs b/modules/mono/editor_templates/EditorPlugin/plugin.cs
new file mode 100644
index 0000000000..6e6a799be6
--- /dev/null
+++ b/modules/mono/editor_templates/EditorPlugin/plugin.cs
@@ -0,0 +1,19 @@
+// meta-description: Basic plugin template
+#if TOOLS
+using _BINDINGS_NAMESPACE_;
+using System;
+
+[Tool]
+public partial class _CLASS_ : _BASE_
+{
+ public override void _EnterTree()
+ {
+ // Initialization of the plugin goes here.
+ }
+
+ public override void _ExitTree()
+ {
+ // Clean-up of the plugin goes here.
+ }
+}
+#endif
diff --git a/modules/mono/editor_templates/EditorScript/basic_editor_script.cs b/modules/mono/editor_templates/EditorScript/basic_editor_script.cs
new file mode 100644
index 0000000000..2088822890
--- /dev/null
+++ b/modules/mono/editor_templates/EditorScript/basic_editor_script.cs
@@ -0,0 +1,14 @@
+// meta-description: Basic editor script template
+#if TOOLS
+using _BINDINGS_NAMESPACE_;
+using System;
+
+[Tool]
+public partial class _CLASS_ : _BASE_
+{
+ public override void _Run()
+ {
+ // Called when the script is executed (using File -> Run in Script Editor).
+ }
+}
+#endif
diff --git a/modules/mono/editor_templates/Node/default.cs b/modules/mono/editor_templates/Node/default.cs
new file mode 100644
index 0000000000..73d69dd993
--- /dev/null
+++ b/modules/mono/editor_templates/Node/default.cs
@@ -0,0 +1,19 @@
+// meta-description: Base template for Node with default Godot cycle methods
+
+using _BINDINGS_NAMESPACE_;
+using System;
+
+public partial class _CLASS_ : _BASE_
+{
+ // Called when the node enters the scene tree for the first time.
+ public override void _Ready()
+ {
+
+ }
+
+ // Called every frame. 'delta' is the elapsed time since the previous frame.
+ public override void _Process(float delta)
+ {
+
+ }
+}
diff --git a/modules/mono/editor_templates/Object/empty.cs b/modules/mono/editor_templates/Object/empty.cs
new file mode 100644
index 0000000000..e5bee64fe1
--- /dev/null
+++ b/modules/mono/editor_templates/Object/empty.cs
@@ -0,0 +1,9 @@
+// meta-description: Empty template suitable for all Objects
+
+using _BINDINGS_NAMESPACE_;
+using System;
+
+public partial class _CLASS_ : _BASE_
+{
+
+}
diff --git a/modules/mono/editor_templates/SCsub b/modules/mono/editor_templates/SCsub
new file mode 100644
index 0000000000..39f6cb5c01
--- /dev/null
+++ b/modules/mono/editor_templates/SCsub
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+Import("env")
+
+import editor.template_builders as build_template_cs
+
+env["BUILDERS"]["MakeCSharpTemplateBuilder"] = Builder(
+ action=env.Run(build_template_cs.make_templates, "Generating C# templates header."),
+ suffix=".h",
+ src_suffix=".cs",
+)
+
+# Template files
+templates_sources = Glob("*/*.cs")
+
+env.Alias("editor_template_cs", [env.MakeCSharpTemplateBuilder("templates.gen.h", templates_sources)])
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
index b3fd678379..1ac5768755 100644
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
@@ -16,85 +16,85 @@
</members>
<constants>
<constant name="MATH_SIN" value="0" enum="BuiltinFunc">
- Return the sine of the input.
+ Returns the sine of the input.
</constant>
<constant name="MATH_COS" value="1" enum="BuiltinFunc">
- Return the cosine of the input.
+ Returns the cosine of the input.
</constant>
<constant name="MATH_TAN" value="2" enum="BuiltinFunc">
- Return the tangent of the input.
+ Returns the tangent of the input.
</constant>
<constant name="MATH_SINH" value="3" enum="BuiltinFunc">
- Return the hyperbolic sine of the input.
+ Returns the hyperbolic sine of the input.
</constant>
<constant name="MATH_COSH" value="4" enum="BuiltinFunc">
- Return the hyperbolic cosine of the input.
+ Returns the hyperbolic cosine of the input.
</constant>
<constant name="MATH_TANH" value="5" enum="BuiltinFunc">
- Return the hyperbolic tangent of the input.
+ Returns the hyperbolic tangent of the input.
</constant>
<constant name="MATH_ASIN" value="6" enum="BuiltinFunc">
- Return the arc sine of the input.
+ Returns the arc sine of the input.
</constant>
<constant name="MATH_ACOS" value="7" enum="BuiltinFunc">
- Return the arc cosine of the input.
+ Returns the arc cosine of the input.
</constant>
<constant name="MATH_ATAN" value="8" enum="BuiltinFunc">
- Return the arc tangent of the input.
+ Returns the arc tangent of the input.
</constant>
<constant name="MATH_ATAN2" value="9" enum="BuiltinFunc">
- Return the arc tangent of the input, using the signs of both parameters to determine the exact angle.
+ Returns the arc tangent of the input, using the signs of both parameters to determine the exact angle.
</constant>
<constant name="MATH_SQRT" value="10" enum="BuiltinFunc">
- Return the square root of the input.
+ Returns the square root of the input.
</constant>
<constant name="MATH_FMOD" value="11" enum="BuiltinFunc">
- Return the remainder of one input divided by the other, using floating-point numbers.
+ Returns the remainder of one input divided by the other, using floating-point numbers.
</constant>
<constant name="MATH_FPOSMOD" value="12" enum="BuiltinFunc">
- Return the positive remainder of one input divided by the other, using floating-point numbers.
+ Returns the positive remainder of one input divided by the other, using floating-point numbers.
</constant>
<constant name="MATH_FLOOR" value="13" enum="BuiltinFunc">
- Return the input rounded down.
+ Returns the input rounded down.
</constant>
<constant name="MATH_CEIL" value="14" enum="BuiltinFunc">
- Return the input rounded up.
+ Returns the input rounded up.
</constant>
<constant name="MATH_ROUND" value="15" enum="BuiltinFunc">
- Return the input rounded to the nearest integer.
+ Returns the input rounded to the nearest integer.
</constant>
<constant name="MATH_ABS" value="16" enum="BuiltinFunc">
- Return the absolute value of the input.
+ Returns the absolute value of the input.
</constant>
<constant name="MATH_SIGN" value="17" enum="BuiltinFunc">
- Return the sign of the input, turning it into 1, -1, or 0. Useful to determine if the input is positive or negative.
+ Returns the sign of the input, turning it into 1, -1, or 0. Useful to determine if the input is positive or negative.
</constant>
<constant name="MATH_POW" value="18" enum="BuiltinFunc">
- Return the input raised to a given power.
+ Returns the input raised to a given power.
</constant>
<constant name="MATH_LOG" value="19" enum="BuiltinFunc">
- Return the natural logarithm of the input. Note that this is not the typical base-10 logarithm function calculators use.
+ Returns the natural logarithm of the input. Note that this is not the typical base-10 logarithm function calculators use.
</constant>
<constant name="MATH_EXP" value="20" enum="BuiltinFunc">
- Return the mathematical constant [b]e[/b] raised to the specified power of the input. [b]e[/b] has an approximate value of 2.71828.
+ Returns the mathematical constant [b]e[/b] raised to the specified power of the input. [b]e[/b] has an approximate value of 2.71828.
</constant>
<constant name="MATH_ISNAN" value="21" enum="BuiltinFunc">
- Return whether the input is NaN (Not a Number) or not. NaN is usually produced by dividing 0 by 0, though other ways exist.
+ Returns whether the input is NaN (Not a Number) or not. NaN is usually produced by dividing 0 by 0, though other ways exist.
</constant>
<constant name="MATH_ISINF" value="22" enum="BuiltinFunc">
- Return whether the input is an infinite floating-point number or not. Infinity is usually produced by dividing a number by 0, though other ways exist.
+ Returns whether the input is an infinite floating-point number or not. Infinity is usually produced by dividing a number by 0, though other ways exist.
</constant>
<constant name="MATH_EASE" value="23" enum="BuiltinFunc">
Easing function, based on exponent. 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in.
</constant>
<constant name="MATH_STEP_DECIMALS" value="24" enum="BuiltinFunc">
- Return the number of digit places after the decimal that the first non-zero digit occurs.
+ Returns the number of digit places after the decimal that the first non-zero digit occurs.
</constant>
<constant name="MATH_SNAPPED" value="25" enum="BuiltinFunc">
- Return the input snapped to a given step.
+ Returns the input snapped to a given step.
</constant>
<constant name="MATH_LERP" value="26" enum="BuiltinFunc">
- Return a number linearly interpolated between the first two inputs, based on the third input. Uses the formula [code]a + (a - b) * t[/code].
+ Returns a number linearly interpolated between the first two inputs, based on the third input. Uses the formula [code]a + (a - b) * t[/code].
</constant>
<constant name="MATH_INVERSE_LERP" value="27" enum="BuiltinFunc">
</constant>
@@ -107,16 +107,16 @@
Randomize the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time.
</constant>
<constant name="MATH_RANDI" value="31" enum="BuiltinFunc">
- Return a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use it with the remainder function.
+ Returns a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use it with the remainder function.
</constant>
<constant name="MATH_RANDF" value="32" enum="BuiltinFunc">
- Return a random floating-point value between 0 and 1. To obtain a random value between 0 to N, you can use it with multiplication.
+ Returns a random floating-point value between 0 and 1. To obtain a random value between 0 to N, you can use it with multiplication.
</constant>
<constant name="MATH_RANDI_RANGE" value="33" enum="BuiltinFunc">
- Return a random 32-bit integer value between the two inputs.
+ Returns a random 32-bit integer value between the two inputs.
</constant>
<constant name="MATH_RANDF_RANGE" value="34" enum="BuiltinFunc">
- Return a random floating-point value between the two inputs.
+ Returns a random floating-point value between the two inputs.
</constant>
<constant name="MATH_RANDFN" value="35" enum="BuiltinFunc">
Returns a normally-distributed pseudo-random number, using Box-Muller transform with the specified mean and a standard deviation. This is also called Gaussian distribution.
@@ -125,7 +125,7 @@
Set the seed for the random number generator.
</constant>
<constant name="MATH_RANDSEED" value="37" enum="BuiltinFunc">
- Return a random value from the given seed, along with the new seed.
+ Returns a random value from the given seed, along with the new seed.
</constant>
<constant name="MATH_DEG2RAD" value="38" enum="BuiltinFunc">
Convert the input from degrees to radians.
@@ -144,19 +144,19 @@
<constant name="MATH_WRAPF" value="43" enum="BuiltinFunc">
</constant>
<constant name="MATH_PINGPONG" value="44" enum="BuiltinFunc">
- Return the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code]. If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave). If [code]length[/code] is less than zero, it becomes positive.
+ Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code]. If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave). If [code]length[/code] is less than zero, it becomes positive.
</constant>
<constant name="LOGIC_MAX" value="45" enum="BuiltinFunc">
- Return the greater of the two numbers, also known as their maximum.
+ Returns the greater of the two numbers, also known as their maximum.
</constant>
<constant name="LOGIC_MIN" value="46" enum="BuiltinFunc">
- Return the lesser of the two numbers, also known as their minimum.
+ Returns the lesser of the two numbers, also known as their minimum.
</constant>
<constant name="LOGIC_CLAMP" value="47" enum="BuiltinFunc">
- Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code].
+ Returns the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code].
</constant>
<constant name="LOGIC_NEAREST_PO2" value="48" enum="BuiltinFunc">
- Return the nearest power of 2 to the input.
+ Returns the nearest power of 2 to the input.
</constant>
<constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc">
Create a [WeakRef] from the input.
@@ -165,13 +165,13 @@
Convert between types.
</constant>
<constant name="TYPE_OF" value="51" enum="BuiltinFunc">
- Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned.
+ Returns the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned.
</constant>
<constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc">
Checks if a type is registered in the [ClassDB].
</constant>
<constant name="TEXT_CHAR" value="53" enum="BuiltinFunc">
- Return a character with the given ascii value.
+ Returns a character with the given ascii value.
</constant>
<constant name="TEXT_STR" value="54" enum="BuiltinFunc">
Convert the input to a string.
@@ -200,7 +200,7 @@
Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES].
</constant>
<constant name="MATH_SMOOTHSTEP" value="63" enum="BuiltinFunc">
- Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula:
+ Returns a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula:
[codeblock]
var t = clamp((weight - from) / (to - from), 0.0, 1.0)
return t * t * (3.0 - 2.0 * t)
diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
index 2c6313c80a..486f60400a 100644
--- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
@@ -12,112 +12,112 @@
<method name="_get_caption" qualifiers="virtual const">
<return type="String" />
<description>
- Return the node's title.
+ Returns the node's title.
</description>
</method>
<method name="_get_category" qualifiers="virtual const">
<return type="String" />
<description>
- Return the node's category.
+ Returns the node's category.
</description>
</method>
<method name="_get_input_value_port_count" qualifiers="virtual const">
<return type="int" />
<description>
- Return the count of input value ports.
+ Returns the count of input value ports.
</description>
</method>
<method name="_get_input_value_port_hint" qualifiers="virtual const">
<return type="int" />
<argument index="0" name="input_idx" type="int" />
<description>
- Return the specified input port's hint. See the [enum @GlobalScope.PropertyHint] hints.
+ Returns the specified input port's hint. See the [enum @GlobalScope.PropertyHint] hints.
</description>
</method>
<method name="_get_input_value_port_hint_string" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="input_idx" type="int" />
<description>
- Return the specified input port's hint string.
+ Returns the specified input port's hint string.
</description>
</method>
<method name="_get_input_value_port_name" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="input_idx" type="int" />
<description>
- Return the specified input port's name.
+ Returns the specified input port's name.
</description>
</method>
<method name="_get_input_value_port_type" qualifiers="virtual const">
<return type="int" />
<argument index="0" name="input_idx" type="int" />
<description>
- Return the specified input port's type. See the [enum Variant.Type] values.
+ Returns the specified input port's type. See the [enum Variant.Type] values.
</description>
</method>
<method name="_get_output_sequence_port_count" qualifiers="virtual const">
<return type="int" />
<description>
- Return the amount of output [b]sequence[/b] ports.
+ Returns the amount of output [b]sequence[/b] ports.
</description>
</method>
<method name="_get_output_sequence_port_text" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="seq_idx" type="int" />
<description>
- Return the specified [b]sequence[/b] output's name.
+ Returns the specified [b]sequence[/b] output's name.
</description>
</method>
<method name="_get_output_value_port_count" qualifiers="virtual const">
<return type="int" />
<description>
- Return the amount of output value ports.
+ Returns the amount of output value ports.
</description>
</method>
<method name="_get_output_value_port_hint" qualifiers="virtual const">
<return type="int" />
<argument index="0" name="output_idx" type="int" />
<description>
- Return the specified output port's hint. See the [enum @GlobalScope.PropertyHint] hints.
+ Returns the specified output port's hint. See the [enum @GlobalScope.PropertyHint] hints.
</description>
</method>
<method name="_get_output_value_port_hint_string" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="output_idx" type="int" />
<description>
- Return the specified output port's hint string.
+ Returns the specified output port's hint string.
</description>
</method>
<method name="_get_output_value_port_name" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="output_idx" type="int" />
<description>
- Return the specified output port's name.
+ Returns the specified output port's name.
</description>
</method>
<method name="_get_output_value_port_type" qualifiers="virtual const">
<return type="int" />
<argument index="0" name="output_idx" type="int" />
<description>
- Return the specified output port's type. See the [enum Variant.Type] values.
+ Returns the specified output port's type. See the [enum Variant.Type] values.
</description>
</method>
<method name="_get_text" qualifiers="virtual const">
<return type="String" />
<description>
- Return the custom node's text, which is shown right next to the input [b]sequence[/b] port (if there is none, on the place that is usually taken by it).
+ Returns the custom node's text, which is shown right next to the input [b]sequence[/b] port (if there is none, on the place that is usually taken by it).
</description>
</method>
<method name="_get_working_memory_size" qualifiers="virtual const">
<return type="int" />
<description>
- Return the size of the custom node's working memory. See [method _step] for more details.
+ Returns the size of the custom node's working memory. See [method _step] for more details.
</description>
</method>
<method name="_has_input_sequence_port" qualifiers="virtual const">
<return type="bool" />
<description>
- Return whether the custom node has an input [b]sequence[/b] port.
+ Returns whether the custom node has an input [b]sequence[/b] port.
</description>
</method>
<method name="_step" qualifiers="virtual const">
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 63bd9bbee4..50c24179ff 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -2241,20 +2241,15 @@ void VisualScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) co
void VisualScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
}
-Ref<Script> VisualScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
- Ref<VisualScript> script;
- script.instantiate();
- script->set_instance_base_type(p_base_class_name);
- return script;
-}
-
bool VisualScriptLanguage::is_using_templates() {
- return true;
+ return false;
}
-void VisualScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {
- Ref<VisualScript> script = p_script;
+Ref<Script> VisualScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {
+ Ref<VisualScript> script;
+ script.instantiate();
script->set_instance_base_type(p_base_class_name);
+ return script;
}
bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 39cef8f68b..e321ef236d 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -554,57 +554,56 @@ public:
//////////////////////////////////////
- virtual String get_name() const;
+ virtual String get_name() const override;
/* LANGUAGE FUNCTIONS */
- virtual void init();
- virtual String get_type() const;
- virtual String get_extension() const;
- virtual Error execute_file(const String &p_path);
- virtual void finish();
+ virtual void init() override;
+ virtual String get_type() const override;
+ virtual String get_extension() const override;
+ virtual Error execute_file(const String &p_path) override;
+ virtual void finish() override;
/* EDITOR FUNCTIONS */
- virtual void get_reserved_words(List<String> *p_words) const;
- virtual bool is_control_flow_keyword(String p_keyword) const;
- virtual void get_comment_delimiters(List<String> *p_delimiters) const;
- virtual void get_string_delimiters(List<String> *p_delimiters) const;
- virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool is_using_templates();
- virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
- virtual Script *create_script() const;
- virtual bool has_named_classes() const;
- virtual bool supports_builtin_mode() const;
- virtual int find_function(const String &p_function, const String &p_code) const;
- virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
- virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const;
- virtual void add_global_constant(const StringName &p_variable, const Variant &p_value);
+ virtual void get_reserved_words(List<String> *p_words) const override;
+ virtual bool is_control_flow_keyword(String p_keyword) const override;
+ virtual void get_comment_delimiters(List<String> *p_delimiters) const override;
+ virtual void get_string_delimiters(List<String> *p_delimiters) const override;
+ virtual bool is_using_templates() override;
+ virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override;
+ virtual Script *create_script() const override;
+ virtual bool has_named_classes() const override;
+ virtual bool supports_builtin_mode() const override;
+ virtual int find_function(const String &p_function, const String &p_code) const override;
+ virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override;
+ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override;
+ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) override;
/* DEBUGGER FUNCTIONS */
- virtual String debug_get_error() const;
- virtual int debug_get_stack_level_count() const;
- virtual int debug_get_stack_level_line(int p_level) const;
- virtual String debug_get_stack_level_function(int p_level) const;
- virtual String debug_get_stack_level_source(int p_level) const;
- virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
- virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
- virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
- virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1);
-
- virtual void reload_all_scripts();
- virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload);
+ virtual String debug_get_error() const override;
+ virtual int debug_get_stack_level_count() const override;
+ virtual int debug_get_stack_level_line(int p_level) const override;
+ virtual String debug_get_stack_level_function(int p_level) const override;
+ virtual String debug_get_stack_level_source(int p_level) const override;
+ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
+ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
+ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
+ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) override;
+
+ virtual void reload_all_scripts() override;
+ virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;
/* LOADER FUNCTIONS */
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual void get_public_functions(List<MethodInfo> *p_functions) const;
- virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+ virtual void get_public_functions(List<MethodInfo> *p_functions) const override;
+ virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override;
- virtual void profiling_start();
- virtual void profiling_stop();
+ virtual void profiling_start() override;
+ virtual void profiling_stop() override;
- virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max);
- virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max);
+ virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override;
+ virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override;
void add_register_func(const String &p_name, VisualScriptNodeRegisterFunc p_func);
void remove_register_func(const String &p_name);
diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
index a8360a4d45..780791c0d9 100644
--- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
@@ -32,7 +32,7 @@
<return type="Dictionary" />
<argument index="0" name="peer_id" type="int" />
<description>
- Return a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open).
+ Returns a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open).
</description>
</method>
<method name="get_peers">
diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml
index 4b515c12a1..5fcf51293d 100644
--- a/modules/websocket/doc_classes/WebSocketClient.xml
+++ b/modules/websocket/doc_classes/WebSocketClient.xml
@@ -39,13 +39,13 @@
<method name="get_connected_host" qualifiers="const">
<return type="String" />
<description>
- Return the IP address of the currently connected host.
+ Returns the IP address of the currently connected host.
</description>
</method>
<method name="get_connected_port" qualifiers="const">
<return type="int" />
<description>
- Return the IP port of the currently connected host.
+ Returns the IP port of the currently connected host.
</description>
</method>
</methods>
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index efdcc6c77b..ac008edbed 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -124,8 +124,11 @@ task zipCustomBuild(type: Zip) {
def templateExcludedBuildTask() {
// We exclude these gradle tasks so we can run the scons command manually.
def excludedTasks = []
- for (String buildType : supportedTargets) {
- excludedTasks += ":lib:" + getSconsTaskName(buildType)
+ if (!isAndroidStudio()) {
+ logger.lifecycle("Excluding Android studio build tasks")
+ for (String buildType : supportedTargets) {
+ excludedTasks += ":lib:" + getSconsTaskName(buildType)
+ }
}
return excludedTasks
}
@@ -155,6 +158,11 @@ def templateBuildTasks() {
return tasks
}
+def isAndroidStudio() {
+ def sysProps = System.getProperties()
+ return sysProps != null && sysProps['idea.platform.prefix'] != null
+}
+
/**
* Master task used to coordinate the tasks defined above to generate the set of Godot templates.
*/
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 899cf5dfad..fc100719cb 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -59,6 +59,15 @@ static String format_error_message(DWORD id) {
return msg;
}
+static void track_mouse_leave_event(HWND hWnd) {
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hWnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&tme);
+}
+
bool DisplayServerWindows::has_feature(Feature p_feature) const {
switch (p_feature) {
case FEATURE_SUBWINDOWS:
@@ -1976,6 +1985,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Run a timer to prevent event catching warning if the focused window is closing.
windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
}
+ if (wParam != WA_INACTIVE) {
+ track_mouse_leave_event(hWnd);
+ }
return 0; // Return to the message loop.
} break;
case WM_GETMINMAXINFO: {
@@ -2260,12 +2272,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
outside = false;
// Once-off notification, must call again.
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
+ track_mouse_leave_event(hWnd);
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
@@ -2366,12 +2373,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
outside = false;
// Once-off notification, must call again.
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
+ track_mouse_leave_event(hWnd);
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
@@ -3126,14 +3128,6 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
#endif
RegisterTouchWindow(wd.hWnd, 0);
-
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = wd.hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
-
DragAcceptFiles(wd.hWnd, true);
if ((tablet_get_current_driver() == "wintab") && wintab_available) {
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 6deb39b9e2..34b1dd4f24 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -1652,7 +1652,7 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("ensure_current_is_visible"), &ItemList::ensure_current_is_visible);
- ClassDB::bind_method(D_METHOD("get_v_scroll"), &ItemList::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &ItemList::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior);
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index e780179e7b..83af879dff 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -255,7 +255,7 @@ public:
void set_autoscroll_to_bottom(const bool p_enable);
- VScrollBar *get_v_scroll() { return scroll_bar; }
+ VScrollBar *get_v_scroll_bar() { return scroll_bar; }
ItemList();
~ItemList();
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index a61d77e95c..8837e5ea63 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -50,7 +50,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int hseparation = get_theme_constant(SNAME("hseparation"));
Size2 minsize = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); // Accounts for margin in the margin container
- minsize.x += scroll_container->get_v_scrollbar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
+ minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
float max_w = 0.0;
float icon_w = 0.0;
@@ -343,11 +343,11 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
// Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar.
Rect2 item_clickable_area = scroll_container->get_rect();
- if (scroll_container->get_v_scrollbar()->is_visible_in_tree()) {
+ if (scroll_container->get_v_scroll_bar()->is_visible_in_tree()) {
if (is_layout_rtl()) {
- item_clickable_area.position.x += scroll_container->get_v_scrollbar()->get_size().width;
+ item_clickable_area.position.x += scroll_container->get_v_scroll_bar()->get_size().width;
} else {
- item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width;
+ item_clickable_area.size.width -= scroll_container->get_v_scroll_bar()->get_size().width;
}
}
@@ -508,7 +508,7 @@ void PopupMenu::_draw_items() {
Color font_hover_color = get_theme_color(SNAME("font_hover_color"));
Color font_separator_color = get_theme_color(SNAME("font_separator_color"));
- float scroll_width = scroll_container->get_v_scrollbar()->is_visible_in_tree() ? scroll_container->get_v_scrollbar()->get_size().width : 0;
+ float scroll_width = scroll_container->get_v_scroll_bar()->is_visible_in_tree() ? scroll_container->get_v_scroll_bar()->get_size().width : 0;
float display_width = control->get_size().width - scroll_width;
// Find the widest icon and whether any items have a checkbox, and store the offsets for each.
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 19d165ef54..dfa871a345 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -4121,7 +4121,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow);
ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following);
- ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &RichTextLabel::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line);
ClassDB::bind_method(D_METHOD("scroll_to_paragraph", "paragraph"), &RichTextLabel::scroll_to_paragraph);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index d803e44b65..63d78810bf 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -544,7 +544,7 @@ public:
int get_content_height() const;
- VScrollBar *get_v_scroll() { return vscroll; }
+ VScrollBar *get_v_scroll_bar() { return vscroll; }
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index dcd2c32a3b..d957202550 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -532,11 +532,11 @@ TypedArray<String> ScrollContainer::get_configuration_warnings() const {
return warnings;
}
-HScrollBar *ScrollContainer::get_h_scrollbar() {
+HScrollBar *ScrollContainer::get_h_scroll_bar() {
return h_scroll;
}
-VScrollBar *ScrollContainer::get_v_scrollbar() {
+VScrollBar *ScrollContainer::get_v_scroll_bar() {
return v_scroll;
}
@@ -561,8 +561,8 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_follow_focus", "enabled"), &ScrollContainer::set_follow_focus);
ClassDB::bind_method(D_METHOD("is_following_focus"), &ScrollContainer::is_following_focus);
- ClassDB::bind_method(D_METHOD("get_h_scrollbar"), &ScrollContainer::get_h_scrollbar);
- ClassDB::bind_method(D_METHOD("get_v_scrollbar"), &ScrollContainer::get_v_scrollbar);
+ ClassDB::bind_method(D_METHOD("get_h_scroll_bar"), &ScrollContainer::get_h_scroll_bar);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &ScrollContainer::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("ensure_control_visible", "control"), &ScrollContainer::ensure_control_visible);
ADD_SIGNAL(MethodInfo("scroll_started"));
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 0cec4db57a..8d710591a1 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -105,8 +105,8 @@ public:
bool is_following_focus() const;
void set_follow_focus(bool p_follow);
- HScrollBar *get_h_scrollbar();
- VScrollBar *get_v_scrollbar();
+ HScrollBar *get_h_scroll_bar();
+ VScrollBar *get_v_scroll_bar();
void ensure_control_visible(Control *p_control);
TypedArray<String> get_configuration_warnings() const override;
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index f7c8f0921c..4d5f215bea 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -1591,7 +1591,7 @@ void AudioServer::set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout) {
Bus::Effect bfx;
bfx.effect = fx;
bfx.enabled = p_bus_layout->buses[i].effects[j].enabled;
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
bfx.prof_time = 0;
#endif
bus->effects.push_back(bfx);
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index 321d86ffda..5e9b66ed1f 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -2655,13 +2655,13 @@ void RendererStorageRD::MaterialData::update_uniform_buffer(const Map<StringName
uint32_t size = 0U;
// The following code enforces a 16-byte alignment of uniform arrays.
if (E.value.array_size > 0) {
- size = ShaderLanguage::get_type_size(E.value.type) * E.value.array_size;
+ size = ShaderLanguage::get_datatype_size(E.value.type) * E.value.array_size;
int m = (16 * E.value.array_size);
if ((size % m) != 0U) {
size += m - (size % m);
}
} else {
- size = ShaderLanguage::get_type_size(E.value.type);
+ size = ShaderLanguage::get_datatype_size(E.value.type);
}
ERR_CONTINUE(offset + size > p_buffer_size);
#endif
@@ -2818,12 +2818,15 @@ void RendererStorageRD::MaterialData::update_textures(const Map<StringName, Vari
case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: {
rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_BLACK);
} break;
- case ShaderLanguage::ShaderNode::Uniform::HINT_NONE: {
- rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL);
- } break;
case ShaderLanguage::ShaderNode::Uniform::HINT_ANISOTROPY: {
rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_ANISO);
} break;
+ case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: {
+ rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL);
+ } break;
+ case ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL: {
+ rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL);
+ } break;
default: {
rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE);
} break;
@@ -9234,7 +9237,7 @@ void RendererStorageRD::_update_global_variables() {
for (uint32_t i = 0; i < total_regions; i++) {
if (global_variables.buffer_dirty_regions[i]) {
- RD::get_singleton()->buffer_update(global_variables.buffer, i * region_byte_size, region_byte_size, global_variables.buffer_values);
+ RD::get_singleton()->buffer_update(global_variables.buffer, i * region_byte_size, region_byte_size, &global_variables.buffer_values[i * GlobalVariables::BUFFER_DIRTY_REGION_SIZE]);
global_variables.buffer_dirty_regions[i] = false;
}
diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.cpp b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
index 794c999d1d..9d1d535eba 100644
--- a/servers/rendering/renderer_rd/shader_compiler_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
@@ -54,81 +54,6 @@ static String _typestr(SL::DataType p_type) {
return type;
}
-static int _get_datatype_size(SL::DataType p_type) {
- switch (p_type) {
- case SL::TYPE_VOID:
- return 0;
- case SL::TYPE_BOOL:
- return 4;
- case SL::TYPE_BVEC2:
- return 8;
- case SL::TYPE_BVEC3:
- return 12;
- case SL::TYPE_BVEC4:
- return 16;
- case SL::TYPE_INT:
- return 4;
- case SL::TYPE_IVEC2:
- return 8;
- case SL::TYPE_IVEC3:
- return 12;
- case SL::TYPE_IVEC4:
- return 16;
- case SL::TYPE_UINT:
- return 4;
- case SL::TYPE_UVEC2:
- return 8;
- case SL::TYPE_UVEC3:
- return 12;
- case SL::TYPE_UVEC4:
- return 16;
- case SL::TYPE_FLOAT:
- return 4;
- case SL::TYPE_VEC2:
- return 8;
- case SL::TYPE_VEC3:
- return 12;
- case SL::TYPE_VEC4:
- return 16;
- case SL::TYPE_MAT2:
- return 32; // 4 * 4 + 4 * 4
- case SL::TYPE_MAT3:
- return 48; // 4 * 4 + 4 * 4 + 4 * 4
- case SL::TYPE_MAT4:
- return 64;
- case SL::TYPE_SAMPLER2D:
- return 16;
- case SL::TYPE_ISAMPLER2D:
- return 16;
- case SL::TYPE_USAMPLER2D:
- return 16;
- case SL::TYPE_SAMPLER2DARRAY:
- return 16;
- case SL::TYPE_ISAMPLER2DARRAY:
- return 16;
- case SL::TYPE_USAMPLER2DARRAY:
- return 16;
- case SL::TYPE_SAMPLER3D:
- return 16;
- case SL::TYPE_ISAMPLER3D:
- return 16;
- case SL::TYPE_USAMPLER3D:
- return 16;
- case SL::TYPE_SAMPLERCUBE:
- return 16;
- case SL::TYPE_SAMPLERCUBEARRAY:
- return 16;
- case SL::TYPE_STRUCT:
- return 0;
-
- case SL::TYPE_MAX: {
- ERR_FAIL_V(0);
- };
- }
-
- ERR_FAIL_V(0);
-}
-
static int _get_datatype_alignment(SL::DataType p_type) {
switch (p_type) {
case SL::TYPE_VOID:
@@ -658,12 +583,12 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
uniform_defines.write[uniform.order] = ucode;
if (is_buffer_global) {
//globals are indices into the global table
- uniform_sizes.write[uniform.order] = _get_datatype_size(ShaderLanguage::TYPE_UINT);
+ uniform_sizes.write[uniform.order] = ShaderLanguage::get_datatype_size(ShaderLanguage::TYPE_UINT);
uniform_alignments.write[uniform.order] = _get_datatype_alignment(ShaderLanguage::TYPE_UINT);
} else {
// The following code enforces a 16-byte alignment of uniform arrays.
if (uniform.array_size > 0) {
- int size = _get_datatype_size(uniform.type) * uniform.array_size;
+ int size = ShaderLanguage::get_datatype_size(uniform.type) * uniform.array_size;
int m = (16 * uniform.array_size);
if ((size % m) != 0) {
size += m - (size % m);
@@ -671,7 +596,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
uniform_sizes.write[uniform.order] = size;
uniform_alignments.write[uniform.order] = 16;
} else {
- uniform_sizes.write[uniform.order] = _get_datatype_size(uniform.type);
+ uniform_sizes.write[uniform.order] = ShaderLanguage::get_datatype_size(uniform.type);
uniform_alignments.write[uniform.order] = _get_datatype_alignment(uniform.type);
}
}
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 63c9b5327a..4ac9d8db64 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -977,8 +977,6 @@ void ShaderLanguage::clear() {
completion_base = TYPE_VOID;
completion_base_array = false;
- unknown_varying_usages.clear();
-
#ifdef DEBUG_ENABLED
used_constants.clear();
used_varyings.clear();
@@ -3868,55 +3866,77 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform
return pi;
}
-uint32_t ShaderLanguage::get_type_size(DataType p_type) {
+uint32_t ShaderLanguage::get_datatype_size(ShaderLanguage::DataType p_type) {
switch (p_type) {
case TYPE_VOID:
return 0;
case TYPE_BOOL:
- case TYPE_INT:
- case TYPE_UINT:
- case TYPE_FLOAT:
return 4;
case TYPE_BVEC2:
- case TYPE_IVEC2:
- case TYPE_UVEC2:
- case TYPE_VEC2:
return 8;
case TYPE_BVEC3:
- case TYPE_IVEC3:
- case TYPE_UVEC3:
- case TYPE_VEC3:
return 12;
case TYPE_BVEC4:
+ return 16;
+ case TYPE_INT:
+ return 4;
+ case TYPE_IVEC2:
+ return 8;
+ case TYPE_IVEC3:
+ return 12;
case TYPE_IVEC4:
+ return 16;
+ case TYPE_UINT:
+ return 4;
+ case TYPE_UVEC2:
+ return 8;
+ case TYPE_UVEC3:
+ return 12;
case TYPE_UVEC4:
+ return 16;
+ case TYPE_FLOAT:
+ return 4;
+ case TYPE_VEC2:
+ return 8;
+ case TYPE_VEC3:
+ return 12;
case TYPE_VEC4:
return 16;
case TYPE_MAT2:
- return 8;
+ return 32; // 4 * 4 + 4 * 4
case TYPE_MAT3:
- return 12;
+ return 48; // 4 * 4 + 4 * 4 + 4 * 4
case TYPE_MAT4:
- return 16;
+ return 64;
case TYPE_SAMPLER2D:
+ return 16;
case TYPE_ISAMPLER2D:
+ return 16;
case TYPE_USAMPLER2D:
+ return 16;
case TYPE_SAMPLER2DARRAY:
+ return 16;
case TYPE_ISAMPLER2DARRAY:
+ return 16;
case TYPE_USAMPLER2DARRAY:
+ return 16;
case TYPE_SAMPLER3D:
+ return 16;
case TYPE_ISAMPLER3D:
+ return 16;
case TYPE_USAMPLER3D:
+ return 16;
case TYPE_SAMPLERCUBE:
+ return 16;
case TYPE_SAMPLERCUBEARRAY:
- return 4; //not really, but useful for indices
+ return 16;
case TYPE_STRUCT:
- // FIXME: Implement.
- return 0;
- case ShaderLanguage::TYPE_MAX:
return 0;
+ case TYPE_MAX: {
+ ERR_FAIL_V(0);
+ };
}
- return 0;
+ ERR_FAIL_V(0);
}
void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {
@@ -4122,43 +4142,6 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St
return true;
}
-bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, String *r_message) {
- switch (p_varying.stage) {
- case ShaderNode::Varying::STAGE_UNKNOWN:
- VaryingUsage usage;
- usage.var = &p_varying;
- usage.line = tk_line;
- unknown_varying_usages.push_back(usage);
- break;
- case ShaderNode::Varying::STAGE_VERTEX:
- if (current_function == varying_function_names.fragment || current_function == varying_function_names.light) {
- p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT;
- }
- break;
- case ShaderNode::Varying::STAGE_FRAGMENT:
- if (current_function == varying_function_names.light) {
- p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT;
- }
- break;
- default:
- break;
- }
- return true;
-}
-
-bool ShaderLanguage::_check_varying_usages(int *r_error_line, String *r_error_message) const {
- for (const List<ShaderLanguage::VaryingUsage>::Element *E = unknown_varying_usages.front(); E; E = E->next()) {
- ShaderNode::Varying::Stage stage = E->get().var->stage;
- if (stage != ShaderNode::Varying::STAGE_UNKNOWN && stage != ShaderNode::Varying::STAGE_VERTEX && stage != ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT) {
- *r_error_line = E->get().line;
- *r_error_message = RTR("Fragment-stage varying could not been accessed in custom function!");
- return false;
- }
- }
-
- return true;
-}
-
bool ShaderLanguage::_check_node_constness(const Node *p_node) const {
switch (p_node->type) {
case Node::TYPE_OPERATOR: {
@@ -4991,55 +4974,104 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
for (int i = 0; i < call_function->arguments.size(); i++) {
int argidx = i + 1;
if (argidx < func->arguments.size()) {
- if (call_function->arguments[i].is_const || call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT || call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT) {
- bool error = false;
- Node *n = func->arguments[argidx];
- if (n->type == Node::TYPE_CONSTANT || n->type == Node::TYPE_OPERATOR) {
- if (!call_function->arguments[i].is_const) {
+ bool error = false;
+ Node *n = func->arguments[argidx];
+ ArgumentQualifier arg_qual = call_function->arguments[i].qualifier;
+ bool is_out_arg = arg_qual != ArgumentQualifier::ARGUMENT_QUALIFIER_IN;
+
+ if (n->type == Node::TYPE_VARIABLE || n->type == Node::TYPE_ARRAY) {
+ StringName varname;
+
+ if (n->type == Node::TYPE_VARIABLE) {
+ VariableNode *vn = static_cast<VariableNode *>(n);
+ varname = vn->name;
+ } else { // TYPE_ARRAY
+ ArrayNode *an = static_cast<ArrayNode *>(n);
+ varname = an->name;
+ }
+
+ if (shader->varyings.has(varname)) {
+ switch (shader->varyings[varname].stage) {
+ case ShaderNode::Varying::STAGE_UNKNOWN: {
+ _set_error(vformat("Varying '%s' must be assigned in the vertex or fragment function first!", varname));
+ return nullptr;
+ }
+ case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT:
+ [[fallthrough]];
+ case ShaderNode::Varying::STAGE_VERTEX:
+ if (is_out_arg && current_function != varying_function_names.vertex) { // inout/out
+ error = true;
+ }
+ break;
+ case ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT:
+ [[fallthrough]];
+ case ShaderNode::Varying::STAGE_FRAGMENT:
+ if (!is_out_arg) {
+ if (current_function != varying_function_names.fragment && current_function != varying_function_names.light) {
+ error = true;
+ }
+ } else if (current_function != varying_function_names.fragment) { // inout/out
+ error = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (error) {
+ _set_error(vformat("Varying '%s' cannot be passed for the '%s' parameter in that context!", varname, _get_qualifier_str(arg_qual)));
+ return nullptr;
+ }
+ }
+ }
+
+ bool is_const_arg = call_function->arguments[i].is_const;
+
+ if (is_const_arg || is_out_arg) {
+ StringName varname;
+
+ if (n->type == Node::TYPE_CONSTANT || n->type == Node::TYPE_OPERATOR || n->type == Node::TYPE_ARRAY_CONSTRUCT) {
+ if (!is_const_arg) {
error = true;
}
} else if (n->type == Node::TYPE_ARRAY) {
ArrayNode *an = static_cast<ArrayNode *>(n);
- if (an->call_expression != nullptr || an->is_const) {
+ if (!is_const_arg && (an->call_expression != nullptr || an->is_const)) {
error = true;
}
+ varname = an->name;
} else if (n->type == Node::TYPE_VARIABLE) {
VariableNode *vn = static_cast<VariableNode *>(n);
- if (vn->is_const) {
+ if (vn->is_const && !is_const_arg) {
error = true;
- } else {
- StringName varname = vn->name;
- if (shader->constants.has(varname)) {
- error = true;
- } else if (shader->uniforms.has(varname)) {
- error = true;
- } else {
- if (shader->varyings.has(varname)) {
- _set_error(vformat("Varyings cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier)));
- return nullptr;
- }
- if (p_function_info.built_ins.has(varname)) {
- BuiltInInfo info = p_function_info.built_ins[varname];
- if (info.constant) {
- error = true;
- }
- }
- }
}
+ varname = vn->name;
} else if (n->type == Node::TYPE_MEMBER) {
MemberNode *mn = static_cast<MemberNode *>(n);
- if (mn->basetype_const) {
+ if (mn->basetype_const && is_out_arg) {
error = true;
}
}
+ if (!error && varname != StringName()) {
+ if (shader->constants.has(varname)) {
+ error = true;
+ } else if (shader->uniforms.has(varname)) {
+ error = true;
+ } else if (p_function_info.built_ins.has(varname)) {
+ BuiltInInfo info = p_function_info.built_ins[varname];
+ if (info.constant) {
+ error = true;
+ }
+ }
+ }
+
if (error) {
- _set_error(vformat("Constant value cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier)));
+ _set_error(vformat("Constant value cannot be passed for '%s' parameter!", _get_qualifier_str(arg_qual)));
return nullptr;
}
}
if (is_sampler_type(call_function->arguments[i].type)) {
//let's see where our argument comes from
- Node *n = func->arguments[argidx];
ERR_CONTINUE(n->type != Node::TYPE_VARIABLE); //bug? this should always be a variable
VariableNode *vn = static_cast<VariableNode *>(n);
StringName varname = vn->name;
@@ -5143,9 +5175,21 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
} else {
- if (!_validate_varying_using(shader->varyings[identifier], &error)) {
- _set_error(error);
- return nullptr;
+ ShaderNode::Varying &var = shader->varyings[identifier];
+
+ switch (var.stage) {
+ case ShaderNode::Varying::STAGE_VERTEX:
+ if (current_function == varying_function_names.fragment || current_function == varying_function_names.light) {
+ var.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT;
+ }
+ break;
+ case ShaderNode::Varying::STAGE_FRAGMENT:
+ if (current_function == varying_function_names.light) {
+ var.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT;
+ }
+ break;
+ default:
+ break;
}
}
}
@@ -5287,7 +5331,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error("Expected expression, found: " + get_token_text(tk));
return nullptr;
} else {
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
if (check_warnings && HAS_WARNING(ShaderWarning::FORMATTING_ERROR_FLAG)) {
_add_line_warning(ShaderWarning::FORMATTING_ERROR, "Empty statement. Remove ';' to fix this warning.");
}
@@ -6098,7 +6142,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
ERR_FAIL_V(nullptr); //unexpected operator
}
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
if (check_warnings && HAS_WARNING(ShaderWarning::FLOAT_COMPARISON_FLAG) && (op == OP_EQUAL || op == OP_NOT_EQUAL) &&
(!expression[i - 1].is_op && !expression[i + 1].is_op) &&
(expression[i - 1].node->get_datatype() == TYPE_FLOAT && expression[i + 1].node->get_datatype() == TYPE_FLOAT)) {
@@ -7549,6 +7593,10 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
int texture_binding = 0;
int uniforms = 0;
int instance_index = 0;
+#ifdef DEBUG_ENABLED
+ int uniform_buffer_size = 0;
+ int max_uniform_buffer_size = RenderingDevice::get_singleton()->limit_get(RenderingDevice::LIMIT_MAX_UNIFORM_BUFFER_SIZE);
+#endif // DEBUG_ENABLED
ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL;
stages = &p_functions;
@@ -7949,6 +7997,18 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
uniform2.texture_order = -1;
if (uniform_scope != ShaderNode::Uniform::SCOPE_INSTANCE) {
uniform2.order = uniforms++;
+#ifdef DEBUG_ENABLED
+ if (uniform2.array_size > 0) {
+ int size = get_datatype_size(uniform2.type) * uniform2.array_size;
+ int m = (16 * uniform2.array_size);
+ if ((size % m) != 0U) {
+ size += m - (size % m);
+ }
+ uniform_buffer_size += size;
+ } else {
+ uniform_buffer_size += get_datatype_size(uniform2.type);
+ }
+#endif // DEBUG_ENABLED
}
}
@@ -8958,14 +9018,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
}
- int error_line;
- String error_message;
- if (!_check_varying_usages(&error_line, &error_message)) {
- _set_tkpos({ 0, error_line });
- _set_error(error_message);
- return ERR_PARSE_ERROR;
+#ifdef DEBUG_ENABLED
+ if (HAS_WARNING(ShaderWarning::DEVICE_LIMIT_EXCEEDED) && (uniform_buffer_size > max_uniform_buffer_size)) {
+ Vector<Variant> args;
+ args.push_back(uniform_buffer_size);
+ args.push_back(max_uniform_buffer_size);
+ _add_global_warning(ShaderWarning::DEVICE_LIMIT_EXCEEDED, "uniform buffer", args);
}
-
+#endif // DEBUG_ENABLED
return OK;
}
@@ -9673,7 +9733,7 @@ ShaderLanguage::ShaderLanguage() {
nodes = nullptr;
completion_class = TAG_GLOBAL;
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants);
warnings_check_map.insert(ShaderWarning::UNUSED_FUNCTION, &used_functions);
warnings_check_map.insert(ShaderWarning::UNUSED_STRUCT, &used_structs);
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 0ed3d9fabf..bf3c601aea 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -787,7 +787,7 @@ public:
static bool is_sampler_type(DataType p_type);
static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
static PropertyInfo uniform_to_property_info(const ShaderNode::Uniform &p_uniform);
- static uint32_t get_type_size(DataType p_type);
+ static uint32_t get_datatype_size(DataType p_type);
static void get_keyword_list(List<String> *r_keywords);
static bool is_control_flow_keyword(String p_keyword);
@@ -919,11 +919,14 @@ private:
bool check_warnings = false;
uint32_t warning_flags;
- void _add_line_warning(ShaderWarning::Code p_code, const StringName &p_subject = "") {
- warnings.push_back(ShaderWarning(p_code, tk_line, p_subject));
+ void _add_line_warning(ShaderWarning::Code p_code, const StringName &p_subject = "", const Vector<Variant> &p_extra_args = Vector<Variant>()) {
+ warnings.push_back(ShaderWarning(p_code, tk_line, p_subject, p_extra_args));
}
- void _add_warning(ShaderWarning::Code p_code, int p_line, const StringName &p_subject = "") {
- warnings.push_back(ShaderWarning(p_code, p_line, p_subject));
+ void _add_global_warning(ShaderWarning::Code p_code, const StringName &p_subject = "", const Vector<Variant> &p_extra_args = Vector<Variant>()) {
+ warnings.push_back(ShaderWarning(p_code, -1, p_subject, p_extra_args));
+ }
+ void _add_warning(ShaderWarning::Code p_code, int p_line, const StringName &p_subject = "", const Vector<Variant> &p_extra_args = Vector<Variant>()) {
+ warnings.push_back(ShaderWarning(p_code, p_line, p_subject, p_extra_args));
}
void _check_warning_accums();
#endif // DEBUG_ENABLED
@@ -938,14 +941,6 @@ private:
VaryingFunctionNames varying_function_names;
- struct VaryingUsage {
- ShaderNode::Varying *var;
- int line;
- };
- List<VaryingUsage> unknown_varying_usages;
-
- bool _check_varying_usages(int *r_error_line, String *r_error_message) const;
-
TkPos _get_tkpos() {
TkPos tkp;
tkp.char_idx = char_idx;
@@ -1047,7 +1042,6 @@ private:
bool _propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat);
bool _propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin);
bool _validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message);
- bool _validate_varying_using(ShaderNode::Varying &p_varying, String *r_message);
bool _check_node_constness(const Node *p_node) const;
Node *_parse_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, int &r_array_size);
diff --git a/servers/rendering/shader_warnings.cpp b/servers/rendering/shader_warnings.cpp
index bffae484a8..a6ec07dba4 100644
--- a/servers/rendering/shader_warnings.cpp
+++ b/servers/rendering/shader_warnings.cpp
@@ -63,6 +63,8 @@ String ShaderWarning::get_message() const {
return vformat("The local variable '%s' is declared but never used.", subject);
case FORMATTING_ERROR:
return subject;
+ case DEVICE_LIMIT_EXCEEDED:
+ return vformat("The total size of the %s for this shader on this device has been exceeded (%s/%s). The shader may not work correctly.", subject, (int)extra_args[0], (int)extra_args[1]);
default:
break;
}
@@ -73,6 +75,10 @@ String ShaderWarning::get_name() const {
return get_name_from_code(code);
}
+Vector<Variant> ShaderWarning::get_extra_args() const {
+ return extra_args;
+}
+
String ShaderWarning::get_name_from_code(Code p_code) {
ERR_FAIL_INDEX_V(p_code, WARNING_MAX, String());
@@ -85,6 +91,7 @@ String ShaderWarning::get_name_from_code(Code p_code) {
"UNUSED_VARYING",
"UNUSED_LOCAL_VARIABLE",
"FORMATTING_ERROR",
+ "DEVICE_LIMIT_EXCEEDED",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
@@ -114,6 +121,7 @@ static void init_code_to_flags_map() {
code_to_flags_map->insert(ShaderWarning::UNUSED_VARYING, ShaderWarning::UNUSED_VARYING_FLAG);
code_to_flags_map->insert(ShaderWarning::UNUSED_LOCAL_VARIABLE, ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG);
code_to_flags_map->insert(ShaderWarning::FORMATTING_ERROR, ShaderWarning::FORMATTING_ERROR_FLAG);
+ code_to_flags_map->insert(ShaderWarning::DEVICE_LIMIT_EXCEEDED, ShaderWarning::DEVICE_LIMIT_EXCEEDED_FLAG);
}
ShaderWarning::CodeFlags ShaderWarning::get_flags_from_codemap(const Map<Code, bool> &p_map) {
@@ -132,8 +140,8 @@ ShaderWarning::CodeFlags ShaderWarning::get_flags_from_codemap(const Map<Code, b
return (CodeFlags)result;
}
-ShaderWarning::ShaderWarning(Code p_code, int p_line, const StringName &p_subject) :
- code(p_code), line(p_line), subject(p_subject) {
+ShaderWarning::ShaderWarning(Code p_code, int p_line, const StringName &p_subject, const Vector<Variant> &p_extra_args) :
+ code(p_code), line(p_line), subject(p_subject), extra_args(p_extra_args) {
}
#endif // DEBUG_ENABLED
diff --git a/servers/rendering/shader_warnings.h b/servers/rendering/shader_warnings.h
index 18915fffd8..2d0ba7b5b2 100644
--- a/servers/rendering/shader_warnings.h
+++ b/servers/rendering/shader_warnings.h
@@ -36,6 +36,7 @@
#include "core/string/string_name.h"
#include "core/templates/list.h"
#include "core/templates/map.h"
+#include "core/variant/variant.h"
class ShaderWarning {
public:
@@ -48,6 +49,7 @@ public:
UNUSED_VARYING,
UNUSED_LOCAL_VARIABLE,
FORMATTING_ERROR,
+ DEVICE_LIMIT_EXCEEDED,
WARNING_MAX,
};
@@ -61,12 +63,14 @@ public:
UNUSED_VARYING_FLAG = 32U,
UNUSED_LOCAL_VARIABLE_FLAG = 64U,
FORMATTING_ERROR_FLAG = 128U,
+ DEVICE_LIMIT_EXCEEDED_FLAG = 256U,
};
private:
Code code;
int line;
StringName subject;
+ Vector<Variant> extra_args;
public:
Code get_code() const;
@@ -74,12 +78,13 @@ public:
const StringName &get_subject() const;
String get_message() const;
String get_name() const;
+ Vector<Variant> get_extra_args() const;
static String get_name_from_code(Code p_code);
static Code get_code_from_name(const String &p_name);
static CodeFlags get_flags_from_codemap(const Map<Code, bool> &p_map);
- ShaderWarning(Code p_code = WARNING_MAX, int p_line = -1, const StringName &p_subject = "");
+ ShaderWarning(Code p_code = WARNING_MAX, int p_line = -1, const StringName &p_subject = "", const Vector<Variant> &p_extra_args = Vector<Variant>());
};
#endif // DEBUG_ENABLED