summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/script_debugger_remote.cpp12
-rw-r--r--doc/classes/Particles2D.xml12
-rw-r--r--doc/classes/VisualScriptClassConstant.xml12
-rw-r--r--doc/classes/VisualScriptConstant.xml8
-rw-r--r--doc/classes/VisualScriptEmitSignal.xml8
-rw-r--r--doc/classes/VisualScriptIterator.xml9
-rw-r--r--doc/classes/VisualScriptLocalVar.xml8
-rw-r--r--doc/classes/VisualScriptLocalVarSet.xml10
-rw-r--r--doc/classes/VisualScriptMathConstant.xml15
-rw-r--r--doc/classes/VisualScriptOperator.xml4
-rw-r--r--doc/classes/VisualScriptPreload.xml7
-rw-r--r--doc/classes/VisualScriptReturn.xml9
-rw-r--r--doc/classes/VisualScriptSelect.xml9
-rw-r--r--doc/classes/VisualScriptSelf.xml6
-rw-r--r--doc/classes/VisualScriptSequence.xml9
-rw-r--r--doc/classes/VisualScriptSwitch.xml11
-rw-r--r--doc/classes/VisualScriptVariableGet.xml7
-rw-r--r--doc/classes/VisualScriptVariableSet.xml8
-rw-r--r--doc/classes/VisualScriptWhile.xml8
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp4
-rw-r--r--drivers/windows/dir_access_windows.cpp2
-rw-r--r--editor/animation_editor.cpp34
-rw-r--r--editor/dependency_editor.cpp154
-rw-r--r--editor/dependency_editor.h27
-rw-r--r--editor/editor_help.cpp9
-rw-r--r--editor/editor_node.cpp21
-rw-r--r--editor/editor_settings.cpp3
-rw-r--r--editor/editor_themes.cpp6
-rw-r--r--editor/filesystem_dock.cpp607
-rw-r--r--editor/filesystem_dock.h41
-rw-r--r--editor/icons/icon_viewport_speed.svg113
-rw-r--r--editor/icons/icon_viewport_zoom.svg64
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp249
-rw-r--r--editor/plugins/spatial_editor_plugin.h12
-rw-r--r--editor/property_editor.cpp4
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme10
-rw-r--r--modules/gdnative/SCsub3
-rw-r--r--modules/gdnative/gdnative/string.cpp4
-rw-r--r--modules/gdnative/include/pluginscript/godot_pluginscript.h170
-rw-r--r--modules/gdnative/nativescript/SCsub1
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp2
-rw-r--r--modules/gdnative/nativescript/api_generator.h2
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp22
-rw-r--r--modules/gdnative/nativescript/nativescript.h6
-rw-r--r--modules/gdnative/pluginscript/SCsub9
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.cpp181
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h90
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp432
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.h131
-rw-r--r--modules/gdnative/pluginscript/pluginscript_loader.cpp113
-rw-r--r--modules/gdnative/pluginscript/pluginscript_loader.h62
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp454
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.h130
-rw-r--r--modules/gdnative/pluginscript/register_types.cpp118
-rw-r--r--modules/gdnative/pluginscript/register_types.h31
-rw-r--r--modules/gdnative/register_types.cpp5
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml26
-rw-r--r--modules/mono/config.py2
-rw-r--r--modules/mono/csharp_script.cpp184
-rw-r--r--modules/mono/csharp_script.h4
-rw-r--r--modules/mono/editor/bindings_generator.cpp34
-rw-r--r--modules/mono/glue/cs_files/ExportAttribute.cs4
-rw-r--r--modules/mono/godotsharp_dirs.cpp21
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp32
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp16
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h1
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp8
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h8
-rw-r--r--modules/mono/signal_awaiter_utils.cpp75
-rw-r--r--modules/mono/signal_awaiter_utils.h19
-rw-r--r--modules/mono/utils/string_utils.cpp29
-rw-r--r--modules/mono/utils/string_utils.h6
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp48
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h2
-rw-r--r--modules/visual_script/visual_script_editor.cpp2
-rw-r--r--platform/iphone/export/export.cpp24
-rw-r--r--platform/osx/export/export.cpp4
-rw-r--r--platform/server/detect.py3
-rw-r--r--platform/x11/os_x11.cpp10
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/gui/rich_text_label.cpp17
-rw-r--r--scene/gui/rich_text_label.h4
-rw-r--r--scene/gui/text_edit.cpp41
-rw-r--r--scene/gui/text_edit.h3
-rw-r--r--servers/visual/shader_language.cpp2
87 files changed, 3465 insertions, 676 deletions
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index 4653ade294..2feb068ecb 100644
--- a/core/script_debugger_remote.cpp
+++ b/core/script_debugger_remote.cpp
@@ -855,15 +855,19 @@ void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string)
}
sdr->char_count += allowed_chars;
-
- if (sdr->char_count >= sdr->max_cps) {
- s += "\n[output overflow, print less text!]\n";
- }
+ bool overflowed = sdr->char_count >= sdr->max_cps;
sdr->mutex->lock();
if (!sdr->locking && sdr->tcp_client->is_connected_to_host()) {
+ if (overflowed)
+ s += "[...]";
+
sdr->output_strings.push_back(s);
+
+ if (overflowed) {
+ sdr->output_strings.push_back("[output overflow, print less text!]");
+ }
}
sdr->mutex->unlock();
}
diff --git a/doc/classes/Particles2D.xml b/doc/classes/Particles2D.xml
index b2c63ea0c3..cfc907b727 100644
--- a/doc/classes/Particles2D.xml
+++ b/doc/classes/Particles2D.xml
@@ -286,7 +286,7 @@
</methods>
<members>
<member name="amount" type="int" setter="set_amount" getter="get_amount">
- Number of particles to emit.
+ Number of particles emitted in one emission cycle.
</member>
<member name="draw_order" type="int" setter="set_draw_order" getter="get_draw_order" enum="Particles2D.DrawOrder">
Particle draw order. Uses [code]DRAW_ORDER_*[/code] values. Default value: [code]DRAW_ORDER_INDEX[/code].
@@ -295,7 +295,7 @@
If [code]true[/code] particles are being emitted. Default value: [code]true[/code].
</member>
<member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio">
- Time ratio between each emission. If [code]0[/code] particles are emitted continuously. If [code]1[/code] all particles are emitted simultaneously. Default value: [code]0[/code].
+ How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins. Default value: [code]0[/code].
</member>
<member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps">
</member>
@@ -313,18 +313,19 @@
<member name="normal_map" type="Texture" setter="set_normal_map" getter="get_normal_map">
</member>
<member name="one_shot" type="bool" setter="set_one_shot" getter="get_one_shot">
- If [code]true[/code] only [code]amount[/code] particles will be emitted. Default value: [code]false[/code].
+ If [code]true[/code] only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end. Default value: [code]false[/code].
</member>
<member name="preprocess" type="float" setter="set_pre_process_time" getter="get_pre_process_time">
+ Particle system starts as if it had already run for this many seconds.
</member>
<member name="process_material" type="Material" setter="set_process_material" getter="get_process_material">
[Material] for processing particles. Can be a [ParticlesMaterial] or a [ShaderMaterial].
</member>
<member name="randomness" type="float" setter="set_randomness_ratio" getter="get_randomness_ratio">
- Emission randomness ratio. Default value: [code]0[/code].
+ Emission lifetime randomness ratio. Default value: [code]0[/code].
</member>
<member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale">
- Speed scaling ratio. Default value: [code]1[/code].
+ Particle system's running speed scaling ratio. Default value: [code]1[/code].
</member>
<member name="texture" type="Texture" setter="set_texture" getter="get_texture">
Particle texture. If [code]null[/code] particles will be squares.
@@ -333,6 +334,7 @@
Number of vertical frames in [code]texture[/code].
</member>
<member name="visibility_rect" type="Rect2" setter="set_visibility_rect" getter="get_visibility_rect">
+ Editor visibility helper.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptClassConstant.xml b/doc/classes/VisualScriptClassConstant.xml
index 70e7de5dd9..0377fa8f09 100644
--- a/doc/classes/VisualScriptClassConstant.xml
+++ b/doc/classes/VisualScriptClassConstant.xml
@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
- A Visual Script node representing a constant from a class.
+ Gets a constant from a given class.
</brief_description>
<description>
- A Visual Script node representing a constant from the classes, such as [@GlobalScope.TYPE_INT].
+ This node returns a constant from a given class, such as [@GlobalScope.TYPE_INT]. See the given class' documentation for available constants.
+ [b]Input Ports:[/b]
+ none
+ [b]Output Ports:[/b]
+ - Data (variant): [code]value[/code]
</description>
<tutorials>
</tutorials>
@@ -42,10 +46,10 @@
</methods>
<members>
<member name="base_type" type="String" setter="set_base_type" getter="get_base_type">
- The type to get the constant from.
+ The constant's parent class.
</member>
<member name="constant" type="String" setter="set_class_constant" getter="get_class_constant">
- The name of the constant to return.
+ The constant to return. See the given class for its available constants.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptConstant.xml b/doc/classes/VisualScriptConstant.xml
index 508087a928..2a704adecf 100644
--- a/doc/classes/VisualScriptConstant.xml
+++ b/doc/classes/VisualScriptConstant.xml
@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
- A Visual Script node which returns a constant value.
+ Gets a contant's value.
</brief_description>
<description>
- A Visual Script node which returns the specified constant value.
+ This node returns a constant's value.
+ [b]Input Ports:[/b]
+ none
+ [b]Output Ports:[/b]
+ - Data (variant): [code]get[/code]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualScriptEmitSignal.xml b/doc/classes/VisualScriptEmitSignal.xml
index 844b5a40ec..8d132ed321 100644
--- a/doc/classes/VisualScriptEmitSignal.xml
+++ b/doc/classes/VisualScriptEmitSignal.xml
@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
- A Visual Script node which emits a specified signal.
+ Emits a specified signal.
</brief_description>
<description>
- A Visual Script node which emits a specified signal when it is executed.
+ Emits a specified signal when it is executed.
+ [b]Input Ports:[/b]
+ - Sequence: [code]emit[/code]
+ [b]Output Ports:[/b]
+ - Sequence
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualScriptIterator.xml b/doc/classes/VisualScriptIterator.xml
index 74309fcf00..1f9a4fddde 100644
--- a/doc/classes/VisualScriptIterator.xml
+++ b/doc/classes/VisualScriptIterator.xml
@@ -1,8 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Steps through items in a given input.
</brief_description>
<description>
+ This node steps through each item in a given input. Input can be any sequence data type, such as an [Array] or [String]. When each item has been processed, execution passed out the [code]exit[/code] Sequence port.
+ [b]Input Ports:[/b]
+ - Sequence: [code]for (elem) in (input)[/code]
+ - Data (variant): [code]input[/code]
+ [b]Output Ports:[/b]
+ - Sequence: [code]each[/code]
+ - Sequence: [code]exit[/code]
+ - Data (variant): [code]elem[/code]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualScriptLocalVar.xml b/doc/classes/VisualScriptLocalVar.xml
index 7db550d5fe..3101b3e34b 100644
--- a/doc/classes/VisualScriptLocalVar.xml
+++ b/doc/classes/VisualScriptLocalVar.xml
@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Gets a local variable's value.
</brief_description>
<description>
+ Returns a local variable's value. "Var Name" must be supplied, with an optional type.
+ [b]Input Ports:[/b]
+ none
+ [b]Output Ports:[/b]
+ - Data (variant): [code]get[/code]
</description>
<tutorials>
</tutorials>
@@ -40,8 +46,10 @@
</methods>
<members>
<member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type">
+ The local variable's type.
</member>
<member name="var_name" type="String" setter="set_var_name" getter="get_var_name">
+ The local variable's name.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptLocalVarSet.xml b/doc/classes/VisualScriptLocalVarSet.xml
index 6e69f13383..e039a7204e 100644
--- a/doc/classes/VisualScriptLocalVarSet.xml
+++ b/doc/classes/VisualScriptLocalVarSet.xml
@@ -1,8 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Changes a local variable's value.
</brief_description>
<description>
+ Changes a local variable's value to the given input. The new value is also provided on an output Data port.
+ [b]Input Ports:[/b]
+ - Sequence
+ - Data (variant): [code]set[/code]
+ [b]Output Ports:[/b]
+ - Sequence
+ - Data (variant): [code]get[/code]
</description>
<tutorials>
</tutorials>
@@ -40,8 +48,10 @@
</methods>
<members>
<member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type">
+ The local variable's type.
</member>
<member name="var_name" type="String" setter="set_var_name" getter="get_var_name">
+ The local variable's name.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptMathConstant.xml b/doc/classes/VisualScriptMathConstant.xml
index 1ef7d71e10..86744e5caf 100644
--- a/doc/classes/VisualScriptMathConstant.xml
+++ b/doc/classes/VisualScriptMathConstant.xml
@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Commonly used mathematical constants.
</brief_description>
<description>
+ Provides common math constants, such as Pi or Euler's constant, on an output Data port.
+ [b]Input Ports:[/b]
+ none
+ [b]Output Ports:[/b]
+ - Data (variant): [code]get[/code]
</description>
<tutorials>
</tutorials>
@@ -26,24 +32,33 @@
</methods>
<members>
<member name="constant" type="int" setter="set_math_constant" getter="get_math_constant" enum="VisualScriptMathConstant.MathConstant">
+ The math constant.
</member>
</members>
<constants>
<constant name="MATH_CONSTANT_ONE" value="0">
+ Unity: [code]1[/code]
</constant>
<constant name="MATH_CONSTANT_PI" value="1">
+ Pi: [code]3.141593[/code]
</constant>
<constant name="MATH_CONSTANT_2PI" value="2">
+ Pi times two: [code]6.283185[/code]
</constant>
<constant name="MATH_CONSTANT_HALF_PI" value="3">
+ Pi divided by two: [code]1.570796[/code]
</constant>
<constant name="MATH_CONSTANT_E" value="4">
+ Natural log: [code]2.718282[/code]
</constant>
<constant name="MATH_CONSTANT_SQRT2" value="5">
+ Square root of two: [code]1.414214[/code]
</constant>
<constant name="MATH_CONSTANT_INF" value="6">
+ Infinity: [code]inf[/code]
</constant>
<constant name="MATH_CONSTANT_NAN" value="7">
+ Not a number: [code]nan[/code]
</constant>
<constant name="MATH_CONSTANT_MAX" value="8">
</constant>
diff --git a/doc/classes/VisualScriptOperator.xml b/doc/classes/VisualScriptOperator.xml
index 7e85af8af2..de08075af2 100644
--- a/doc/classes/VisualScriptOperator.xml
+++ b/doc/classes/VisualScriptOperator.xml
@@ -7,9 +7,7 @@
- Data (variant): [code]A[/code]
- Data (variant): [code]B[/code]
[b]Output Ports:[/b]
- - Sequence: [code]true[/code]
- - Sequence: [code]false[/code]
- - Sequence: [code]done[/code]
+ - Data (variant): [code]result[/code]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualScriptPreload.xml b/doc/classes/VisualScriptPreload.xml
index b68bf5546b..b683439751 100644
--- a/doc/classes/VisualScriptPreload.xml
+++ b/doc/classes/VisualScriptPreload.xml
@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Creates a new [Resource] or loads one from the filesystem.
</brief_description>
<description>
+ Creates a new [Resource] or loads one from the filesystem.
+ [b]Input Ports:[/b]
+ none
+ [b]Output Ports:[/b]
+ - Data (object): [code]res[/code]
</description>
<tutorials>
</tutorials>
@@ -26,6 +32,7 @@
</methods>
<members>
<member name="resource" type="Resource" setter="set_preload" getter="get_preload">
+ The [Resource] to load.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptReturn.xml b/doc/classes/VisualScriptReturn.xml
index 55c53e17a0..ad18e96230 100644
--- a/doc/classes/VisualScriptReturn.xml
+++ b/doc/classes/VisualScriptReturn.xml
@@ -1,8 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Exits a function and returns an optional value.
</brief_description>
<description>
+ Ends the execution of a function and returns control to the calling function. Optionally, it can return a [Variant] value.
+ [b]Input Ports:[/b]
+ - Sequence
+ - Data (variant): [code]result[/code] (optional)
+ [b]Output Ports:[/b]
+ none
</description>
<tutorials>
</tutorials>
@@ -40,8 +47,10 @@
</methods>
<members>
<member name="return_enabled" type="bool" setter="set_enable_return_value" getter="is_return_value_enabled">
+ If [code]true[/code] the [code]return[/code] input port is available.
</member>
<member name="return_type" type="int" setter="set_return_type" getter="get_return_type" enum="Variant.Type">
+ The return value's data type.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptSelect.xml b/doc/classes/VisualScriptSelect.xml
index 855da76e6c..f265c57645 100644
--- a/doc/classes/VisualScriptSelect.xml
+++ b/doc/classes/VisualScriptSelect.xml
@@ -1,8 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Chooses between two input values.
</brief_description>
<description>
+ Chooses between two input values based on a Boolean condition.
+ [b]Input Ports:[/b]
+ - Data (boolean): [code]cond[/code]
+ - Data (variant): [code]a[/code]
+ - Data (variant): [code]b[/code]
+ [b]Output Ports:[/b]
+ - Data (variant): [code]out[/code]
</description>
<tutorials>
</tutorials>
@@ -26,6 +34,7 @@
</methods>
<members>
<member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type">
+ The input variables' type.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptSelf.xml b/doc/classes/VisualScriptSelf.xml
index a60f7eee03..723b138722 100644
--- a/doc/classes/VisualScriptSelf.xml
+++ b/doc/classes/VisualScriptSelf.xml
@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Outputs a reference to the current instance.
</brief_description>
<description>
+ Provides a reference to the node running the visual script.
+ [b]Input Ports:[/b]
+ none
+ [b]Output Ports:[/b]
+ - Data (object): [code]instance[/code]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualScriptSequence.xml b/doc/classes/VisualScriptSequence.xml
index a60c9e782b..4ea4203407 100644
--- a/doc/classes/VisualScriptSequence.xml
+++ b/doc/classes/VisualScriptSequence.xml
@@ -1,8 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Executes a series of Sequence ports.
</brief_description>
<description>
+ Steps through a series of one or more output Sequence ports. The [code]current[/code] data port outputs the currently executing item.
+ [b]Input Ports:[/b]
+ - Sequence: [code]in order[/code]
+ [b]Output Ports:[/b]
+ - Sequence: [code]1[/code]
+ - Sequence: [code]2 - n[/code] (optional)
+ - Data (int): [code]current[/code]
</description>
<tutorials>
</tutorials>
@@ -26,6 +34,7 @@
</methods>
<members>
<member name="steps" type="int" setter="set_steps" getter="get_steps">
+ The number of steps in the sequence.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptSwitch.xml b/doc/classes/VisualScriptSwitch.xml
index 95ed737372..2540ae54cc 100644
--- a/doc/classes/VisualScriptSwitch.xml
+++ b/doc/classes/VisualScriptSwitch.xml
@@ -1,8 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Branches program flow based on a given input's value.
</brief_description>
<description>
+ Branches the flow based on an input's value. Use "Case Count" in the Inspector to set the number of branches and each comparison's optional type.
+ [b]Input Ports:[/b]
+ - Sequence: [code]'input' is[/code]
+ - Data (variant): [code]=[/code]
+ - Data (variant): [code]=[/code] (optional)
+ - Data (variant): [code]input[/code]
+ [b]Output Ports:[/b]
+ - Sequence
+ - Sequence (optional)
+ - Sequence: [code]done[/code]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualScriptVariableGet.xml b/doc/classes/VisualScriptVariableGet.xml
index 8411933756..5b45dd0cc4 100644
--- a/doc/classes/VisualScriptVariableGet.xml
+++ b/doc/classes/VisualScriptVariableGet.xml
@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Gets a variable's value.
</brief_description>
<description>
+ Returns a variable's value. "Var Name" must be supplied, with an optional type.
+ [b]Input Ports:[/b]
+ none
+ [b]Output Ports:[/b]
+ - Data (variant): [code]value[/code]
</description>
<tutorials>
</tutorials>
@@ -26,6 +32,7 @@
</methods>
<members>
<member name="var_name" type="String" setter="set_variable" getter="get_variable">
+ The variable's name.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptVariableSet.xml b/doc/classes/VisualScriptVariableSet.xml
index fbe0f8e275..51f85f6881 100644
--- a/doc/classes/VisualScriptVariableSet.xml
+++ b/doc/classes/VisualScriptVariableSet.xml
@@ -1,8 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Changes a variable's value.
</brief_description>
<description>
+ Changes a variable's value to the given input.
+ [b]Input Ports:[/b]
+ - Sequence
+ - Data (variant): [code]set[/code]
+ [b]Output Ports:[/b]
+ - Sequence
</description>
<tutorials>
</tutorials>
@@ -26,6 +33,7 @@
</methods>
<members>
<member name="var_name" type="String" setter="set_variable" getter="get_variable">
+ The variable's name.
</member>
</members>
<constants>
diff --git a/doc/classes/VisualScriptWhile.xml b/doc/classes/VisualScriptWhile.xml
index b49678582e..60bf161339 100644
--- a/doc/classes/VisualScriptWhile.xml
+++ b/doc/classes/VisualScriptWhile.xml
@@ -1,8 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Conditional loop.
</brief_description>
<description>
+ Loops while a condition is [code]true[/code]. Execution continues out the [code]exit[/code] Sequence port when the loop terminates.
+ [b]Input Ports:[/b]
+ - Sequence: [code]while(cond)[/code]
+ - Data (bool): [code]cond[/code]
+ [b]Output Ports:[/b]
+ - Sequence: [code]repeat[/code]
+ - Sequence: [code]exit[/code]
</description>
<tutorials>
</tutorials>
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 6117c91a6a..39f027a5aa 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -2569,8 +2569,8 @@ void RasterizerSceneGLES3::_setup_directional_light(int p_index, const Transform
ubo_data.light_direction_attenuation[3] = 1.0;
ubo_data.light_params[0] = 0;
- ubo_data.light_params[1] = li->light_ptr->param[VS::LIGHT_PARAM_SPECULAR];
- ubo_data.light_params[2] = 0;
+ ubo_data.light_params[1] = 0;
+ ubo_data.light_params[2] = li->light_ptr->param[VS::LIGHT_PARAM_SPECULAR];
ubo_data.light_params[3] = 0;
Color shadow_color = li->light_ptr->shadow_color.to_linear();
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 8d6e78dbee..0bc4201ba3 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -164,7 +164,7 @@ Error DirAccessWindows::make_dir(String p_dir) {
p_dir = fix_path(p_dir);
if (p_dir.is_rel_path())
- p_dir = get_current_dir().plus_file(p_dir);
+ p_dir = current_dir.plus_file(p_dir);
p_dir = p_dir.replace("/", "\\");
diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp
index 5bb10f495e..54eb695178 100644
--- a/editor/animation_editor.cpp
+++ b/editor/animation_editor.cpp
@@ -64,6 +64,8 @@ private:
float transition;
Mode mode;
+ LineEdit *value_edit;
+
void _notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
@@ -144,14 +146,11 @@ private:
}
}
- String txt = String::num(exp, 2);
if (mode == MODE_DISABLED) {
- txt = TTR("Disabled");
+ f->draw(ci, Point2(5, 5 + f->get_ascent()), TTR("Disabled"), color);
} else if (mode == MODE_MULTIPLE) {
- txt += " - " + TTR("All Selection");
+ f->draw(ci, Point2(5, 5 + f->get_ascent() + value_edit->get_size().height), TTR("All Selection"), color);
}
-
- f->draw(ci, Point2(10, 10 + f->get_ascent()), txt, color);
}
}
@@ -163,6 +162,8 @@ private:
if (mode == MODE_DISABLED)
return;
+ value_edit->release_focus();
+
float rel = mm->get_relative().x;
if (rel == 0)
return;
@@ -187,24 +188,28 @@ private:
if (sg)
val = -val;
- transition = val;
- update();
- //emit_signal("variant_changed");
- emit_signal("transition_changed", transition);
+ force_transition(val);
}
}
+ void _edit_value_changed(const String &p_value_str) {
+
+ force_transition(p_value_str.to_float());
+ }
+
public:
static void _bind_methods() {
//ClassDB::bind_method("_update_obj",&AnimationKeyEdit::_update_obj);
ClassDB::bind_method("_gui_input", &AnimationCurveEdit::_gui_input);
+ ClassDB::bind_method("_edit_value_changed", &AnimationCurveEdit::_edit_value_changed);
ADD_SIGNAL(MethodInfo("transition_changed"));
}
void set_mode(Mode p_mode) {
mode = p_mode;
+ value_edit->set_visible(mode != MODE_DISABLED);
update();
}
@@ -218,7 +223,8 @@ public:
}
void set_transition(float p_transition) {
- transition = p_transition;
+ transition = Math::stepify(p_transition, 0.01);
+ value_edit->set_text(String::num(transition));
update();
}
@@ -229,9 +235,8 @@ public:
void force_transition(float p_value) {
if (mode == MODE_DISABLED)
return;
- transition = p_value;
+ set_transition(p_value);
emit_signal("transition_changed", p_value);
- update();
}
AnimationCurveEdit() {
@@ -239,6 +244,11 @@ public:
transition = 1.0;
set_default_cursor_shape(CURSOR_HSPLIT);
mode = MODE_DISABLED;
+
+ value_edit = memnew(LineEdit);
+ value_edit->hide();
+ value_edit->connect("text_entered", this, "_edit_value_changed");
+ add_child(value_edit);
}
};
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index 5305c4f256..64f1c4ccb2 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -337,92 +337,142 @@ DependencyEditorOwners::DependencyEditorOwners() {
///////////////////////
-void DependencyRemoveDialog::_fill_owners(EditorFileSystemDirectory *efsd) {
+void DependencyRemoveDialog::_find_files_in_removed_folder(EditorFileSystemDirectory *efsd, const String &p_folder) {
+ if (!efsd)
+ return;
+
+ for (int i = 0; i < efsd->get_subdir_count(); ++i) {
+ _find_files_in_removed_folder(efsd->get_subdir(i), p_folder);
+ }
+ for (int i = 0; i < efsd->get_file_count(); i++) {
+ String file = efsd->get_file_path(i);
+ ERR_FAIL_COND(all_remove_files.has(file)); //We are deleting a directory which is contained in a directory we are deleting...
+ all_remove_files[file] = p_folder; //Point the file to the ancestor directory we are deleting so we know what to parent it under in the tree.
+ }
+}
+void DependencyRemoveDialog::_find_all_removed_dependencies(EditorFileSystemDirectory *efsd, Vector<RemovedDependency> &p_removed) {
if (!efsd)
return;
for (int i = 0; i < efsd->get_subdir_count(); i++) {
- _fill_owners(efsd->get_subdir(i));
+ _find_all_removed_dependencies(efsd->get_subdir(i), p_removed);
}
for (int i = 0; i < efsd->get_file_count(); i++) {
+ const String path = efsd->get_file_path(i);
- Vector<String> deps = efsd->get_file_deps(i);
- //print_line(":::"+efsd->get_file_path(i));
- Set<String> met;
- for (int j = 0; j < deps.size(); j++) {
- if (files.has(deps[j])) {
- met.insert(deps[j]);
- }
- }
- if (!met.size())
+ //It doesn't matter if a file we are about to delete will have some of its dependencies removed too
+ if (all_remove_files.has(path))
continue;
- exist = true;
-
- Ref<Texture> icon;
- String type = efsd->get_file_type(i);
- if (!has_icon(type, "EditorIcons")) {
- icon = get_icon("Object", "EditorIcons");
- } else {
- icon = get_icon(type, "EditorIcons");
+ Vector<String> all_deps = efsd->get_file_deps(i);
+ for (int j = 0; j < all_deps.size(); ++j) {
+ if (all_remove_files.has(all_deps[j])) {
+ RemovedDependency dep;
+ dep.file = path;
+ dep.file_type = efsd->get_file_type(i);
+ dep.dependency = all_deps[j];
+ dep.dependency_folder = all_remove_files[all_deps[j]];
+ p_removed.push_back(dep);
+ }
}
+ }
+}
- for (Set<String>::Element *E = met.front(); E; E = E->next()) {
+void DependencyRemoveDialog::_build_removed_dependency_tree(const Vector<RemovedDependency> &p_removed) {
+ owners->clear();
+ owners->create_item(); // root
- String which = E->get();
- if (!files[which]) {
- TreeItem *ti = owners->create_item(owners->get_root());
- ti->set_text(0, which.get_file());
- files[which] = ti;
+ Map<String, TreeItem *> tree_items;
+ for (int i = 0; i < p_removed.size(); i++) {
+ RemovedDependency rd = p_removed[i];
+
+ //Ensure that the dependency is already in the tree
+ if (!tree_items.has(rd.dependency)) {
+ if (rd.dependency_folder.length() > 0) {
+ //Ensure the ancestor folder is already in the tree
+ if (!tree_items.has(rd.dependency_folder)) {
+ TreeItem *folder_item = owners->create_item(owners->get_root());
+ folder_item->set_text(0, rd.dependency_folder);
+ folder_item->set_icon(0, get_icon("Folder", "EditorIcons"));
+ tree_items[rd.dependency_folder] = folder_item;
+ }
+ TreeItem *dependency_item = owners->create_item(tree_items[rd.dependency_folder]);
+ dependency_item->set_text(0, rd.dependency);
+ dependency_item->set_icon(0, get_icon("Warning", "EditorIcons"));
+ tree_items[rd.dependency] = dependency_item;
+ } else {
+ TreeItem *dependency_item = owners->create_item(owners->get_root());
+ dependency_item->set_text(0, rd.dependency);
+ dependency_item->set_icon(0, get_icon("Warning", "EditorIcons"));
+ tree_items[rd.dependency] = dependency_item;
}
- TreeItem *ti = owners->create_item(files[which]);
- ti->set_text(0, efsd->get_file_path(i));
- ti->set_icon(0, icon);
}
+
+ //List this file under this dependency
+ Ref<Texture> icon = has_icon(rd.file_type, "EditorIcons") ? get_icon(rd.file_type, "EditorIcons") : get_icon("Object", "EditorIcons");
+ TreeItem *file_item = owners->create_item(tree_items[rd.dependency]);
+ file_item->set_text(0, rd.file);
+ file_item->set_icon(0, icon);
}
}
-void DependencyRemoveDialog::show(const Vector<String> &to_erase) {
-
- exist = false;
+void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<String> &p_files) {
+ all_remove_files.clear();
+ to_delete.clear();
owners->clear();
- files.clear();
- owners->create_item(); // root
- for (int i = 0; i < to_erase.size(); i++) {
- files[to_erase[i]] = NULL;
+
+ for (int i = 0; i < p_folders.size(); ++i) {
+ String folder = p_folders[i].ends_with("/") ? p_folders[i] : (p_folders[i] + "/");
+ _find_files_in_removed_folder(EditorFileSystem::get_singleton()->get_filesystem_path(folder), folder);
+ to_delete.push_back(folder);
+ }
+ for (int i = 0; i < p_files.size(); ++i) {
+ all_remove_files[p_files[i]] = String();
+ to_delete.push_back(p_files[i]);
}
- _fill_owners(EditorFileSystem::get_singleton()->get_filesystem());
+ Vector<RemovedDependency> removed_deps;
+ _find_all_removed_dependencies(EditorFileSystem::get_singleton()->get_filesystem(), removed_deps);
+ removed_deps.sort();
- if (exist) {
- owners->show();
- text->set_text(TTR("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (no undo)"));
- popup_centered_minsize(Size2(500, 220));
- } else {
+ if (removed_deps.empty()) {
owners->hide();
text->set_text(TTR("Remove selected files from the project? (no undo)"));
popup_centered_minsize(Size2(400, 100));
+ } else {
+ _build_removed_dependency_tree(removed_deps);
+ owners->show();
+ text->set_text(TTR("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (no undo)"));
+ popup_centered_minsize(Size2(500, 350));
}
}
void DependencyRemoveDialog::ok_pressed() {
-
- bool changed = false;
-
- for (Map<String, TreeItem *>::Element *E = files.front(); E; E = E->next()) {
-
- if (ResourceCache::has(E->key())) {
- Resource *res = ResourceCache::get(E->key());
+ bool files_only = true;
+ for (int i = 0; i < to_delete.size(); ++i) {
+ if (to_delete[i].ends_with("/")) {
+ files_only = false;
+ } else if (ResourceCache::has(to_delete[i])) {
+ Resource *res = ResourceCache::get(to_delete[i]);
res->set_path(""); //clear reference to path
}
- String fpath = OS::get_singleton()->get_resource_dir() + E->key().replace_first("res://", "/");
- OS::get_singleton()->move_to_trash(fpath);
- changed = true;
+
+ String path = OS::get_singleton()->get_resource_dir() + to_delete[i].replace_first("res://", "/");
+ print_line("Moving to trash: " + path);
+ Error err = OS::get_singleton()->move_to_trash(path);
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:\n") + to_delete[i] + "\n");
+ }
}
- if (changed) {
+ if (files_only) {
+ //If we only deleted files we should only need to tell the file system about the files we touched.
+ for (int i = 0; i < to_delete.size(); ++i) {
+ EditorFileSystem::get_singleton()->update_file(to_delete[i]);
+ }
+ } else {
EditorFileSystem::get_singleton()->scan_changes();
}
}
diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h
index 4dfb9de268..c7e9baa5c2 100644
--- a/editor/dependency_editor.h
+++ b/editor/dependency_editor.h
@@ -84,14 +84,33 @@ class DependencyRemoveDialog : public ConfirmationDialog {
Label *text;
Tree *owners;
- bool exist;
- Map<String, TreeItem *> files;
- void _fill_owners(EditorFileSystemDirectory *efsd);
+
+ Map<String, String> all_remove_files;
+ Vector<String> to_delete;
+
+ struct RemovedDependency {
+ String file;
+ String file_type;
+ String dependency;
+ String dependency_folder;
+
+ bool operator<(const RemovedDependency &p_other) const {
+ if (dependency_folder.empty() != p_other.dependency_folder.empty()) {
+ return p_other.dependency_folder.empty();
+ } else {
+ return dependency < p_other.dependency;
+ }
+ }
+ };
+
+ void _find_files_in_removed_folder(EditorFileSystemDirectory *efsd, const String &p_folder);
+ void _find_all_removed_dependencies(EditorFileSystemDirectory *efsd, Vector<RemovedDependency> &p_removed);
+ void _build_removed_dependency_tree(const Vector<RemovedDependency> &p_removed);
void ok_pressed();
public:
- void show(const Vector<String> &to_erase);
+ void show(const Vector<String> &p_folders, const Vector<String> &p_files);
DependencyRemoveDialog();
};
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 20ffbde378..2c4d3035a4 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1699,7 +1699,7 @@ void EditorHelp::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- class_desc->add_color_override("selection_color", EDITOR_DEF("text_editor/highlighting/selection_color", Color(0.2, 0.2, 1)));
+ class_desc->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor"));
} break;
@@ -1788,7 +1788,7 @@ EditorHelp::EditorHelp() {
class_desc = memnew(RichTextLabel);
vbc->add_child(class_desc);
class_desc->set_v_size_flags(SIZE_EXPAND_FILL);
- class_desc->add_color_override("selection_color", EDITOR_DEF("text_editor/highlighting/selection_color", Color(0.2, 0.2, 1)));
+ class_desc->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor"));
class_desc->connect("meta_clicked", this, "_class_desc_select");
class_desc->connect("gui_input", this, "_class_desc_input");
}
@@ -1879,7 +1879,7 @@ void EditorHelpBit::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- rich_text->add_color_override("selection_color", EDITOR_DEF("text_editor/highlighting/selection_color", Color(0.2, 0.2, 1)));
+ rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor"));
} break;
default: break;
@@ -1898,6 +1898,7 @@ EditorHelpBit::EditorHelpBit() {
add_child(rich_text);
rich_text->set_anchors_and_margins_preset(Control::PRESET_WIDE);
rich_text->connect("meta_clicked", this, "_meta_clicked");
- rich_text->add_color_override("selection_color", EDITOR_DEF("text_editor/highlighting/selection_color", Color(0.2, 0.2, 1)));
+ rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor"));
+ rich_text->set_override_selected_font_color(false);
set_custom_minimum_size(Size2(0, 70 * EDSCALE));
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 1ca88133b8..aca2f59134 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -3156,12 +3156,19 @@ void EditorNode::_add_to_recent_scenes(const String &p_scene) {
void EditorNode::_open_recent_scene(int p_idx) {
String base = "_" + ProjectSettings::get_singleton()->get_resource_path().replace("\\", "::").replace("/", "::");
- Vector<String> rc = EDITOR_DEF(base + "/_recent_scenes", Array());
- ERR_FAIL_INDEX(p_idx, rc.size());
+ if (p_idx == recent_scenes->get_item_count() - 1) {
+
+ EditorSettings::get_singleton()->erase(base + "/_recent_scenes");
+ call_deferred("_update_recent_scenes");
+ } else {
+
+ Vector<String> rc = EDITOR_DEF(base + "/_recent_scenes", Array());
+ ERR_FAIL_INDEX(p_idx, rc.size());
- String path = "res://" + rc[p_idx];
- load_scene(path);
+ String path = "res://" + rc[p_idx];
+ load_scene(path);
+ }
}
void EditorNode::_update_recent_scenes() {
@@ -3169,10 +3176,15 @@ void EditorNode::_update_recent_scenes() {
String base = "_" + ProjectSettings::get_singleton()->get_resource_path().replace("\\", "::").replace("/", "::");
Vector<String> rc = EDITOR_DEF(base + "/_recent_scenes", Array());
recent_scenes->clear();
+
for (int i = 0; i < rc.size(); i++) {
recent_scenes->add_item(rc[i], i);
}
+
+ recent_scenes->add_separator();
+ recent_scenes->add_shortcut(ED_SHORTCUT("editor/clear_recent", TTR("Clear Recent Scenes")));
+ recent_scenes->set_as_minsize();
}
void EditorNode::_quick_opened() {
@@ -4513,6 +4525,7 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("_set_main_scene_state", &EditorNode::_set_main_scene_state);
ClassDB::bind_method("_update_scene_tabs", &EditorNode::_update_scene_tabs);
ClassDB::bind_method("_discard_changes", &EditorNode::_discard_changes);
+ ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes);
ClassDB::bind_method("_prepare_history", &EditorNode::_prepare_history);
ClassDB::bind_method("_select_history", &EditorNode::_select_history);
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index b532bb793a..7c45e19f5f 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -719,12 +719,13 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// freelook
_initial_set("editors/3d/freelook/freelook_inertia", 0.1);
hints["editors/3d/freelook/freelook_inertia"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_inertia", PROPERTY_HINT_RANGE, "0.0, 1, 0.01");
- _initial_set("editors/3d/freelook/freelook_base_speed", 0.1);
+ _initial_set("editors/3d/freelook/freelook_base_speed", 5.0);
hints["editors/3d/freelook/freelook_base_speed"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_base_speed", PROPERTY_HINT_RANGE, "0.0, 10, 0.01");
_initial_set("editors/3d/freelook/freelook_activation_modifier", 0);
hints["editors/3d/freelook/freelook_activation_modifier"] = PropertyInfo(Variant::INT, "editors/3d/freelook/freelook_activation_modifier", PROPERTY_HINT_ENUM, "None,Shift,Alt,Meta,Ctrl");
_initial_set("editors/3d/freelook/freelook_modifier_speed_factor", 3.0);
hints["editors/3d/freelook/freelook_modifier_speed_factor"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_modifier_speed_factor", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.1");
+ _initial_set("editors/3d/freelook/freelook_speed_zoom_link", false);
_initial_set("editors/2d/bone_width", 5);
_initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9));
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 3fc1dcb0bd..13ed7a7f30 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -972,8 +972,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color dim_color = Color(font_color.r, font_color.g, font_color.b, 0.5);
const float mono_value = mono_color.r;
- const Color alpha1 = Color(mono_value, mono_value, mono_value, 0.1);
- const Color alpha2 = Color(mono_value, mono_value, mono_value, 0.3);
+ const Color alpha1 = Color(mono_value, mono_value, mono_value, 0.07);
+ const Color alpha2 = Color(mono_value, mono_value, mono_value, 0.14);
const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.5);
const Color alpha4 = Color(mono_value, mono_value, mono_value, 0.7);
@@ -998,7 +998,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color caret_color = mono_color;
const Color caret_background_color = mono_color.inverted();
const Color text_selected_color = dark_color_3;
- const Color selection_color = alpha3;
+ const Color selection_color = alpha2;
const Color brace_mismatch_color = error_color;
const Color current_line_color = alpha1;
const Color line_length_guideline_color = warning_color;
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index a9d72607b4..dfd35fdd96 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -458,9 +458,9 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
if (path != "res://") {
if (use_thumbnails) {
- files->add_item("..", folder_thumbnail, true);
+ files->add_item("..", folder_thumbnail, false);
} else {
- files->add_item("..", get_icon("folder", "FileDialog"), true);
+ files->add_item("..", get_icon("folder", "FileDialog"), false);
}
String bd = path.get_base_dir();
@@ -567,9 +567,22 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
}
void FileSystemDock::_select_file(int p_idx) {
-
- files->select(p_idx, true);
- _file_option(FILE_OPEN);
+ String path = files->get_item_metadata(p_idx);
+ if (path.ends_with("/")) {
+ if (path != "res://") {
+ path = path.substr(0, path.length() - 1);
+ }
+ this->path = path;
+ _update_files(false);
+ current_path->set_text(path);
+ _push_to_history();
+ } else {
+ if (ResourceLoader::get_resource_type(path) == "PackedScene") {
+ editor->open_request(path);
+ } else {
+ editor->load_resource(path);
+ }
+ }
}
void FileSystemDock::_go_to_tree() {
@@ -704,18 +717,19 @@ void FileSystemDock::_push_to_history() {
button_hist_next->set_disabled(history_pos + 1 == history.size());
}
-void FileSystemDock::_find_inside_move_files(EditorFileSystemDirectory *efsd, Vector<String> &files) {
+void FileSystemDock::_get_all_files_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files) const {
+ if (efsd == NULL)
+ return;
for (int i = 0; i < efsd->get_subdir_count(); i++) {
- _find_inside_move_files(efsd->get_subdir(i), files);
+ _get_all_files_in_dir(efsd->get_subdir(i), files);
}
for (int i = 0; i < efsd->get_file_count(); i++) {
files.push_back(efsd->get_file_path(i));
}
}
-void FileSystemDock::_find_remaps(EditorFileSystemDirectory *efsd, Map<String, String> &renames, List<String> &to_remaps) {
-
+void FileSystemDock::_find_remaps(EditorFileSystemDirectory *efsd, const Map<String, String> &renames, Vector<String> &to_remaps) const {
for (int i = 0; i < efsd->get_subdir_count(); i++) {
_find_remaps(efsd->get_subdir(i), renames, to_remaps);
}
@@ -730,199 +744,157 @@ void FileSystemDock::_find_remaps(EditorFileSystemDirectory *efsd, Map<String, S
}
}
-void FileSystemDock::_rename_operation(const String &p_to_path) {
+void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_renames) const {
+ //Ensure folder paths end with "/"
+ String old_path = (p_item.is_file || p_item.path.ends_with("/")) ? p_item.path : (p_item.path + "/");
+ String new_path = (p_item.is_file || p_new_path.ends_with("/")) ? p_new_path : (p_new_path + "/");
- if (move_files[0] == p_to_path) {
- EditorNode::get_singleton()->show_warning(TTR("Same source and destination files, doing nothing."));
+ if (new_path == old_path) {
return;
- }
- if (FileAccess::exists(p_to_path)) {
- EditorNode::get_singleton()->show_warning(TTR("Target file exists, can't overwrite. Delete first."));
+ } else if (old_path == "res://") {
+ EditorNode::get_singleton()->add_io_error(TTR("Cannot move/rename resources root."));
+ return;
+ } else if (!p_item.is_file && new_path.begins_with(old_path)) {
+ //This check doesn't erroneously catch renaming to a longer name as folder paths always end with "/"
+ EditorNode::get_singleton()->add_io_error(TTR("Cannot move a folder into itself.\n") + old_path + "\n");
return;
}
- Map<String, String> renames;
- renames[move_files[0]] = p_to_path;
-
- List<String> remap;
-
- _find_remaps(EditorFileSystem::get_singleton()->get_filesystem(), renames, remap);
- print_line("found files to remap: " + itos(remap.size()));
-
- //perform remaps
- for (List<String>::Element *E = remap.front(); E; E = E->next()) {
-
- Error err = ResourceLoader::rename_dependencies(E->get(), renames);
- print_line("remapping: " + E->get());
-
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error("Can't rename deps for:\n" + E->get() + "\n");
- }
+ //Build a list of files which will have new paths as a result of this operation
+ Vector<String> changed_paths;
+ if (p_item.is_file) {
+ changed_paths.push_back(old_path);
+ } else {
+ _get_all_files_in_dir(EditorFileSystem::get_singleton()->get_filesystem_path(old_path), changed_paths);
}
- //finally, perform moves
-
DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ print_line("Moving " + old_path + " -> " + new_path);
+ Error err = da->rename(old_path, new_path);
+ if (err == OK) {
+ //Move/Rename any corresponding import settings too
+ if (p_item.is_file && FileAccess::exists(old_path + ".import")) {
+ err = da->rename(old_path + ".import", new_path + ".import");
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Error moving:\n") + old_path + ".import\n");
+ }
+ }
- Error err = da->rename(move_files[0], p_to_path);
- print_line("moving file " + move_files[0] + " to " + p_to_path);
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error("Error moving file:\n" + move_files[0] + "\n");
+ //Only treat as a changed dependency if it was successfully moved
+ for (int i = 0; i < changed_paths.size(); ++i) {
+ p_renames[changed_paths[i]] = changed_paths[i].replace_first(old_path, new_path);
+ print_line(" Remap: " + changed_paths[i] + " -> " + p_renames[changed_paths[i]]);
+ }
+ } else {
+ EditorNode::get_singleton()->add_io_error(TTR("Error moving:\n") + old_path + "\n");
}
-
- //rescan everything
memdelete(da);
- print_line("call rescan!");
- _rescan();
}
-void FileSystemDock::_move_operation(const String &p_to_path) {
-
- if (p_to_path == path) {
- EditorNode::get_singleton()->show_warning(TTR("Same source and destination paths, doing nothing."));
- return;
+void FileSystemDock::_update_dependencies_after_move(const Map<String, String> &p_renames) const {
+ //The following code assumes that the following holds:
+ // 1) EditorFileSystem contains the old paths/folder structure from before the rename/move.
+ // 2) ResourceLoader can use the new paths without needing to call rescan.
+ Vector<String> remaps;
+ _find_remaps(EditorFileSystem::get_singleton()->get_filesystem(), p_renames, remaps);
+ for (int i = 0; i < remaps.size(); ++i) {
+ //Because we haven't called a rescan yet the found remap might still be an old path itself.
+ String file = p_renames.has(remaps[i]) ? p_renames[remaps[i]] : remaps[i];
+ print_line("Remapping dependencies for: " + file);
+ Error err = ResourceLoader::rename_dependencies(file, p_renames);
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Unable to update dependencies:\n") + remaps[i] + "\n");
+ }
}
+}
- //find files inside dirs to be moved
-
- Vector<String> inside_files;
-
- for (int i = 0; i < move_dirs.size(); i++) {
- if (p_to_path.begins_with(move_dirs[i])) {
- EditorNode::get_singleton()->show_warning(TTR("Can't move directories to within themselves."));
- return;
- }
+void FileSystemDock::_make_dir_confirm() {
+ String dir_name = make_dir_dialog_text->get_text().strip_edges();
- EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->get_filesystem_path(move_dirs[i]);
- if (!efsd)
- continue;
- _find_inside_move_files(efsd, inside_files);
+ if (dir_name.length() == 0) {
+ EditorNode::get_singleton()->show_warning(TTR("No name provided"));
+ return;
+ } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1) {
+ EditorNode::get_singleton()->show_warning(TTR("Provided name contains invalid characters"));
+ return;
}
- //make list of remaps
- Map<String, String> renames;
- String repfrom = path == "res://" ? path : String(path + "/");
- String repto = p_to_path;
- if (!repto.ends_with("/")) {
- repto += "/";
+ print_line("Making folder " + dir_name + " in " + path);
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Error err = da->change_dir(path);
+ if (err == OK) {
+ err = da->make_dir(dir_name);
}
+ memdelete(da);
- print_line("reprfrom: " + repfrom + " repto " + repto);
-
- for (int i = 0; i < move_files.size(); i++) {
- renames[move_files[i]] = move_files[i].replace_first(repfrom, repto);
- print_line("move file " + move_files[i] + " -> " + renames[move_files[i]]);
- }
- for (int i = 0; i < inside_files.size(); i++) {
- renames[inside_files[i]] = inside_files[i].replace_first(repfrom, repto);
- print_line("inside file " + inside_files[i] + " -> " + renames[inside_files[i]]);
+ if (err == OK) {
+ print_line("call rescan!");
+ _rescan();
+ } else {
+ EditorNode::get_singleton()->show_warning(TTR("Could not create folder."));
}
+}
- //make list of files that will be run the remapping
- List<String> remap;
-
- _find_remaps(EditorFileSystem::get_singleton()->get_filesystem(), renames, remap);
- print_line("found files to remap: " + itos(remap.size()));
-
- //perform remaps
- for (List<String>::Element *E = remap.front(); E; E = E->next()) {
-
- Error err = ResourceLoader::rename_dependencies(E->get(), renames);
- print_line("remapping: " + E->get());
+void FileSystemDock::_rename_operation_confirm() {
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Can't rename deps for:\n") + E->get() + "\n");
- }
+ String new_name = rename_dialog_text->get_text().strip_edges();
+ if (new_name.length() == 0) {
+ EditorNode::get_singleton()->show_warning(TTR("No name provided."));
+ return;
+ } else if (new_name.find("/") != -1 || new_name.find("\\") != -1 || new_name.find(":") != -1) {
+ EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
+ return;
}
- //finally, perform moves
+ String old_path = to_rename.path.ends_with("/") ? to_rename.path.substr(0, to_rename.path.length() - 1) : to_rename.path;
+ String new_path = old_path.get_base_dir().plus_file(new_name);
+ if (old_path == new_path) {
+ return;
+ }
+ //Present a more user friendly warning for name conflict
DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
-
- for (int i = 0; i < move_files.size(); i++) {
-
- String to = move_files[i].replace_first(repfrom, repto);
- Error err = da->rename(move_files[i], to);
- print_line("moving file " + move_files[i] + " to " + to);
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Error moving file:\n") + move_files[i] + "\n");
- }
- if (FileAccess::exists(move_files[i] + ".import")) { //move imported files too
- //@todo should remove the files in .import folder
- err = da->rename(move_files[i] + ".import", to + ".import");
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Error moving file:\n") + move_files[i] + ".import\n");
- }
- }
+ if (da->file_exists(new_path) || da->dir_exists(new_path)) {
+ EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists."));
+ memdelete(da);
+ return;
}
+ memdelete(da);
- for (int i = 0; i < move_dirs.size(); i++) {
+ Map<String, String> renames;
+ _try_move_item(to_rename, new_path, renames);
+ _update_dependencies_after_move(renames);
- String mdir = move_dirs[i];
- if (mdir == "res://")
- continue;
+ //Rescan everything
+ print_line("call rescan!");
+ _rescan();
+}
- if (mdir.ends_with("/")) {
- mdir = mdir.substr(0, mdir.length() - 1);
- }
+void FileSystemDock::_move_operation_confirm(const String &p_to_path) {
- String to = p_to_path.plus_file(mdir.get_file());
- Error err = da->rename(mdir, to);
- print_line("moving dir " + mdir + " to " + to);
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Error moving dir:\n") + move_dirs[i] + "\n");
- }
+ Map<String, String> renames;
+ for (int i = 0; i < to_move.size(); i++) {
+ String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
+ String new_path = p_to_path.plus_file(old_path.get_file());
+ _try_move_item(to_move[i], new_path, renames);
}
- memdelete(da);
- //rescan everything
+ _update_dependencies_after_move(renames);
print_line("call rescan!");
_rescan();
}
void FileSystemDock::_file_option(int p_option) {
-
switch (p_option) {
-
- case FILE_SHOW_IN_EXPLORER:
+ case FILE_SHOW_IN_EXPLORER: {
+ String dir = ProjectSettings::get_singleton()->globalize_path(this->path);
+ OS::get_singleton()->shell_open(String("file://") + dir);
+ } break;
case FILE_OPEN: {
- int idx = -1;
- for (int i = 0; i < files->get_item_count(); i++) {
- if (files->is_selected(i)) {
- idx = i;
- break;
- }
- }
-
- if (idx < 0)
- return;
-
- String path = files->get_item_metadata(idx);
- if (p_option == FILE_SHOW_IN_EXPLORER) {
- String dir = ProjectSettings::get_singleton()->globalize_path(path);
- dir = dir.substr(0, dir.find_last("/"));
- OS::get_singleton()->shell_open(String("file://") + dir);
- return;
- }
-
- if (path.ends_with("/")) {
- if (path != "res://") {
- path = path.substr(0, path.length() - 1);
- }
- this->path = path;
- _update_files(false);
- current_path->set_text(path);
- _push_to_history();
- } else {
-
- if (ResourceLoader::get_resource_type(path) == "PackedScene") {
-
- editor->open_request(path);
- } else {
-
- editor->load_resource(path);
- }
- }
+ int idx = files->get_current();
+ if (idx < 0 || idx >= files->get_item_count())
+ break;
+ _select_file(idx);
} break;
case FILE_INSTANCE: {
@@ -958,64 +930,59 @@ void FileSystemDock::_file_option(int p_option) {
owners_editor->show(path);
} break;
case FILE_MOVE: {
-
- move_dirs.clear();
- move_files.clear();
-
+ to_move.clear();
for (int i = 0; i < files->get_item_count(); i++) {
-
- String path = files->get_item_metadata(i);
if (!files->is_selected(i))
continue;
- if (files->get_item_text(i) == "..") {
- EditorNode::get_singleton()->show_warning(TTR("Can't operate on '..'"));
- return;
- }
-
- if (path.ends_with("/")) {
- move_dirs.push_back(path.substr(0, path.length() - 1));
- } else {
- move_files.push_back(path);
- }
+ String path = files->get_item_metadata(i);
+ to_move.push_back(FileOrFolder(path, !path.ends_with("/")));
}
-
- if (move_dirs.empty() && move_files.size() == 1) {
-
- rename_dialog->clear_filters();
- rename_dialog->add_filter("*." + move_files[0].get_extension());
- rename_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
- rename_dialog->set_current_path(move_files[0]);
- rename_dialog->popup_centered_ratio();
- rename_dialog->set_title(TTR("Pick New Name and Location For:") + " " + move_files[0].get_file());
-
- } else {
- //just move
+ if (to_move.size() > 0) {
move_dialog->popup_centered_ratio();
}
+ } break;
+ case FILE_RENAME: {
+ int idx = files->get_current();
+ if (idx < 0 || idx >= files->get_item_count())
+ break;
+ to_rename.path = files->get_item_metadata(idx);
+ to_rename.is_file = !to_rename.path.ends_with("/");
+ if (to_rename.is_file) {
+ String name = to_rename.path.get_file();
+ rename_dialog->set_title(TTR("Renaming file:") + " " + name);
+ rename_dialog_text->set_text(name);
+ rename_dialog_text->select(0, name.find_last("."));
+ } else {
+ String name = to_rename.path.substr(0, to_rename.path.length() - 1).get_file();
+ rename_dialog->set_title(TTR("Renaming folder:") + " " + name);
+ rename_dialog_text->set_text(name);
+ rename_dialog_text->select(0, name.length());
+ }
+ rename_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE);
+ rename_dialog_text->grab_focus();
} break;
case FILE_REMOVE: {
-
- Vector<String> torem;
+ Vector<String> remove_files;
+ Vector<String> remove_folders;
for (int i = 0; i < files->get_item_count(); i++) {
-
String path = files->get_item_metadata(i);
- if (!files->is_selected(i))
- continue;
- torem.push_back(path);
+ if (files->is_selected(i) && path != "res://") {
+ if (path.ends_with("/")) {
+ remove_folders.push_back(path);
+ } else {
+ remove_files.push_back(path);
+ }
+ }
}
- if (torem.empty()) {
- EditorNode::get_singleton()->show_warning(TTR("No files selected!"));
- break;
+ if (remove_files.size() + remove_folders.size() > 0) {
+ remove_dialog->show(remove_folders, remove_files);
+ //1) find if used
+ //2) warn
}
-
- remove_dialog->show(torem);
- //1) find if used
- //2) warn
-
} break;
case FILE_INFO: {
@@ -1052,15 +1019,20 @@ void FileSystemDock::_file_option(int p_option) {
}
*/
-
} break;
- case FILE_COPY_PATH:
-
+ case FILE_NEW_FOLDER: {
+ make_dir_dialog_text->set_text("new folder");
+ make_dir_dialog_text->select_all();
+ make_dir_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE);
+ make_dir_dialog_text->grab_focus();
+ } break;
+ case FILE_COPY_PATH: {
int idx = files->get_current();
if (idx < 0 || idx >= files->get_item_count())
break;
String path = files->get_item_metadata(idx);
OS::get_singleton()->set_clipboard(path);
+ } break;
}
}
@@ -1070,26 +1042,64 @@ void FileSystemDock::_folder_option(int p_option) {
TreeItem *child = item->get_children();
switch (p_option) {
-
- case FOLDER_EXPAND_ALL:
+ case FOLDER_EXPAND_ALL: {
item->set_collapsed(false);
while (child) {
child->set_collapsed(false);
child = child->get_next();
}
- break;
-
- case FOLDER_COLLAPSE_ALL:
+ } break;
+ case FOLDER_COLLAPSE_ALL: {
while (child) {
child->set_collapsed(true);
child = child->get_next();
}
- break;
- case FOLDER_SHOW_IN_EXPLORER:
+ } break;
+ case FOLDER_MOVE: {
+ to_move.clear();
+ String fpath = item->get_metadata(tree->get_selected_column());
+ if (fpath != "res://") {
+ fpath = fpath.ends_with("/") ? fpath.substr(0, fpath.length() - 1) : fpath;
+ to_move.push_back(FileOrFolder(fpath, false));
+ move_dialog->popup_centered_ratio();
+ }
+ } break;
+ case FOLDER_RENAME: {
+ to_rename.path = item->get_metadata(tree->get_selected_column());
+ to_rename.is_file = false;
+ if (to_rename.path != "res://") {
+ String name = to_rename.path.ends_with("/") ? to_rename.path.substr(0, to_rename.path.length() - 1).get_file() : to_rename.path.get_file();
+ rename_dialog->set_title(TTR("Renaming folder:") + " " + name);
+ rename_dialog_text->set_text(name);
+ rename_dialog_text->select(0, name.length());
+ rename_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE);
+ rename_dialog_text->grab_focus();
+ }
+ } break;
+ case FOLDER_REMOVE: {
+ Vector<String> remove_folders;
+ Vector<String> remove_files;
+ String path = item->get_metadata(tree->get_selected_column());
+ if (path != "res://") {
+ remove_folders.push_back(path);
+ remove_dialog->show(remove_folders, remove_files);
+ }
+ } break;
+ case FOLDER_NEW_FOLDER: {
+ make_dir_dialog_text->set_text("new folder");
+ make_dir_dialog_text->select_all();
+ make_dir_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE);
+ make_dir_dialog_text->grab_focus();
+ } break;
+ case FOLDER_COPY_PATH: {
+ String path = item->get_metadata(tree->get_selected_column());
+ OS::get_singleton()->set_clipboard(path);
+ } break;
+ case FOLDER_SHOW_IN_EXPLORER: {
String path = item->get_metadata(tree->get_selected_column());
String dir = ProjectSettings::get_singleton()->globalize_path(path);
OS::get_singleton()->shell_open(String("file://") + dir);
- return;
+ } break;
}
}
@@ -1128,9 +1138,20 @@ void FileSystemDock::_dir_rmb_pressed(const Vector2 &p_pos) {
folder_options->add_item(TTR("Expand all"), FOLDER_EXPAND_ALL);
folder_options->add_item(TTR("Collapse all"), FOLDER_COLLAPSE_ALL);
- folder_options->add_separator();
- folder_options->add_item(TTR("Show In File Manager"), FOLDER_SHOW_IN_EXPLORER);
-
+ TreeItem *item = tree->get_selected();
+ if (item) {
+ String fpath = item->get_metadata(tree->get_selected_column());
+ folder_options->add_separator();
+ folder_options->add_item(TTR("Copy Path"), FOLDER_COPY_PATH);
+ if (fpath != "res://") {
+ folder_options->add_item(TTR("Rename.."), FOLDER_RENAME);
+ folder_options->add_item(TTR("Move To.."), FOLDER_MOVE);
+ folder_options->add_item(TTR("Delete"), FOLDER_REMOVE);
+ }
+ folder_options->add_separator();
+ folder_options->add_item(TTR("New Folder.."), FOLDER_NEW_FOLDER);
+ folder_options->add_item(TTR("Show In File Manager"), FOLDER_SHOW_IN_EXPLORER);
+ }
folder_options->set_position(tree->get_global_position() + p_pos);
folder_options->popup();
}
@@ -1445,117 +1466,79 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
}
Vector<String> fnames = drag_data["files"];
- move_files.clear();
- move_dirs.clear();
-
+ to_move.clear();
for (int i = 0; i < fnames.size(); i++) {
- if (fnames[i].ends_with("/"))
- move_dirs.push_back(fnames[i]);
- else
- move_files.push_back(fnames[i]);
+ to_move.push_back(FileOrFolder(fnames[i], !fnames[i].ends_with("/")));
}
-
- _move_operation(to_dir);
+ _move_operation_confirm(to_dir);
}
}
}
void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) {
- Vector<String> filenames;
+ //Right clicking ".." should clear current selection
+ if (files->get_item_text(p_item) == "..") {
+ for (int i = 0; i < files->get_item_count(); i++) {
+ files->unselect(i);
+ }
+ }
- bool all_scenes = true;
- bool all_can_reimport = true;
- bool is_dir = false;
- Set<String> types;
+ Vector<String> filenames;
+ Vector<String> foldernames;
+ bool all_files = true;
+ bool all_files_scenes = true;
+ bool all_folders = true;
for (int i = 0; i < files->get_item_count(); i++) {
-
- if (!files->is_selected(i))
+ if (!files->is_selected(i)) {
continue;
-
- String path = files->get_item_metadata(i);
-
- if (files->get_item_text(i) == "..") {
- // no operate on ..
- return;
}
+ String path = files->get_item_metadata(i);
if (path.ends_with("/")) {
- is_dir = true;
- }
-
- int pos;
-
- EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->find_file(path, &pos);
-
- if (efsd) {
-
+ foldernames.push_back(path);
+ all_files = false;
} else {
- all_can_reimport = false;
+ filenames.push_back(path);
+ all_folders = false;
+ all_files_scenes &= (EditorFileSystem::get_singleton()->get_file_type(path) == "PackedScene");
}
-
- filenames.push_back(path);
- if (EditorFileSystem::get_singleton()->get_file_type(path) != "PackedScene")
- all_scenes = false;
}
- if (filenames.size() == 0)
- return;
-
file_options->clear();
file_options->set_size(Size2(1, 1));
+ if (all_files && filenames.size() > 0) {
+ file_options->add_item(TTR("Open"), FILE_OPEN);
+ if (all_files_scenes) {
+ file_options->add_item(TTR("Instance"), FILE_INSTANCE);
+ }
+ file_options->add_separator();
- file_options->add_item(TTR("Open"), FILE_OPEN);
- if (all_scenes) {
- file_options->add_item(TTR("Instance"), FILE_INSTANCE);
- }
-
- file_options->add_separator();
-
- if (filenames.size() == 1 && !is_dir) {
- file_options->add_item(TTR("Edit Dependencies.."), FILE_DEPENDENCIES);
- file_options->add_item(TTR("View Owners.."), FILE_OWNERS);
+ if (filenames.size() == 1) {
+ file_options->add_item(TTR("Edit Dependencies.."), FILE_DEPENDENCIES);
+ file_options->add_item(TTR("View Owners.."), FILE_OWNERS);
+ file_options->add_separator();
+ }
+ } else if (all_folders && foldernames.size() > 0) {
+ file_options->add_item(TTR("Open"), FILE_OPEN);
file_options->add_separator();
}
- if (!is_dir) {
- if (filenames.size() == 1) {
+ int num_items = filenames.size() + foldernames.size();
+ if (num_items >= 1) {
+ if (num_items == 1) {
file_options->add_item(TTR("Copy Path"), FILE_COPY_PATH);
- file_options->add_item(TTR("Rename or Move.."), FILE_MOVE);
- } else {
- file_options->add_item(TTR("Move To.."), FILE_MOVE);
+ file_options->add_item(TTR("Rename.."), FILE_RENAME);
}
+ file_options->add_item(TTR("Move To.."), FILE_MOVE);
+ file_options->add_item(TTR("Delete"), FILE_REMOVE);
+ file_options->add_separator();
}
- file_options->add_item(TTR("Delete"), FILE_REMOVE);
-
- //file_options->add_item(TTR("Info"),FILE_INFO);
-
- file_options->add_separator();
+ file_options->add_item(TTR("New Folder.."), FILE_NEW_FOLDER);
file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER);
- if (all_can_reimport && types.size() == 1) { //all can reimport and are of the same type
-
- /*
- bool valid=true;
- Ref<EditorImportPlugin> rimp = EditorImportExport::get_singleton()->get_import_plugin_by_name(types.front()->get());
- if (rimp.is_valid()) {
-
- if (filenames.size()>1 && !rimp->can_reimport_multiple_files()) {
- valid=false;
- }
- } else {
- valid=false;
- }
-
- if (valid) {
- file_options->add_separator();
- file_options->add_item(TTR("Re-Import.."),FILE_REIMPORT);
- }
- */
- }
-
file_options->set_position(files->get_global_position() + p_pos);
file_options->popup();
}
@@ -1640,8 +1623,9 @@ void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_dir_selected"), &FileSystemDock::_dir_selected);
ClassDB::bind_method(D_METHOD("_file_option"), &FileSystemDock::_file_option);
ClassDB::bind_method(D_METHOD("_folder_option"), &FileSystemDock::_folder_option);
- ClassDB::bind_method(D_METHOD("_move_operation"), &FileSystemDock::_move_operation);
- ClassDB::bind_method(D_METHOD("_rename_operation"), &FileSystemDock::_rename_operation);
+ ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileSystemDock::_make_dir_confirm);
+ ClassDB::bind_method(D_METHOD("_move_operation_confirm"), &FileSystemDock::_move_operation_confirm);
+ ClassDB::bind_method(D_METHOD("_rename_operation_confirm"), &FileSystemDock::_rename_operation_confirm);
ClassDB::bind_method(D_METHOD("_search_changed"), &FileSystemDock::_search_changed);
@@ -1796,13 +1780,30 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
move_dialog = memnew(EditorDirDialog);
add_child(move_dialog);
- move_dialog->connect("dir_selected", this, "_move_operation");
+ move_dialog->connect("dir_selected", this, "_move_operation_confirm");
move_dialog->get_ok()->set_text(TTR("Move"));
- rename_dialog = memnew(EditorFileDialog);
- rename_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
- rename_dialog->connect("file_selected", this, "_rename_operation");
+ rename_dialog = memnew(ConfirmationDialog);
+ VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);
+ rename_dialog->add_child(rename_dialog_vb);
+
+ rename_dialog_text = memnew(LineEdit);
+ rename_dialog_vb->add_margin_child(TTR("Name:"), rename_dialog_text);
+ rename_dialog->get_ok()->set_text(TTR("Rename"));
add_child(rename_dialog);
+ rename_dialog->register_text_enter(rename_dialog_text);
+ rename_dialog->connect("confirmed", this, "_rename_operation_confirm");
+
+ make_dir_dialog = memnew(ConfirmationDialog);
+ make_dir_dialog->set_title(TTR("Create Folder"));
+ VBoxContainer *make_folder_dialog_vb = memnew(VBoxContainer);
+ make_dir_dialog->add_child(make_folder_dialog_vb);
+
+ make_dir_dialog_text = memnew(LineEdit);
+ make_folder_dialog_vb->add_margin_child(TTR("Name:"), make_dir_dialog_text);
+ add_child(make_dir_dialog);
+ make_dir_dialog->register_text_enter(make_dir_dialog_text);
+ make_dir_dialog->connect("confirmed", this, "_make_dir_confirm");
updating_tree = false;
initialized = false;
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index aec049ba43..89b250e295 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -32,6 +32,7 @@
#include "scene/gui/box_container.h"
#include "scene/gui/control.h"
+#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
#include "scene/gui/label.h"
#include "scene/gui/menu_button.h"
@@ -67,9 +68,11 @@ private:
FILE_DEPENDENCIES,
FILE_OWNERS,
FILE_MOVE,
+ FILE_RENAME,
FILE_REMOVE,
FILE_REIMPORT,
FILE_INFO,
+ FILE_NEW_FOLDER,
FILE_SHOW_IN_EXPLORER,
FILE_COPY_PATH
};
@@ -77,7 +80,12 @@ private:
enum FolderMenu {
FOLDER_EXPAND_ALL,
FOLDER_COLLAPSE_ALL,
- FOLDER_SHOW_IN_EXPLORER
+ FOLDER_MOVE,
+ FOLDER_RENAME,
+ FOLDER_REMOVE,
+ FOLDER_NEW_FOLDER,
+ FOLDER_SHOW_IN_EXPLORER,
+ FOLDER_COPY_PATH
};
VBoxContainer *scanning_vb;
@@ -110,10 +118,23 @@ private:
DependencyRemoveDialog *remove_dialog;
EditorDirDialog *move_dialog;
- EditorFileDialog *rename_dialog;
+ ConfirmationDialog *rename_dialog;
+ LineEdit *rename_dialog_text;
+ ConfirmationDialog *make_dir_dialog;
+ LineEdit *make_dir_dialog_text;
- Vector<String> move_dirs;
- Vector<String> move_files;
+ class FileOrFolder {
+ public:
+ String path;
+ bool is_file;
+
+ FileOrFolder()
+ : path(""), is_file(false) {}
+ FileOrFolder(const String &p_path, bool p_is_file)
+ : path(p_path), is_file(p_is_file) {}
+ };
+ FileOrFolder to_rename;
+ Vector<FileOrFolder> to_move;
Vector<String> history;
int history_pos;
@@ -135,11 +156,15 @@ private:
bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir);
void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata);
- void _find_inside_move_files(EditorFileSystemDirectory *efsd, Vector<String> &files);
- void _find_remaps(EditorFileSystemDirectory *efsd, Map<String, String> &renames, List<String> &to_remaps);
- void _rename_operation(const String &p_to_path);
- void _move_operation(const String &p_to_path);
+ void _get_all_files_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files) const;
+ void _find_remaps(EditorFileSystemDirectory *efsd, const Map<String, String> &renames, Vector<String> &to_remaps) const;
+ void _try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_renames) const;
+ void _update_dependencies_after_move(const Map<String, String> &p_renames) const;
+
+ void _make_dir_confirm();
+ void _rename_operation_confirm();
+ void _move_operation_confirm(const String &p_to_path);
void _file_option(int p_option);
void _folder_option(int p_option);
diff --git a/editor/icons/icon_viewport_speed.svg b/editor/icons/icon_viewport_speed.svg
new file mode 100644
index 0000000000..5d47fbbe89
--- /dev/null
+++ b/editor/icons/icon_viewport_speed.svg
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="24"
+ height="24"
+ version="1.1"
+ viewBox="0 0 24 24"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="icon_viewport_speed.svg">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1017"
+ id="namedview8"
+ showgrid="true"
+ inkscape:zoom="55.837564"
+ inkscape:cx="12.355509"
+ inkscape:cy="13.455858"
+ inkscape:window-x="-8"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g4">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4196" />
+ </sodipodi:namedview>
+ <g
+ transform="translate(0,-1028.4)"
+ id="g4">
+ <circle
+ style="fill:#e0e0e0;fill-opacity:0.99607843;stroke:#000000;stroke-width:1.02133572;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4150"
+ cy="1033.9"
+ cx="16.5"
+ r="2.9893322" />
+ <rect
+ y="1031.4"
+ x="5"
+ height="0.99997556"
+ width="7"
+ id="rect4188"
+ style="fill:#000000;fill-opacity:0.99607843;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:0.99607843;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
+ id="rect4178"
+ width="7"
+ height="1.0000244"
+ x="5"
+ y="1030.4" />
+ <rect
+ y="1035.4"
+ x="1"
+ height="0.99997556"
+ width="6"
+ id="rect4190"
+ style="fill:#000000;fill-opacity:0.99607843;stroke:none;stroke-width:0.98120075;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:0.99607843;stroke:none;stroke-width:0.98308611;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
+ id="rect4180"
+ width="6"
+ height="0.99997556"
+ x="1"
+ y="1034.4" />
+ <rect
+ y="1042.4"
+ x="2"
+ height="1.0000244"
+ width="7"
+ id="rect4192"
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:0.99607843;stroke:none;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
+ id="rect4182"
+ width="7"
+ height="0.99997556"
+ x="2"
+ y="1041.4" />
+ <path
+ style="fill:#e0e0e0;fill-opacity:0.99607843;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 4.121611,1045.8307 -0.1638725,2.0132 6.1803345,0.6321 2.865785,-3.8285 3.825066,1.2671 -1.634214,3.6617 1.802597,0.9365 2.751611,-5.7254 -5.323873,-2.3187 1.381211,-2.6534 2.589918,2.1082 4.477133,-2.2965 -0.976935,-1.5052 -3.180386,1.3802 -3.253887,-2.6678 -4.922628,-2.5117 -3.6554378,3.6189 1.4307035,1.5154 2.4736963,-2.3877 2.012407,0.9882 -1.262113,3.517 -2.5945824,4.631 z"
+ id="path4186"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccccccc" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_viewport_zoom.svg b/editor/icons/icon_viewport_zoom.svg
new file mode 100644
index 0000000000..3b5c8d2096
--- /dev/null
+++ b/editor/icons/icon_viewport_zoom.svg
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="24"
+ height="24"
+ version="1.1"
+ viewBox="0 0 24 24"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="icon_viewport_zoom.svg">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1017"
+ id="namedview8"
+ showgrid="true"
+ inkscape:zoom="32"
+ inkscape:cx="12.62433"
+ inkscape:cy="11.793762"
+ inkscape:window-x="-8"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g4">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4798" />
+ </sodipodi:namedview>
+ <g
+ transform="translate(0,-1028.4)"
+ id="g4">
+ <path
+ d="m 8.9917563,1029.9083 a 7.4877313,7.4846399 0 0 0 -7.4877312,7.4847 7.4877313,7.4846399 0 0 0 7.4877312,7.4847 7.4877313,7.4846399 0 0 0 4.1240927,-1.2455 l 6.464009,6.4613 2.11768,-2.1168 -6.464008,-6.4613 a 7.4877313,7.4846399 0 0 0 1.246002,-4.1224 7.4877313,7.4846399 0 0 0 -7.4877307,-7.4847 z m 0,2.9939 a 4.4926389,4.4907841 0 0 1 4.4926387,4.4908 4.4926389,4.4907841 0 0 1 -4.4926387,4.4909 4.4926389,4.4907841 0 0 1 -4.4926386,-4.4909 4.4926389,4.4907841 0 0 1 4.4926386,-4.4908 z"
+ id="path6"
+ style="fill:#e0e0e0;fill-opacity:0.99607999;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index a65b8b20da..703c9e96b6 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -63,7 +63,8 @@
#define ZOOM_MULTIPLIER 1.08
#define ZOOM_INDICATOR_DELAY_S 1.5
-#define FREELOOK_MIN_SPEED 0.1
+#define FREELOOK_MIN_SPEED 0.01
+#define FREELOOK_SPEED_MULTIPLIER 1.08
#define MIN_Z 0.01
#define MAX_Z 10000
@@ -75,34 +76,66 @@ void SpatialEditorViewport::_update_camera(float p_interp_delta) {
bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
- //when not being manipulated, move softly
- float free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
- float free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia");
- //when being manipulated, move more quickly
- float manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia");
- float manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia");
+ Cursor old_camera_cursor = camera_cursor;
+ camera_cursor = cursor;
- float zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia");
+ if (p_interp_delta > 0) {
- //determine if being manipulated
- bool manipulated = (Input::get_singleton()->get_mouse_button_mask() & (2 | 4)) || Input::get_singleton()->is_key_pressed(KEY_SHIFT) || Input::get_singleton()->is_key_pressed(KEY_ALT) || Input::get_singleton()->is_key_pressed(KEY_CONTROL);
+ //-------
+ // Perform smoothing
- float orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia);
- float translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia);
+ if (is_freelook_active()) {
- Cursor old_camera_cursor = camera_cursor;
- camera_cursor = cursor;
+ // Higher inertia should increase "lag" (lerp with factor between 0 and 1)
+ // Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1.
+ real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia");
+ inertia = MAX(0.001, inertia);
+ real_t factor = (1.0 / inertia) * p_interp_delta;
- camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
- camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
+ // We interpolate a different point here, because in freelook mode the focus point (cursor.pos) orbits around eye_pos
+ camera_cursor.eye_pos = old_camera_cursor.eye_pos.linear_interpolate(cursor.eye_pos, CLAMP(factor, 0, 1));
+ //camera_cursor.pos = camera_cursor.eye_pos + (cursor.pos - cursor.eye_pos);
- camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia)));
- camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia)));
+ float orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
+ orbit_inertia = MAX(0.0001, orbit_inertia);
+ camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
+ camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
+
+ Vector3 forward = to_camera_transform(camera_cursor).basis.xform(Vector3(0, 0, -1));
+ camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance;
+
+ } else {
- if (p_interp_delta == 0 || is_freelook_active()) {
- camera_cursor = cursor;
+ //when not being manipulated, move softly
+ float free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
+ float free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia");
+ //when being manipulated, move more quickly
+ float manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia");
+ float manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia");
+
+ float zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia");
+
+ //determine if being manipulated
+ bool manipulated = Input::get_singleton()->get_mouse_button_mask() & (2 | 4);
+ manipulated |= Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+ manipulated |= Input::get_singleton()->is_key_pressed(KEY_ALT);
+ manipulated |= Input::get_singleton()->is_key_pressed(KEY_CONTROL);
+
+ float orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia);
+ float translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia);
+ zoom_inertia = MAX(0.0001, zoom_inertia);
+
+ camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
+ camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
+
+ camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia)));
+ camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia)));
+ }
}
+ //-------
+ // Apply camera transform
+
float tolerance = 0.001;
bool equal = true;
if (Math::abs(old_camera_cursor.x_rot - camera_cursor.x_rot) > tolerance || Math::abs(old_camera_cursor.y_rot - camera_cursor.y_rot) > tolerance) {
@@ -845,11 +878,17 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (b->get_button_index()) {
case BUTTON_WHEEL_UP: {
- scale_cursor_distance(is_freelook_active() ? zoom_factor : 1.0 / zoom_factor);
+ if (is_freelook_active())
+ scale_freelook_speed(zoom_factor);
+ else
+ scale_cursor_distance(1.0 / zoom_factor);
} break;
case BUTTON_WHEEL_DOWN: {
- scale_cursor_distance(is_freelook_active() ? 1.0 / zoom_factor : zoom_factor);
+ if (is_freelook_active())
+ scale_freelook_speed(1.0 / zoom_factor);
+ else
+ scale_cursor_distance(zoom_factor);
} break;
case BUTTON_RIGHT: {
@@ -901,10 +940,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (b->is_pressed()) {
int mod = _get_key_modifier(b);
if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) {
- freelook_active = true;
+ set_freelook_active(true);
}
} else {
- freelook_active = false;
+ set_freelook_active(false);
}
if (freelook_active && !surface->has_focus()) {
@@ -1645,6 +1684,9 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
+ // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag".
+ Transform prev_camera_transform = to_camera_transform(cursor);
+
cursor.x_rot += relative.y * radians_per_pixel;
cursor.y_rot += relative.x * radians_per_pixel;
if (cursor.x_rot > Math_PI / 2.0)
@@ -1652,12 +1694,12 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (cursor.x_rot < -Math_PI / 2.0)
cursor.x_rot = -Math_PI / 2.0;
- // Look is like Orbit, except the cursor translates, not the camera
+ // Look is like the opposite of Orbit: the focus point rotates around the camera
Transform camera_transform = to_camera_transform(cursor);
Vector3 pos = camera_transform.xform(Vector3(0, 0, 0));
- Vector3 diff = camera->get_translation() - pos;
+ Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0));
+ Vector3 diff = prev_pos - pos;
cursor.pos += diff;
- freelook_target_position += diff;
name = "";
_update_name();
@@ -1755,23 +1797,57 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
}
+void SpatialEditorViewport::set_freelook_active(bool active_now) {
+
+ if (!freelook_active && active_now) {
+ // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential
+ cursor = camera_cursor;
+
+ // Make sure eye_pos is synced, because freelook referential is eye pos rather than orbit pos
+ Vector3 forward = to_camera_transform(cursor).basis.xform(Vector3(0, 0, -1));
+ cursor.eye_pos = cursor.pos - cursor.distance * forward;
+ // Also sync the camera cursor, otherwise switching to freelook will be trippy if inertia is active
+ camera_cursor.eye_pos = cursor.eye_pos;
+
+ if (EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_speed_zoom_link")) {
+ // Re-adjust freelook speed from the current zoom level
+ real_t base_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed");
+ freelook_speed = base_speed * cursor.distance;
+ }
+
+ } else if (freelook_active && !active_now) {
+ // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential
+ cursor = camera_cursor;
+ }
+
+ freelook_active = active_now;
+}
+
void SpatialEditorViewport::scale_cursor_distance(real_t scale) {
// Prevents zero distance which would short-circuit any scaling
if (cursor.distance < ZOOM_MIN_DISTANCE)
cursor.distance = ZOOM_MIN_DISTANCE;
- real_t prev_distance = cursor.distance;
cursor.distance *= scale;
if (cursor.distance < ZOOM_MIN_DISTANCE)
cursor.distance = ZOOM_MIN_DISTANCE;
- if (is_freelook_active()) {
- // In freelook mode, cursor reference is reversed so it needs to be adjusted
- Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1));
- cursor.pos += (cursor.distance - prev_distance) * forward;
- }
+ zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S;
+ surface->update();
+}
+
+void SpatialEditorViewport::scale_freelook_speed(real_t scale) {
+
+ // Prevents zero distance which would short-circuit any scaling
+ if (freelook_speed < FREELOOK_MIN_SPEED)
+ freelook_speed = FREELOOK_MIN_SPEED;
+
+ freelook_speed *= scale;
+
+ if (freelook_speed < FREELOOK_MIN_SPEED)
+ freelook_speed = FREELOOK_MIN_SPEED;
zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S;
surface->update();
@@ -1790,7 +1866,6 @@ Point2i SpatialEditorViewport::_get_warped_mouse_motion(const Ref<InputEventMous
void SpatialEditorViewport::_update_freelook(real_t delta) {
if (!is_freelook_active()) {
- freelook_target_position = cursor.pos;
return;
}
@@ -1833,21 +1908,15 @@ void SpatialEditorViewport::_update_freelook(real_t delta) {
speed_modifier = true;
}
- real_t inertia = EDITOR_DEF("editors/3d/freelook/freelook_inertia", 0.1);
- inertia = MAX(0, inertia);
- const real_t base_speed = EDITOR_DEF("editors/3d/freelook/freelook_base_speed", 0.5);
- const real_t modifier_speed_factor = EDITOR_DEF("editors/3d/freelook/freelook_modifier_speed_factor", 3);
-
- real_t speed = base_speed * cursor.distance;
- if (speed_modifier)
+ real_t speed = freelook_speed;
+ if (speed_modifier) {
+ real_t modifier_speed_factor = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_modifier_speed_factor");
speed *= modifier_speed_factor;
+ }
- // Higher inertia should increase "lag" (lerp with factor between 0 and 1)
- // Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1.
-
- freelook_target_position += direction * speed;
- real_t factor = (1.0 / (inertia + 0.001)) * delta;
- cursor.pos = cursor.pos.linear_interpolate(freelook_target_position, CLAMP(factor, 0, 1));
+ Vector3 motion = direction * speed * delta;
+ cursor.pos += motion;
+ cursor.eye_pos += motion;
}
void SpatialEditorViewport::set_message(String p_message, float p_time) {
@@ -1886,7 +1955,7 @@ void SpatialEditorViewport::_notification(int p_what) {
}
*/
- real_t delta = get_tree()->get_idle_process_time();
+ real_t delta = get_process_delta_time();
if (zoom_indicator_delay > 0) {
zoom_indicator_delay -= delta;
@@ -1897,7 +1966,7 @@ void SpatialEditorViewport::_notification(int p_what) {
_update_freelook(delta);
- _update_camera(get_process_delta_time());
+ _update_camera(delta);
Map<Node *, Object *> &selection = editor_selection->get_selection();
@@ -2026,7 +2095,7 @@ void SpatialEditorViewport::_notification(int p_what) {
}
// TODO That should be part of the drawing API...
-static void stroke_rect(CanvasItem *ci, Rect2 rect, Color color, real_t width = 1.0) {
+static void stroke_rect(CanvasItem &ci, Rect2 rect, Color color, real_t width = 1.0) {
// a---b
// | |
@@ -2036,10 +2105,31 @@ static void stroke_rect(CanvasItem *ci, Rect2 rect, Color color, real_t width =
Vector2 c(rect.position.x, rect.position.y + rect.size.y);
Vector2 d(rect.position + rect.size);
- ci->draw_line(a, b, color, width);
- ci->draw_line(b, d, color, width);
- ci->draw_line(d, c, color, width);
- ci->draw_line(c, a, color, width);
+ ci.draw_line(a, b, color, width);
+ ci.draw_line(b, d, color, width);
+ ci.draw_line(d, c, color, width);
+ ci.draw_line(c, a, color, width);
+}
+
+static void draw_indicator_bar(Control &surface, real_t fill, Ref<Texture> icon) {
+
+ // Adjust bar size from control height
+ Vector2 surface_size = surface.get_size();
+ real_t h = surface_size.y / 2.0;
+ real_t y = (surface_size.y - h) / 2.0;
+
+ Rect2 r(10, y, 6, h);
+ real_t sy = r.size.y * fill;
+
+ // Note: because this bar appears over the viewport, it has to stay readable for any background color
+ // Draw both neutral dark and bright colors to account this
+ surface.draw_rect(r, Color(1, 1, 1, 0.2));
+ surface.draw_rect(Rect2(r.position.x, r.position.y + r.size.y - sy, r.size.x, sy), Color(1, 1, 1, 0.6));
+ stroke_rect(surface, r.grow(1), Color(0, 0, 0, 0.7));
+
+ Vector2 icon_size = icon->get_size();
+ Vector2 icon_pos = Vector2(r.position.x - (icon_size.x - r.size.x) / 2, r.position.y + r.size.y + 2);
+ surface.draw_texture(icon, icon_pos);
}
void SpatialEditorViewport::_draw() {
@@ -2098,35 +2188,47 @@ void SpatialEditorViewport::_draw() {
draw_rect = Rect2(Vector2(), s).clip(draw_rect);
- stroke_rect(surface, draw_rect, Color(0.6, 0.6, 0.1, 0.5), 2.0);
+ stroke_rect(*surface, draw_rect, Color(0.6, 0.6, 0.1, 0.5), 2.0);
} else {
if (zoom_indicator_delay > 0.0) {
- // Show indicative zoom factor
- real_t min_distance = ZOOM_MIN_DISTANCE; // TODO Why not pick znear to limit zoom?
- real_t max_distance = camera->get_zfar();
- real_t scale_length = (max_distance - min_distance);
+ if (is_freelook_active()) {
+ // Show speed
+
+ real_t min_speed = FREELOOK_MIN_SPEED;
+ real_t max_speed = camera->get_zfar();
+ real_t scale_length = (max_speed - min_speed);
- if (Math::abs(scale_length) > CMP_EPSILON) {
- real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length);
+ if (Math::abs(scale_length) > CMP_EPSILON) {
+ real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length);
- // There is no real maximum distance so that factor can become negative,
- // Let's make it look asymptotic instead (will decrease slower and slower).
- if (logscale_t < 0.25)
- logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0);
+ // There is no real maximum speed so that factor can become negative,
+ // Let's make it look asymptotic instead (will decrease slower and slower).
+ if (logscale_t < 0.25)
+ logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0);
- Vector2 surface_size = surface->get_size();
- real_t h = surface_size.y / 2.0;
- real_t y = (surface_size.y - h) / 2.0;
+ draw_indicator_bar(*surface, 1.0 - logscale_t, get_icon("ViewportSpeed", "EditorIcons"));
+ }
+
+ } else {
+ // Show zoom
- Rect2 r(10, y, 6, h);
- real_t sy = r.size.y * logscale_t;
+ real_t min_distance = ZOOM_MIN_DISTANCE; // TODO Why not pick znear to limit zoom?
+ real_t max_distance = camera->get_zfar();
+ real_t scale_length = (max_distance - min_distance);
- surface->draw_rect(r, Color(1, 1, 1, 0.2));
- surface->draw_rect(Rect2(r.position.x, r.position.y + r.size.y - sy, r.size.x, sy), Color(1, 1, 1, 0.6));
- stroke_rect(surface, r.grow(1), Color(0, 0, 0, 0.7));
+ if (Math::abs(scale_length) > CMP_EPSILON) {
+ real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length);
+
+ // There is no real maximum distance so that factor can become negative,
+ // Let's make it look asymptotic instead (will decrease slower and slower).
+ if (logscale_t < 0.25)
+ logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0);
+
+ draw_indicator_bar(*surface, logscale_t, get_icon("ViewportZoom", "EditorIcons"));
+ }
}
}
}
@@ -3037,6 +3139,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
accept = NULL;
freelook_active = false;
+ freelook_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed");
selection_menu = memnew(PopupMenu);
add_child(selection_menu);
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index 4f4c540048..a9dd1f1327 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -131,7 +131,7 @@ private:
float gizmo_scale;
bool freelook_active;
- Vector3 freelook_target_position;
+ real_t freelook_speed;
PanelContainer *info;
Label *info_label;
@@ -231,6 +231,7 @@ private:
Vector3 pos;
float x_rot, y_rot, distance;
+ Vector3 eye_pos; // Used in freelook mode
bool region_select;
Point2 region_begin, region_end;
@@ -239,10 +240,17 @@ private:
distance = 4;
region_select = false;
}
- } cursor, camera_cursor;
+ };
+ // Viewport camera supports movement smoothing,
+ // so one cursor is the real cursor, while the other can be an interpolated version.
+ Cursor cursor; // Immediate cursor
+ Cursor camera_cursor; // That one may be interpolated (don't modify this one except for smoothing purposes)
void scale_cursor_distance(real_t scale);
+ void set_freelook_active(bool active_now);
+ void scale_freelook_speed(real_t scale);
+
real_t zoom_indicator_delay;
RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[3], scale_gizmo_instance[3];
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 658f67d6a4..b7cc9347f2 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -376,7 +376,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
if (hint == PROPERTY_HINT_RANGE) {
int c = hint_text.get_slice_count(",");
- float min = 0, max = 100, step = 1;
+ float min = 0, max = 100, step = type == Variant::REAL ? .01 : 1;
if (c >= 1) {
if (!hint_text.get_slice(",", 0).empty())
@@ -3032,7 +3032,7 @@ void PropertyEditor::update_tree() {
if (p.hint == PROPERTY_HINT_SPRITE_FRAME || p.hint == PROPERTY_HINT_RANGE || p.hint == PROPERTY_HINT_EXP_RANGE) {
int c = p.hint_string.get_slice_count(",");
- float min = 0, max = 100, step = 1;
+ float min = 0, max = 100, step = p.type == Variant::REAL ? .01 : 1;
if (c >= 1) {
min = p.hint_string.get_slice(",", 0).to_double();
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme b/misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme
index 3f0df5c437..b6beeb012f 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme
@@ -23,7 +23,7 @@
</BuildActionEntries>
</BuildAction>
<TestAction
- buildConfiguration = "Development"
+ buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
@@ -42,7 +42,7 @@
</AdditionalOptions>
</TestAction>
<LaunchAction
- buildConfiguration = "Development"
+ buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
@@ -67,7 +67,7 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
- buildConfiguration = "Development"
+ buildConfiguration = "Debug"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
@@ -84,10 +84,10 @@
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
- buildConfiguration = "Development">
+ buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
- buildConfiguration = "Development"
+ buildConfiguration = "Debug"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index ba4163aab7..a6ae143947 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -13,6 +13,7 @@ gdn_env.add_source_files(env.modules_sources, "nativescript/*.cpp")
gdn_env.Append(CPPPATH=['#modules/gdnative/include/'])
SConscript("nativearvr/SCsub")
+SConscript("pluginscript/SCsub")
def _spaced(e):
return e if e[-1] == '*' else e + ' '
@@ -26,6 +27,7 @@ def _build_gdnative_api_struct_header(api):
'#include <gdnative/gdnative.h>',
'#include <nativearvr/godot_nativearvr.h>',
'#include <nativescript/godot_nativescript.h>',
+ '#include <pluginscript/godot_pluginscript.h>',
'',
'#define GDNATIVE_API_INIT(options) do { extern const godot_gdnative_api_struct *_gdnative_wrapper_api_struct; _gdnative_wrapper_api_struct = options->api_struct; } while (0)',
'',
@@ -98,6 +100,7 @@ def _build_gdnative_wrapper_code(api):
'',
'#include <gdnative/gdnative.h>',
'#include <nativescript/godot_nativescript.h>',
+ '#include <pluginscript/godot_pluginscript.h>',
'',
'#include <gdnative_api_struct.gen.h>',
'',
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 9b715ce36a..905c513d9d 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -29,9 +29,9 @@
/*************************************************************************/
#include "gdnative/string.h"
+#include "core/string_db.h"
+#include "core/ustring.h"
#include "core/variant.h"
-#include "string_db.h"
-#include "ustring.h"
#include <string.h>
diff --git a/modules/gdnative/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h
new file mode 100644
index 0000000000..ec109bac83
--- /dev/null
+++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h
@@ -0,0 +1,170 @@
+/*************************************************************************/
+/* godot_nativescript.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GODOT_PLUGINSCRIPT_H
+#define GODOT_PLUGINSCRIPT_H
+
+#include <gdnative/gdnative.h>
+#include <nativescript/godot_nativescript.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void godot_pluginscript_instance_data;
+typedef void godot_pluginscript_script_data;
+typedef void godot_pluginscript_language_data;
+
+// --- Instance ---
+
+// TODO: use godot_string_name for faster lookup ?
+typedef struct {
+ godot_pluginscript_instance_data *(*init)(godot_pluginscript_script_data *p_data, godot_object *p_owner);
+ void (*finish)(godot_pluginscript_instance_data *p_data);
+
+ godot_bool (*set_prop)(godot_pluginscript_instance_data *p_data, const godot_string *p_name, const godot_variant *p_value);
+ godot_bool (*get_prop)(godot_pluginscript_instance_data *p_data, const godot_string *p_name, godot_variant *r_ret);
+
+ godot_variant (*call_method)(godot_pluginscript_instance_data *p_data,
+ const godot_string_name *p_method, const godot_variant **p_args,
+ int p_argcount, godot_variant_call_error *r_error);
+
+ void (*notification)(godot_pluginscript_instance_data *p_data, int p_notification);
+ // TODO: could this rpc mode stuff be moved to the godot_pluginscript_script_manifest ?
+ godot_method_rpc_mode (*get_rpc_mode)(godot_pluginscript_instance_data *p_data, const godot_string *p_method);
+ godot_method_rpc_mode (*get_rset_mode)(godot_pluginscript_instance_data *p_data, const godot_string *p_variable);
+
+ //this is used by script languages that keep a reference counter of their own
+ //you can make make Ref<> not die when it reaches zero, so deleting the reference
+ //depends entirely from the script.
+ // Note: You can set thoses function pointer to NULL if not needed.
+ void (*refcount_incremented)(godot_pluginscript_instance_data *p_data);
+ bool (*refcount_decremented)(godot_pluginscript_instance_data *p_data); // return true if it can die
+} godot_pluginscript_instance_desc;
+
+// --- Script ---
+
+typedef struct {
+ godot_pluginscript_script_data *data;
+ godot_string_name name;
+ godot_bool is_tool;
+ godot_string_name base;
+
+ // Member lines format: {<string>: <int>}
+ godot_dictionary member_lines;
+ // Method info dictionary format
+ // {
+ // name: <string>
+ // args: [<dict:property>]
+ // default_args: [<variant>]
+ // return: <dict:property>
+ // flags: <int>
+ // rpc_mode: <int:godot_method_rpc_mode>
+ // }
+ godot_array methods;
+ // Same format than for methods
+ godot_array signals;
+ // Property info dictionary format
+ // {
+ // name: <string>
+ // type: <int:godot_variant_type>
+ // hint: <int:godot_property_hint>
+ // hint_string: <string>
+ // usage: <int:godot_property_usage_flags>
+ // default_value: <variant>
+ // rset_mode: <int:godot_method_rpc_mode>
+ // }
+ godot_array properties;
+} godot_pluginscript_script_manifest;
+
+typedef struct {
+ godot_pluginscript_script_manifest (*init)(godot_pluginscript_language_data *p_data, const godot_string *p_path, const godot_string *p_source, godot_error *r_error);
+ void (*finish)(godot_pluginscript_script_data *p_data);
+ godot_pluginscript_instance_desc instance_desc;
+} godot_pluginscript_script_desc;
+
+// --- Language ---
+
+typedef struct {
+ godot_string_name signature;
+ godot_int call_count;
+ godot_int total_time; // In microseconds
+ godot_int self_time; // In microseconds
+} godot_pluginscript_profiling_data;
+
+typedef struct {
+ const char *name;
+ const char *type;
+ const char *extension;
+ const char **recognized_extensions; // NULL terminated array
+ godot_pluginscript_language_data *(*init)();
+ void (*finish)(godot_pluginscript_language_data *p_data);
+ const char **reserved_words; // NULL terminated array
+ const char **comment_delimiters; // NULL terminated array
+ const char **string_delimiters; // NULL terminated array
+ godot_bool has_named_classes;
+
+ godot_string (*get_template_source_code)(godot_pluginscript_language_data *p_data, const godot_string *p_class_name, const godot_string *p_base_class_name);
+ godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_pool_string_array *r_functions);
+ int (*find_function)(godot_pluginscript_language_data *p_data, const godot_string *p_function, const godot_string *p_code); // Can be NULL
+ godot_string (*make_function)(godot_pluginscript_language_data *p_data, const godot_string *p_class, const godot_string *p_name, const godot_pool_string_array *p_args);
+ godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_base_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint);
+ void (*auto_indent_code)(godot_pluginscript_language_data *p_data, godot_string *p_code, int p_from_line, int p_to_line);
+
+ void (*add_global_constant)(godot_pluginscript_language_data *p_data, const godot_string *p_variable, const godot_variant *p_value);
+ godot_string (*debug_get_error)(godot_pluginscript_language_data *p_data);
+ int (*debug_get_stack_level_count)(godot_pluginscript_language_data *p_data);
+ int (*debug_get_stack_level_line)(godot_pluginscript_language_data *p_data, int p_level);
+ godot_string (*debug_get_stack_level_function)(godot_pluginscript_language_data *p_data, int p_level);
+ godot_string (*debug_get_stack_level_source)(godot_pluginscript_language_data *p_data, int p_level);
+ void (*debug_get_stack_level_locals)(godot_pluginscript_language_data *p_data, int p_level, godot_pool_string_array *p_locals, godot_array *p_values, int p_max_subitems, int p_max_depth);
+ void (*debug_get_stack_level_members)(godot_pluginscript_language_data *p_data, int p_level, godot_pool_string_array *p_members, godot_array *p_values, int p_max_subitems, int p_max_depth);
+ void (*debug_get_globals)(godot_pluginscript_language_data *p_data, godot_pool_string_array *p_locals, godot_array *p_values, int p_max_subitems, int p_max_depth);
+ godot_string (*debug_parse_stack_level_expression)(godot_pluginscript_language_data *p_data, int p_level, const godot_string *p_expression, int p_max_subitems, int p_max_depth);
+
+ // TODO: could this stuff be moved to the godot_pluginscript_language_desc ?
+ void (*get_public_functions)(godot_pluginscript_language_data *p_data, godot_array *r_functions);
+ void (*get_public_constants)(godot_pluginscript_language_data *p_data, godot_dictionary *r_constants);
+
+ void (*profiling_start)(godot_pluginscript_language_data *p_data);
+ void (*profiling_stop)(godot_pluginscript_language_data *p_data);
+ int (*profiling_get_accumulated_data)(godot_pluginscript_language_data *p_data, godot_pluginscript_profiling_data *r_info, int p_info_max);
+ int (*profiling_get_frame_data)(godot_pluginscript_language_data *p_data, godot_pluginscript_profiling_data *r_info, int p_info_max);
+ void (*profiling_frame)(godot_pluginscript_language_data *p_data);
+
+ godot_pluginscript_script_desc script_desc;
+} godot_pluginscript_language_desc;
+
+void GDAPI godot_pluginscript_register_language(const godot_pluginscript_language_desc *language_desc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GODOT_PLUGINSCRIPT_H
diff --git a/modules/gdnative/nativescript/SCsub b/modules/gdnative/nativescript/SCsub
index 178afec64a..ee3b9c351d 100644
--- a/modules/gdnative/nativescript/SCsub
+++ b/modules/gdnative/nativescript/SCsub
@@ -4,7 +4,6 @@ Import('env')
mod_env = env.Clone()
mod_env.add_source_files(env.modules_sources, "*.cpp")
-mod_env.Append(CPPPATH='#modules/gdnative')
mod_env.Append(CPPFLAGS=['-DGDAPI_BUILT_IN'])
if "platform" in env and env["platform"] in ["x11", "iphone"]:
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index 9a956ff594..63fb71feb6 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -31,7 +31,7 @@
#ifdef TOOLS_ENABLED
-#include "class_db.h"
+#include "core/class_db.h"
#include "core/global_constants.h"
#include "core/pair.h"
#include "core/project_settings.h"
diff --git a/modules/gdnative/nativescript/api_generator.h b/modules/gdnative/nativescript/api_generator.h
index 56c2d786e6..a8e2eaf0bf 100644
--- a/modules/gdnative/nativescript/api_generator.h
+++ b/modules/gdnative/nativescript/api_generator.h
@@ -30,8 +30,8 @@
#ifndef API_GENERATOR_H
#define API_GENERATOR_H
+#include "core/typedefs.h"
#include "core/ustring.h"
-#include "typedefs.h"
Error generate_c_api(const String &p_path);
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 52379560b3..aa1fdc32da 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -31,11 +31,11 @@
#include "gdnative/gdnative.h"
-#include "global_constants.h"
+#include "core/global_constants.h"
+#include "core/project_settings.h"
#include "io/file_access_encrypted.h"
#include "os/file_access.h"
#include "os/os.h"
-#include "project_settings.h"
#include "scene/main/scene_tree.h"
#include "scene/resources/scene_format_text.h"
@@ -1011,10 +1011,13 @@ void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) {
void *proc_ptr;
- gdn->get_symbol(_init_call_name, proc_ptr);
-
- ((void (*)(godot_string *))proc_ptr)((godot_string *)&lib_path);
+ Error err = gdn->get_symbol(_init_call_name, proc_ptr);
+ if (err != OK) {
+ ERR_PRINT(String("No " + _init_call_name + " in \"" + lib_path + "\" found").utf8().get_data());
+ } else {
+ ((void (*)(godot_string *))proc_ptr)((godot_string *)&lib_path);
+ }
} else {
// already initialized. Nice.
}
@@ -1138,9 +1141,12 @@ void NativeReloadNode::_notification(int p_what) {
// here the library registers all the classes and stuff.
void *proc_ptr;
- L->get()->get_symbol("godot_nativescript_init", proc_ptr);
-
- ((void (*)(void *))proc_ptr)((void *)&L->key());
+ Error err = L->get()->get_symbol("godot_nativescript_init", proc_ptr);
+ if (err != OK) {
+ ERR_PRINT(String("No godot_nativescript_init in \"" + L->key() + "\" found").utf8().get_data());
+ } else {
+ ((void (*)(void *))proc_ptr)((void *)&L->key());
+ }
for (Map<String, Set<NativeScript *> >::Element *U = NSL->library_script_users.front(); U; U = U->next()) {
for (Set<NativeScript *>::Element *S = U->get().front(); S; S = S->next()) {
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index bc7e850d3e..b5db641179 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -30,14 +30,14 @@
#ifndef NATIVE_SCRIPT_H
#define NATIVE_SCRIPT_H
+#include "core/resource.h"
+#include "core/script_language.h"
+#include "core/self_list.h"
#include "io/resource_loader.h"
#include "io/resource_saver.h"
#include "ordered_hash_map.h"
#include "os/thread_safe.h"
-#include "resource.h"
#include "scene/main/node.h"
-#include "script_language.h"
-#include "self_list.h"
#include "modules/gdnative/gdnative.h"
#include <nativescript/godot_nativescript.h>
diff --git a/modules/gdnative/pluginscript/SCsub b/modules/gdnative/pluginscript/SCsub
new file mode 100644
index 0000000000..2031a4236b
--- /dev/null
+++ b/modules/gdnative/pluginscript/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_pluginscript = env_modules.Clone()
+
+env_pluginscript.Append(CPPPATH=['#modules/gdnative/include/'])
+env_pluginscript.add_source_files(env.modules_sources, '*.cpp')
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp
new file mode 100644
index 0000000000..8f01350826
--- /dev/null
+++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp
@@ -0,0 +1,181 @@
+/*************************************************************************/
+/* pluginscript_instance.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// Godot imports
+#include "core/os/os.h"
+#include "core/variant.h"
+// PluginScript imports
+#include "pluginscript_instance.h"
+#include "pluginscript_language.h"
+#include "pluginscript_script.h"
+
+bool PluginScriptInstance::set(const StringName &p_name, const Variant &p_value) {
+ String name = String(p_name);
+ return _desc->set_prop(_data, (const godot_string *)&name, (const godot_variant *)&p_value);
+}
+
+bool PluginScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
+ String name = String(p_name);
+ return _desc->get_prop(_data, (const godot_string *)&name, (godot_variant *)&r_ret);
+}
+
+Ref<Script> PluginScriptInstance::get_script() const {
+ return _script;
+}
+
+ScriptLanguage *PluginScriptInstance::get_language() {
+ return _script->get_language();
+}
+
+Variant::Type PluginScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
+ if (!_script->has_property(p_name)) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return Variant::NIL;
+ }
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return _script->get_property_info(p_name).type;
+}
+
+void PluginScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
+ _script->get_script_property_list(p_properties);
+}
+
+void PluginScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
+ _script->get_script_method_list(p_list);
+}
+
+bool PluginScriptInstance::has_method(const StringName &p_method) const {
+ return _script->has_method(p_method);
+}
+
+Variant PluginScriptInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+ // TODO: optimize when calling a Godot method from Godot to avoid param conversion ?
+ godot_variant ret = _desc->call_method(
+ _data, (godot_string_name *)&p_method, (const godot_variant **)p_args,
+ p_argcount, (godot_variant_call_error *)&r_error);
+ Variant *var_ret = (Variant *)&ret;
+ return *var_ret;
+}
+
+#if 0 // TODO: Don't rely on default implementations provided by ScriptInstance ?
+void PluginScriptInstance::call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount) {
+
+#if 0
+ PluginScript *sptr=script.ptr();
+ Variant::CallError ce;
+
+ while(sptr) {
+ Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(p_method);
+ if (E) {
+ E->get()->call(this,p_args,p_argcount,ce);
+ }
+ sptr = sptr->_base;
+ }
+#endif
+
+}
+
+#if 0
+void PluginScriptInstance::_ml_call_reversed(PluginScript *sptr,const StringName& p_method,const Variant** p_args,int p_argcount) {
+
+ if (sptr->_base)
+ _ml_call_reversed(sptr->_base,p_method,p_args,p_argcount);
+
+ Variant::CallError ce;
+
+ Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(p_method);
+ if (E) {
+ E->get()->call(this,p_args,p_argcount,ce);
+ }
+
+}
+#endif
+
+
+void PluginScriptInstance::call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount) {
+
+#if 0
+ if (script.ptr()) {
+ _ml_call_reversed(script.ptr(),p_method,p_args,p_argcount);
+ }
+#endif
+}
+#endif // Multilevel stuff
+
+void PluginScriptInstance::notification(int p_notification) {
+ _desc->notification(_data, p_notification);
+}
+
+ScriptInstance::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const {
+ return _script->get_rpc_mode(p_method);
+}
+
+ScriptInstance::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const {
+ return _script->get_rset_mode(p_variable);
+}
+
+void PluginScriptInstance::refcount_incremented() {
+ if (_desc->refcount_decremented) {
+ _desc->refcount_incremented(_data);
+ }
+}
+
+bool PluginScriptInstance::refcount_decremented() {
+ // Return true if it can die
+ if (_desc->refcount_decremented) {
+ return _desc->refcount_decremented(_data);
+ }
+ return true;
+}
+
+PluginScriptInstance::PluginScriptInstance() {
+}
+
+bool PluginScriptInstance::init(PluginScript *p_script, Object *p_owner) {
+ _owner = p_owner;
+ _owner_variant = Variant(p_owner);
+ _script = Ref<PluginScript>(p_script);
+ _desc = &p_script->_desc->instance_desc;
+ _data = _desc->init(p_script->_data, (godot_object *)p_owner);
+ ERR_FAIL_COND_V(_data == NULL, false);
+ p_owner->set_script_instance(this);
+ return true;
+}
+
+PluginScriptInstance::~PluginScriptInstance() {
+ _desc->finish(_data);
+ _script->_language->lock();
+ _script->_instances.erase(_owner);
+ _script->_language->unlock();
+}
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
new file mode 100644
index 0000000000..68696b4417
--- /dev/null
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* pluginscript_instance.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef PLUGINSCRIPT_INSTANCE_H
+#define PLUGINSCRIPT_INSTANCE_H
+
+// Godot imports
+#include "core/script_language.h"
+// PluginScript imports
+#include <pluginscript/godot_pluginscript.h>
+
+class PluginScript;
+
+class PluginScriptInstance : public ScriptInstance {
+ friend class PluginScript;
+
+private:
+ Ref<PluginScript> _script;
+ Object *_owner;
+ Variant _owner_variant;
+ godot_pluginscript_instance_data *_data;
+ const godot_pluginscript_instance_desc *_desc;
+
+public:
+ _FORCE_INLINE_ Object *get_owner() { return _owner; }
+
+ virtual bool set(const StringName &p_name, const Variant &p_value);
+ virtual bool get(const StringName &p_name, Variant &r_ret) const;
+ virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+ virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = NULL) const;
+
+ virtual void get_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName &p_method) const;
+
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
+#if 0
+ // Rely on default implementations provided by ScriptInstance for the moment.
+ // Note that multilevel call could be removed in 3.0 release, so stay tunned
+ // (see https://godotengine.org/qa/9244/can-override-the-_ready-and-_process-functions-child-classes)
+ virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
+ virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount);
+#endif
+
+ virtual void notification(int p_notification);
+
+ virtual Ref<Script> get_script() const;
+
+ virtual ScriptLanguage *get_language();
+
+ void set_path(const String &p_path);
+
+ virtual RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual RPCMode get_rset_mode(const StringName &p_variable) const;
+
+ virtual void refcount_incremented();
+ virtual bool refcount_decremented();
+
+ PluginScriptInstance();
+ bool init(PluginScript *p_script, Object *p_owner);
+ virtual ~PluginScriptInstance();
+};
+
+#endif // PLUGINSCRIPT_INSTANCE_H
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
new file mode 100644
index 0000000000..2198e66ae4
--- /dev/null
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -0,0 +1,432 @@
+/*************************************************************************/
+/* pluginscript_language.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include <stdlib.h>
+// Godot imports
+#include "core/os/file_access.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+// PluginScript imports
+#include "pluginscript_language.h"
+#include "pluginscript_script.h"
+
+String PluginScriptLanguage::get_name() const {
+ return String(_desc.name);
+}
+
+void PluginScriptLanguage::init() {
+ _data = _desc.init();
+}
+
+String PluginScriptLanguage::get_type() const {
+ return String(_desc.type);
+}
+
+String PluginScriptLanguage::get_extension() const {
+ return String(_desc.extension);
+}
+
+Error PluginScriptLanguage::execute_file(const String &p_path) {
+ // TODO: pretty sure this method is totally deprecated and should be removed...
+ return OK;
+}
+
+void PluginScriptLanguage::finish() {
+ _desc.finish(_data);
+}
+
+/* EDITOR FUNCTIONS */
+
+void PluginScriptLanguage::get_reserved_words(List<String> *p_words) const {
+ if (_desc.reserved_words) {
+ const char **w = _desc.reserved_words;
+ while (*w) {
+ p_words->push_back(*w);
+ w++;
+ }
+ }
+}
+
+void PluginScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
+ if (_desc.comment_delimiters) {
+ const char **w = _desc.comment_delimiters;
+ while (*w) {
+ p_delimiters->push_back(*w);
+ w++;
+ }
+ }
+}
+
+void PluginScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
+ if (_desc.string_delimiters) {
+ const char **w = _desc.string_delimiters;
+ while (*w) {
+ p_delimiters->push_back(*w);
+ w++;
+ }
+ }
+}
+
+Ref<Script> PluginScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
+ Script *ns = create_script();
+ Ref<Script> script = Ref<Script>(ns);
+ if (_desc.get_template_source_code) {
+ godot_string src = _desc.get_template_source_code(_data, (godot_string *)&p_class_name, (godot_string *)&p_base_class_name);
+ script->set_source_code(*(String *)&src);
+ }
+ return script;
+}
+
+bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+ PoolStringArray functions;
+ if (_desc.validate) {
+ bool ret = _desc.validate(
+ _data,
+ (godot_string *)&p_script,
+ &r_line_error,
+ &r_col_error,
+ (godot_string *)&r_test_error,
+ (godot_string *)&p_path,
+ (godot_pool_string_array *)&functions);
+ for (int i = 0; i < functions.size(); i++) {
+ r_functions->push_back(functions[i]);
+ }
+ return ret;
+ }
+ return true;
+}
+
+Script *PluginScriptLanguage::create_script() const {
+ PluginScript *script = memnew(PluginScript());
+ // I'm hurting kittens doing this I guess...
+ script->init(const_cast<PluginScriptLanguage *>(this));
+ return script;
+}
+
+bool PluginScriptLanguage::has_named_classes() const {
+ return _desc.has_named_classes;
+}
+
+int PluginScriptLanguage::find_function(const String &p_function, const String &p_code) const {
+ if (_desc.find_function) {
+ return _desc.find_function(_data, (godot_string *)&p_function, (godot_string *)&p_code);
+ }
+ return -1;
+}
+
+String PluginScriptLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
+ if (_desc.make_function) {
+ godot_string tmp = _desc.make_function(_data, (godot_string *)&p_class, (godot_string *)&p_name, (godot_pool_string_array *)&p_args);
+ String ret = *(String *)&tmp;
+ godot_string_destroy(&tmp);
+ return ret;
+ }
+ return String();
+}
+
+Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) {
+ if (_desc.complete_code) {
+ Array options;
+ godot_error tmp = _desc.complete_code(
+ _data,
+ (godot_string *)&p_code,
+ (godot_string *)&p_base_path,
+ (godot_object *)p_owner,
+ (godot_array *)&options,
+ &r_force,
+ (godot_string *)&r_call_hint);
+ for (int i = 0; i < options.size(); i++) {
+ r_options->push_back(String(options[i]));
+ }
+ Error err = *(Error *)tmp;
+ return err;
+ }
+ return ERR_UNAVAILABLE;
+}
+
+void PluginScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {
+ if (_desc.auto_indent_code) {
+ _desc.auto_indent_code(_data, (godot_string *)&p_code, p_from_line, p_to_line);
+ }
+ return;
+}
+
+void PluginScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) {
+ const String variable = String(p_variable);
+ _desc.add_global_constant(_data, (godot_string *)&variable, (godot_variant *)&p_value);
+}
+
+/* LOADER FUNCTIONS */
+
+void PluginScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const {
+ for (int i = 0; _desc.recognized_extensions[i]; ++i) {
+ p_extensions->push_back(String(_desc.recognized_extensions[i]));
+ }
+}
+
+void PluginScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
+ // TODO: provid this statically in `godot_pluginscript_language_desc` ?
+ if (_desc.get_public_functions) {
+ Array functions;
+ _desc.get_public_functions(_data, (godot_array *)&functions);
+ for (int i = 0; i < functions.size(); i++) {
+ MethodInfo mi = MethodInfo::from_dict(functions[i]);
+ p_functions->push_back(mi);
+ }
+ }
+}
+
+void PluginScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_constants) const {
+ // TODO: provid this statically in `godot_pluginscript_language_desc` ?
+ if (_desc.get_public_constants) {
+ Dictionary constants;
+ _desc.get_public_constants(_data, (godot_dictionary *)&constants);
+ for (const Variant *key = constants.next(); key; key = constants.next(key)) {
+ Variant value = constants[key];
+ p_constants->push_back(Pair<String, Variant>(*key, value));
+ }
+ }
+}
+
+void PluginScriptLanguage::profiling_start() {
+#ifdef DEBUG_ENABLED
+ if (_desc.profiling_start) {
+ lock();
+ _desc.profiling_start(_data);
+ unlock();
+ }
+#endif
+}
+
+void PluginScriptLanguage::profiling_stop() {
+#ifdef DEBUG_ENABLED
+ if (_desc.profiling_stop) {
+ lock();
+ _desc.profiling_stop(_data);
+ unlock();
+ }
+#endif
+}
+
+int PluginScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) {
+ int info_count = 0;
+#ifdef DEBUG_ENABLED
+ if (_desc.profiling_get_accumulated_data) {
+ godot_pluginscript_profiling_data *info = (godot_pluginscript_profiling_data *)memalloc(
+ sizeof(godot_pluginscript_profiling_data) * p_info_max);
+ info_count = _desc.profiling_get_accumulated_data(_data, info, p_info_max);
+ for (int i = 0; i < info_count; ++i) {
+ p_info_arr[i].signature = *(StringName *)&info[i].signature;
+ p_info_arr[i].call_count = info[i].call_count;
+ p_info_arr[i].total_time = info[i].total_time;
+ p_info_arr[i].self_time = info[i].self_time;
+ godot_string_name_destroy(&info[i].signature);
+ }
+ }
+#endif
+ return info_count;
+}
+
+int PluginScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) {
+ int info_count = 0;
+#ifdef DEBUG_ENABLED
+ if (_desc.profiling_get_frame_data) {
+ godot_pluginscript_profiling_data *info = (godot_pluginscript_profiling_data *)memalloc(
+ sizeof(godot_pluginscript_profiling_data) * p_info_max);
+ info_count = _desc.profiling_get_frame_data(_data, info, p_info_max);
+ for (int i = 0; i < info_count; ++i) {
+ p_info_arr[i].signature = *(StringName *)&info[i].signature;
+ p_info_arr[i].call_count = info[i].call_count;
+ p_info_arr[i].total_time = info[i].total_time;
+ p_info_arr[i].self_time = info[i].self_time;
+ godot_string_name_destroy(&info[i].signature);
+ }
+ }
+#endif
+ return info_count;
+}
+
+void PluginScriptLanguage::frame() {
+#ifdef DEBUG_ENABLED
+ if (_desc.profiling_frame) {
+ _desc.profiling_frame(_data);
+ }
+#endif
+}
+
+/* DEBUGGER FUNCTIONS */
+
+String PluginScriptLanguage::debug_get_error() const {
+ if (_desc.debug_get_error) {
+ godot_string tmp = _desc.debug_get_error(_data);
+ String ret = *(String *)&tmp;
+ godot_string_destroy(&tmp);
+ return ret;
+ }
+ return String("Nothing");
+}
+
+int PluginScriptLanguage::debug_get_stack_level_count() const {
+ if (_desc.debug_get_stack_level_count) {
+ return _desc.debug_get_stack_level_count(_data);
+ }
+ return 1;
+}
+
+int PluginScriptLanguage::debug_get_stack_level_line(int p_level) const {
+ if (_desc.debug_get_stack_level_line) {
+ return _desc.debug_get_stack_level_line(_data, p_level);
+ }
+ return 1;
+}
+
+String PluginScriptLanguage::debug_get_stack_level_function(int p_level) const {
+ if (_desc.debug_get_stack_level_function) {
+ godot_string tmp = _desc.debug_get_stack_level_function(_data, p_level);
+ String ret = *(String *)&tmp;
+ godot_string_destroy(&tmp);
+ return ret;
+ }
+ return String("Nothing");
+}
+
+String PluginScriptLanguage::debug_get_stack_level_source(int p_level) const {
+ if (_desc.debug_get_stack_level_source) {
+ godot_string tmp = _desc.debug_get_stack_level_source(_data, p_level);
+ String ret = *(String *)&tmp;
+ godot_string_destroy(&tmp);
+ return ret;
+ }
+ return String("Nothing");
+}
+
+void PluginScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
+ if (_desc.debug_get_stack_level_locals) {
+ PoolStringArray locals;
+ Array values;
+ _desc.debug_get_stack_level_locals(_data, p_level, (godot_pool_string_array *)&locals, (godot_array *)&values, p_max_subitems, p_max_depth);
+ for (int i = 0; i < locals.size(); i++) {
+ p_locals->push_back(locals[i]);
+ }
+ for (int i = 0; i < values.size(); i++) {
+ p_values->push_back(values[i]);
+ }
+ }
+}
+
+void PluginScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
+ if (_desc.debug_get_stack_level_members) {
+ PoolStringArray members;
+ Array values;
+ _desc.debug_get_stack_level_members(_data, p_level, (godot_pool_string_array *)&members, (godot_array *)&values, p_max_subitems, p_max_depth);
+ for (int i = 0; i < members.size(); i++) {
+ p_members->push_back(members[i]);
+ }
+ for (int i = 0; i < values.size(); i++) {
+ p_values->push_back(values[i]);
+ }
+ }
+}
+
+void PluginScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
+ if (_desc.debug_get_globals) {
+ PoolStringArray locals;
+ Array values;
+ _desc.debug_get_globals(_data, (godot_pool_string_array *)&locals, (godot_array *)&values, p_max_subitems, p_max_depth);
+ for (int i = 0; i < locals.size(); i++) {
+ p_locals->push_back(locals[i]);
+ }
+ for (int i = 0; i < values.size(); i++) {
+ p_values->push_back(values[i]);
+ }
+ }
+}
+
+String PluginScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) {
+ if (_desc.debug_parse_stack_level_expression) {
+ godot_string tmp = _desc.debug_parse_stack_level_expression(_data, p_level, (godot_string *)&p_expression, p_max_subitems, p_max_depth);
+ String ret = *(String *)&tmp;
+ godot_string_destroy(&tmp);
+ return ret;
+ }
+ return String("Nothing");
+}
+
+void PluginScriptLanguage::reload_all_scripts() {
+ // TODO
+}
+
+void PluginScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
+#ifdef DEBUG_ENABLED
+ lock();
+ // TODO
+ unlock();
+#endif
+}
+
+void PluginScriptLanguage::lock() {
+#ifndef NO_THREADS
+ if (_lock) {
+ _lock->lock();
+ }
+#endif
+}
+
+void PluginScriptLanguage::unlock() {
+#ifndef NO_THREADS
+ if (_lock) {
+ _lock->unlock();
+ }
+#endif
+}
+
+PluginScriptLanguage::PluginScriptLanguage(const godot_pluginscript_language_desc *desc)
+ : _desc(*desc) {
+ _resource_loader = memnew(ResourceFormatLoaderPluginScript(this));
+ _resource_saver = memnew(ResourceFormatSaverPluginScript(this));
+
+// TODO: totally remove _lock attribute if NO_THREADS is set
+#ifdef NO_THREADS
+ _lock = NULL;
+#else
+ _lock = Mutex::create();
+#endif
+}
+
+PluginScriptLanguage::~PluginScriptLanguage() {
+ memdelete(_resource_loader);
+ memdelete(_resource_saver);
+#ifndef NO_THREADS
+ if (_lock) {
+ memdelete(_lock);
+ _lock = NULL;
+ }
+#endif
+}
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
new file mode 100644
index 0000000000..a48dde97ce
--- /dev/null
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -0,0 +1,131 @@
+/*************************************************************************/
+/* pluginscript_language.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef PLUGINSCRIPT_LANGUAGE_H
+#define PLUGINSCRIPT_LANGUAGE_H
+
+// Godot imports
+#include "core/io/resource_loader.h"
+#include "core/io/resource_saver.h"
+#include "core/map.h"
+#include "core/script_language.h"
+#include "core/self_list.h"
+// PluginScript imports
+#include "pluginscript_loader.h"
+#include <pluginscript/godot_pluginscript.h>
+
+class PluginScript;
+class PluginScriptInstance;
+
+class PluginScriptLanguage : public ScriptLanguage {
+ friend class PluginScript;
+ friend class PluginScriptInstance;
+
+ ResourceFormatLoaderPluginScript *_resource_loader;
+ ResourceFormatSaverPluginScript *_resource_saver;
+ const godot_pluginscript_language_desc _desc;
+ godot_pluginscript_language_data *_data;
+
+ Mutex *_lock;
+ SelfList<PluginScript>::List _script_list;
+
+public:
+ virtual String get_name() const;
+
+ _FORCE_INLINE_ ResourceFormatLoaderPluginScript *get_resource_loader() { return _resource_loader; };
+ _FORCE_INLINE_ ResourceFormatSaverPluginScript *get_resource_saver() { return _resource_saver; };
+
+ /* 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();
+
+ /* EDITOR FUNCTIONS */
+ virtual void get_reserved_words(List<String> *p_words) 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 validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const;
+ virtual Script *create_script() const;
+ virtual bool has_named_classes() const;
+ virtual bool can_inherit_from_file() { 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 PoolStringArray &p_args) const;
+ virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint);
+ 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);
+
+ /* MULTITHREAD FUNCTIONS */
+
+ //some VMs need to be notified of thread creation/exiting to allocate a stack
+ // void thread_enter() {}
+ // void thread_exit() {}
+
+ /* 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 Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
+
+ virtual void reload_all_scripts();
+ virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload);
+
+ /* 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 profiling_start();
+ virtual void profiling_stop();
+
+ 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 void frame();
+
+ void lock();
+ void unlock();
+
+ PluginScriptLanguage(const godot_pluginscript_language_desc *desc);
+ virtual ~PluginScriptLanguage();
+};
+
+#endif // PLUGINSCRIPT_LANGUAGE_H
diff --git a/modules/gdnative/pluginscript/pluginscript_loader.cpp b/modules/gdnative/pluginscript/pluginscript_loader.cpp
new file mode 100644
index 0000000000..3648e1a5b4
--- /dev/null
+++ b/modules/gdnative/pluginscript/pluginscript_loader.cpp
@@ -0,0 +1,113 @@
+/*************************************************************************/
+/* pluginscript_loader.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// Godot imports
+#include "os/file_access.h"
+// Pythonscript imports
+#include "pluginscript_language.h"
+#include "pluginscript_loader.h"
+#include "pluginscript_script.h"
+
+ResourceFormatLoaderPluginScript::ResourceFormatLoaderPluginScript(PluginScriptLanguage *language) {
+ _language = language;
+}
+
+RES ResourceFormatLoaderPluginScript::load(const String &p_path, const String &p_original_path, Error *r_error) {
+ if (r_error)
+ *r_error = ERR_FILE_CANT_OPEN;
+
+ PluginScript *script = memnew(PluginScript);
+ script->init(_language);
+
+ Ref<PluginScript> scriptres(script);
+
+ Error err = script->load_source_code(p_path);
+ ERR_FAIL_COND_V(err != OK, RES());
+
+ script->set_path(p_original_path);
+
+ script->reload();
+
+ if (r_error)
+ *r_error = OK;
+
+ return scriptres;
+}
+
+void ResourceFormatLoaderPluginScript::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back(_language->get_extension());
+}
+
+bool ResourceFormatLoaderPluginScript::handles_type(const String &p_type) const {
+ return p_type == "Script" || p_type == _language->get_type();
+}
+
+String ResourceFormatLoaderPluginScript::get_resource_type(const String &p_path) const {
+ String el = p_path.get_extension().to_lower();
+ if (el == _language->get_extension())
+ return _language->get_type();
+ return "";
+}
+
+ResourceFormatSaverPluginScript::ResourceFormatSaverPluginScript(PluginScriptLanguage *language) {
+ _language = language;
+}
+
+Error ResourceFormatSaverPluginScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
+ Ref<PluginScript> sqscr = p_resource;
+ ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);
+
+ String source = sqscr->get_source_code();
+
+ Error err;
+ FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V(err, err);
+
+ file->store_string(source);
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ memdelete(file);
+ return ERR_CANT_CREATE;
+ }
+ file->close();
+ memdelete(file);
+ return OK;
+}
+
+void ResourceFormatSaverPluginScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
+
+ if (Object::cast_to<PluginScript>(*p_resource)) {
+ p_extensions->push_back(_language->get_extension());
+ }
+}
+
+bool ResourceFormatSaverPluginScript::recognize(const RES &p_resource) const {
+
+ return Object::cast_to<PluginScript>(*p_resource) != NULL;
+}
diff --git a/modules/gdnative/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h
new file mode 100644
index 0000000000..b85e7725a1
--- /dev/null
+++ b/modules/gdnative/pluginscript/pluginscript_loader.h
@@ -0,0 +1,62 @@
+/*************************************************************************/
+/* pluginscript_loader.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef PYTHONSCRIPT_PY_LOADER_H
+#define PYTHONSCRIPT_PY_LOADER_H
+
+// Godot imports
+#include "core/script_language.h"
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+
+class PluginScriptLanguage;
+
+class ResourceFormatLoaderPluginScript : public ResourceFormatLoader {
+ PluginScriptLanguage *_language;
+
+public:
+ ResourceFormatLoaderPluginScript(PluginScriptLanguage *language);
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+class ResourceFormatSaverPluginScript : public ResourceFormatSaver {
+ PluginScriptLanguage *_language;
+
+public:
+ ResourceFormatSaverPluginScript(PluginScriptLanguage *language);
+ virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
+ virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
+ virtual bool recognize(const RES &p_resource) const;
+};
+
+#endif // PYTHONSCRIPT_PY_LOADER_H
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
new file mode 100644
index 0000000000..7dd10a8bdf
--- /dev/null
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -0,0 +1,454 @@
+/*************************************************************************/
+/* pluginscript_script.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// Godot imports
+#include "core/os/file_access.h"
+// PluginScript imports
+#include "pluginscript_instance.h"
+#include "pluginscript_script.h"
+
+#if DEBUG_ENABLED
+#define __ASSERT_SCRIPT_REASON "Cannot retrieve pluginscript class for this script, is you code correct ?"
+#define ASSERT_SCRIPT_VALID() \
+ { \
+ ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \
+ ERR_FAIL_COND(!can_instance()) \
+ }
+#define ASSERT_SCRIPT_VALID_V(ret) \
+ { \
+ ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \
+ ERR_FAIL_COND_V(!can_instance(), ret) \
+ }
+#else
+#define ASSERT_SCRIPT_VALID()
+#define ASSERT_SCRIPT_VALID_V(ret)
+#endif
+
+void PluginScript::_bind_methods() {
+}
+
+#ifdef TOOLS_ENABLED
+
+void PluginScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
+ placeholders.erase(p_placeholder);
+}
+
+#endif
+
+bool PluginScript::can_instance() const {
+ bool can = _valid || (!_tool && !ScriptServer::is_scripting_enabled());
+ return can;
+}
+
+Ref<Script> PluginScript::get_base_script() const {
+ if (_ref_base_parent.is_valid()) {
+ return Ref<PluginScript>(_ref_base_parent);
+ } else {
+ return Ref<Script>();
+ }
+}
+
+StringName PluginScript::get_instance_base_type() const {
+ if (_native_parent)
+ return _native_parent;
+ if (_ref_base_parent.is_valid())
+ return _ref_base_parent->get_instance_base_type();
+ return StringName();
+}
+
+void PluginScript::update_exports() {
+// TODO
+#ifdef TOOLS_ENABLED
+#if 0
+ ASSERT_SCRIPT_VALID();
+ if (/*changed &&*/ placeholders.size()) { //hm :(
+
+ //update placeholders if any
+ Map<StringName, Variant> propdefvalues;
+ List<PropertyInfo> propinfos;
+ const String *props = (const String *)pybind_get_prop_list(_py_exposed_class);
+ for (int i = 0; props[i] != ""; ++i) {
+ const String propname = props[i];
+ pybind_get_prop_default_value(_py_exposed_class, propname.c_str(), (godot_variant *)&propdefvalues[propname]);
+ pybind_prop_info raw_info;
+ pybind_get_prop_info(_py_exposed_class, propname.c_str(), &raw_info);
+ PropertyInfo info;
+ info.type = (Variant::Type)raw_info.type;
+ info.name = propname;
+ info.hint = (PropertyHint)raw_info.hint;
+ info.hint_string = *(String *)&raw_info.hint_string;
+ info.usage = raw_info.usage;
+ propinfos.push_back(info);
+ }
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->update(propinfos, propdefvalues);
+ }
+ }
+#endif
+#endif
+}
+
+// TODO: rename p_this "p_owner" ?
+ScriptInstance *PluginScript::instance_create(Object *p_this) {
+ ASSERT_SCRIPT_VALID_V(NULL);
+ // TODO check script validity ?
+ if (!_tool && !ScriptServer::is_scripting_enabled()) {
+#ifdef TOOLS_ENABLED
+ // Instance a fake script for editing the values
+ PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(get_language(), Ref<Script>(this), p_this));
+ placeholders.insert(si);
+ update_exports();
+ return si;
+#else
+ return NULL;
+#endif
+ }
+
+ PluginScript *top = this;
+ // TODO: can be optimized by storing a PluginScript::_base_parent direct pointer
+ while (top->_ref_base_parent.is_valid())
+ top = top->_ref_base_parent.ptr();
+ if (top->_native_parent) {
+ if (!ClassDB::is_parent_class(p_this->get_class_name(), top->_native_parent)) {
+ String msg = "Script inherits from native type '" + String(top->_native_parent) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'";
+ // TODO: implement PluginscriptLanguage::debug_break_parse
+ // if (ScriptDebugger::get_singleton()) {
+ // _language->debug_break_parse(get_path(), 0, msg);
+ // }
+ ERR_EXPLAIN(msg);
+ ERR_FAIL_V(NULL);
+ }
+ }
+
+ PluginScriptInstance *instance = memnew(PluginScriptInstance());
+ const bool success = instance->init(this, p_this);
+ if (success) {
+ _language->lock();
+ _instances.insert(instance->get_owner());
+ _language->unlock();
+ return instance;
+ } else {
+ memdelete(instance);
+ ERR_FAIL_V(NULL);
+ }
+}
+
+bool PluginScript::instance_has(const Object *p_this) const {
+ _language->lock();
+ bool hasit = _instances.has((Object *)p_this);
+ _language->unlock();
+ return hasit;
+}
+
+bool PluginScript::has_source_code() const {
+ bool has = _source != "";
+ return has;
+}
+
+String PluginScript::get_source_code() const {
+ return _source;
+}
+
+void PluginScript::set_source_code(const String &p_code) {
+ if (_source == p_code)
+ return;
+ _source = p_code;
+}
+
+Error PluginScript::reload(bool p_keep_state) {
+ _language->lock();
+ ERR_FAIL_COND_V(!p_keep_state && _instances.size(), ERR_ALREADY_IN_USE);
+ _language->unlock();
+
+ _valid = false;
+ String basedir = _path;
+
+ if (basedir == "")
+ basedir = get_path();
+
+ if (basedir != "")
+ basedir = basedir.get_base_dir();
+
+ if (_data) {
+ _desc->finish(_data);
+ }
+
+ Error err;
+ godot_pluginscript_script_manifest manifest = _desc->init(
+ _language->_data,
+ (godot_string *)&_path,
+ (godot_string *)&_source,
+ (godot_error *)&err);
+ if (err) {
+ // TODO: GDscript uses `ScriptDebugger` here to jump into the parsing error
+ return err;
+ }
+ _valid = true;
+ // Use the manifest to configure this script object
+ _data = manifest.data;
+ _name = *(StringName *)&manifest.name;
+ _tool = manifest.is_tool;
+ // Base name is either another PluginScript or a regular class accessible
+ // through ClassDB
+ StringName *base_name = (StringName *)&manifest.base;
+ for (SelfList<PluginScript> *e = _language->_script_list.first(); e != NULL; e = e->next()) {
+ if (e->self()->_name == *base_name) {
+ // Found you, base is a PluginScript !
+ _ref_base_parent = Ref<PluginScript>(e->self());
+ break;
+ }
+ }
+ if (!_ref_base_parent.is_valid()) {
+ // Base is a native ClassDB
+ if (!ClassDB::class_exists(*base_name)) {
+ ERR_EXPLAIN("Unknown script '" + String(_name) + "' parent '" + String(*base_name) + "'.");
+ ERR_FAIL_V(ERR_PARSE_ERROR);
+ }
+ _native_parent = *base_name;
+ }
+
+ Dictionary *members = (Dictionary *)&manifest.member_lines;
+ for (const Variant *key = members->next(); key != NULL; key = members->next(key)) {
+ _member_lines[*key] = (*members)[key];
+ }
+ Array *methods = (Array *)&manifest.methods;
+ for (int i = 0; i < methods->size(); ++i) {
+ Dictionary v = (*methods)[i];
+ MethodInfo mi = MethodInfo::from_dict(v);
+ _methods_info[mi.name] = mi;
+ // rpc_mode is passed as an optional field and is not part of MethodInfo
+ Variant var = v["rpc_mode"];
+ if (var == Variant()) {
+ _methods_rpc_mode[mi.name] = ScriptInstance::RPC_MODE_DISABLED;
+ } else {
+ _methods_rpc_mode[mi.name] = ScriptInstance::RPCMode(int(var));
+ }
+ }
+ Array *signals = (Array *)&manifest.signals;
+ for (int i = 0; i < signals->size(); ++i) {
+ Variant v = (*signals)[i];
+ MethodInfo mi = MethodInfo::from_dict(v);
+ _signals_info[mi.name] = mi;
+ }
+ Array *properties = (Array *)&manifest.properties;
+ for (int i = 0; i < properties->size(); ++i) {
+ Dictionary v = (*properties)[i];
+ PropertyInfo pi = PropertyInfo::from_dict(v);
+ _properties_info[pi.name] = pi;
+ _properties_default_values[pi.name] = v["default_value"];
+ // rset_mode is passed as an optional field and is not part of PropertyInfo
+ Variant var = v["rset_mode"];
+ if (var == Variant()) {
+ _methods_rpc_mode[pi.name] = ScriptInstance::RPC_MODE_DISABLED;
+ } else {
+ _methods_rpc_mode[pi.name] = ScriptInstance::RPCMode(int(var));
+ }
+ }
+ // Manifest's attributes must be explicitly freed
+ godot_string_name_destroy(&manifest.name);
+ godot_string_name_destroy(&manifest.base);
+ godot_dictionary_destroy(&manifest.member_lines);
+ godot_array_destroy(&manifest.methods);
+ godot_array_destroy(&manifest.signals);
+ godot_array_destroy(&manifest.properties);
+
+#ifdef TOOLS_ENABLED
+/*for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
+
+ _update_placeholder(E->get());
+ }*/
+#endif
+ return OK;
+}
+
+void PluginScript::get_script_method_list(List<MethodInfo> *r_methods) const {
+ ASSERT_SCRIPT_VALID();
+ for (Map<StringName, MethodInfo>::Element *e = _methods_info.front(); e != NULL; e = e->next()) {
+ r_methods->push_back(e->get());
+ }
+}
+
+void PluginScript::get_script_property_list(List<PropertyInfo> *r_properties) const {
+ ASSERT_SCRIPT_VALID();
+ for (Map<StringName, PropertyInfo>::Element *e = _properties_info.front(); e != NULL; e = e->next()) {
+ r_properties->push_back(e->get());
+ }
+}
+
+bool PluginScript::has_method(const StringName &p_method) const {
+ ASSERT_SCRIPT_VALID_V(false);
+ return _methods_info.has(p_method);
+}
+
+MethodInfo PluginScript::get_method_info(const StringName &p_method) const {
+ ASSERT_SCRIPT_VALID_V(MethodInfo());
+ const Map<StringName, MethodInfo>::Element *e = _methods_info.find(p_method);
+ if (e != NULL) {
+ return e->get();
+ } else {
+ return MethodInfo();
+ }
+}
+
+bool PluginScript::has_property(const StringName &p_method) const {
+ ASSERT_SCRIPT_VALID_V(false);
+ return _properties_info.has(p_method);
+}
+
+PropertyInfo PluginScript::get_property_info(const StringName &p_property) const {
+ ASSERT_SCRIPT_VALID_V(PropertyInfo());
+ const Map<StringName, PropertyInfo>::Element *e = _properties_info.find(p_property);
+ if (e != NULL) {
+ return e->get();
+ } else {
+ return PropertyInfo();
+ }
+}
+
+bool PluginScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
+ ASSERT_SCRIPT_VALID_V(false);
+#ifdef TOOLS_ENABLED
+ const Map<StringName, Variant>::Element *e = _properties_default_values.find(p_property);
+ if (e != NULL) {
+ r_value = e->get();
+ return true;
+ } else {
+ return false;
+ }
+#endif
+ return false;
+}
+
+String PluginScript::get_node_type() const {
+ // Even GDscript doesn't know what to put here !
+ return ""; // ?
+}
+
+ScriptLanguage *PluginScript::get_language() const {
+ return _language;
+}
+
+Error PluginScript::load_source_code(const String &p_path) {
+
+ PoolVector<uint8_t> sourcef;
+ Error err;
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (err) {
+ ERR_FAIL_COND_V(err, err);
+ }
+
+ int len = f->get_len();
+ sourcef.resize(len + 1);
+ PoolVector<uint8_t>::Write w = sourcef.write();
+ int r = f->get_buffer(w.ptr(), len);
+ f->close();
+ memdelete(f);
+ ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
+ w[len] = 0;
+
+ String s;
+ if (s.parse_utf8((const char *)w.ptr())) {
+ ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode.");
+ ERR_FAIL_V(ERR_INVALID_DATA);
+ }
+
+ _source = s;
+#ifdef TOOLS_ENABLED
+// source_changed_cache=true;
+#endif
+ _path = p_path;
+ return OK;
+}
+
+bool PluginScript::has_script_signal(const StringName &p_signal) const {
+ ASSERT_SCRIPT_VALID_V(false);
+ return _signals_info.has(p_signal);
+}
+
+void PluginScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+ ASSERT_SCRIPT_VALID();
+ for (Map<StringName, MethodInfo>::Element *e = _signals_info.front(); e != NULL; e = e->next()) {
+ r_signals->push_back(e->get());
+ }
+}
+
+int PluginScript::get_member_line(const StringName &p_member) const {
+#ifdef TOOLS_ENABLED
+ if (_member_lines.has(p_member))
+ return _member_lines[p_member];
+ else
+#endif
+ return -1;
+}
+
+ScriptInstance::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const {
+ ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED);
+ const Map<StringName, ScriptInstance::RPCMode>::Element *e = _methods_rpc_mode.find(p_method);
+ if (e != NULL) {
+ return e->get();
+ } else {
+ return ScriptInstance::RPC_MODE_DISABLED;
+ }
+}
+
+ScriptInstance::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const {
+ ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED);
+ const Map<StringName, ScriptInstance::RPCMode>::Element *e = _variables_rset_mode.find(p_variable);
+ if (e != NULL) {
+ return e->get();
+ } else {
+ return ScriptInstance::RPC_MODE_DISABLED;
+ }
+}
+
+PluginScript::PluginScript()
+ : _data(NULL), _tool(false), _valid(false), _script_list(this) {
+}
+
+void PluginScript::init(PluginScriptLanguage *language) {
+ _desc = &language->_desc.script_desc;
+ _language = language;
+
+#ifdef DEBUG_ENABLED
+ _language->lock();
+ _language->_script_list.add(&_script_list);
+ _language->unlock();
+#endif
+}
+
+PluginScript::~PluginScript() {
+ _desc->finish(_data);
+
+#ifdef DEBUG_ENABLED
+ _language->lock();
+ _language->_script_list.remove(&_script_list);
+ _language->unlock();
+#endif
+}
diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h
new file mode 100644
index 0000000000..051ef46bae
--- /dev/null
+++ b/modules/gdnative/pluginscript/pluginscript_script.h
@@ -0,0 +1,130 @@
+/*************************************************************************/
+/* pluginscript_script.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef PLUGINSCRIPT_SCRIPT_H
+#define PLUGINSCRIPT_SCRIPT_H
+
+#include <iostream>
+// Godot imports
+#include "core/script_language.h"
+// PluginScript imports
+#include "pluginscript_language.h"
+#include <pluginscript/godot_pluginscript.h>
+
+class PyInstance;
+
+class PluginScript : public Script {
+
+ GDCLASS(PluginScript, Script);
+
+ friend class PluginScriptInstance;
+ friend class PluginScriptLanguage;
+
+private:
+ godot_pluginscript_script_data *_data;
+ const godot_pluginscript_script_desc *_desc;
+ PluginScriptLanguage *_language;
+ bool _tool;
+ bool _valid;
+
+ Ref<PluginScript> _ref_base_parent;
+ StringName _native_parent;
+ SelfList<PluginScript> _script_list;
+
+ Map<StringName, int> _member_lines;
+ Map<StringName, Variant> _properties_default_values;
+ Map<StringName, PropertyInfo> _properties_info;
+ Map<StringName, MethodInfo> _signals_info;
+ Map<StringName, MethodInfo> _methods_info;
+ Map<StringName, ScriptInstance::RPCMode> _variables_rset_mode;
+ Map<StringName, ScriptInstance::RPCMode> _methods_rpc_mode;
+
+ Set<Object *> _instances;
+ //exported members
+ String _source;
+ String _path;
+ StringName _name;
+
+protected:
+ static void _bind_methods();
+
+#ifdef TOOLS_ENABLED
+ Set<PlaceHolderScriptInstance *> placeholders;
+ //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
+ virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+#endif
+public:
+ virtual bool can_instance() const;
+
+ virtual Ref<Script> get_base_script() const; //for script inheritance
+
+ virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
+ virtual ScriptInstance *instance_create(Object *p_this);
+ virtual bool instance_has(const Object *p_this) const;
+
+ virtual bool has_source_code() const;
+ virtual String get_source_code() const;
+ virtual void set_source_code(const String &p_code);
+ virtual Error reload(bool p_keep_state = false);
+ // TODO: load_source_code only allow utf-8 file, should handle bytecode as well ?
+ virtual Error load_source_code(const String &p_path);
+
+ virtual bool has_method(const StringName &p_method) const;
+ virtual MethodInfo get_method_info(const StringName &p_method) const;
+
+ bool has_property(const StringName &p_method) const;
+ PropertyInfo get_property_info(const StringName &p_property) const;
+
+ bool is_tool() const { return _tool; }
+
+ virtual String get_node_type() const;
+
+ virtual ScriptLanguage *get_language() const;
+
+ virtual bool has_script_signal(const StringName &p_signal) const;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+
+ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
+
+ virtual void update_exports();
+ virtual void get_script_method_list(List<MethodInfo> *r_methods) const;
+ virtual void get_script_property_list(List<PropertyInfo> *r_propertieslist) const;
+
+ virtual int get_member_line(const StringName &p_member) const;
+
+ ScriptInstance::RPCMode get_rpc_mode(const StringName &p_method) const;
+ ScriptInstance::RPCMode get_rset_mode(const StringName &p_variable) const;
+
+ PluginScript();
+ void init(PluginScriptLanguage *language);
+ virtual ~PluginScript();
+};
+
+#endif // PLUGINSCRIPT_SCRIPT_H
diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp
new file mode 100644
index 0000000000..5829d08dff
--- /dev/null
+++ b/modules/gdnative/pluginscript/register_types.cpp
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "core/project_settings.h"
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+#include "os/dir_access.h"
+#include "os/os.h"
+#include "scene/main/scene_tree.h"
+
+#include "pluginscript_language.h"
+#include "pluginscript_script.h"
+#include <pluginscript/godot_pluginscript.h>
+
+static List<PluginScriptLanguage *> pluginscript_languages;
+
+static Error _check_language_desc(const godot_pluginscript_language_desc *desc) {
+ ERR_FAIL_COND_V(!desc->name || desc->name == String(), ERR_BUG);
+ ERR_FAIL_COND_V(!desc->type || desc->type == String(), ERR_BUG);
+ ERR_FAIL_COND_V(!desc->extension || desc->extension == String(), ERR_BUG);
+ ERR_FAIL_COND_V(!desc->recognized_extensions || !desc->recognized_extensions[0], ERR_BUG);
+ ERR_FAIL_COND_V(!desc->init, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->finish, ERR_BUG);
+
+ // desc->reserved_words is not mandatory
+ // desc->comment_delimiters is not mandatory
+ // desc->string_delimiters is not mandatory
+
+ // desc->get_template_source_code is not mandatory
+ // desc->validate is not mandatory
+
+ // desc->get_template_source_code is not mandatory
+ // desc->validate is not mandatory
+ // desc->find_function is not mandatory
+ // desc->make_function is not mandatory
+ // desc->complete_code is not mandatory
+ // desc->auto_indent_code is not mandatory
+ // desc->add_global_constant is not mandatory
+ // desc->debug_get_error is not mandatory
+ // desc->debug_get_stack_level_count is not mandatory
+ // desc->debug_get_stack_level_line is not mandatory
+ // desc->debug_get_stack_level_function is not mandatory
+ // desc->debug_get_stack_level_source is not mandatory
+ // desc->debug_get_stack_level_locals is not mandatory
+ // desc->debug_get_stack_level_members is not mandatory
+ // desc->debug_get_globals is not mandatory
+ // desc->debug_parse_stack_level_expression is not mandatory
+ // desc->profiling_start is not mandatory
+ // desc->profiling_stop is not mandatory
+ // desc->profiling_get_accumulated_data is not mandatory
+ // desc->profiling_get_frame_data is not mandatory
+ // desc->frame is not mandatory
+
+ ERR_FAIL_COND_V(!desc->script_desc.init, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->script_desc.finish, ERR_BUG);
+
+ ERR_FAIL_COND_V(!desc->script_desc.instance_desc.init, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->script_desc.instance_desc.finish, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->script_desc.instance_desc.set_prop, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->script_desc.instance_desc.get_prop, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->script_desc.instance_desc.call_method, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->script_desc.instance_desc.notification, ERR_BUG);
+ // desc->script_desc.instance_desc.refcount_incremented is not mandatory
+ // desc->script_desc.instance_desc.refcount_decremented is not mandatory
+ return OK;
+}
+
+void GDAPI godot_pluginscript_register_language(const godot_pluginscript_language_desc *language_desc) {
+ Error ret = _check_language_desc(language_desc);
+ if (ret) {
+ ERR_FAIL();
+ }
+ PluginScriptLanguage *language = memnew(PluginScriptLanguage(language_desc));
+ ScriptServer::register_language(language);
+ ResourceLoader::add_resource_format_loader(language->get_resource_loader());
+ ResourceSaver::add_resource_format_saver(language->get_resource_saver());
+ pluginscript_languages.push_back(language);
+}
+
+void register_pluginscript_types() {
+ ClassDB::register_class<PluginScript>();
+}
+
+void unregister_pluginscript_types() {
+ for (List<PluginScriptLanguage *>::Element *e = pluginscript_languages.front(); e; e = e->next()) {
+ PluginScriptLanguage *language = e->get();
+ ScriptServer::unregister_language(language);
+ memdelete(language);
+ }
+}
diff --git a/modules/gdnative/pluginscript/register_types.h b/modules/gdnative/pluginscript/register_types.h
new file mode 100644
index 0000000000..70bbb16c62
--- /dev/null
+++ b/modules/gdnative/pluginscript/register_types.h
@@ -0,0 +1,31 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_pluginscript_types();
+void unregister_pluginscript_types();
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index 8e5f58524b..d2a3e29849 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -37,6 +37,7 @@
#include "nativearvr/register_types.h"
#include "nativescript/register_types.h"
+#include "pluginscript/register_types.h"
#include "core/engine.h"
#include "core/os/os.h"
@@ -158,6 +159,7 @@ void register_gdnative_types() {
register_nativearvr_types();
register_nativescript_types();
+ register_pluginscript_types();
// run singletons
@@ -207,8 +209,9 @@ void unregister_gdnative_types() {
}
singleton_gdnatives.clear();
- unregister_nativearvr_types();
+ unregister_pluginscript_types();
unregister_nativescript_types();
+ unregister_nativearvr_types();
memdelete(GDNativeCallRegistry::singleton);
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index cd01233ce4..ed8c27e5d0 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -1,8 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GridMap" inherits="Spatial" category="Core" version="3.0.alpha.custom_build">
<brief_description>
+ Node for 3D tile-based maps.
</brief_description>
<description>
+ GridMap lets you place meshes on a grid interactively. It works both from the editor and can help you create in-game level editors.
+ GridMaps use a [MeshLibrary] which contain a list of tiles: meshes with materials plus optional collisions and extra elements.
+ A GridMap contains a collection of cells. Each grid cell refers to a [MeshLibrary] item. All cells in the map have the same dimensions.
+ A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells.
</description>
<tutorials>
</tutorials>
@@ -13,6 +18,7 @@
<return type="void">
</return>
<description>
+ Clear all cells.
</description>
</method>
<method name="get_cell_item" qualifiers="const">
@@ -25,6 +31,7 @@
<argument index="2" name="z" type="int">
</argument>
<description>
+ The [MeshLibrary] item index located at the grid-based X, Y and Z coordinates. If the cell is empty, [INVALID_CELL_ITEM] will be returned.
</description>
</method>
<method name="get_cell_item_orientation" qualifiers="const">
@@ -37,54 +44,63 @@
<argument index="2" name="z" type="int">
</argument>
<description>
+ The orientation of the cell at the grid-based X, Y and Z coordinates. -1 is retuned if the cell is empty.
</description>
</method>
<method name="get_cell_size" qualifiers="const">
<return type="Vector3">
</return>
<description>
+ The dimensions of the grid's cells.
</description>
</method>
<method name="get_center_x" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether or not grid items are centered on the X axis.
</description>
</method>
<method name="get_center_y" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether or not grid items are centered on the Y axis.
</description>
</method>
<method name="get_center_z" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether or not grid items are centered on the Z axis.
</description>
</method>
<method name="get_meshes">
<return type="Array">
</return>
<description>
+ Array of [Transform] and [Mesh] references corresponding to the non empty cells in the grid. The transforms are specified in world space.
</description>
</method>
<method name="get_octant_size" qualifiers="const">
<return type="int">
</return>
<description>
+ The size of each octant measured in number of cells. This applies to all three axis.
</description>
</method>
<method name="get_theme" qualifiers="const">
<return type="MeshLibrary">
</return>
<description>
+ The assigned [MeshLibrary].
</description>
</method>
<method name="get_used_cells" qualifiers="const">
<return type="Array">
</return>
<description>
+ Array of [Vector3] with the non empty cell coordinates in the grid map.
</description>
</method>
<method name="resource_changed">
@@ -109,6 +125,9 @@
<argument index="4" name="orientation" type="int" default="0">
</argument>
<description>
+ Set the mesh index for the cell referenced by its grid-based X, Y and Z coordinates.
+ A negative item index will clear the cell.
+ Optionally, the item's orientation can be passed.
</description>
</method>
<method name="set_cell_size">
@@ -117,6 +136,7 @@
<argument index="0" name="size" type="Vector3">
</argument>
<description>
+ Sets the height, width and depth of the grid's cells.
</description>
</method>
<method name="set_center_x">
@@ -125,6 +145,7 @@
<argument index="0" name="enable" type="bool">
</argument>
<description>
+ Set grid items to be centered on the X axis. By default it is enabled.
</description>
</method>
<method name="set_center_y">
@@ -133,6 +154,7 @@
<argument index="0" name="enable" type="bool">
</argument>
<description>
+ Set grid items to be centered on the Y axis. By default it is enabled.
</description>
</method>
<method name="set_center_z">
@@ -141,6 +163,7 @@
<argument index="0" name="enable" type="bool">
</argument>
<description>
+ Set grid items to be centered on the Z axis. By default it is enabled.
</description>
</method>
<method name="set_clip">
@@ -163,6 +186,7 @@
<argument index="0" name="size" type="int">
</argument>
<description>
+ Sets the size for each octant measured in number of cells. This applies to all three axis.
</description>
</method>
<method name="set_theme">
@@ -171,11 +195,13 @@
<argument index="0" name="theme" type="MeshLibrary">
</argument>
<description>
+ Sets the collection of meshes for the map.
</description>
</method>
</methods>
<constants>
<constant name="INVALID_CELL_ITEM" value="-1" enum="">
+ Invalid cell item that can be used in [method set_cell_item] to clear cells (or represent an empty cell in [method get_cell_item]).
</constant>
</constants>
</class>
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 9de199bb5a..13b9a4b1e6 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -130,7 +130,7 @@ def configure(env):
if mono_static:
raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually')
- env.ParseConfig('pkg-config mono-2 --cflags --libs')
+ env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
env.Append(LINKFLAGS='-rdynamic')
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index b475782729..01ac4d83bb 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -41,6 +41,7 @@
#include "editor/csharp_project.h"
#include "editor/editor_node.h"
#include "editor/godotsharp_editor.h"
+#include "utils/string_utils.h"
#endif
#include "godotsharp_dirs.h"
@@ -48,7 +49,7 @@
#include "mono_gd/gd_mono_marshal.h"
#include "signal_awaiter_utils.h"
-#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->string_names.m_var)
+#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
static bool _create_project_solution_if_needed() {
@@ -295,20 +296,88 @@ bool CSharpLanguage::has_named_classes() const {
return true;
}
-String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
+static String variant_type_to_managed_name(const String &p_var_type_name) {
+
+ if (p_var_type_name.empty())
+ return "object";
+
+ if (!ClassDB::class_exists(p_var_type_name)) {
+ Variant::Type var_types[] = {
+ Variant::BOOL,
+ Variant::INT,
+ Variant::REAL,
+ Variant::STRING,
+ Variant::VECTOR2,
+ Variant::RECT2,
+ Variant::VECTOR3,
+ Variant::TRANSFORM2D,
+ Variant::PLANE,
+ Variant::QUAT,
+ Variant::RECT3,
+ Variant::BASIS,
+ Variant::TRANSFORM,
+ Variant::COLOR,
+ Variant::NODE_PATH,
+ Variant::_RID
+ };
+
+ for (int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) {
+ if (p_var_type_name == Variant::get_type_name(var_types[i]))
+ return p_var_type_name;
+ }
+
+ if (p_var_type_name == "String")
+ return "string"; // I prefer this one >:[
+
+ // TODO these will be rewritten later into custom containers
+ if (p_var_type_name == "Array")
+ return "object[]";
+
+ if (p_var_type_name == "Dictionary")
+ return "Dictionary<object, object>";
+
+ if (p_var_type_name == "PoolByteArray")
+ return "byte[]";
+ if (p_var_type_name == "PoolIntArray")
+ return "int[]";
+ if (p_var_type_name == "PoolRealArray")
+ return "float[]";
+ if (p_var_type_name == "PoolStringArray")
+ return "string[]";
+ if (p_var_type_name == "PoolVector2Array")
+ return "Vector2[]";
+ if (p_var_type_name == "PoolVector3Array")
+ return "Vector3[]";
+ if (p_var_type_name == "PoolColorArray")
+ return "Color[]";
+
+ return "object";
+ }
+
+ return p_var_type_name;
+}
+
+String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
+#ifdef TOOLS_ENABLED
// FIXME
- // Due to Godot's API limitation this just appends the function to the end of the file
- // Another limitation is that the parameter types are not specified, so we must use System.Object
+ // - Due to Godot's API limitation this just appends the function to the end of the file
+ // - Use fully qualified name if there is ambiguity
String s = "private void " + p_name + "(";
for (int i = 0; i < p_args.size(); i++) {
+ const String &arg = p_args[i];
+
if (i > 0)
s += ", ";
- s += "object " + p_args[i];
+
+ s += variant_type_to_managed_name(arg.get_slice(":", 1)) + " " + escape_csharp_keyword(arg.get_slice(":", 0));
}
s += ")\n{\n // Replace with function body\n}\n";
return s;
+#else
+ return String();
+#endif
}
void CSharpLanguage::frame() {
@@ -903,46 +972,6 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args,
} else {
return Variant();
}
- } else if (p_method == CACHED_STRING_NAME(_awaited_signal_callback)) {
- // shitty hack..
- // TODO move to its own function, thx
-
- if (p_argcount < 1) {
- r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- return Variant();
- }
-
- Ref<SignalAwaiterHandle> awaiter = *p_args[p_argcount - 1];
-
- if (awaiter.is_null()) {
- r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = p_argcount - 1;
- r_error.expected = Variant::OBJECT;
- return Variant();
- }
-
- awaiter->set_completed(true);
-
- int extra_argc = p_argcount - 1;
- MonoArray *extra_args = mono_array_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(MonoObject), extra_argc);
-
- for (int i = 0; i < extra_argc; i++) {
- MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
- mono_array_set(extra_args, MonoObject *, i, boxed);
- }
-
- GDMonoUtils::GodotObject__AwaitedSignalCallback thunk = CACHED_METHOD_THUNK(GodotObject, _AwaitedSignalCallback);
-
- MonoObject *ex = NULL;
- thunk(mono_object, &extra_args, awaiter->get_target(), &ex);
-
- if (ex) {
- mono_print_unhandled_exception(ex);
- ERR_FAIL_V(Variant());
- }
-
- return Variant();
}
top = top->get_parent_class();
@@ -1239,8 +1268,10 @@ bool CSharpScript::_update_exports() {
for (int i = 0; i < fields.size(); i++) {
GDMonoField *field = fields[i];
- if (field->is_static() || field->get_visibility() != GDMono::PUBLIC)
+ if (field->is_static()) {
+ ERR_PRINTS("Cannot export field because it is static: " + top->get_full_name() + "." + field->get_name());
continue;
+ }
String name = field->get_name();
StringName cname = name;
@@ -1248,17 +1279,39 @@ bool CSharpScript::_update_exports() {
if (member_info.has(cname))
continue;
- Variant::Type type = GDMonoMarshal::managed_to_variant_type(field->get_type());
+ ManagedType field_type = field->get_type();
+ Variant::Type type = GDMonoMarshal::managed_to_variant_type(field_type);
if (field->has_attribute(CACHED_CLASS(ExportAttribute))) {
+ // Field has Export attribute
MonoObject *attr = field->get_attribute(CACHED_CLASS(ExportAttribute));
- // Field has Export attribute
- int hint = CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr);
- String hint_string = CACHED_FIELD(ExportAttribute, hint_string)->get_string_value(attr);
- int usage = CACHED_FIELD(ExportAttribute, usage)->get_int_value(attr);
+ PropertyHint hint;
+ String hint_string;
+
+ if (type == Variant::NIL) {
+ ERR_PRINTS("Unknown type of exported field: " + top->get_full_name() + "." + field->get_name());
+ continue;
+ } else if (type == Variant::INT && field_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(field_type.type_class->get_raw())) {
+ type = Variant::INT;
+ hint = PROPERTY_HINT_ENUM;
+
+ Vector<MonoClassField *> fields = field_type.type_class->get_enum_fields();
+
+ for (int i = 0; i < fields.size(); i++) {
+ if (i > 0)
+ hint_string += ",";
+ hint_string += mono_field_get_name(fields[i]);
+ }
+ } else if (type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(field_type.type_class)) {
+ hint = PROPERTY_HINT_RESOURCE_TYPE;
+ hint_string = NATIVE_GDMONOCLASS_NAME(field_type.type_class);
+ } else {
+ hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
+ hint_string = CACHED_FIELD(ExportAttribute, hint_string)->get_string_value(attr);
+ }
- PropertyInfo prop_info = PropertyInfo(type, name, PropertyHint(hint), hint_string, PropertyUsageFlags(usage));
+ PropertyInfo prop_info = PropertyInfo(type, name, hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
member_info[cname] = prop_info;
exported_members_cache.push_back(prop_info);
@@ -1392,12 +1445,15 @@ bool CSharpScript::can_instance() const {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- if (_create_project_solution_if_needed()) {
- CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
- "Compile",
- ProjectSettings::get_singleton()->globalize_path(get_path()));
- } else {
- ERR_PRINTS("Cannot add " + get_path() + " to the C# project because it could not be created.");
+
+ if (get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted...
+ if (_create_project_solution_if_needed()) {
+ CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
+ "Compile",
+ ProjectSettings::get_singleton()->globalize_path(get_path()));
+ } else {
+ ERR_PRINTS("Cannot add " + get_path() + " to the C# project because it could not be created.");
+ }
}
}
#endif
@@ -1679,16 +1735,6 @@ void CSharpScript::update_exports() {
#ifdef TOOLS_ENABLED
_update_exports();
-
- if (placeholders.size()) {
- Map<StringName, Variant> values;
- List<PropertyInfo> propnames;
- _update_exports_values(values, propnames);
-
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
- }
- }
#endif
}
@@ -1915,7 +1961,7 @@ bool ResourceFormatSaverCSharpScript::recognize(const RES &p_resource) const {
CSharpLanguage::StringNameCache::StringNameCache() {
- _awaited_signal_callback = StaticCString::create("_AwaitedSignalCallback");
+ _signal_callback = StaticCString::create("_signal_callback");
_set = StaticCString::create("_set");
_get = StaticCString::create("_get");
_notification = StaticCString::create("_notification");
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 3fcc3bdf04..6b8475fb61 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -225,7 +225,7 @@ class CSharpLanguage : public ScriptLanguage {
struct StringNameCache {
- StringName _awaited_signal_callback;
+ StringName _signal_callback;
StringName _set;
StringName _get;
StringName _notification;
@@ -242,6 +242,8 @@ public:
_FORCE_INLINE_ int get_language_index() { return lang_idx; }
void set_language_index(int p_idx);
+ _FORCE_INLINE_ const StringNameCache &get_string_names() { return string_names; }
+
_FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; }
bool debug_break(const String &p_error, bool p_allow_continue = true);
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 704910c5b9..95e75f9103 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -108,36 +108,6 @@ const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in
bool BindingsGenerator::verbose_output = false;
-static bool is_csharp_keyword(const String &p_name) {
-
- // Reserved keywords
-
- return p_name == "abstract" || p_name == "as" || p_name == "base" || p_name == "bool" ||
- p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" ||
- p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" ||
- p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" ||
- p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" ||
- p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" ||
- p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" ||
- p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
- p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" ||
- p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" ||
- p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" ||
- p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" ||
- p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" ||
- p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" ||
- p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" ||
- p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" ||
- p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" ||
- p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" ||
- p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while";
-}
-
-inline static String escape_csharp_keyword(const String &p_name) {
-
- return is_csharp_keyword(p_name) ? "@" + p_name : p_name;
-}
-
static String snake_to_pascal_case(const String &p_identifier) {
String ret;
@@ -904,10 +874,6 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
return ERR_BUG;
}
- cs_file.push_back(MEMBER_BEGIN "private void _AwaitedSignalCallback(");
- cs_file.push_back(array_itype->get().cs_type);
- cs_file.push_back(" args, SignalAwaiter awaiter)\n" OPEN_BLOCK_L2 "awaiter.SignalCallback(args);\n" CLOSE_BLOCK_L2);
-
Map<String, TypeInterface>::Element *object_itype = obj_types.find("Object");
if (!object_itype) {
diff --git a/modules/mono/glue/cs_files/ExportAttribute.cs b/modules/mono/glue/cs_files/ExportAttribute.cs
index af3f603d6d..a4e7d447dd 100644
--- a/modules/mono/glue/cs_files/ExportAttribute.cs
+++ b/modules/mono/glue/cs_files/ExportAttribute.cs
@@ -7,13 +7,11 @@ namespace Godot
{
private int hint;
private string hint_string;
- private int usage;
- public ExportAttribute(int hint = GD.PROPERTY_HINT_NONE, string hint_string = "", int usage = GD.PROPERTY_USAGE_DEFAULT)
+ public ExportAttribute(int hint = GD.PROPERTY_HINT_NONE, string hint_string = "")
{
this.hint = hint;
this.hint_string = hint_string;
- this.usage = usage;
}
}
}
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 0a2010e99d..6bcf0e2355 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -33,6 +33,7 @@
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
+#include "os/dir_access.h"
#include "project_settings.h"
#include "version.h"
#endif
@@ -60,12 +61,20 @@ String _get_mono_user_dir() {
} else {
String settings_path;
- if (OS::get_singleton()->has_environment("APPDATA")) {
- String app_data = OS::get_singleton()->get_environment("APPDATA").replace("\\", "/");
- settings_path = app_data.plus_file(String(_MKSTR(VERSION_SHORT_NAME)).capitalize());
- } else if (OS::get_singleton()->has_environment("HOME")) {
- String home = OS::get_singleton()->get_environment("HOME");
- settings_path = home.plus_file("." + String(_MKSTR(VERSION_SHORT_NAME)).to_lower());
+ String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
+ DirAccessRef d = DirAccess::create_for_path(exe_dir);
+
+ if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
+ // contain yourself
+ settings_path = exe_dir.plus_file("editor_data");
+ } else {
+ if (OS::get_singleton()->has_environment("APPDATA")) {
+ String app_data = OS::get_singleton()->get_environment("APPDATA").replace("\\", "/");
+ settings_path = app_data.plus_file(String(_MKSTR(VERSION_SHORT_NAME)).capitalize());
+ } else if (OS::get_singleton()->has_environment("HOME")) {
+ String home = OS::get_singleton()->get_environment("HOME");
+ settings_path = home.plus_file("." + String(_MKSTR(VERSION_SHORT_NAME)).to_lower());
+ }
}
return settings_path.plus_file("mono");
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 0134ace5d7..77ba0ee90e 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -43,6 +43,14 @@ bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
return mono_class_is_assignable_from(mono_class, p_from->mono_class);
}
+String GDMonoClass::get_full_name() const {
+
+ String res = namespace_name;
+ if (res.length())
+ res += ".";
+ return res + class_name;
+}
+
GDMonoClass *GDMonoClass::get_parent_class() {
if (assembly) {
@@ -56,6 +64,30 @@ GDMonoClass *GDMonoClass::get_parent_class() {
return NULL;
}
+#ifdef TOOLS_ENABLED
+Vector<MonoClassField *> GDMonoClass::get_enum_fields() {
+
+ bool class_is_enum = mono_class_is_enum(mono_class);
+ ERR_FAIL_COND_V(!class_is_enum, Vector<MonoClassField *>());
+
+ Vector<MonoClassField *> enum_fields;
+
+ void *iter = NULL;
+ MonoClassField *raw_field = NULL;
+ while ((raw_field = mono_class_get_fields(get_raw(), &iter)) != NULL) {
+ uint32_t field_flags = mono_field_get_flags(raw_field);
+
+ // Enums have an instance field named value__ which holds the value of the enum.
+ // Enum constants are static, so we will use this to ignore the value__ field.
+ if (field_flags & MONO_FIELD_ATTR_PUBLIC && field_flags & MONO_FIELD_ATTR_STATIC) {
+ enum_fields.push_back(raw_field);
+ }
+ }
+
+ return enum_fields;
+}
+#endif
+
bool GDMonoClass::has_method(const StringName &p_name) {
return get_method(p_name) != NULL;
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 1e72553879..ef1ca425a7 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -98,8 +98,14 @@ public:
_FORCE_INLINE_ MonoClass *get_raw() const { return mono_class; }
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
+ String get_full_name() const;
+
GDMonoClass *get_parent_class();
+#ifdef TOOLS_ENABLED
+ Vector<MonoClassField *> get_enum_fields();
+#endif
+
bool has_method(const StringName &p_name);
bool has_attribute(GDMonoClass *p_attr_class);
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index c2d8eeaa32..81315ee87a 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -51,6 +51,7 @@ void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) {
{ \
m_type val = p_value.operator m_type(); \
mono_field_set_value(p_object, mono_field, &val); \
+ break; \
}
#define SET_FROM_ARRAY_AND_BREAK(m_type) \
@@ -137,6 +138,9 @@ void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) {
if (tclass == CACHED_CLASS(Plane))
SET_FROM_STRUCT_AND_BREAK(Plane);
+ if (mono_class_is_enum(tclass->get_raw()))
+ SET_FROM_PRIMITIVE(signed int);
+
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + tclass->get_name());
ERR_FAIL();
} break;
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 9a6c8f0cd6..77a1ef3cb0 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -112,6 +112,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (tclass == CACHED_CLASS(Plane))
return Variant::PLANE;
+
+ if (mono_class_is_enum(tclass->get_raw()))
+ return Variant::INT;
} break;
case MONO_TYPE_ARRAY:
@@ -165,9 +168,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
return Variant::DICTIONARY;
}
} break;
+
+ default: {
+ } break;
}
- // No error, the caller will decide what to do in this case
+ // Unknown
return Variant::NIL;
}
@@ -299,6 +305,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
if (tclass == CACHED_CLASS(Plane))
RETURN_BOXED_STRUCT(Plane, p_var);
+
+ if (mono_class_is_enum(tclass->get_raw())) {
+ int val = p_var->operator signed int();
+ return BOX_ENUM(tclass->get_raw(), val);
+ }
} break;
case MONO_TYPE_ARRAY:
@@ -515,6 +526,9 @@ Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) {
if (tclass == CACHED_CLASS(Plane))
RETURN_UNBOXED_STRUCT(Plane, p_obj);
+
+ if (mono_class_is_enum(tclass->get_raw()))
+ return unbox<int32_t>(p_obj);
} break;
case MONO_TYPE_ARRAY:
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 38dd22357d..9f403b787f 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -53,6 +53,7 @@ T unbox(MonoObject *p_obj) {
#define BOX_UINT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), &x)
#define BOX_BOOLEAN(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(bool), &x)
#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
+#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
Variant::Type managed_to_variant_type(const ManagedType &p_type);
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 5deca8e64d..53e45002c4 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -86,6 +86,7 @@ void MonoCache::clear_members() {
class_NodePath = NULL;
class_RID = NULL;
class_GodotObject = NULL;
+ class_GodotReference = NULL;
class_Node = NULL;
class_Control = NULL;
class_Spatial = NULL;
@@ -95,7 +96,6 @@ void MonoCache::clear_members() {
class_ExportAttribute = NULL;
field_ExportAttribute_hint = NULL;
field_ExportAttribute_hint_string = NULL;
- field_ExportAttribute_usage = NULL;
class_ToolAttribute = NULL;
class_RemoteAttribute = NULL;
class_SyncAttribute = NULL;
@@ -111,7 +111,7 @@ void MonoCache::clear_members() {
methodthunk_MarshalUtils_DictionaryToArrays = NULL;
methodthunk_MarshalUtils_ArraysToDictionary = NULL;
- methodthunk_GodotObject__AwaitedSignalCallback = NULL;
+ methodthunk_SignalAwaiter_SignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
@@ -153,6 +153,7 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(NodePath));
CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
+ CACHE_CLASS_AND_CHECK(GodotReference, GODOT_API_CLASS(Reference));
CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
@@ -163,7 +164,6 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint_string, CACHED_CLASS(ExportAttribute)->get_field("hint_string"));
- CACHE_FIELD_AND_CHECK(ExportAttribute, usage, CACHED_CLASS(ExportAttribute)->get_field("usage"));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute));
@@ -178,7 +178,7 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk());
- CACHE_METHOD_THUNK_AND_CHECK(GodotObject, _AwaitedSignalCallback, (GodotObject__AwaitedSignalCallback)CACHED_CLASS(GodotObject)->get_method("_AwaitedSignalCallback", 2)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index f97f048aa9..e3af57e78a 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -42,7 +42,7 @@ namespace GDMonoUtils {
typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **);
typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **);
-typedef MonoObject *(*GodotObject__AwaitedSignalCallback)(MonoObject *, MonoArray **, MonoObject *, MonoObject **);
+typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray **, MonoObject **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
@@ -88,6 +88,7 @@ struct MonoCache {
GDMonoClass *class_NodePath;
GDMonoClass *class_RID;
GDMonoClass *class_GodotObject;
+ GDMonoClass *class_GodotReference;
GDMonoClass *class_Node;
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
@@ -97,7 +98,6 @@ struct MonoCache {
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hint_string;
- GDMonoField *field_ExportAttribute_usage;
GDMonoClass *class_ToolAttribute;
GDMonoClass *class_RemoteAttribute;
GDMonoClass *class_SyncAttribute;
@@ -113,7 +113,7 @@ struct MonoCache {
MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays;
MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary;
- GodotObject__AwaitedSignalCallback methodthunk_GodotObject__AwaitedSignalCallback;
+ SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
@@ -164,7 +164,7 @@ String get_exception_name_and_message(MonoObject *p_ex);
} // GDMonoUtils
-#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field("nativeName")->get_value(NULL)))
+#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL)))
#define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class)
#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_raw())
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 012dd119b1..99bcc72b41 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -29,6 +29,9 @@
/*************************************************************************/
#include "signal_awaiter_utils.h"
+#include "csharp_script.h"
+#include "mono_gd/gd_mono_class.h"
+#include "mono_gd/gd_mono_marshal.h"
#include "mono_gd/gd_mono_utils.h"
namespace SignalAwaiterUtils {
@@ -40,13 +43,20 @@ Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p
uint32_t awaiter_handle = MonoGCHandle::make_strong_handle(p_awaiter);
Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(awaiter_handle));
+#ifdef DEBUG_ENABLED
+ sa_con->set_connection_target(p_target);
+#endif
+
Vector<Variant> binds;
binds.push_back(sa_con);
- Error err = p_source->connect(p_signal, p_target, "_AwaitedSignalCallback", binds, Object::CONNECT_ONESHOT);
+
+ Error err = p_source->connect(p_signal, sa_con.ptr(),
+ CSharpLanguage::get_singleton()->get_string_names()._signal_callback,
+ binds, Object::CONNECT_ONESHOT);
if (err != OK) {
- // set it as completed to prevent it from calling the failure callback when deleted
- // the awaiter will be aware of the failure by checking the returned error
+ // Set it as completed to prevent it from calling the failure callback when released.
+ // The awaiter will be aware of the failure by checking the returned error.
sa_con->set_completed(true);
}
@@ -54,11 +64,66 @@ Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p
}
}
-SignalAwaiterHandle::SignalAwaiterHandle(uint32_t p_handle)
- : MonoGCHandle(p_handle) {
+Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+
+#ifdef DEBUG_ENABLED
+ if (conn_target_id && !ObjectDB::get_instance(conn_target_id)) {
+ ERR_EXPLAIN("Resumed after await, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
+
+ if (p_argcount < 1) {
+ r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ return Variant();
+ }
+
+ Ref<SignalAwaiterHandle> self = *p_args[p_argcount - 1];
+
+ if (self.is_null()) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = p_argcount - 1;
+ r_error.expected = Variant::OBJECT;
+ return Variant();
+ }
+
+ set_completed(true);
+
+ int signal_argc = p_argcount - 1;
+ MonoArray *signal_args = mono_array_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(MonoObject), signal_argc);
+
+ for (int i = 0; i < signal_argc; i++) {
+ MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
+ mono_array_set(signal_args, MonoObject *, i, boxed);
+ }
+
+ GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback);
+
+ MonoObject *ex = NULL;
+ thunk(get_target(), &signal_args, &ex);
+
+ if (ex) {
+ mono_print_unhandled_exception(ex);
+ ERR_FAIL_V(Variant());
+ }
+
+ return Variant();
+}
+
+void SignalAwaiterHandle::_bind_methods() {
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback"));
+}
+
+SignalAwaiterHandle::SignalAwaiterHandle(uint32_t p_managed_handle)
+ : MonoGCHandle(p_managed_handle) {
+
+ conn_target_id = 0;
}
SignalAwaiterHandle::~SignalAwaiterHandle() {
+
if (!completed) {
GDMonoUtils::SignalAwaiter_FailureCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback);
diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h
index 422ed4754f..0d615b5826 100644
--- a/modules/mono/signal_awaiter_utils.h
+++ b/modules/mono/signal_awaiter_utils.h
@@ -40,13 +40,30 @@ Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p
class SignalAwaiterHandle : public MonoGCHandle {
+ GDCLASS(SignalAwaiterHandle, MonoGCHandle)
+
bool completed;
+#ifdef DEBUG_ENABLED
+ ObjectID conn_target_id;
+#endif
+
+ Variant _signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
+
+protected:
+ static void _bind_methods();
+
public:
_FORCE_INLINE_ bool is_completed() { return completed; }
_FORCE_INLINE_ void set_completed(bool p_completed) { completed = p_completed; }
- SignalAwaiterHandle(uint32_t p_handle);
+#ifdef DEBUG_ENABLED
+ _FORCE_INLINE_ void set_connection_target(Object *p_target) {
+ conn_target_id = p_target->get_instance_id();
+ }
+#endif
+
+ SignalAwaiterHandle(uint32_t p_managed_handle);
~SignalAwaiterHandle();
};
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index de1a60dbd1..f26663ea11 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -126,3 +126,32 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const
return new_string;
}
+
+bool is_csharp_keyword(const String &p_name) {
+
+ // Reserved keywords
+
+ return p_name == "abstract" || p_name == "as" || p_name == "base" || p_name == "bool" ||
+ p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" ||
+ p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" ||
+ p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" ||
+ p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" ||
+ p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" ||
+ p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" ||
+ p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
+ p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" ||
+ p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" ||
+ p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" ||
+ p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" ||
+ p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" ||
+ p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" ||
+ p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" ||
+ p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" ||
+ p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" ||
+ p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" ||
+ p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while";
+}
+
+String escape_csharp_keyword(const String &p_name) {
+ return is_csharp_keyword(p_name) ? "@" + p_name : p_name;
+}
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index 2f2c3c2d89..a0d66ebdc3 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -35,4 +35,10 @@
String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant());
+#ifdef TOOLS_ENABLED
+bool is_csharp_keyword(const String &p_name);
+
+String escape_csharp_keyword(const String &p_name);
+#endif
+
#endif // STRING_FORMAT_H
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 972be5f5a4..1980f86114 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -65,6 +65,8 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"decimals",
"stepify",
"lerp",
+ "inverse_lerp",
+ "range_lerp",
"dectime",
"randomize",
"randi",
@@ -194,9 +196,12 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case COLORN:
return 2;
case MATH_LERP:
+ case MATH_INVERSE_LERP:
case MATH_DECTIME:
case LOGIC_CLAMP:
return 3;
+ case MATH_RANGE_LERP:
+ return 5;
case FUNC_MAX: {
}
}
@@ -297,7 +302,26 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
return PropertyInfo(Variant::REAL, "to");
else
return PropertyInfo(Variant::REAL, "weight");
-
+ } break;
+ case MATH_INVERSE_LERP: {
+ if (p_idx == 0)
+ return PropertyInfo(Variant::REAL, "from");
+ else if (p_idx == 1)
+ return PropertyInfo(Variant::REAL, "to");
+ else
+ return PropertyInfo(Variant::REAL, "value");
+ } break;
+ case MATH_RANGE_LERP: {
+ if (p_idx == 0)
+ return PropertyInfo(Variant::REAL, "value");
+ else if (p_idx == 1)
+ return PropertyInfo(Variant::REAL, "istart");
+ else if (p_idx == 2)
+ return PropertyInfo(Variant::REAL, "istop");
+ else if (p_idx == 3)
+ return PropertyInfo(Variant::REAL, "ostart");
+ else
+ return PropertyInfo(Variant::REAL, "ostop");
} break;
case MATH_DECTIME: {
if (p_idx == 0)
@@ -495,6 +519,8 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
} break;
case MATH_STEPIFY:
case MATH_LERP:
+ case MATH_INVERSE_LERP:
+ case MATH_RANGE_LERP:
case MATH_DECTIME: {
t = Variant::REAL;
@@ -795,6 +821,22 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
VALIDATE_ARG_NUM(2);
*r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
} break;
+ case VisualScriptBuiltinFunc::MATH_INVERSE_LERP: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANGE_LERP: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ VALIDATE_ARG_NUM(3);
+ VALIDATE_ARG_NUM(4);
+ *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]);
+ } break;
case VisualScriptBuiltinFunc::MATH_DECTIME: {
VALIDATE_ARG_NUM(0);
@@ -1203,6 +1245,8 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(MATH_DECIMALS);
BIND_ENUM_CONSTANT(MATH_STEPIFY);
BIND_ENUM_CONSTANT(MATH_LERP);
+ BIND_ENUM_CONSTANT(MATH_INVERSE_LERP);
+ BIND_ENUM_CONSTANT(MATH_RANGE_LERP);
BIND_ENUM_CONSTANT(MATH_DECTIME);
BIND_ENUM_CONSTANT(MATH_RANDOMIZE);
BIND_ENUM_CONSTANT(MATH_RAND);
@@ -1282,6 +1326,8 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/decimals", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECIMALS>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/stepify", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEPIFY>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index 97ab307039..af24f16a2f 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -64,6 +64,8 @@ public:
MATH_DECIMALS,
MATH_STEPIFY,
MATH_LERP,
+ MATH_INVERSE_LERP,
+ MATH_RANGE_LERP,
MATH_DECTIME,
MATH_RANDOMIZE,
MATH_RAND,
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 47ef0182dc..03015df844 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -809,7 +809,7 @@ void VisualScriptEditor::_update_members() {
ti->set_text(0, E->get());
Variant var = script->get_variable_default_value(E->get());
- ti->set_suffix(0, "=" + String(var));
+ ti->set_suffix(0, "= " + String(var));
ti->set_icon(0, type_icons[script->get_variable_info(E->get()).type]);
ti->set_selectable(0, true);
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index c91781ce1d..0507ef19d6 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -397,7 +397,7 @@ Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) {
codesign_args.push_back("-s");
codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release"));
codesign_args.push_back(p_file);
- return OS::get_singleton()->execute("/usr/bin/codesign", codesign_args, true);
+ return OS::get_singleton()->execute("codesign", codesign_args, true);
}
return OK;
}
@@ -592,7 +592,15 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
return err;
#ifdef OSX_ENABLED
- ep.step("Making .xcarchive", 2);
+ ep.step("Code-signing dylibs", 2);
+ DirAccess *dylibs_dir = DirAccess::open(dest_dir + "dylibs");
+ ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN);
+ CodesignData codesign_data(p_preset, p_debug);
+ err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
+ memdelete(dylibs_dir);
+ ERR_FAIL_COND_V(err, err);
+
+ ep.step("Making .xcarchive", 3);
String archive_path = p_path.get_basename() + ".xcarchive";
List<String> archive_args;
archive_args.push_back("-project");
@@ -608,15 +616,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
archive_args.push_back("archive");
archive_args.push_back("-archivePath");
archive_args.push_back(archive_path);
- err = OS::get_singleton()->execute("/usr/bin/xcodebuild", archive_args, true);
- ERR_FAIL_COND_V(err, err);
-
- ep.step("Code-signing dylibs", 3);
- DirAccess *dylibs_dir = DirAccess::open(archive_path + "/Products/Applications/" + binary_name + ".app/dylibs");
- ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN);
- CodesignData codesign_data(p_preset, p_debug);
- err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
- memdelete(dylibs_dir);
+ err = OS::get_singleton()->execute("xcodebuild", archive_args, true);
ERR_FAIL_COND_V(err, err);
ep.step("Making .ipa", 4);
@@ -628,7 +628,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
export_args.push_back(dest_dir + "export_options.plist");
export_args.push_back("-exportPath");
export_args.push_back(dest_dir);
- err = OS::get_singleton()->execute("/usr/bin/xcodebuild", export_args, true);
+ err = OS::get_singleton()->execute("xcodebuild", export_args, true);
ERR_FAIL_COND_V(err, err);
#else
print_line(".ipa can only be built on macOS. Leaving XCode project without building the package.");
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 0ba0ddec7d..8a6f1dc04c 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -244,7 +244,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
args.push_back(p_path);
String str;
- Error err = OS::get_singleton()->execute("/usr/bin/codesign", args, true, NULL, &str, NULL, true);
+ Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true);
ERR_FAIL_COND_V(err != OK, err);
print_line("codesign: " + str);
@@ -271,7 +271,7 @@ Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const Strin
args.push_back(p_app_path_name);
String str;
- Error err = OS::get_singleton()->execute("/usr/bin/hdiutil", args, true, NULL, &str, NULL, true);
+ Error err = OS::get_singleton()->execute("hdiutil", args, true, NULL, &str, NULL, true);
ERR_FAIL_COND_V(err != OK, err);
print_line("hdiutil returned: " + str);
diff --git a/platform/server/detect.py b/platform/server/detect.py
index 04b38f280d..ffec2af933 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -12,6 +12,9 @@ def get_name():
def can_build():
+ # Doesn't build against Godot 3.0 for now, disable to avoid confusing users
+ return False
+
if (os.name != "posix" or sys.platform == "darwin"):
return False
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index e389c6932e..bc18d0c1f0 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -1939,7 +1939,7 @@ Error OS_X11::shell_open(String p_uri) {
Error ok;
List<String> args;
args.push_back(p_uri);
- ok = execute("/usr/bin/xdg-open", args, false);
+ ok = execute("xdg-open", args, false);
if (ok == OK)
return OK;
ok = execute("gnome-open", args, false);
@@ -2003,7 +2003,7 @@ String OS_X11::get_system_dir(SystemDir p_dir) const {
String pipe;
List<String> arg;
arg.push_back(xdgparam);
- Error err = const_cast<OS_X11 *>(this)->execute("/usr/bin/xdg-user-dir", arg, true, NULL, &pipe);
+ Error err = const_cast<OS_X11 *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe);
if (err != OK)
return ".";
return pipe.strip_edges();
@@ -2053,7 +2053,7 @@ void OS_X11::alert(const String &p_alert, const String &p_title) {
args.push_back(p_title);
args.push_back(p_alert);
- execute("/usr/bin/xmessage", args, true);
+ execute("xmessage", args, true);
}
void OS_X11::set_icon(const Ref<Image> &p_icon) {
@@ -2236,12 +2236,12 @@ Error OS_X11::move_to_trash(const String &p_path) {
List<String> args;
args.push_back("-p");
args.push_back(trashcan);
- Error err = execute("/bin/mkdir", args, true);
+ Error err = execute("mkdir", args, true);
if (err == OK) {
List<String> args2;
args2.push_back(p_path);
args2.push_back(trashcan);
- err = execute("/bin/mv", args2, true);
+ err = execute("mv", args2, true);
}
return err;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 05963acf56..c4cfce5d72 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -529,12 +529,12 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, f
if (&cd == &playback.current) {
- if (!backwards && cd.pos < len && next_pos == len /*&& playback.blend.empty()*/) {
+ if (!backwards && cd.pos <= len && next_pos == len /*&& playback.blend.empty()*/) {
//playback finished
end_notify = true;
}
- if (backwards && cd.pos > 0 && next_pos == 0 /*&& playback.blend.empty()*/) {
+ if (backwards && cd.pos >= 0 && next_pos == 0 /*&& playback.blend.empty()*/) {
//playback finished
end_notify = true;
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index c2ce2a633e..07b49538d9 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -354,7 +354,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
cw = font->get_char_size(c[i], c[i + 1]).x;
draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
if (visible)
- font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - (fh - ascent)), c[i], c[i + 1], selection_fg);
+ font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - (fh - ascent)), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
} else {
if (visible)
@@ -1376,6 +1376,16 @@ bool RichTextLabel::is_meta_underlined() const {
return underline_meta;
}
+void RichTextLabel::set_override_selected_font_color(bool p_override_selected_font_color) {
+
+ override_selected_font_color = p_override_selected_font_color;
+}
+
+bool RichTextLabel::is_overriding_selected_font_color() const {
+
+ return override_selected_font_color;
+}
+
void RichTextLabel::set_offset(int p_pixel) {
vscroll->set_value(p_pixel);
@@ -1906,6 +1916,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline);
ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined);
+ ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &RichTextLabel::set_override_selected_font_color);
+ ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &RichTextLabel::is_overriding_selected_font_color);
+
ClassDB::bind_method(D_METHOD("set_scroll_active", "active"), &RichTextLabel::set_scroll_active);
ClassDB::bind_method(D_METHOD("is_scroll_active"), &RichTextLabel::is_scroll_active);
@@ -1948,6 +1961,7 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
@@ -2003,6 +2017,7 @@ RichTextLabel::RichTextLabel() {
tab_size = 4;
default_align = ALIGN_LEFT;
underline_meta = true;
+ override_selected_font_color = false;
scroll_visible = false;
scroll_follow = false;
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 24c1e5eb59..f9e37b1094 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -221,6 +221,7 @@ private:
int tab_size;
bool underline_meta;
+ bool override_selected_font_color;
Align default_align;
@@ -313,6 +314,9 @@ public:
void set_meta_underline(bool p_underline);
bool is_meta_underlined() const;
+ void set_override_selected_font_color(bool p_override_selected_font_color);
+ bool is_overriding_selected_font_color() const;
+
void set_scroll_active(bool p_active);
bool is_scroll_active() const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 33c29547be..de69406b37 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -732,10 +732,6 @@ void TextEdit::_notification(int p_what) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color);
}
- if (line == cursor.line) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
- }
-
if (text.is_breakpoint(line) && !draw_breakpoint_gutter) {
#ifdef TOOLS_ENABLED
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
@@ -764,6 +760,7 @@ void TextEdit::_notification(int p_what) {
cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
}
+ //loop through charcters in one line
for (int j = 0; j < str.length(); j++) {
//look for keyword
@@ -952,10 +949,22 @@ void TextEdit::_notification(int p_what) {
}
}
+ //current line highlighting
bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || j >= selection.from_column) && (line < selection.to_line || j < selection.to_column));
+ if (line == cursor.line) {
+ if (j == 0)
+ //first char
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color);
+ else if (j == str.length() - 1)
+ //last char
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
+
+ if (!in_selection)
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color);
+ }
+
if (in_selection) {
- //inside selection!
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color);
}
@@ -998,7 +1007,7 @@ void TextEdit::_notification(int p_what) {
if (brace_open_mismatch)
color = cache.brace_mismatch_color;
- cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection ? cache.font_selected_color : color);
+ cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
if (
@@ -1007,7 +1016,7 @@ void TextEdit::_notification(int p_what) {
if (brace_close_mismatch)
color = cache.brace_mismatch_color;
- cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection ? cache.font_selected_color : color);
+ cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
}
@@ -1066,15 +1075,15 @@ void TextEdit::_notification(int p_what) {
}
if (str[j] >= 32) {
- int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), str[j], str[j + 1], in_selection ? cache.font_selected_color : color);
+ int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
if (underlined) {
- draw_rect(Rect2(char_ofs + char_margin, ofs_y + ascent + 2, w, 1), in_selection ? cache.font_selected_color : color);
+ draw_rect(Rect2(char_ofs + char_margin, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
}
else if (draw_tabs && str[j] == '\t') {
int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
- cache.tab_icon->draw(ci, Point2(char_ofs + char_margin, ofs_y + yofs), in_selection ? cache.font_selected_color : color);
+ cache.tab_icon->draw(ci, Point2(char_ofs + char_margin, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
char_ofs += char_w;
@@ -4256,6 +4265,13 @@ bool TextEdit::is_drawing_tabs() const {
return draw_tabs;
}
+void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) {
+ override_selected_font_color = p_override_selected_font_color;
+}
+bool TextEdit::is_overriding_selected_font_color() const {
+ return override_selected_font_color;
+}
+
void TextEdit::set_insert_mode(bool p_enabled) {
insert_mode = p_enabled;
update();
@@ -4821,6 +4837,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences);
ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled);
+ ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color);
+ ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color);
+
ClassDB::bind_method(D_METHOD("set_syntax_coloring", "enable"), &TextEdit::set_syntax_coloring);
ClassDB::bind_method(D_METHOD("is_syntax_coloring_enabled"), &TextEdit::is_syntax_coloring_enabled);
@@ -4838,6 +4857,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed");
@@ -4868,6 +4888,7 @@ TextEdit::TextEdit() {
readonly = false;
setting_row = false;
draw_tabs = false;
+ override_selected_font_color = false;
draw_caret = true;
max_chars = 0;
clear();
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 7e61c4e8b1..03f412729d 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -238,6 +238,7 @@ class TextEdit : public Control {
bool setting_row;
bool wrap;
bool draw_tabs;
+ bool override_selected_font_color;
bool cursor_changed_dirty;
bool text_changed_dirty;
bool undo_enabled;
@@ -482,6 +483,8 @@ public:
void set_indent_size(const int p_size);
void set_draw_tabs(bool p_draw);
bool is_drawing_tabs() const;
+ void set_override_selected_font_color(bool p_override_selected_font_color);
+ bool is_overriding_selected_font_color() const;
void set_insert_mode(bool p_enabled);
bool is_insert_mode() const;
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index b2a11deea1..8fee6050a0 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -2586,6 +2586,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_BRACKET_OPEN) {
Node *index = _parse_and_reduce_expression(p_block, p_builtin_types);
+ if (!index)
+ return NULL;
if (index->get_datatype() != TYPE_INT && index->get_datatype() != TYPE_UINT) {
_set_error("Only integer datatypes are allowed for indexing");