summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/math/math_funcs.cpp9
-rw-r--r--core/math/math_funcs.h1
-rw-r--r--core/object.h6
-rw-r--r--core/os/dir_access.cpp8
-rw-r--r--core/os/dir_access.h1
-rw-r--r--doc/classes/KinematicBody2D.xml1
-rw-r--r--doc/classes/PointMesh.xml17
-rw-r--r--doc/classes/VehicleWheel.xml16
-rwxr-xr-xdoc/tools/makerst.py1
-rw-r--r--editor/animation_track_editor.cpp865
-rw-r--r--editor/animation_track_editor.h2
-rw-r--r--editor/doc/doc_data.cpp14
-rw-r--r--editor/editor_file_dialog.cpp19
-rw-r--r--editor/editor_file_system.cpp10
-rw-r--r--editor/editor_inspector.cpp2
-rw-r--r--editor/editor_node.cpp9
-rw-r--r--editor/editor_properties.cpp22
-rw-r--r--editor/editor_settings.cpp4
-rw-r--r--editor/editor_spin_slider.cpp4
-rw-r--r--editor/export_template_manager.cpp20
-rw-r--r--editor/icons/icon_point_mesh.svg7
-rw-r--r--editor/plugins/script_editor_plugin.cpp22
-rw-r--r--editor/plugins/script_text_editor.cpp9
-rw-r--r--editor/plugins/script_text_editor.h1
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp92
-rw-r--r--editor/plugins/spatial_editor_plugin.h3
-rw-r--r--editor/plugins/text_editor.cpp9
-rw-r--r--editor/plugins/text_editor.h1
-rw-r--r--editor/project_manager.cpp2
-rw-r--r--editor/script_editor_debugger.cpp5
-rw-r--r--main/input_default.cpp3
-rw-r--r--methods.py6
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h6
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml2
-rw-r--r--modules/mono/csharp_script.cpp1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs38
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs17
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs11
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs65
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs22
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs5
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp38
-rw-r--r--modules/mono/mono_gd/gd_mono.h13
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp15
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp17
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h2
-rw-r--r--platform/osx/os_osx.mm1
-rw-r--r--platform/windows/os_windows.cpp1
-rw-r--r--platform/x11/os_x11.cpp1
-rw-r--r--scene/2d/animated_sprite.cpp8
-rw-r--r--scene/2d/sprite.cpp7
-rw-r--r--scene/3d/collision_object.cpp2
-rw-r--r--scene/3d/sprite_3d.cpp16
-rw-r--r--scene/3d/vehicle_body.cpp68
-rw-r--r--scene/3d/vehicle_body.h11
-rw-r--r--scene/gui/control.cpp55
-rw-r--r--scene/gui/file_dialog.cpp9
-rw-r--r--scene/gui/spin_box.cpp2
-rw-r--r--scene/gui/tree.cpp6
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/primitive_meshes.cpp16
-rw-r--r--scene/resources/primitive_meshes.h15
-rw-r--r--scene/resources/theme.cpp30
-rw-r--r--scene/resources/theme.h6
67 files changed, 1368 insertions, 338 deletions
diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp
index 7a2e74a413..f04e40cb6c 100644
--- a/core/math/math_funcs.cpp
+++ b/core/math/math_funcs.cpp
@@ -79,6 +79,15 @@ int Math::step_decimals(double p_step) {
return 0;
}
+// Only meant for editor usage in float ranges, where a step of 0
+// means that decimal digits should not be limited in String::num.
+int Math::range_step_decimals(double p_step) {
+ if (p_step < 0.0000000000001) {
+ return 16; // Max value hardcoded in String::num
+ }
+ return step_decimals(p_step);
+}
+
double Math::dectime(double p_value, double p_amount, double p_step) {
double sgn = p_value < 0 ? -1.0 : 1.0;
double val = Math::abs(p_value);
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index b8b5151802..a712356ddc 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -270,6 +270,7 @@ public:
// double only, as these functions are mainly used by the editor and not performance-critical,
static double ease(double p_x, double p_c);
static int step_decimals(double p_step);
+ static int range_step_decimals(double p_step);
static double stepify(double p_value, double p_step);
static double dectime(double p_value, double p_amount, double p_step);
diff --git a/core/object.h b/core/object.h
index e6c5b7c5b9..dce1cc74ae 100644
--- a/core/object.h
+++ b/core/object.h
@@ -58,7 +58,7 @@ enum PropertyHint {
PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
- PROPERTY_HINT_SPRITE_FRAME,
+ PROPERTY_HINT_SPRITE_FRAME, // FIXME: Obsolete: drop whenever we can break compat. Keeping now for GDNative compat.
PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
PROPERTY_HINT_LAYERS_2D_RENDER,
@@ -104,7 +104,8 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings
PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
PROPERTY_USAGE_CATEGORY = 256,
- //those below are deprecated thanks to ClassDB's now class value cache
+ // FIXME: Drop in 4.0, possibly reorder other flags?
+ // Those below are deprecated thanks to ClassDB's now class value cache
//PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero
//PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false
PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
@@ -121,6 +122,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23,
PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24,
+ PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp
index 0cdb5b41b7..b444f0ae1e 100644
--- a/core/os/dir_access.cpp
+++ b/core/os/dir_access.cpp
@@ -179,14 +179,6 @@ Error DirAccess::make_dir_recursive(String p_dir) {
return OK;
}
-String DirAccess::get_next(bool *p_is_dir) {
-
- String next = get_next();
- if (p_is_dir)
- *p_is_dir = current_is_dir();
- return next;
-}
-
String DirAccess::fix_path(String p_path) const {
switch (_access_type) {
diff --git a/core/os/dir_access.h b/core/os/dir_access.h
index bde19bd5ae..704eedae5b 100644
--- a/core/os/dir_access.h
+++ b/core/os/dir_access.h
@@ -71,7 +71,6 @@ protected:
public:
virtual Error list_dir_begin() = 0; ///< This starts dir listing
- virtual String get_next(bool *p_is_dir); // compatibility
virtual String get_next() = 0;
virtual bool current_is_dir() const = 0;
virtual bool current_is_hidden() const = 0;
diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml
index 39d84c6e31..22db9e3960 100644
--- a/doc/classes/KinematicBody2D.xml
+++ b/doc/classes/KinematicBody2D.xml
@@ -76,6 +76,7 @@
</argument>
<description>
Moves the body along the vector [code]rel_vec[/code]. The body will stop if it collides. Returns a [KinematicCollision2D], which contains information about the collision.
+ If [code]test_only[/code] is [code]true[/code], the body does not move but the would-be collision information is given.
</description>
</method>
<method name="move_and_slide">
diff --git a/doc/classes/PointMesh.xml b/doc/classes/PointMesh.xml
new file mode 100644
index 0000000000..dc7dd065cf
--- /dev/null
+++ b/doc/classes/PointMesh.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="PointMesh" inherits="PrimitiveMesh" category="Core" version="3.2">
+ <brief_description>
+ Mesh with a single Point primitive.
+ </brief_description>
+ <description>
+ The PointMesh is made from a single point. Instead of relying on triangles, points are rendered as a single rectangle on the screen with a constant size. They are intended to be used with Particle systems, but can be used as a cheap way to render constant size billboarded sprites (for example in a point cloud).
+ PointMeshes, must be used with a material that has a point size. Point size can be accessed in a shader with [code]POINT_SIZE[/code], or in a [SpatialMaterial] by setting [member SpatialMaterial.flags_use_point_size] and the variable [member SpatialMaterial.params_point_size].
+ When using PointMeshes, properties that normally alter vertices will be ignored, including billboard mode, grow, and cull face.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml
index 6de6429531..ff6004bcba 100644
--- a/doc/classes/VehicleWheel.xml
+++ b/doc/classes/VehicleWheel.xml
@@ -13,6 +13,7 @@
<return type="float">
</return>
<description>
+ Returns the rotational speed of the wheel in revolutions per minute.
</description>
</method>
<method name="get_skidinfo" qualifiers="const">
@@ -31,12 +32,23 @@
</method>
</methods>
<members>
+ <member name="brake" type="float" setter="set_brake" getter="get_brake" default="0.0">
+ Slows down the wheel by applying a braking force. The wheel is only slowed down if it is in contact with a surface. The force you need to apply to adequately slow down your vehicle depends on the [member RigidBody.mass] of the vehicle. For a vehicle with a mass set to 1000, try a value in the 25 - 30 range for hard braking.
+ </member>
<member name="damping_compression" type="float" setter="set_damping_compression" getter="get_damping_compression" default="0.83">
The damping applied to the spring when the spring is being compressed. This value should be between 0.0 (no damping) and 1.0. A value of 0.0 means the car will keep bouncing as the spring keeps its energy. A good value for this is around 0.3 for a normal car, 0.5 for a race car.
</member>
<member name="damping_relaxation" type="float" setter="set_damping_relaxation" getter="get_damping_relaxation" default="0.88">
The damping applied to the spring when relaxing. This value should be between 0.0 (no damping) and 1.0. This value should always be slightly higher than the [member damping_compression] property. For a [member damping_compression] value of 0.3, try a relaxation value of 0.5.
</member>
+ <member name="engine_force" type="float" setter="set_engine_force" getter="get_engine_force" default="0.0">
+ Accelerates the wheel by applying an engine force. The wheel is only speed up if it is in contact with a surface. The [member RigidBody.mass] of the vehicle has an effect on the acceleration of the vehicle. For a vehicle with a mass set to 1000, try a value in the 25 - 50 range for acceleration.
+ [b]Note:[/b] The simulation does not take the effect of gears into account, you will need to add logic for this if you wish to simulate gears.
+ A negative value will result in the wheel reversing.
+ </member>
+ <member name="steering" type="float" setter="set_steering" getter="get_steering" default="0.0">
+ The steering angle for the wheel. Setting this to a non-zero value will result in the vehicle turning when it's moving.
+ </member>
<member name="suspension_max_force" type="float" setter="set_suspension_max_force" getter="get_suspension_max_force" default="6000.0">
The maximum force the spring can resist. This value should be higher than a quarter of the [member RigidBody.mass] of the [VehicleBody] or the spring will not carry the weight of the vehicle. Good results are often obtained by a value that is about 3× to 4× this number.
</member>
@@ -47,10 +59,10 @@
This is the distance the suspension can travel. As Godot units are equivalent to meters, keep this setting relatively low. Try a value between 0.1 and 0.3 depending on the type of car.
</member>
<member name="use_as_steering" type="bool" setter="set_use_as_steering" getter="is_used_as_steering" default="false">
- If [code]true[/code], this wheel will be turned when the car steers.
+ If [code]true[/code], this wheel will be turned when the car steers. This value is used in conjunction with [member VehicleBody.steering] and ignored if you are using the per-wheel [member steering] value instead.
</member>
<member name="use_as_traction" type="bool" setter="set_use_as_traction" getter="is_used_as_traction" default="false">
- If [code]true[/code], this wheel transfers engine force to the ground to propel the vehicle forward.
+ If [code]true[/code], this wheel transfers engine force to the ground to propel the vehicle forward. This value is used in conjunction with [member VehicleBody.engine_force] and ignored if you are using the per-wheel [member engine_force] value instead.
</member>
<member name="wheel_friction_slip" type="float" setter="set_friction_slip" getter="get_friction_slip" default="10.5">
This determines how much grip this wheel has. It is combined with the friction setting of the surface the wheel is in contact with. 0.0 means no grip, 1.0 is normal grip. For a drift car setup, try setting the grip of the rear wheels slightly lower than the front wheels, or use a lower value to simulate tire wear.
diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py
index 763c29ab4e..b42ae3ce01 100755
--- a/doc/tools/makerst.py
+++ b/doc/tools/makerst.py
@@ -347,6 +347,7 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
f = open(os.path.join(output_dir, "class_" + class_name.lower() + '.rst'), 'w', encoding='utf-8')
# Warn contributors not to edit this file directly
+ f.write(":github_url: hide\n\n")
f.write(".. Generated automatically by doc/tools/makerst.py in Godot's source tree.\n")
f.write(".. DO NOT EDIT THIS FILE, but the " + class_name + ".xml source instead.\n")
f.write(".. The source is found in doc/classes or modules/<name>/doc_classes.\n\n")
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index d1ac69c8d8..9b376ae090 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -82,22 +82,23 @@ public:
}
void _update_obj(const Ref<Animation> &p_anim) {
- if (setting)
- return;
- if (!(animation == p_anim))
+
+ if (setting || animation != p_anim)
return;
notify_change();
}
void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) {
- if (!(animation == p_anim))
- return;
- if (from != key_ofs)
+
+ if (animation != p_anim || from != key_ofs)
return;
+
key_ofs = to;
+
if (setting)
return;
+
notify_change();
}
@@ -118,6 +119,7 @@ public:
}
new_time /= fps;
}
+
if (new_time == key_ofs)
return true;
@@ -141,12 +143,13 @@ public:
trans = animation->track_get_key_transition(track, existing);
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, new_time, v, trans);
}
-
undo_redo->commit_action();
- setting = false;
+ setting = false;
return true;
- } else if (name == "easing") {
+ }
+
+ if (name == "easing") {
float val = p_value;
float prev_val = animation->track_get_key_transition(track, key);
@@ -157,6 +160,7 @@ public:
undo_redo->add_do_method(this, "_update_obj", animation);
undo_redo->add_undo_method(this, "_update_obj", animation);
undo_redo->commit_action();
+
setting = false;
return true;
}
@@ -166,7 +170,7 @@ public:
case Animation::TYPE_TRANSFORM: {
Dictionary d_old = animation->track_get_key_value(track, key);
- Dictionary d_new = d_old;
+ Dictionary d_new = d_old.duplicate();
d_new[p_name] = p_value;
setting = true;
undo_redo->create_action(TTR("Anim Change Transform"));
@@ -178,7 +182,6 @@ public:
setting = false;
return true;
-
} break;
case Animation::TYPE_VALUE: {
@@ -187,7 +190,6 @@ public:
Variant value = p_value;
if (value.get_type() == Variant::NODE_PATH) {
-
_fix_node_path(value);
}
@@ -203,12 +205,11 @@ public:
setting = false;
return true;
}
-
} break;
case Animation::TYPE_METHOD: {
Dictionary d_old = animation->track_get_key_value(track, key);
- Dictionary d_new = d_old;
+ Dictionary d_new = d_old.duplicate();
bool change_notify_deserved = false;
bool mergeable = false;
@@ -216,17 +217,13 @@ public:
if (name == "name") {
d_new["method"] = p_value;
- }
-
- if (name == "arg_count") {
+ } else if (name == "arg_count") {
Vector<Variant> args = d_old["args"];
args.resize(p_value);
d_new["args"] = args;
change_notify_deserved = true;
- }
-
- if (name.begins_with("args/")) {
+ } else if (name.begins_with("args/")) {
Vector<Variant> args = d_old["args"];
int idx = name.get_slice("/", 1).to_int();
@@ -249,8 +246,7 @@ public:
change_notify_deserved = true;
d_new["args"] = args;
}
- }
- if (what == "value") {
+ } else if (what == "value") {
Variant value = p_value;
if (value.get_type() == Variant::NODE_PATH) {
@@ -300,6 +296,7 @@ public:
setting = false;
return true;
}
+
if (name == "in_handle") {
const Variant &value = p_value;
@@ -316,6 +313,7 @@ public:
setting = false;
return true;
}
+
if (name == "out_handle") {
const Variant &value = p_value;
@@ -332,7 +330,6 @@ public:
setting = false;
return true;
}
-
} break;
case Animation::TYPE_AUDIO: {
@@ -352,6 +349,7 @@ public:
setting = false;
return true;
}
+
if (name == "start_offset") {
float value = p_value;
@@ -368,6 +366,7 @@ public:
setting = false;
return true;
}
+
if (name == "end_offset") {
float value = p_value;
@@ -384,7 +383,6 @@ public:
setting = false;
return true;
}
-
} break;
case Animation::TYPE_ANIMATION: {
@@ -400,10 +398,10 @@ public:
undo_redo->add_do_method(this, "_update_obj", animation);
undo_redo->add_undo_method(this, "_update_obj", animation);
undo_redo->commit_action();
+
setting = false;
return true;
}
-
} break;
}
@@ -419,20 +417,24 @@ public:
if (name == "time") {
r_ret = key_ofs;
return true;
- } else if (name == "frame") {
+ }
+
+ if (name == "frame") {
+
float fps = animation->get_step();
if (fps > 0) {
fps = 1.0 / fps;
}
r_ret = key_ofs * fps;
return true;
- } else if (name == "easing") {
+ }
+
+ if (name == "easing") {
r_ret = animation->track_get_key_transition(track, key);
return true;
}
switch (animation->track_get_type(track)) {
-
case Animation::TYPE_TRANSFORM: {
Dictionary d = animation->track_get_key_value(track, key);
@@ -465,7 +467,6 @@ public:
Vector<Variant> args = d["args"];
if (name == "arg_count") {
-
r_ret = args.size();
return true;
}
@@ -480,6 +481,7 @@ public:
r_ret = args[idx].get_type();
return true;
}
+
if (what == "value") {
r_ret = args[idx];
return true;
@@ -493,10 +495,12 @@ public:
r_ret = animation->bezier_track_get_key_value(track, key);
return true;
}
+
if (name == "in_handle") {
r_ret = animation->bezier_track_get_key_in_handle(track, key);
return true;
}
+
if (name == "out_handle") {
r_ret = animation->bezier_track_get_key_out_handle(track, key);
return true;
@@ -509,10 +513,12 @@ public:
r_ret = animation->audio_track_get_key_stream(track, key);
return true;
}
+
if (name == "start_offset") {
r_ret = animation->audio_track_get_key_start_offset(track, key);
return true;
}
+
if (name == "end_offset") {
r_ret = animation->audio_track_get_key_end_offset(track, key);
return true;
@@ -691,6 +697,702 @@ public:
}
};
+class AnimationMultiTrackKeyEdit : public Object {
+
+ GDCLASS(AnimationMultiTrackKeyEdit, Object);
+
+public:
+ bool setting;
+
+ bool _hide_script_from_inspector() {
+ return true;
+ }
+
+ bool _dont_undo_redo() {
+ return true;
+ }
+
+ static void _bind_methods() {
+
+ ClassDB::bind_method("_update_obj", &AnimationMultiTrackKeyEdit::_update_obj);
+ ClassDB::bind_method("_key_ofs_changed", &AnimationMultiTrackKeyEdit::_key_ofs_changed);
+ ClassDB::bind_method("_hide_script_from_inspector", &AnimationMultiTrackKeyEdit::_hide_script_from_inspector);
+ ClassDB::bind_method("get_root_path", &AnimationMultiTrackKeyEdit::get_root_path);
+ ClassDB::bind_method("_dont_undo_redo", &AnimationMultiTrackKeyEdit::_dont_undo_redo);
+ }
+
+ void _fix_node_path(Variant &value, NodePath &base) {
+
+ NodePath np = value;
+
+ if (np == NodePath())
+ return;
+
+ Node *root = EditorNode::get_singleton()->get_tree()->get_root();
+
+ Node *np_node = root->get_node(np);
+ ERR_FAIL_COND(!np_node);
+
+ Node *edited_node = root->get_node(base);
+ ERR_FAIL_COND(!edited_node);
+
+ value = edited_node->get_path_to(np_node);
+ }
+
+ void _update_obj(const Ref<Animation> &p_anim) {
+
+ if (setting || animation != p_anim)
+ return;
+
+ notify_change();
+ }
+
+ void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) {
+
+ if (animation != p_anim)
+ return;
+
+ for (Map<int, List<float> >::Element *E = key_ofs_map.front(); E; E = E->next()) {
+
+ for (List<float>::Element *F = E->value().front(); F; F = F->next()) {
+
+ float key_ofs = F->get();
+ if (from != key_ofs)
+ continue;
+
+ int track = E->key();
+ key_ofs_map[track][key_ofs] = to;
+
+ if (setting)
+ return;
+
+ notify_change();
+
+ return;
+ }
+ }
+ }
+
+ bool _set(const StringName &p_name, const Variant &p_value) {
+
+ bool update_obj = false;
+ bool change_notify_deserved = false;
+ for (Map<int, List<float> >::Element *E = key_ofs_map.front(); E; E = E->next()) {
+
+ int track = E->key();
+ for (List<float>::Element *F = E->value().front(); F; F = F->next()) {
+
+ float key_ofs = F->get();
+ int key = animation->track_find_key(track, key_ofs, true);
+ ERR_FAIL_COND_V(key == -1, false);
+
+ String name = p_name;
+ if (name == "time" || name == "frame") {
+
+ float new_time = p_value;
+
+ if (name == "frame") {
+ float fps = animation->get_step();
+ if (fps > 0) {
+ fps = 1.0 / fps;
+ }
+ new_time /= fps;
+ }
+
+ int existing = animation->track_find_key(track, new_time, true);
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Time"), UndoRedo::MERGE_ENDS);
+ }
+
+ Variant val = animation->track_get_key_value(track, key);
+ float trans = animation->track_get_key_transition(track, key);
+
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key);
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans);
+ undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans);
+ undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs);
+
+ if (existing != -1) {
+ Variant v = animation->track_get_key_value(track, existing);
+ trans = animation->track_get_key_transition(track, existing);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, new_time, v, trans);
+ }
+ } else if (name == "easing") {
+
+ float val = p_value;
+ float prev_val = animation->track_get_key_transition(track, key);
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Transition"), UndoRedo::MERGE_ENDS);
+ }
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val);
+ update_obj = true;
+ }
+
+ switch (animation->track_get_type(track)) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ Dictionary d_old = animation->track_get_key_value(track, key);
+ Dictionary d_new = d_old.duplicate();
+ d_new[p_name] = p_value;
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Transform"));
+ }
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
+ update_obj = true;
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ if (name == "value") {
+
+ Variant value = p_value;
+
+ if (value.get_type() == Variant::NODE_PATH) {
+ _fix_node_path(value, base_map[track]);
+ }
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ }
+ Variant prev = animation->track_get_key_value(track, key);
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, prev);
+ update_obj = true;
+ }
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ Dictionary d_old = animation->track_get_key_value(track, key);
+ Dictionary d_new = d_old.duplicate();
+
+ bool mergeable = false;
+
+ if (name == "name") {
+
+ d_new["method"] = p_value;
+ } else if (name == "arg_count") {
+
+ Vector<Variant> args = d_old["args"];
+ args.resize(p_value);
+ d_new["args"] = args;
+ change_notify_deserved = true;
+ } else if (name.begins_with("args/")) {
+
+ Vector<Variant> args = d_old["args"];
+ int idx = name.get_slice("/", 1).to_int();
+ ERR_FAIL_INDEX_V(idx, args.size(), false);
+
+ String what = name.get_slice("/", 2);
+ if (what == "type") {
+ Variant::Type t = Variant::Type(int(p_value));
+
+ if (t != args[idx].get_type()) {
+ Variant::CallError err;
+ if (Variant::can_convert(args[idx].get_type(), t)) {
+ Variant old = args[idx];
+ Variant *ptrs[1] = { &old };
+ args.write[idx] = Variant::construct(t, (const Variant **)ptrs, 1, err);
+ } else {
+
+ args.write[idx] = Variant::construct(t, NULL, 0, err);
+ }
+ change_notify_deserved = true;
+ d_new["args"] = args;
+ }
+ } else if (what == "value") {
+
+ Variant value = p_value;
+ if (value.get_type() == Variant::NODE_PATH) {
+
+ _fix_node_path(value, base_map[track]);
+ }
+
+ args.write[idx] = value;
+ d_new["args"] = args;
+ mergeable = true;
+ }
+ }
+
+ Variant prev = animation->track_get_key_value(track, key);
+
+ if (!setting) {
+ if (mergeable)
+ undo_redo->create_action(TTR("Anim Multi Change Call"), UndoRedo::MERGE_ENDS);
+ else
+ undo_redo->create_action(TTR("Anim Multi Change Call"));
+
+ setting = true;
+ }
+
+ undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
+ update_obj = true;
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ if (name == "value") {
+
+ const Variant &value = p_value;
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ }
+ float prev = animation->bezier_track_get_key_value(track, key);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_value", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev);
+ update_obj = true;
+ } else if (name == "in_handle") {
+
+ const Variant &value = p_value;
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ }
+ Vector2 prev = animation->bezier_track_get_key_in_handle(track, key);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, prev);
+ update_obj = true;
+ } else if (name == "out_handle") {
+
+ const Variant &value = p_value;
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ }
+ Vector2 prev = animation->bezier_track_get_key_out_handle(track, key);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev);
+ update_obj = true;
+ }
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ if (name == "stream") {
+
+ Ref<AudioStream> stream = p_value;
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ }
+ RES prev = animation->audio_track_get_key_stream(track, key);
+ undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_stream", track, key, stream);
+ undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_stream", track, key, prev);
+ update_obj = true;
+ } else if (name == "start_offset") {
+
+ float value = p_value;
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ }
+ float prev = animation->audio_track_get_key_start_offset(track, key);
+ undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_start_offset", track, key, prev);
+ update_obj = true;
+ } else if (name == "end_offset") {
+
+ float value = p_value;
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ }
+ float prev = animation->audio_track_get_key_end_offset(track, key);
+ undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, value);
+ undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_end_offset", track, key, prev);
+ update_obj = true;
+ }
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ if (name == "animation") {
+
+ StringName anim_name = p_value;
+
+ if (!setting) {
+ setting = true;
+ undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS);
+ }
+ StringName prev = animation->animation_track_get_key_animation(track, key);
+ undo_redo->add_do_method(animation.ptr(), "animation_track_set_key_animation", track, key, anim_name);
+ undo_redo->add_undo_method(animation.ptr(), "animation_track_set_key_animation", track, key, prev);
+ update_obj = true;
+ }
+ } break;
+ }
+ }
+ }
+
+ if (setting) {
+
+ if (update_obj) {
+ undo_redo->add_do_method(this, "_update_obj", animation);
+ undo_redo->add_undo_method(this, "_update_obj", animation);
+ }
+
+ undo_redo->commit_action();
+ setting = false;
+
+ if (change_notify_deserved)
+ notify_change();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ bool _get(const StringName &p_name, Variant &r_ret) const {
+
+ for (Map<int, List<float> >::Element *E = key_ofs_map.front(); E; E = E->next()) {
+
+ int track = E->key();
+ for (List<float>::Element *F = E->value().front(); F; F = F->next()) {
+
+ float key_ofs = F->get();
+ int key = animation->track_find_key(track, key_ofs, true);
+ ERR_CONTINUE(key == -1);
+
+ String name = p_name;
+ if (name == "time") {
+ r_ret = key_ofs;
+ return true;
+ }
+
+ if (name == "frame") {
+
+ float fps = animation->get_step();
+ if (fps > 0) {
+ fps = 1.0 / fps;
+ }
+ r_ret = key_ofs * fps;
+ return true;
+ }
+
+ if (name == "easing") {
+ r_ret = animation->track_get_key_transition(track, key);
+ return true;
+ }
+
+ switch (animation->track_get_type(track)) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ Dictionary d = animation->track_get_key_value(track, key);
+ ERR_FAIL_COND_V(!d.has(name), false);
+ r_ret = d[p_name];
+ return true;
+
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ if (name == "value") {
+ r_ret = animation->track_get_key_value(track, key);
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ Dictionary d = animation->track_get_key_value(track, key);
+
+ if (name == "name") {
+
+ ERR_FAIL_COND_V(!d.has("method"), false);
+ r_ret = d["method"];
+ return true;
+ }
+
+ ERR_FAIL_COND_V(!d.has("args"), false);
+
+ Vector<Variant> args = d["args"];
+
+ if (name == "arg_count") {
+
+ r_ret = args.size();
+ return true;
+ }
+
+ if (name.begins_with("args/")) {
+
+ int idx = name.get_slice("/", 1).to_int();
+ ERR_FAIL_INDEX_V(idx, args.size(), false);
+
+ String what = name.get_slice("/", 2);
+ if (what == "type") {
+ r_ret = args[idx].get_type();
+ return true;
+ }
+
+ if (what == "value") {
+ r_ret = args[idx];
+ return true;
+ }
+ }
+
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ if (name == "value") {
+ r_ret = animation->bezier_track_get_key_value(track, key);
+ return true;
+ }
+
+ if (name == "in_handle") {
+ r_ret = animation->bezier_track_get_key_in_handle(track, key);
+ return true;
+ }
+
+ if (name == "out_handle") {
+ r_ret = animation->bezier_track_get_key_out_handle(track, key);
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ if (name == "stream") {
+ r_ret = animation->audio_track_get_key_stream(track, key);
+ return true;
+ }
+
+ if (name == "start_offset") {
+ r_ret = animation->audio_track_get_key_start_offset(track, key);
+ return true;
+ }
+
+ if (name == "end_offset") {
+ r_ret = animation->audio_track_get_key_end_offset(track, key);
+ return true;
+ }
+
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ if (name == "animation") {
+ r_ret = animation->animation_track_get_key_animation(track, key);
+ return true;
+ }
+
+ } break;
+ }
+ }
+ }
+
+ return false;
+ }
+ void _get_property_list(List<PropertyInfo> *p_list) const {
+
+ if (animation.is_null())
+ return;
+
+ int first_track = -1;
+ float first_key = -1.0;
+
+ bool show_time = true;
+ bool same_track_type = true;
+ bool same_key_type = true;
+ for (Map<int, List<float> >::Element *E = key_ofs_map.front(); E; E = E->next()) {
+
+ int track = E->key();
+ ERR_FAIL_INDEX(track, animation->get_track_count());
+
+ if (first_track < 0)
+ first_track = track;
+
+ if (show_time && E->value().size() > 1)
+ show_time = false;
+
+ if (same_track_type) {
+
+ if (animation->track_get_type(first_track) != animation->track_get_type(track)) {
+ same_track_type = false;
+ same_key_type = false;
+ }
+
+ for (List<float>::Element *F = E->value().front(); F; F = F->next()) {
+
+ int key = animation->track_find_key(track, F->get(), true);
+ ERR_FAIL_COND(key == -1);
+ if (first_key < 0)
+ first_key = key;
+
+ if (animation->track_get_key_value(first_track, first_key).get_type() != animation->track_get_key_value(track, key).get_type())
+ same_key_type = false;
+ }
+ }
+ }
+
+ if (show_time) {
+
+ if (use_fps && animation->get_step() > 0) {
+ float max_frame = animation->get_length() / animation->get_step();
+ p_list->push_back(PropertyInfo(Variant::REAL, "frame", PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",1"));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01"));
+ }
+ }
+
+ if (same_track_type) {
+ switch (animation->track_get_type(first_track)) {
+
+ case Animation::TYPE_TRANSFORM: {
+
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
+ p_list->push_back(PropertyInfo(Variant::QUAT, "rotation"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
+ } break;
+ case Animation::TYPE_VALUE: {
+
+ if (!same_key_type)
+ break;
+
+ Variant v = animation->track_get_key_value(first_track, first_key);
+
+ if (hint.type != Variant::NIL) {
+
+ PropertyInfo pi = hint;
+ pi.name = "value";
+ p_list->push_back(pi);
+ } else {
+
+ PropertyHint hint = PROPERTY_HINT_NONE;
+ String hint_string;
+
+ if (v.get_type() == Variant::OBJECT) {
+ //could actually check the object property if exists..? yes i will!
+ Ref<Resource> res = v;
+ if (res.is_valid()) {
+
+ hint = PROPERTY_HINT_RESOURCE_TYPE;
+ hint_string = res->get_class();
+ }
+ }
+
+ if (v.get_type() != Variant::NIL)
+ p_list->push_back(PropertyInfo(v.get_type(), "value", hint, hint_string));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
+ } break;
+ case Animation::TYPE_METHOD: {
+
+ p_list->push_back(PropertyInfo(Variant::STRING, "name"));
+ p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,5,1"));
+
+ Dictionary d = animation->track_get_key_value(first_track, first_key);
+ ERR_FAIL_COND(!d.has("args"));
+ Vector<Variant> args = d["args"];
+ String vtypes;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+
+ if (i > 0)
+ vtypes += ",";
+ vtypes += Variant::get_type_name(Variant::Type(i));
+ }
+
+ for (int i = 0; i < args.size(); i++) {
+
+ p_list->push_back(PropertyInfo(Variant::INT, "args/" + itos(i) + "/type", PROPERTY_HINT_ENUM, vtypes));
+ if (args[i].get_type() != Variant::NIL)
+ p_list->push_back(PropertyInfo(args[i].get_type(), "args/" + itos(i) + "/value"));
+ }
+ } break;
+ case Animation::TYPE_BEZIER: {
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "value"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle"));
+ } break;
+ case Animation::TYPE_AUDIO: {
+
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "start_offset", PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "end_offset", PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater"));
+ } break;
+ case Animation::TYPE_ANIMATION: {
+
+ if (key_ofs_map.size() > 1)
+ break;
+
+ String animations;
+
+ if (root_path && root_path->has_node(animation->track_get_path(first_track))) {
+
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(first_track)));
+ if (ap) {
+ List<StringName> anims;
+ ap->get_animation_list(&anims);
+ for (List<StringName>::Element *G = anims.front(); G; G = G->next()) {
+ if (animations != String()) {
+ animations += ",";
+ }
+
+ animations += String(G->get());
+ }
+ }
+ }
+
+ if (animations != String()) {
+ animations += ",";
+ }
+ animations += "[stop]";
+
+ p_list->push_back(PropertyInfo(Variant::STRING, "animation", PROPERTY_HINT_ENUM, animations));
+ } break;
+ }
+ }
+ }
+
+ Ref<Animation> animation;
+
+ Map<int, List<float> > key_ofs_map;
+ Map<int, NodePath> base_map;
+ PropertyInfo hint;
+
+ Node *root_path;
+
+ bool use_fps;
+
+ UndoRedo *undo_redo;
+
+ void notify_change() {
+
+ _change_notify();
+ }
+
+ Node *get_root_path() {
+ return root_path;
+ }
+
+ void set_use_fps(bool p_enable) {
+ use_fps = p_enable;
+ _change_notify();
+ }
+
+ AnimationMultiTrackKeyEdit() {
+ use_fps = false;
+ setting = false;
+ root_path = NULL;
+ }
+};
+
void AnimationTimelineEdit::_zoom_changed(double) {
update();
@@ -4133,12 +4835,19 @@ void AnimationTrackEditor::_clear_key_edit() {
}
#else
//if key edit is the object being inspected, remove it first
- if (EditorNode::get_singleton()->get_inspector()->get_edited_object() == key_edit) {
+ if (EditorNode::get_singleton()->get_inspector()->get_edited_object() == key_edit ||
+ EditorNode::get_singleton()->get_inspector()->get_edited_object() == multi_key_edit) {
EditorNode::get_singleton()->push_item(NULL);
}
+
//then actually delete it
- memdelete(key_edit);
- key_edit = NULL;
+ if (key_edit) {
+ memdelete(key_edit);
+ key_edit = NULL;
+ } else if (multi_key_edit) {
+ memdelete(multi_key_edit);
+ multi_key_edit = NULL;
+ }
#endif
}
}
@@ -4156,38 +4865,70 @@ void AnimationTrackEditor::_update_key_edit() {
_clear_key_edit();
if (!animation.is_valid())
return;
- if (selection.size() != 1) {
- return;
- }
- key_edit = memnew(AnimationTrackKeyEdit);
- key_edit->animation = animation;
- key_edit->track = selection.front()->key().track;
- key_edit->use_fps = timeline->is_using_fps();
+ if (selection.size() == 1) {
+
+ key_edit = memnew(AnimationTrackKeyEdit);
+ key_edit->animation = animation;
+ key_edit->track = selection.front()->key().track;
+ key_edit->use_fps = timeline->is_using_fps();
+
+ float ofs = animation->track_get_key_time(key_edit->track, selection.front()->key().key);
+ key_edit->key_ofs = ofs;
+ key_edit->root_path = root;
+
+ NodePath np;
+ key_edit->hint = _find_hint_for_track(key_edit->track, np);
+ key_edit->undo_redo = undo_redo;
+ key_edit->base = np;
- float ofs = animation->track_get_key_time(key_edit->track, selection.front()->key().key);
- key_edit->key_ofs = ofs;
- key_edit->root_path = root;
+ EditorNode::get_singleton()->push_item(key_edit);
+ } else if (selection.size() > 1) {
- NodePath np;
- key_edit->hint = _find_hint_for_track(key_edit->track, np);
- key_edit->undo_redo = undo_redo;
- key_edit->base = np;
+ multi_key_edit = memnew(AnimationMultiTrackKeyEdit);
+ multi_key_edit->animation = animation;
- EditorNode::get_singleton()->push_item(key_edit);
+ Map<int, List<float> > key_ofs_map;
+ Map<int, NodePath> base_map;
+ int first_track = -1;
+ for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
+
+ int track = E->key().track;
+ if (first_track < 0)
+ first_track = track;
+
+ if (!key_ofs_map.has(track)) {
+ key_ofs_map[track] = List<float>();
+ base_map[track] = *memnew(NodePath);
+ }
+
+ key_ofs_map[track].push_back(animation->track_get_key_time(track, E->key().key));
+ }
+ multi_key_edit->key_ofs_map = key_ofs_map;
+ multi_key_edit->base_map = base_map;
+ multi_key_edit->hint = _find_hint_for_track(first_track, base_map[first_track]);
+
+ multi_key_edit->use_fps = timeline->is_using_fps();
+
+ multi_key_edit->root_path = root;
+
+ multi_key_edit->undo_redo = undo_redo;
+
+ EditorNode::get_singleton()->push_item(multi_key_edit);
+ }
}
void AnimationTrackEditor::_clear_selection_for_anim(const Ref<Animation> &p_anim) {
- if (!(animation == p_anim))
+ if (animation != p_anim)
return;
- //selection.clear();
+
_clear_selection();
}
void AnimationTrackEditor::_select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos) {
- if (!(animation == p_anim))
+ if (animation != p_anim)
return;
int idx = animation->track_find_key(p_track, p_pos, true);
@@ -4209,12 +4950,12 @@ void AnimationTrackEditor::_move_selection_commit() {
List<_AnimMoveRestore> to_restore;
float motion = moving_selection_offset;
- // 1-remove the keys
+ // 1 - remove the keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
}
- // 2- remove overlapped keys
+ // 2 - remove overlapped keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newtime = snap_time(E->get().pos + motion);
@@ -4238,35 +4979,27 @@ void AnimationTrackEditor::_move_selection_commit() {
to_restore.push_back(amr);
}
- // 3-move the keys (re insert them)
+ // 3 - move the keys (re insert them)
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = snap_time(E->get().pos + motion);
- /*
- if (newpos<0)
- continue; //no add at the beginning
- */
undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
}
- // 4-(undo) remove inserted keys
+ // 4 - (undo) remove inserted keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = snap_time(E->get().pos + motion);
- /*
- if (newpos<0)
- continue; //no remove what no inserted
- */
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
}
- // 5-(undo) reinsert keys
+ // 5 - (undo) reinsert keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
}
- // 6-(undo) reinsert overlapped keys
+ // 6 - (undo) reinsert overlapped keys
for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
_AnimMoveRestore &amr = E->get();
@@ -4276,12 +5009,12 @@ void AnimationTrackEditor::_move_selection_commit() {
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
- // 7-reselect
+ // 7 - reselect
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float oldpos = E->get().pos;
float newpos = snap_time(oldpos + motion);
- //if (newpos>=0)
+
undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
}
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 8dc2304a95..9e16f2faf7 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -246,6 +246,7 @@ public:
};
class AnimationTrackKeyEdit;
+class AnimationMultiTrackKeyEdit;
class AnimationBezierTrackEdit;
class AnimationTrackEditGroup : public Control {
@@ -415,6 +416,7 @@ class AnimationTrackEditor : public VBoxContainer {
void _move_selection_cancel();
AnimationTrackKeyEdit *key_edit;
+ AnimationMultiTrackKeyEdit *multi_key_edit;
void _update_key_edit();
void _clear_key_edit();
diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp
index 6f09e73fab..5b8c8fffb8 100644
--- a/editor/doc/doc_data.cpp
+++ b/editor/doc/doc_data.cpp
@@ -741,10 +741,9 @@ Error DocData::load_classes(const String &p_dir) {
da->list_dir_begin();
String path;
- bool isdir;
- path = da->get_next(&isdir);
+ path = da->get_next();
while (path != String()) {
- if (!isdir && path.ends_with("xml")) {
+ if (!da->current_is_dir() && path.ends_with("xml")) {
Ref<XMLParser> parser = memnew(XMLParser);
Error err2 = parser->open(p_dir.plus_file(path));
if (err2)
@@ -752,7 +751,7 @@ Error DocData::load_classes(const String &p_dir) {
_load(parser);
}
- path = da->get_next(&isdir);
+ path = da->get_next();
}
da->list_dir_end();
@@ -771,13 +770,12 @@ Error DocData::erase_classes(const String &p_dir) {
da->list_dir_begin();
String path;
- bool isdir;
- path = da->get_next(&isdir);
+ path = da->get_next();
while (path != String()) {
- if (!isdir && path.ends_with("xml")) {
+ if (!da->current_is_dir() && path.ends_with("xml")) {
to_erase.push_back(path);
}
- path = da->get_next(&isdir);
+ path = da->get_next();
}
da->list_dir_end();
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 24c5a788b6..be01df76f7 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "editor_file_dialog.h"
+
#include "core/os/file_access.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -731,19 +732,15 @@ void EditorFileDialog::update_file_list() {
List<String> files;
List<String> dirs;
- bool is_dir;
- bool is_hidden;
String item;
- while ((item = dir_access->get_next(&is_dir)) != "") {
+ while ((item = dir_access->get_next()) != "") {
if (item == "." || item == "..")
continue;
- is_hidden = dir_access->current_is_hidden();
-
- if (show_hidden_files || !is_hidden) {
- if (!is_dir)
+ if (show_hidden_files || !dir_access->current_is_hidden()) {
+ if (!dir_access->current_is_dir())
files.push_back(item);
else
dirs.push_back(item);
@@ -1507,9 +1504,9 @@ EditorFileDialog::EditorFileDialog() {
HBoxContainer *pathhb = memnew(HBoxContainer);
dir_prev = memnew(ToolButton);
- dir_prev->set_tooltip(TTR("Previous Folder"));
+ dir_prev->set_tooltip(TTR("Go to previous folder."));
dir_next = memnew(ToolButton);
- dir_next->set_tooltip(TTR("Next Folder"));
+ dir_next->set_tooltip(TTR("Go to next folder."));
dir_up = memnew(ToolButton);
dir_up->set_tooltip(TTR("Go to parent folder."));
@@ -1528,7 +1525,7 @@ EditorFileDialog::EditorFileDialog() {
dir->set_h_size_flags(SIZE_EXPAND_FILL);
refresh = memnew(ToolButton);
- refresh->set_tooltip(TTR("Refresh"));
+ refresh->set_tooltip(TTR("Refresh files."));
refresh->connect("pressed", this, "_update_file_list");
pathhb->add_child(refresh);
@@ -1541,7 +1538,7 @@ EditorFileDialog::EditorFileDialog() {
show_hidden = memnew(ToolButton);
show_hidden->set_toggle_mode(true);
show_hidden->set_pressed(is_showing_hidden_files());
- show_hidden->set_tooltip(TTR("Toggle visibility of hidden files."));
+ show_hidden->set_tooltip(TTR("Toggle the visibility of hidden files."));
show_hidden->connect("toggled", this, "set_show_hidden_files");
pathhb->add_child(show_hidden);
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 87a37acac6..be3df2815e 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -673,12 +673,11 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
da->list_dir_begin();
while (true) {
- bool isdir;
- String f = da->get_next(&isdir);
+ String f = da->get_next();
if (f == "")
break;
- if (isdir) {
+ if (da->current_is_dir()) {
if (f.begins_with(".")) //ignore hidden and . / ..
continue;
@@ -870,12 +869,11 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
da->list_dir_begin();
while (true) {
- bool isdir;
- String f = da->get_next(&isdir);
+ String f = da->get_next();
if (f == "")
break;
- if (isdir) {
+ if (da->current_is_dir()) {
if (f.begins_with(".")) //ignore hidden and . / ..
continue;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index e4ddf44bc4..70bbd0fd6c 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -504,7 +504,7 @@ bool EditorProperty::use_keying_next() const {
PropertyInfo &p = I->get();
if (p.name == property) {
- return p.hint == PROPERTY_HINT_SPRITE_FRAME;
+ return (p.usage & PROPERTY_USAGE_KEYING_INCREMENTS);
}
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index f1944bf63f..e9a28368bf 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2615,7 +2615,7 @@ void EditorNode::_exit_editor() {
// Dim the editor window while it's quitting to make it clearer that it's busy.
// No transition is applied, as the effect needs to be visible immediately
- float c = 1.0f - float(EDITOR_GET("interface/editor/dim_amount"));
+ float c = 0.4f;
Color dim_color = Color(c, c, c);
gui_base->set_modulate(dim_color);
@@ -5051,9 +5051,8 @@ void EditorNode::_start_dimming(bool p_dimming) {
void EditorNode::_dim_timeout() {
_dim_time += _dim_timer->get_wait_time();
- float wait_time = EditorSettings::get_singleton()->get("interface/editor/dim_transition_time");
-
- float c = 1.0f - (float)EditorSettings::get_singleton()->get("interface/editor/dim_amount");
+ float wait_time = 0.08f;
+ float c = 0.4f;
Color base = _dimming ? Color(1, 1, 1) : Color(c, c, c);
Color final = _dimming ? Color(c, c, c) : Color(1, 1, 1);
@@ -5557,6 +5556,8 @@ EditorNode::EditorNode() {
EDITOR_DEF_RST("interface/scene_tabs/restore_scenes_on_load", false);
EDITOR_DEF_RST("interface/scene_tabs/show_thumbnail_on_hover", true);
EDITOR_DEF_RST("interface/inspector/capitalize_properties", true);
+ EDITOR_DEF_RST("interface/inspector/default_float_step", 0.001);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::REAL, "interface/inspector/default_float_step", PROPERTY_HINT_EXP_RANGE, "0,1,0"));
EDITOR_DEF_RST("interface/inspector/disable_folding", false);
EDITOR_DEF_RST("interface/inspector/auto_unfold_foreign_scenes", true);
EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index d54f72382c..3300228921 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2904,6 +2904,8 @@ void EditorInspectorDefaultPlugin::parse_begin(Object *p_object) {
bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+ float default_float_step = EDITOR_GET("interface/inspector/default_float_step");
+
switch (p_type) {
// atomic types
@@ -3010,7 +3012,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} else {
EditorPropertyFloat *editor = memnew(EditorPropertyFloat);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
bool exp_range = false;
bool greater = true, lesser = true;
@@ -3107,7 +3109,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
case Variant::VECTOR2: {
EditorPropertyVector2 *editor = memnew(EditorPropertyVector2);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -3125,7 +3127,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break; // 5
case Variant::RECT2: {
EditorPropertyRect2 *editor = memnew(EditorPropertyRect2);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -3142,7 +3144,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break;
case Variant::VECTOR3: {
EditorPropertyVector3 *editor = memnew(EditorPropertyVector3);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -3160,7 +3162,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break;
case Variant::TRANSFORM2D: {
EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -3178,7 +3180,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break;
case Variant::PLANE: {
EditorPropertyPlane *editor = memnew(EditorPropertyPlane);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -3195,7 +3197,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break;
case Variant::QUAT: {
EditorPropertyQuat *editor = memnew(EditorPropertyQuat);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -3212,7 +3214,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break; // 10
case Variant::AABB: {
EditorPropertyAABB *editor = memnew(EditorPropertyAABB);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -3229,7 +3231,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break;
case Variant::BASIS: {
EditorPropertyBasis *editor = memnew(EditorPropertyBasis);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
@@ -3246,7 +3248,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
} break;
case Variant::TRANSFORM: {
EditorPropertyTransform *editor = memnew(EditorPropertyTransform);
- double min = -65535, max = 65535, step = 0.001;
+ double min = -65535, max = 65535, step = default_float_step;
bool hide_slider = true;
if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index b4c1fc3463..948a86924b 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -336,10 +336,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("interface/editor/code_font", "");
hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/dim_editor_on_dialog_popup", true);
- _initial_set("interface/editor/dim_amount", 0.6f);
- hints["interface/editor/dim_amount"] = PropertyInfo(Variant::REAL, "interface/editor/dim_amount", PROPERTY_HINT_RANGE, "0,1,0.01", PROPERTY_USAGE_DEFAULT);
- _initial_set("interface/editor/dim_transition_time", 0.08f);
- hints["interface/editor/dim_transition_time"] = PropertyInfo(Variant::REAL, "interface/editor/dim_transition_time", PROPERTY_HINT_RANGE, "0,1,0.001", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/low_processor_mode_sleep_usec", 6900); // ~144 FPS
hints["interface/editor/low_processor_mode_sleep_usec"] = PropertyInfo(Variant::REAL, "interface/editor/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "1,100000,1", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/unfocused_low_processor_mode_sleep_usec", 50000); // 20 FPS
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index dcb106899e..9966394025 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -38,9 +38,9 @@ String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const {
}
String EditorSpinSlider::get_text_value() const {
- int zeros = Math::step_decimals(get_step());
- return String::num(get_value(), zeros);
+ return String::num(get_value(), Math::range_step_decimals(get_step()));
}
+
void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
if (read_only)
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index bd61e6182c..ecfad4d146 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -52,18 +52,16 @@ void ExportTemplateManager::_update_template_list() {
DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
Error err = d->change_dir(EditorSettings::get_singleton()->get_templates_dir());
- d->list_dir_begin();
Set<String> templates;
-
+ d->list_dir_begin();
if (err == OK) {
- bool isdir;
- String c = d->get_next(&isdir);
+ String c = d->get_next();
while (c != String()) {
- if (isdir && !c.begins_with(".")) {
+ if (d->current_is_dir() && !c.begins_with(".")) {
templates.insert(c);
}
- c = d->get_next(&isdir);
+ c = d->get_next();
}
}
d->list_dir_end();
@@ -154,18 +152,14 @@ void ExportTemplateManager::_uninstall_template_confirm() {
ERR_FAIL_COND(err != OK);
Vector<String> files;
-
d->list_dir_begin();
-
- bool isdir;
- String c = d->get_next(&isdir);
+ String c = d->get_next();
while (c != String()) {
- if (!isdir) {
+ if (!d->current_is_dir()) {
files.push_back(c);
}
- c = d->get_next(&isdir);
+ c = d->get_next();
}
-
d->list_dir_end();
for (int i = 0; i < files.size(); i++) {
diff --git a/editor/icons/icon_point_mesh.svg b/editor/icons/icon_point_mesh.svg
new file mode 100644
index 0000000000..8da7759daf
--- /dev/null
+++ b/editor/icons/icon_point_mesh.svg
@@ -0,0 +1,7 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
+<g fill="#ffd684" stroke="#000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0" stroke-width="0">
+<ellipse cx="3.7237" cy="3.0268" rx="2.0114" ry="1.9956"/>
+<ellipse cx="11.717" cy="6.1734" rx="2.0114" ry="1.9956"/>
+<ellipse cx="6.5219" cy="12.477" rx="2.0114" ry="1.9956"/>
+</g>
+</svg>
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index d999f3189e..9cf889c5b0 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -2088,16 +2088,18 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
}
ERR_FAIL_COND_V(!se, false);
- bool highlighter_set = false;
- for (int i = 0; i < syntax_highlighters_func_count; i++) {
- SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i]();
- se->add_syntax_highlighter(highlighter);
-
- if (script != NULL && !highlighter_set) {
- List<String> languages = highlighter->get_supported_languages();
- if (languages.find(script->get_language()->get_name())) {
- se->set_syntax_highlighter(highlighter);
- highlighter_set = true;
+ if (p_resource->get_class_name() != StringName("VisualScript")) {
+ bool highlighter_set = false;
+ for (int i = 0; i < syntax_highlighters_func_count; i++) {
+ SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i]();
+ se->add_syntax_highlighter(highlighter);
+
+ if (script != NULL && !highlighter_set) {
+ List<String> languages = highlighter->get_supported_languages();
+ if (languages.find(script->get_language()->get_name())) {
+ se->set_syntax_highlighter(highlighter);
+ highlighter_set = true;
+ }
}
}
}
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 438621115b..07303da2ff 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1819,6 +1819,15 @@ ScriptTextEditor::ScriptTextEditor() {
code_editor->get_text_edit()->set_drag_forwarding(this);
}
+ScriptTextEditor::~ScriptTextEditor() {
+ for (const Map<String, SyntaxHighlighter *>::Element *E = highlighters.front(); E; E = E->next()) {
+ if (E->get() != NULL) {
+ memdelete(E->get());
+ }
+ }
+ highlighters.clear();
+}
+
static ScriptEditorBase *create_editor(const RES &p_resource) {
if (Object::cast_to<Script>(*p_resource)) {
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 9a2a514a6e..4dbade472c 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -231,6 +231,7 @@ public:
virtual void validate();
ScriptTextEditor();
+ ~ScriptTextEditor();
};
#endif // SCRIPT_TEXT_EDITOR_H
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 6e4af5cd25..630f3ba9e1 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -1291,6 +1291,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector3 ray_pos = _get_ray_pos(m->get_position());
Vector3 ray = _get_ray(m->get_position());
+ float snap = EDITOR_GET("interface/inspector/default_float_step");
+ int snap_step_decimals = Math::range_step_decimals(snap);
switch (_edit.mode) {
@@ -1372,18 +1374,14 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
// Disable local transformation for TRANSFORM_VIEW
bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
- float snap = 0;
if (_edit.snap || spatial_editor->is_snap_enabled()) {
-
snap = spatial_editor->get_scale_snap() / 100;
-
- Vector3 motion_snapped = motion;
- motion_snapped.snap(Vector3(snap, snap, snap));
- set_message(TTR("Scaling: ") + motion_snapped);
-
- } else {
- set_message(TTR("Scaling: ") + motion);
}
+ Vector3 motion_snapped = motion;
+ motion_snapped.snap(Vector3(snap, snap, snap));
+ // This might not be necessary anymore after issue #288 is solved (in 4.0?).
+ set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
+ String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
@@ -1502,17 +1500,13 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
// Disable local transformation for TRANSFORM_VIEW
bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
- float snap = 0;
if (_edit.snap || spatial_editor->is_snap_enabled()) {
-
snap = spatial_editor->get_translate_snap();
-
- Vector3 motion_snapped = motion;
- motion_snapped.snap(Vector3(snap, snap, snap));
- set_message(TTR("Translating: ") + motion_snapped);
- } else {
- set_message(TTR("Translating: ") + motion);
}
+ Vector3 motion_snapped = motion;
+ motion_snapped.snap(Vector3(snap, snap, snap));
+ set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
+ String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
@@ -1601,20 +1595,12 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
float angle = Math::atan2(x_axis.dot(intersection - _edit.center), y_axis.dot(intersection - _edit.center));
if (_edit.snap || spatial_editor->is_snap_enabled()) {
-
- float snap = spatial_editor->get_rotate_snap();
-
- if (snap) {
- angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180
- angle -= Math::fmod(angle, snap);
- set_message(vformat(TTR("Rotating %s degrees."), rtos(angle)));
- angle = Math::deg2rad(angle);
- } else
- set_message(vformat(TTR("Rotating %s degrees."), rtos(Math::rad2deg(angle))));
-
- } else {
- set_message(vformat(TTR("Rotating %s degrees."), rtos(Math::rad2deg(angle))));
+ snap = spatial_editor->get_rotate_snap();
}
+ angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180
+ angle -= Math::fmod(angle, snap);
+ set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals)));
+ angle = Math::deg2rad(angle);
List<Node *> &selection = editor_selection->get_selected_node_list();
@@ -1835,8 +1821,11 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
_menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL);
_update_name();
}
- if (ED_IS_SHORTCUT("spatial_editor/align_selection_with_view", p_event)) {
- _menu_option(VIEW_ALIGN_SELECTION_WITH_VIEW);
+ if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", p_event)) {
+ _menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW);
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/align_rotation_with_view", p_event)) {
+ _menu_option(VIEW_ALIGN_ROTATION_WITH_VIEW);
}
if (ED_IS_SHORTCUT("spatial_editor/insert_anim_key", p_event)) {
if (!get_selected_count() || _edit.mode != TRANSFORM_NONE)
@@ -2562,7 +2551,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
focus_selection();
} break;
- case VIEW_ALIGN_SELECTION_WITH_VIEW: {
+ case VIEW_ALIGN_TRANSFORM_WITH_VIEW: {
if (!get_selected_count())
break;
@@ -2571,7 +2560,8 @@ void SpatialEditorViewport::_menu_option(int p_option) {
List<Node *> &selection = editor_selection->get_selected_node_list();
- undo_redo->create_action(TTR("Align with View"));
+ undo_redo->create_action(TTR("Align Transform with View"));
+
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
Spatial *sp = Object::cast_to<Spatial>(E->get());
@@ -2595,6 +2585,34 @@ void SpatialEditorViewport::_menu_option(int p_option) {
undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
}
undo_redo->commit_action();
+ focus_selection();
+
+ } break;
+ case VIEW_ALIGN_ROTATION_WITH_VIEW: {
+
+ if (!get_selected_count())
+ break;
+
+ Transform camera_transform = camera->get_global_transform();
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Align Rotation with View"));
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+
+ Spatial *sp = Object::cast_to<Spatial>(E->get());
+ if (!sp)
+ continue;
+
+ SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
+ if (!se)
+ continue;
+
+ undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_rotation());
+ undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation());
+ }
+ undo_redo->commit_action();
+
} break;
case VIEW_ENVIRONMENT: {
@@ -3544,7 +3562,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION);
- view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_selection_with_view"), VIEW_ALIGN_SELECTION_WITH_VIEW);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_transform_with_view"), VIEW_ALIGN_TRANSFORM_WITH_VIEW);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_rotation_with_view"), VIEW_ALIGN_ROTATION_WITH_VIEW);
view_menu->get_popup()->connect("id_pressed", this, "_menu_option");
view_menu->set_disable_shortcuts(true);
@@ -5601,7 +5620,8 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K);
ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O);
ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F);
- ED_SHORTCUT("spatial_editor/align_selection_with_view", TTR("Align Selection With View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_F);
+ ED_SHORTCUT("spatial_editor/align_transform_with_view", TTR("Align Transform with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_M);
+ ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_F);
ED_SHORTCUT("spatial_editor/tool_select", TTR("Tool Select"), KEY_Q);
ED_SHORTCUT("spatial_editor/tool_move", TTR("Tool Move"), KEY_W);
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index 1a32d6e047..dde402c0ff 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -153,7 +153,8 @@ class SpatialEditorViewport : public Control {
VIEW_REAR,
VIEW_CENTER_TO_ORIGIN,
VIEW_CENTER_TO_SELECTION,
- VIEW_ALIGN_SELECTION_WITH_VIEW,
+ VIEW_ALIGN_TRANSFORM_WITH_VIEW,
+ VIEW_ALIGN_ROTATION_WITH_VIEW,
VIEW_PERSPECTIVE,
VIEW_ENVIRONMENT,
VIEW_ORTHOGONAL,
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index fae88f4eb7..34d8e6aff5 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -695,5 +695,14 @@ TextEditor::TextEditor() {
code_editor->get_text_edit()->set_drag_forwarding(this);
}
+TextEditor::~TextEditor() {
+ for (const Map<String, SyntaxHighlighter *>::Element *E = highlighters.front(); E; E = E->next()) {
+ if (E->get() != NULL) {
+ memdelete(E->get());
+ }
+ }
+ highlighters.clear();
+}
+
void TextEditor::validate() {
}
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index ae0c0bcf93..3a330576ae 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -154,6 +154,7 @@ public:
static void register_editor();
TextEditor();
+ ~TextEditor();
};
#endif // TEXT_EDITOR_H
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 48a587d4db..feb2cdd071 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1730,7 +1730,7 @@ void ProjectManager::_dim_window() {
// Dim the project manager window while it's quitting to make it clearer that it's busy.
// No transition is applied, as the effect needs to be visible immediately
- float c = 1.0f - float(EDITOR_GET("interface/editor/dim_amount"));
+ float c = 0.4f;
Color dim_color = Color(c, c, c);
gui_base->set_modulate(dim_color);
}
diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp
index 0c280b16b2..a749509ce4 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -1009,7 +1009,10 @@ void ScriptEditorDebugger::_performance_draw() {
Ref<Font> graph_font = get_font("font", "TextEdit");
if (which.empty()) {
- perf_draw->draw_string(graph_font, Point2(0, graph_font->get_ascent()), TTR("Pick one or more items from the list to display the graph."), get_color("font_color", "Label"), perf_draw->get_size().x);
+ String text = TTR("Pick one or more items from the list to display the graph.");
+
+ perf_draw->draw_string(graph_font, Point2i(MAX(0, perf_draw->get_size().x - graph_font->get_string_size(text).x), perf_draw->get_size().y + graph_font->get_ascent()) / 2, text, get_color("font_color", "Label"), perf_draw->get_size().x);
+
return;
}
diff --git a/main/input_default.cpp b/main/input_default.cpp
index a03d015fc3..caa5c10518 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -686,7 +686,8 @@ void InputDefault::release_pressed_events() {
_joy_axis.clear();
for (Map<StringName, InputDefault::Action>::Element *E = action_state.front(); E; E = E->next()) {
- action_release(E->key());
+ if (E->get().pressed)
+ action_release(E->key());
}
}
diff --git a/methods.py b/methods.py
index 7840fb1b64..86ab7cd9af 100644
--- a/methods.py
+++ b/methods.py
@@ -617,7 +617,11 @@ def detect_darwin_sdk_path(platform, env):
raise
def get_compiler_version(env):
- version = decode_utf8(subprocess.check_output([env['CXX'], '--version']).strip())
+ # Not using this method on clang because it returns 4.2.1 # https://reviews.llvm.org/D56803
+ if using_gcc(env):
+ version = decode_utf8(subprocess.check_output([env['CXX'], '-dumpversion']).strip())
+ else:
+ version = decode_utf8(subprocess.check_output([env['CXX'], '--version']).strip())
match = re.search('[0-9][0-9.]*', version)
if match is not None:
return match.group().split('.')
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index f3b9f7fb31..7f52f5736c 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -56,7 +56,7 @@ typedef enum {
GODOT_PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
GODOT_PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease)
GODOT_PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
- GODOT_PROPERTY_HINT_SPRITE_FRAME,
+ GODOT_PROPERTY_HINT_SPRITE_FRAME, // FIXME: Obsolete: drop whenever we can break compat
GODOT_PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
GODOT_PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
GODOT_PROPERTY_HINT_LAYERS_2D_RENDER,
@@ -98,8 +98,8 @@ typedef enum {
GODOT_PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings
GODOT_PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
GODOT_PROPERTY_USAGE_CATEGORY = 256,
- GODOT_PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero
- GODOT_PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false
+ GODOT_PROPERTY_USAGE_STORE_IF_NONZERO = 512, // FIXME: Obsolete: drop whenever we can break compat
+ GODOT_PROPERTY_USAGE_STORE_IF_NONONE = 1024, // FIXME: Obsolete: drop whenever we can break compat
GODOT_PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
GODOT_PROPERTY_USAGE_RESTART_IF_CHANGED = 4096,
GODOT_PROPERTY_USAGE_SCRIPT_VARIABLE = 8192,
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 62a3794c6d..f65f2a8935 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -1095,7 +1095,7 @@
<argument index="0" name="step" type="float">
</argument>
<description>
- Returns the position of the first non-zero digit, after the decimal point.
+ Returns the position of the first non-zero digit, after the decimal point. Note that the maximum return value is 10, which is a design decision in the implementation.
[codeblock]
# n is 0
n = step_decimals(5)
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 078a490b22..846c84d222 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -629,7 +629,6 @@ void CSharpLanguage::frame() {
if (exc) {
GDMonoUtils::debug_unhandled_exception(exc);
- GD_UNREACHABLE();
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 1edc426e00..233aab45b3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -85,7 +85,7 @@ namespace GodotTools.ProjectEditor
void AddPropertyIfNotPresent(string name, string condition, string value)
{
if (root.PropertyGroups
- .Any(g => g.Condition == string.Empty || g.Condition == condition &&
+ .Any(g => (g.Condition == string.Empty || g.Condition == condition) &&
g.Properties
.Any(p => p.Name == name &&
p.Value == value &&
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index f849356919..d8cb9024c3 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -137,7 +137,7 @@ namespace GodotTools.Build
private static string BuildArguments(string solution, string config, string loggerOutputDir, List<string> customProperties)
{
- string arguments = $@"""{solution}"" /v:normal /t:Rebuild ""/p:{"Configuration=" + config}"" " +
+ string arguments = $@"""{solution}"" /v:normal /t:Build ""/p:{"Configuration=" + config}"" " +
$@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{loggerOutputDir}""";
foreach (string customProperty in customProperties)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index f0068385f4..926aabdf89 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -15,7 +15,6 @@ namespace GodotTools.Build
{
private static string _msbuildToolsPath = string.Empty;
private static string _msbuildUnixPath = string.Empty;
- private static string _xbuildUnixPath = string.Empty;
public static string FindMsBuild()
{
@@ -44,7 +43,6 @@ namespace GodotTools.Build
return Path.Combine(_msbuildToolsPath, "MSBuild.exe");
}
-
case GodotSharpBuilds.BuildTool.MsBuildMono:
{
string msbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "msbuild.bat");
@@ -56,19 +54,6 @@ namespace GodotTools.Build
return msbuildPath;
}
-
- case GodotSharpBuilds.BuildTool.XBuild:
- {
- string xbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "xbuild.bat");
-
- if (!File.Exists(xbuildPath))
- {
- throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameXbuild}'. Tried with path: {xbuildPath}");
- }
-
- return xbuildPath;
- }
-
default:
throw new IndexOutOfRangeException("Invalid build tool in editor settings");
}
@@ -76,20 +61,7 @@ namespace GodotTools.Build
if (OS.IsUnix())
{
- if (buildTool == GodotSharpBuilds.BuildTool.XBuild)
- {
- if (_xbuildUnixPath.Empty() || !File.Exists(_xbuildUnixPath))
- {
- // Try to search it again if it wasn't found last time or if it was removed from its location
- _xbuildUnixPath = FindBuildEngineOnUnix("msbuild");
- }
-
- if (_xbuildUnixPath.Empty())
- {
- throw new FileNotFoundException($"Cannot find binary for '{GodotSharpBuilds.PropNameXbuild}'");
- }
- }
- else
+ if (buildTool == GodotSharpBuilds.BuildTool.MsBuildMono)
{
if (_msbuildUnixPath.Empty() || !File.Exists(_msbuildUnixPath))
{
@@ -101,9 +73,13 @@ namespace GodotTools.Build
{
throw new FileNotFoundException($"Cannot find binary for '{GodotSharpBuilds.PropNameMsbuildMono}'");
}
- }
- return buildTool != GodotSharpBuilds.BuildTool.XBuild ? _msbuildUnixPath : _xbuildUnixPath;
+ return _msbuildUnixPath;
+ }
+ else
+ {
+ throw new IndexOutOfRangeException("Invalid build tool in editor settings");
+ }
}
throw new PlatformNotSupportedException();
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs
index a884b0ead0..de3a4d9a6e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs
@@ -18,7 +18,6 @@ namespace GodotTools
public const string PropNameMsbuildMono = "MSBuild (Mono)";
public const string PropNameMsbuildVs = "MSBuild (VS Build Tools)";
- public const string PropNameXbuild = "xbuild (Deprecated)";
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
public const string MsBuildLogFileName = "msbuild_log.txt";
@@ -26,8 +25,7 @@ namespace GodotTools
public enum BuildTool
{
MsBuildMono,
- MsBuildVs,
- XBuild // Deprecated
+ MsBuildVs
}
private static void RemoveOldIssuesFile(MonoBuildInfo buildInfo)
@@ -64,7 +62,7 @@ namespace GodotTools
private static string GetIssuesFilePath(MonoBuildInfo buildInfo)
{
- return Path.Combine(Godot.ProjectSettings.LocalizePath(buildInfo.LogsDirPath), MsBuildIssuesFileName);
+ return Path.Combine(buildInfo.LogsDirPath, MsBuildIssuesFileName);
}
private static void PrintVerbose(string text)
@@ -202,6 +200,9 @@ namespace GodotTools
// case the user decided to delete them at some point after they were loaded.
Internal.UpdateApiAssembliesFromPrebuilt();
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
+
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
{
pr.Step("Building project solution", 0);
@@ -209,7 +210,7 @@ namespace GodotTools
var buildInfo = new MonoBuildInfo(GodotSharpDirs.ProjectSlnPath, config);
// Add Godot defines
- string constants = OS.IsWindows() ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
+ string constants = buildTool == BuildTool.MsBuildVs ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
foreach (var godotDefine in godotDefines)
constants += $"GODOT_{godotDefine.ToUpper().Replace("-", "_").Replace(" ", "_").Replace(";", "_")};";
@@ -217,7 +218,7 @@ namespace GodotTools
if (Internal.GodotIsRealTDouble())
constants += "GODOT_REAL_T_IS_DOUBLE;";
- constants += OS.IsWindows() ? "\"" : "\\\"";
+ constants += buildTool == BuildTool.MsBuildVs ? "\"" : "\\\"";
buildInfo.CustomProperties.Add(constants);
@@ -267,8 +268,8 @@ namespace GodotTools
["name"] = "mono/builds/build_tool",
["hint"] = Godot.PropertyHint.Enum,
["hint_string"] = OS.IsWindows() ?
- $"{PropNameMsbuildMono},{PropNameMsbuildVs},{PropNameXbuild}" :
- $"{PropNameMsbuildMono},{PropNameXbuild}"
+ $"{PropNameMsbuildMono},{PropNameMsbuildVs}" :
+ $"{PropNameMsbuildMono}"
});
EditorDef("mono/builds/print_build_output", false);
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 90dec43412..9b5afb94a3 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -298,7 +298,16 @@ namespace GodotTools
if (line >= 0)
scriptPath += $";{line + 1};{col}";
- GetMonoDevelopInstance(GodotSharpDirs.ProjectSlnPath).Execute(scriptPath);
+ try
+ {
+ GetMonoDevelopInstance(GodotSharpDirs.ProjectSlnPath).Execute(scriptPath);
+ }
+ catch (FileNotFoundException)
+ {
+ string editorName = editor == ExternalEditor.VisualStudioForMac ? "Visual Studio" : "MonoDevelop";
+ GD.PushError($"Cannot find code editor: {editorName}");
+ return Error.FileNotFound;
+ }
break;
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs
index 75fdacc0da..3a74fa2f66 100644
--- a/modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs
@@ -61,41 +61,48 @@ namespace GodotTools
{
using (var file = new Godot.File())
{
- Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read);
-
- if (openError != Error.Ok)
- return;
-
- while (!file.EofReached())
+ try
{
- string[] csvColumns = file.GetCsvLine();
+ Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read);
- if (csvColumns.Length == 1 && csvColumns[0].Empty())
+ if (openError != Error.Ok)
return;
- if (csvColumns.Length != 7)
+ while (!file.EofReached())
{
- GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
- continue;
+ string[] csvColumns = file.GetCsvLine();
+
+ if (csvColumns.Length == 1 && csvColumns[0].Empty())
+ return;
+
+ if (csvColumns.Length != 7)
+ {
+ GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
+ continue;
+ }
+
+ var issue = new BuildIssue
+ {
+ Warning = csvColumns[0] == "warning",
+ File = csvColumns[1],
+ Line = int.Parse(csvColumns[2]),
+ Column = int.Parse(csvColumns[3]),
+ Code = csvColumns[4],
+ Message = csvColumns[5],
+ ProjectFile = csvColumns[6]
+ };
+
+ if (issue.Warning)
+ WarningCount += 1;
+ else
+ ErrorCount += 1;
+
+ issues.Add(issue);
}
-
- var issue = new BuildIssue
- {
- Warning = csvColumns[0] == "warning",
- File = csvColumns[1],
- Line = int.Parse(csvColumns[2]),
- Column = int.Parse(csvColumns[3]),
- Code = csvColumns[4],
- Message = csvColumns[5],
- ProjectFile = csvColumns[6]
- };
-
- if (issue.Warning)
- WarningCount += 1;
- else
- ErrorCount += 1;
-
- issues.Add(issue);
+ }
+ finally
+ {
+ file.Close(); // Disposing it is not enough. We need to call Close()
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs b/modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs
index 0c8d86e799..61a0a992ce 100644
--- a/modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using GodotTools.Internals;
+using GodotTools.Utils;
namespace GodotTools
{
@@ -30,7 +31,7 @@ namespace GodotTools
if (Utils.OS.IsOSX())
{
- string bundleId = CodeEditorBundleIds[editorId];
+ string bundleId = BundleIds[editorId];
if (Internal.IsOsxAppBundleInstalled(bundleId))
{
@@ -47,12 +48,12 @@ namespace GodotTools
}
else
{
- command = CodeEditorPaths[editorId];
+ command = OS.PathWhich(ExecutableNames[editorId]);
}
}
else
{
- command = CodeEditorPaths[editorId];
+ command = OS.PathWhich(ExecutableNames[editorId]);
}
args.Add("--ipc-tcp");
@@ -70,6 +71,9 @@ namespace GodotTools
args.Add("\"" + Path.GetFullPath(filePath.NormalizePath()) + cursor + "\"");
}
+ if (command == null)
+ throw new FileNotFoundException();
+
if (newWindow)
{
process = Process.Start(new ProcessStartInfo
@@ -99,20 +103,20 @@ namespace GodotTools
this.editorId = editorId;
}
- private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorPaths;
- private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorBundleIds;
+ private static readonly IReadOnlyDictionary<EditorId, string> ExecutableNames;
+ private static readonly IReadOnlyDictionary<EditorId, string> BundleIds;
static MonoDevelopInstance()
{
if (Utils.OS.IsOSX())
{
- CodeEditorPaths = new Dictionary<EditorId, string>
+ ExecutableNames = new Dictionary<EditorId, string>
{
// Rely on PATH
{EditorId.MonoDevelop, "monodevelop"},
{EditorId.VisualStudioForMac, "VisualStudio"}
};
- CodeEditorBundleIds = new Dictionary<EditorId, string>
+ BundleIds = new Dictionary<EditorId, string>
{
// TODO EditorId.MonoDevelop
{EditorId.VisualStudioForMac, "com.microsoft.visual-studio"}
@@ -120,7 +124,7 @@ namespace GodotTools
}
else if (Utils.OS.IsWindows())
{
- CodeEditorPaths = new Dictionary<EditorId, string>
+ ExecutableNames = new Dictionary<EditorId, string>
{
// XamarinStudio is no longer a thing, and the latest version is quite old
// MonoDevelop is available from source only on Windows. The recommendation
@@ -131,7 +135,7 @@ namespace GodotTools
}
else if (Utils.OS.IsUnix())
{
- CodeEditorPaths = new Dictionary<EditorId, string>
+ ExecutableNames = new Dictionary<EditorId, string>
{
// Rely on PATH
{EditorId.MonoDevelop, "monodevelop"}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs
index 3ae6c10bbf..288c65de74 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs
@@ -10,8 +10,9 @@ namespace GodotTools.Utils
{
foreach (T elem in enumerable)
{
- if (predicate(elem) != null)
- return elem;
+ T result = predicate(elem);
+ if (result != null)
+ return result;
}
return orElse;
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 571bf598f3..45f79074be 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -283,6 +283,18 @@ void GDMono::initialize() {
add_mono_shared_libs_dir_to_path();
+ {
+ PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
+ vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
+ unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
+ ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
+
+ if (Engine::get_singleton()->is_editor_hint()) {
+ // Unhandled exceptions should not terminate the editor
+ unhandled_exception_policy = POLICY_LOG_ERROR;
+ }
+ }
+
GDMonoAssembly::initialize();
gdmono_profiler_init();
@@ -645,7 +657,14 @@ bool GDMono::_load_core_api_assembly() {
#ifdef TOOLS_ENABLED
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
- String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+
+ // If running the project manager, load it from the prebuilt API directory
+ String assembly_dir = !Main::is_project_manager() ?
+ GodotSharpDirs::get_res_assemblies_dir() :
+ GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
+
+ String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+
bool success = FileAccess::exists(assembly_path) &&
load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly);
#else
@@ -676,7 +695,14 @@ bool GDMono::_load_editor_api_assembly() {
return true;
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
- String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+
+ // If running the project manager, load it from the prebuilt API directory
+ String assembly_dir = !Main::is_project_manager() ?
+ GodotSharpDirs::get_res_assemblies_dir() :
+ GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
+
+ String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+
bool success = FileAccess::exists(assembly_path) &&
load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly);
@@ -728,6 +754,12 @@ void GDMono::_load_api_assemblies() {
// The API assemblies are out of sync. Fine, try one more time, but this time
// update them from the prebuilt assemblies directory before trying to load them.
+ // Shouldn't happen. The project manager loads the prebuilt API assemblies
+ if (Main::is_project_manager()) {
+ ERR_EXPLAIN("Failed to load one of the prebuilt API assemblies");
+ CRASH_NOW();
+ }
+
// 1. Unload the scripts domain
if (_unload_scripts_domain() != OK) {
ERR_EXPLAIN("Mono: Failed to unload scripts domain");
@@ -1063,6 +1095,8 @@ GDMono::GDMono() {
#ifdef TOOLS_ENABLED
api_editor_hash = 0;
#endif
+
+ unhandled_exception_policy = POLICY_TERMINATE_APP;
}
GDMono::~GDMono() {
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index deebe5fd50..c5bcce4fa1 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -80,6 +80,13 @@ String to_string(Type p_type);
class GDMono {
+public:
+ enum UnhandledExceptionPolicy {
+ POLICY_TERMINATE_APP,
+ POLICY_LOG_ERROR
+ };
+
+private:
bool runtime_initialized;
bool finalizing_scripts_domain;
@@ -102,6 +109,8 @@ class GDMono {
HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
+ UnhandledExceptionPolicy unhandled_exception_policy;
+
void _domain_assemblies_cleanup(uint32_t p_domain_id);
bool _are_api_assemblies_out_of_sync();
@@ -162,7 +171,9 @@ public:
static GDMono *get_singleton() { return singleton; }
- static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+ GD_NORETURN static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+
+ UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
// Do not use these, unless you know what you're doing
void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index a84332d4cd..e50e3b0794 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -108,9 +108,18 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
void unhandled_exception(MonoException *p_exc) {
mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
- // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
- GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
- GD_UNREACHABLE();
+
+ if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
+ // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
+ GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
+ GD_UNREACHABLE();
+ } else {
+#ifdef DEBUG_ENABLED
+ GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
+ if (ScriptDebugger::get_singleton())
+ ScriptDebugger::get_singleton()->idle_poll();
+#endif
+ }
}
} // namespace GDMonoInternals
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h
index 2d77bde27c..0d82723913 100644
--- a/modules/mono/mono_gd/gd_mono_internals.h
+++ b/modules/mono/mono_gd/gd_mono_internals.h
@@ -45,7 +45,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
* Do not call this function directly.
* Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
*/
-GD_NORETURN void unhandled_exception(MonoException *p_exc);
+void unhandled_exception(MonoException *p_exc);
} // namespace GDMonoInternals
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 5987fa8ebb..7afdfc8ac8 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -37,6 +37,10 @@
#include "core/project_settings.h"
#include "core/reference.h"
+#ifdef TOOLS_ENABLED
+#include "editor/script_editor_debugger.h"
+#endif
+
#include "../csharp_script.h"
#include "../utils/macros.h"
#include "../utils/mutex_utils.h"
@@ -596,8 +600,14 @@ void debug_print_unhandled_exception(MonoException *p_exc) {
void debug_send_unhandled_exception_error(MonoException *p_exc) {
#ifdef DEBUG_ENABLED
- if (!ScriptDebugger::get_singleton())
+ if (!ScriptDebugger::get_singleton()) {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ ERR_PRINTS(GDMonoUtils::get_exception_name_and_message(p_exc));
+ }
+#endif
return;
+ }
_TLS_RECURSION_GUARD_;
@@ -621,7 +631,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
if (unexpected_exc) {
GDMonoInternals::unhandled_exception(unexpected_exc);
- GD_UNREACHABLE();
+ return;
}
Vector<ScriptLanguage::StackInfo> _si;
@@ -655,7 +665,6 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
void debug_unhandled_exception(MonoException *p_exc) {
GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
- GD_UNREACHABLE();
}
void print_unhandled_exception(MonoException *p_exc) {
@@ -665,11 +674,9 @@ void print_unhandled_exception(MonoException *p_exc) {
void set_pending_exception(MonoException *p_exc) {
#ifdef NO_PENDING_EXCEPTIONS
debug_unhandled_exception(p_exc);
- GD_UNREACHABLE();
#else
if (get_runtime_invoke_count() == 0) {
debug_unhandled_exception(p_exc);
- GD_UNREACHABLE();
}
if (!mono_runtime_set_pending_exception(p_exc, false)) {
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index f535fbb6d0..d73743bf0b 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -289,7 +289,7 @@ void set_exception_message(MonoException *p_exc, String message);
void debug_print_unhandled_exception(MonoException *p_exc);
void debug_send_unhandled_exception_error(MonoException *p_exc);
-GD_NORETURN void debug_unhandled_exception(MonoException *p_exc);
+void debug_unhandled_exception(MonoException *p_exc);
void print_unhandled_exception(MonoException *p_exc);
/**
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 726882438b..992aff54f1 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -1599,6 +1599,7 @@ void OS_OSX::finalize() {
memdelete(joypad_osx);
memdelete(input);
+ cursors_cache.clear();
visual_server->finish();
memdelete(visual_server);
//memdelete(rasterizer);
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 745f3ce379..4c7e35ed88 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1537,6 +1537,7 @@ void OS_Windows::finalize() {
memdelete(camera_server);
touch_state.clear();
+ cursors_cache.clear();
visual_server->finish();
memdelete(visual_server);
#ifdef OPENGL_ENABLED
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index 9b35648046..36bf51e7f9 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -788,6 +788,7 @@ void OS_X11::finalize() {
memdelete(camera_server);
+ cursors_cache.clear();
visual_server->finish();
memdelete(visual_server);
//memdelete(rasterizer);
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index c7f622dee3..83cc1eeb46 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "animated_sprite.h"
+
#include "core/os/os.h"
#include "scene/scene_string_names.h"
@@ -356,12 +357,11 @@ void AnimatedSprite::_validate_property(PropertyInfo &property) const {
}
if (property.name == "frame") {
-
- property.hint = PROPERTY_HINT_SPRITE_FRAME;
-
+ property.hint = PROPERTY_HINT_RANGE;
if (frames->has_animation(animation) && frames->get_frame_count(animation) > 1) {
property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1";
}
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
}
@@ -709,7 +709,7 @@ void AnimatedSprite::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index 6626fccf1c..dbe47e89ec 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -371,10 +371,9 @@ Rect2 Sprite::get_rect() const {
void Sprite::_validate_property(PropertyInfo &property) const {
if (property.name == "frame") {
-
- property.hint = PROPERTY_HINT_SPRITE_FRAME;
-
+ property.hint = PROPERTY_HINT_RANGE;
property.hint_string = "0," + itos(vframes * hframes - 1) + ",1";
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
}
@@ -442,7 +441,7 @@ void Sprite::_bind_methods() {
ADD_GROUP("Animation", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_GROUP("Region", "region_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region");
diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp
index 9d3e2983c4..63301fc226 100644
--- a/scene/3d/collision_object.cpp
+++ b/scene/3d/collision_object.cpp
@@ -370,7 +370,7 @@ String CollisionObject::get_configuration_warning() const {
String warning = Spatial::get_configuration_warning();
if (shapes.empty()) {
- if (warning == String()) {
+ if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape or CollisionPolygon as a child to define its shape.");
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 2c315790ac..b39762fde8 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -628,10 +628,9 @@ Rect2 Sprite3D::get_item_rect() const {
void Sprite3D::_validate_property(PropertyInfo &property) const {
if (property.name == "frame") {
-
- property.hint = PROPERTY_HINT_SPRITE_FRAME;
-
+ property.hint = PROPERTY_HINT_RANGE;
property.hint_string = "0," + itos(vframes * hframes - 1) + ",1";
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
}
@@ -659,7 +658,7 @@ void Sprite3D::_bind_methods() {
ADD_GROUP("Animation", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_GROUP("Region", "region_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region");
ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
@@ -851,14 +850,11 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &property) const {
}
if (property.name == "frame") {
-
property.hint = PROPERTY_HINT_RANGE;
-
- if (frames->has_animation(animation)) {
+ if (frames->has_animation(animation) && frames->get_frame_count(animation) > 1) {
property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1";
- } else {
- property.hint_string = "0,0,0";
}
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
}
@@ -1091,7 +1087,7 @@ void AnimatedSprite3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
}
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index 89e96e0227..98e68cfbda 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -272,6 +272,20 @@ void VehicleWheel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rpm"), &VehicleWheel::get_rpm);
+ ClassDB::bind_method(D_METHOD("set_engine_force", "engine_force"), &VehicleWheel::set_engine_force);
+ ClassDB::bind_method(D_METHOD("get_engine_force"), &VehicleWheel::get_engine_force);
+
+ ClassDB::bind_method(D_METHOD("set_brake", "brake"), &VehicleWheel::set_brake);
+ ClassDB::bind_method(D_METHOD("get_brake"), &VehicleWheel::get_brake);
+
+ ClassDB::bind_method(D_METHOD("set_steering", "steering"), &VehicleWheel::set_steering);
+ ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel::get_steering);
+
+ ADD_GROUP("Per-Wheel Motion", "");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering");
+ ADD_GROUP("VehicleBody Motion", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_traction"), "set_use_as_traction", "is_used_as_traction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_as_steering"), "set_use_as_steering", "is_used_as_steering");
ADD_GROUP("Wheel", "wheel_");
@@ -288,6 +302,34 @@ void VehicleWheel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping_relaxation"), "set_damping_relaxation", "get_damping_relaxation");
}
+void VehicleWheel::set_engine_force(float p_engine_force) {
+
+ m_engineForce = p_engine_force;
+}
+
+float VehicleWheel::get_engine_force() const {
+
+ return m_engineForce;
+}
+
+void VehicleWheel::set_brake(float p_brake) {
+
+ m_brake = p_brake;
+}
+float VehicleWheel::get_brake() const {
+
+ return m_brake;
+}
+
+void VehicleWheel::set_steering(float p_steering) {
+
+ m_steering = p_steering;
+}
+float VehicleWheel::get_steering() const {
+
+ return m_steering;
+}
+
void VehicleWheel::set_use_as_traction(bool p_enable) {
engine_traction = p_enable;
@@ -374,10 +416,7 @@ void VehicleBody::_update_wheel(int p_idx, PhysicsDirectBodyState *s) {
Vector3 fwd = up.cross(right);
fwd = fwd.normalized();
- //rotate around steering over de wheelAxleWS
- real_t steering = wheel.steers ? m_steeringValue : 0.0;
-
- Basis steeringMat(up, steering);
+ Basis steeringMat(up, wheel.m_steering);
Basis rotatingMat(right, wheel.m_rotation);
@@ -723,12 +762,11 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
real_t rollingFriction = 0.f;
if (wheelInfo.m_raycastInfo.m_isInContact) {
- if (engine_force != 0.f && wheelInfo.engine_traction) {
- rollingFriction = -engine_force * s->get_step();
+ if (wheelInfo.m_engineForce != 0.f) {
+ rollingFriction = -wheelInfo.m_engineForce * s->get_step();
} else {
real_t defaultRollingFrictionImpulse = 0.f;
- float cbrake = MAX(wheelInfo.m_brake, brake);
- real_t maxImpulse = cbrake ? cbrake : defaultRollingFrictionImpulse;
+ real_t maxImpulse = wheelInfo.m_brake ? wheelInfo.m_brake : defaultRollingFrictionImpulse;
btVehicleWheelContactPoint contactPt(s, wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, m_forwardWS[wheel], maxImpulse);
rollingFriction = _calc_rolling_friction(contactPt);
}
@@ -886,6 +924,11 @@ void VehicleBody::_direct_state_changed(Object *p_state) {
void VehicleBody::set_engine_force(float p_engine_force) {
engine_force = p_engine_force;
+ for (int i = 0; i < wheels.size(); i++) {
+ VehicleWheel &wheelInfo = *wheels[i];
+ if (wheelInfo.engine_traction)
+ wheelInfo.m_engineForce = p_engine_force;
+ }
}
float VehicleBody::get_engine_force() const {
@@ -896,6 +939,10 @@ float VehicleBody::get_engine_force() const {
void VehicleBody::set_brake(float p_brake) {
brake = p_brake;
+ for (int i = 0; i < wheels.size(); i++) {
+ VehicleWheel &wheelInfo = *wheels[i];
+ wheelInfo.m_brake = p_brake;
+ }
}
float VehicleBody::get_brake() const {
@@ -905,6 +952,11 @@ float VehicleBody::get_brake() const {
void VehicleBody::set_steering(float p_steering) {
m_steeringValue = p_steering;
+ for (int i = 0; i < wheels.size(); i++) {
+ VehicleWheel &wheelInfo = *wheels[i];
+ if (wheelInfo.steers)
+ wheelInfo.m_steering = p_steering;
+ }
}
float VehicleBody::get_steering() const {
diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h
index 9e3fe72282..914bfd54bd 100644
--- a/scene/3d/vehicle_body.h
+++ b/scene/3d/vehicle_body.h
@@ -70,7 +70,7 @@ class VehicleWheel : public Spatial {
real_t m_deltaRotation;
real_t m_rpm;
real_t m_rollInfluence;
- //real_t m_engineForce;
+ real_t m_engineForce;
real_t m_brake;
real_t m_clippedInvContactDotSuspension;
@@ -137,6 +137,15 @@ public:
float get_rpm() const;
+ void set_engine_force(float p_engine_force);
+ float get_engine_force() const;
+
+ void set_brake(float p_brake);
+ float get_brake() const;
+
+ void set_steering(float p_steering);
+ float get_steering() const;
+
String get_configuration_warning() const;
VehicleWheel();
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 26be17f6c1..1d6f3dc782 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -850,6 +850,12 @@ Ref<Texture> Control::get_icon(const StringName &p_name, const StringName &p_typ
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_icon(p_name, type)) {
+ return Theme::get_project_default()->get_icon(p_name, type);
+ }
+ }
+
return Theme::get_default()->get_icon(p_name, type);
}
@@ -886,6 +892,12 @@ Ref<Shader> Control::get_shader(const StringName &p_name, const StringName &p_ty
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_shader(p_name, type)) {
+ return Theme::get_project_default()->get_shader(p_name, type);
+ }
+ }
+
return Theme::get_default()->get_shader(p_name, type);
}
@@ -925,6 +937,9 @@ Ref<StyleBox> Control::get_stylebox(const StringName &p_name, const StringName &
}
while (class_name != StringName()) {
+ if (Theme::get_project_default().is_valid() && Theme::get_project_default()->has_stylebox(p_name, type))
+ return Theme::get_project_default()->get_stylebox(p_name, type);
+
if (Theme::get_default()->has_stylebox(p_name, class_name))
return Theme::get_default()->get_stylebox(p_name, class_name);
@@ -1001,6 +1016,11 @@ Color Control::get_color(const StringName &p_name, const StringName &p_type) con
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_color(p_name, type)) {
+ return Theme::get_project_default()->get_color(p_name, type);
+ }
+ }
return Theme::get_default()->get_color(p_name, type);
}
@@ -1036,6 +1056,11 @@ int Control::get_constant(const StringName &p_name, const StringName &p_type) co
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_constant(p_name, type)) {
+ return Theme::get_project_default()->get_constant(p_name, type);
+ }
+ }
return Theme::get_default()->get_constant(p_name, type);
}
@@ -1106,6 +1131,11 @@ bool Control::has_icon(const StringName &p_name, const StringName &p_type) const
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_color(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_icon(p_name, type);
}
@@ -1140,6 +1170,11 @@ bool Control::has_shader(const StringName &p_name, const StringName &p_type) con
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_shader(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_shader(p_name, type);
}
bool Control::has_stylebox(const StringName &p_name, const StringName &p_type) const {
@@ -1173,6 +1208,11 @@ bool Control::has_stylebox(const StringName &p_name, const StringName &p_type) c
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_stylebox(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_stylebox(p_name, type);
}
bool Control::has_font(const StringName &p_name, const StringName &p_type) const {
@@ -1206,6 +1246,11 @@ bool Control::has_font(const StringName &p_name, const StringName &p_type) const
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_font(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_font(p_name, type);
}
@@ -1240,6 +1285,11 @@ bool Control::has_color(const StringName &p_name, const StringName &p_type) cons
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_color(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_color(p_name, type);
}
@@ -1274,6 +1324,11 @@ bool Control::has_constant(const StringName &p_name, const StringName &p_type) c
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_constant(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_constant(p_name, type);
}
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 04fb991f78..f1bdbb5ff5 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -403,11 +403,10 @@ void FileDialog::update_file_list() {
List<String> files;
List<String> dirs;
- bool is_dir;
bool is_hidden;
String item;
- while ((item = dir_access->get_next(&is_dir)) != "") {
+ while ((item = dir_access->get_next()) != "") {
if (item == "." || item == "..")
continue;
@@ -415,7 +414,7 @@ void FileDialog::update_file_list() {
is_hidden = dir_access->current_is_hidden();
if (show_hidden_files || !is_hidden) {
- if (!is_dir)
+ if (!dir_access->current_is_dir())
files.push_back(item);
else
dirs.push_back(item);
@@ -880,14 +879,14 @@ FileDialog::FileDialog() {
dir->set_h_size_flags(SIZE_EXPAND_FILL);
refresh = memnew(ToolButton);
- refresh->set_tooltip(RTR("Refresh"));
+ refresh->set_tooltip(RTR("Refresh files."));
refresh->connect("pressed", this, "_update_file_list");
hbc->add_child(refresh);
show_hidden = memnew(ToolButton);
show_hidden->set_toggle_mode(true);
show_hidden->set_pressed(is_showing_hidden_files());
- show_hidden->set_tooltip(RTR("Toggle Hidden Files"));
+ show_hidden->set_tooltip(RTR("Toggle the visibility of hidden files."));
show_hidden->connect("toggled", this, "set_show_hidden_files");
hbc->add_child(show_hidden);
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 279253889c..db277d3705 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -40,7 +40,7 @@ Size2 SpinBox::get_minimum_size() const {
void SpinBox::_value_changed(double) {
- String value = String::num(get_value(), Math::step_decimals(get_step()));
+ String value = String::num(get_value(), Math::range_step_decimals(get_step()));
if (prefix != "")
value = prefix + " " + value;
if (suffix != "")
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 814100afdc..81f2f46a80 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1315,7 +1315,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
Ref<Texture> updown = cache.updown;
- String valtext = String::num(p_item->cells[i].val, Math::step_decimals(p_item->cells[i].step));
+ String valtext = String::num(p_item->cells[i].val, Math::range_step_decimals(p_item->cells[i].step));
//String valtext = rtos( p_item->cells[i].val );
if (p_item->cells[i].suffix != String())
@@ -1926,7 +1926,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
} else {
- editor_text = String::num(p_item->cells[col].val, Math::step_decimals(p_item->cells[col].step));
+ editor_text = String::num(p_item->cells[col].val, Math::range_step_decimals(p_item->cells[col].step));
if (select_mode == SELECT_MULTI && get_tree()->get_event_count() == focus_in_id)
bring_up_editor = false;
}
@@ -2755,7 +2755,7 @@ bool Tree::edit_selected() {
text_editor->set_position(textedpos);
text_editor->set_size(rect.size);
text_editor->clear();
- text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::step_decimals(c.step)));
+ text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
text_editor->select_all();
if (c.mode == TreeItem::CELL_MODE_RANGE) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 2533d91156..974c9771e0 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -607,6 +607,7 @@ void register_scene_types() {
ClassDB::register_class<PrismMesh>();
ClassDB::register_class<QuadMesh>();
ClassDB::register_class<SphereMesh>();
+ ClassDB::register_class<PointMesh>();
ClassDB::register_virtual_class<Material>();
ClassDB::register_class<SpatialMaterial>();
SceneTree::add_idle_callback(SpatialMaterial::flush_changes);
@@ -751,7 +752,7 @@ void register_scene_types() {
if (theme_path != String()) {
Ref<Theme> theme = ResourceLoader::load(theme_path);
if (theme.is_valid()) {
- Theme::set_default(theme);
+ Theme::set_project_default(theme);
if (font.is_valid()) {
Theme::set_default_font(font);
}
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 74a493d3b5..24fdaafbe1 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -1572,3 +1572,19 @@ SphereMesh::SphereMesh() {
rings = 32;
is_hemisphere = false;
}
+
+/**
+ PointMesh
+*/
+
+void PointMesh::_create_mesh_array(Array &p_arr) const {
+ PoolVector<Vector3> faces;
+ faces.resize(1);
+ faces.set(0, Vector3(0.0, 0.0, 0.0));
+
+ p_arr[VS::ARRAY_VERTEX] = faces;
+}
+
+PointMesh::PointMesh() {
+ primitive_type = PRIMITIVE_POINTS;
+}
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 312899c028..fad49f9642 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -322,4 +322,19 @@ public:
SphereMesh();
};
+/**
+ A single point for use in particle systems
+*/
+
+class PointMesh : public PrimitiveMesh {
+
+ GDCLASS(PointMesh, PrimitiveMesh)
+
+protected:
+ virtual void _create_mesh_array(Array &p_arr) const;
+
+public:
+ PointMesh();
+};
+
#endif
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 69258bc834..ae18be1695 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -32,8 +32,6 @@
#include "core/os/file_access.h"
#include "core/print_string.h"
-Ref<Theme> Theme::default_theme;
-
void Theme::_emit_theme_changed() {
emit_changed();
@@ -186,11 +184,6 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
-Ref<Theme> Theme::get_default() {
-
- return default_theme;
-}
-
void Theme::set_default_theme_font(const Ref<Font> &p_default_font) {
if (default_theme_font == p_default_font)
@@ -215,14 +208,31 @@ Ref<Font> Theme::get_default_theme_font() const {
return default_theme_font;
}
+Ref<Theme> Theme::project_default_theme;
+Ref<Theme> Theme::default_theme;
+Ref<Texture> Theme::default_icon;
+Ref<StyleBox> Theme::default_style;
+Ref<Font> Theme::default_font;
+
+Ref<Theme> Theme::get_default() {
+
+ return default_theme;
+}
+
void Theme::set_default(const Ref<Theme> &p_default) {
default_theme = p_default;
}
-Ref<Texture> Theme::default_icon;
-Ref<StyleBox> Theme::default_style;
-Ref<Font> Theme::default_font;
+Ref<Theme> Theme::get_project_default() {
+
+ return project_default_theme;
+}
+
+void Theme::set_project_default(const Ref<Theme> &p_project_default) {
+
+ project_default_theme = p_project_default;
+}
void Theme::set_default_icon(const Ref<Texture> &p_icon) {
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index fb59073cbe..4c4f9b5aba 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -46,7 +46,6 @@ class Theme : public Resource {
GDCLASS(Theme, Resource);
RES_BASE_EXTENSION("theme");
- static Ref<Theme> default_theme;
void _emit_theme_changed();
HashMap<StringName, HashMap<StringName, Ref<Texture> > > icon_map;
@@ -61,6 +60,8 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ static Ref<Theme> project_default_theme;
+ static Ref<Theme> default_theme;
static Ref<Texture> default_icon;
static Ref<StyleBox> default_style;
static Ref<Font> default_font;
@@ -137,6 +138,9 @@ public:
static Ref<Theme> get_default();
static void set_default(const Ref<Theme> &p_default);
+ static Ref<Theme> get_project_default();
+ static void set_project_default(const Ref<Theme> &p_default);
+
static void set_default_icon(const Ref<Texture> &p_icon);
static void set_default_style(const Ref<StyleBox> &p_style);
static void set_default_font(const Ref<Font> &p_font);