Browse Source

Add inspector plugin for key time edit & Change find key argument

Silc Renew 2 years ago
parent
commit
060fb2d093

+ 11 - 2
doc/classes/Animation.xml

@@ -305,9 +305,9 @@
 			<return type="int" />
 			<return type="int" />
 			<param index="0" name="track_idx" type="int" />
 			<param index="0" name="track_idx" type="int" />
 			<param index="1" name="time" type="float" />
 			<param index="1" name="time" type="float" />
-			<param index="2" name="exact" type="bool" default="false" />
+			<param index="2" name="find_mode" type="int" enum="Animation.FindMode" default="0" />
 			<description>
 			<description>
-				Finds the key index by time in a given track. Optionally, only find it if the exact time is given.
+				Finds the key index by time in a given track. Optionally, only find it if the approx/exact time is given.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="track_get_interpolation_loop_wrap" qualifiers="const">
 		<method name="track_get_interpolation_loop_wrap" qualifiers="const">
@@ -622,5 +622,14 @@
 		<constant name="LOOPED_FLAG_START" value="2" enum="LoopedFlag">
 		<constant name="LOOPED_FLAG_START" value="2" enum="LoopedFlag">
 			This flag indicates that the animation has reached the start of the animation and just after loop processed.
 			This flag indicates that the animation has reached the start of the animation and just after loop processed.
 		</constant>
 		</constant>
+		<constant name="FIND_MODE_NEAREST" value="0" enum="FindMode">
+			Finds the nearest time key.
+		</constant>
+		<constant name="FIND_MODE_APPROX" value="1" enum="FindMode">
+			Finds only the key with approximating the time.
+		</constant>
+		<constant name="FIND_MODE_EXACT" value="2" enum="FindMode">
+			Finds only the key with matching the time.
+		</constant>
 	</constants>
 	</constants>
 </class>
 </class>

+ 22 - 0
doc/classes/EditorSpinSlider.xml

@@ -28,4 +28,26 @@
 			The suffix to display after the value (in a faded color). This should generally be a plural word. You may have to use an abbreviation if the suffix is too long to be displayed.
 			The suffix to display after the value (in a faded color). This should generally be a plural word. You may have to use an abbreviation if the suffix is too long to be displayed.
 		</member>
 		</member>
 	</members>
 	</members>
+	<signals>
+		<signal name="grabbed">
+			<description>
+				Emitted when the spinner/slider is grabbed.
+			</description>
+		</signal>
+		<signal name="ungrabbed">
+			<description>
+				Emitted when the spinner/slider is ungrabbed.
+			</description>
+		</signal>
+		<signal name="value_focus_entered">
+			<description>
+				Emitted when the value form gains focus.
+			</description>
+		</signal>
+		<signal name="value_focus_exited">
+			<description>
+				Emitted when the value form loses focus.
+			</description>
+		</signal>
+	</signals>
 </class>
 </class>

+ 9 - 9
editor/animation_bezier_editor.cpp

@@ -575,7 +575,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
 							ep.point_rect.size = bezier_icon->get_size();
 							ep.point_rect.size = bezier_icon->get_size();
 							if (selection.has(IntPair(i, j))) {
 							if (selection.has(IntPair(i, j))) {
 								draw_texture(selected_icon, ep.point_rect.position);
 								draw_texture(selected_icon, ep.point_rect.position);
-								draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
+								draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.0001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
 								draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
 								draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
 							} else {
 							} else {
 								Color track_color = Color(1, 1, 1, 1);
 								Color track_color = Color(1, 1, 1, 1);
@@ -812,7 +812,7 @@ void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int
 		return;
 		return;
 	}
 	}
 
 
-	int idx = animation->track_find_key(p_track, p_pos, true);
+	int idx = animation->track_find_key(p_track, p_pos, Animation::FIND_MODE_APPROX);
 	ERR_FAIL_COND(idx < 0);
 	ERR_FAIL_COND(idx < 0);
 
 
 	selection.insert(IntPair(p_track, idx));
 	selection.insert(IntPair(p_track, idx));
@@ -1168,8 +1168,8 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
 			new_point[4] = 0;
 			new_point[4] = 0;
 
 
 			real_t time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
 			real_t time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
-			while (animation->track_find_key(selected_track, time, true) != -1) {
-				time += 0.001;
+			while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) {
+				time += 0.0001;
 			}
 			}
 
 
 			Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
 			Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
@@ -1179,7 +1179,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
 			undo_redo->commit_action();
 			undo_redo->commit_action();
 
 
 			//then attempt to move
 			//then attempt to move
-			int index = animation->track_find_key(selected_track, time, true);
+			int index = animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX);
 			ERR_FAIL_COND(index == -1);
 			ERR_FAIL_COND(index == -1);
 			_clear_selection();
 			_clear_selection();
 			selection.insert(IntPair(selected_track, index));
 			selection.insert(IntPair(selected_track, index));
@@ -1283,7 +1283,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
 				for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
 				for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
 					real_t newtime = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x);
 					real_t newtime = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x);
 
 
-					int idx = animation->track_find_key(E->get().first, newtime, true);
+					int idx = animation->track_find_key(E->get().first, newtime, Animation::FIND_MODE_APPROX);
 					if (idx == -1) {
 					if (idx == -1) {
 						continue;
 						continue;
 					}
 					}
@@ -1539,7 +1539,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
 
 
 				real_t time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
 				real_t time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
 
 
-				while (animation->track_find_key(selected_track, time, true) != -1) {
+				while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) {
 					time += 0.001;
 					time += 0.001;
 				}
 				}
 
 
@@ -1599,7 +1599,7 @@ void AnimationBezierTrackEdit::duplicate_selection() {
 	for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
 	for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
 		real_t t = animation->track_get_key_time(E->get().first, E->get().second);
 		real_t t = animation->track_get_key_time(E->get().first, E->get().second);
 		real_t dst_time = t + (timeline->get_play_position() - top_time);
 		real_t dst_time = t + (timeline->get_play_position() - top_time);
-		int existing_idx = animation->track_find_key(E->get().first, dst_time, true);
+		int existing_idx = animation->track_find_key(E->get().first, dst_time, Animation::FIND_MODE_APPROX);
 
 
 		undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, E->get().second), animation->track_get_key_transition(E->get().first, E->get().second));
 		undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, E->get().second), animation->track_get_key_transition(E->get().first, E->get().second));
 		undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, dst_time);
 		undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, dst_time);
@@ -1623,7 +1623,7 @@ void AnimationBezierTrackEdit::duplicate_selection() {
 		int track = E.first;
 		int track = E.first;
 		real_t time = E.second;
 		real_t time = E.second;
 
 
-		int existing_idx = animation->track_find_key(track, time, true);
+		int existing_idx = animation->track_find_key(track, time, Animation::FIND_MODE_APPROX);
 
 
 		if (existing_idx == -1) {
 		if (existing_idx == -1) {
 			continue;
 			continue;

File diff suppressed because it is too large
+ 923 - 1097
editor/animation_track_editor.cpp


+ 100 - 2
editor/animation_track_editor.h

@@ -50,9 +50,83 @@
 #include "scene/resources/animation.h"
 #include "scene/resources/animation.h"
 #include "scene_tree_editor.h"
 #include "scene_tree_editor.h"
 
 
+class AnimationTrackEditor;
 class AnimationTrackEdit;
 class AnimationTrackEdit;
 class ViewPanner;
 class ViewPanner;
 
 
+class AnimationTrackKeyEdit : public Object {
+	GDCLASS(AnimationTrackKeyEdit, Object);
+
+public:
+	bool setting = false;
+	bool animation_read_only = false;
+
+	Ref<Animation> animation;
+	int track = -1;
+	float key_ofs = 0;
+	Node *root_path = nullptr;
+
+	PropertyInfo hint;
+	NodePath base;
+	bool use_fps = false;
+
+	bool _hide_script_from_inspector() { return true; }
+	bool _hide_metadata_from_inspector() { return true; }
+	bool _dont_undo_redo() { return true; }
+
+	bool _is_read_only() { return animation_read_only; }
+
+	void notify_change();
+	Node *get_root_path();
+	void set_use_fps(bool p_enable);
+
+protected:
+	static void _bind_methods();
+	void _fix_node_path(Variant &value);
+	void _update_obj(const Ref<Animation> &p_anim);
+	void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to);
+	bool _set(const StringName &p_name, const Variant &p_value);
+	bool _get(const StringName &p_name, Variant &r_ret) const;
+	void _get_property_list(List<PropertyInfo> *p_list) const;
+};
+
+class AnimationMultiTrackKeyEdit : public Object {
+	GDCLASS(AnimationMultiTrackKeyEdit, Object);
+
+public:
+	bool setting = false;
+	bool animation_read_only = false;
+
+	Ref<Animation> animation;
+
+	RBMap<int, List<float>> key_ofs_map;
+	RBMap<int, NodePath> base_map;
+	PropertyInfo hint;
+
+	Node *root_path = nullptr;
+
+	bool use_fps = false;
+
+	bool _hide_script_from_inspector() { return true; }
+	bool _hide_metadata_from_inspector() { return true; }
+	bool _dont_undo_redo() { return true; }
+
+	bool _is_read_only() { return animation_read_only; }
+
+	void notify_change();
+	Node *get_root_path();
+	void set_use_fps(bool p_enable);
+
+protected:
+	static void _bind_methods();
+	void _fix_node_path(Variant &value, NodePath &base);
+	void _update_obj(const Ref<Animation> &p_anim);
+	void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to);
+	bool _set(const StringName &p_name, const Variant &p_value);
+	bool _get(const StringName &p_name, Variant &r_ret) const;
+	void _get_property_list(List<PropertyInfo> *p_list) const;
+};
+
 class AnimationTimelineEdit : public Range {
 class AnimationTimelineEdit : public Range {
 	GDCLASS(AnimationTimelineEdit, Range);
 	GDCLASS(AnimationTimelineEdit, Range);
 
 
@@ -129,8 +203,6 @@ public:
 	AnimationTimelineEdit();
 	AnimationTimelineEdit();
 };
 };
 
 
-class AnimationTrackEditor;
-
 class AnimationTrackEdit : public Control {
 class AnimationTrackEdit : public Control {
 	GDCLASS(AnimationTrackEdit, Control);
 	GDCLASS(AnimationTrackEdit, Control);
 	friend class AnimationTimelineEdit;
 	friend class AnimationTimelineEdit;
@@ -592,4 +664,30 @@ public:
 	~AnimationTrackEditor();
 	~AnimationTrackEditor();
 };
 };
 
 
+// AnimationTrackKeyEditEditorPlugin
+
+class AnimationTrackKeyEditEditor : public EditorProperty {
+	GDCLASS(AnimationTrackKeyEditEditor, EditorProperty);
+
+	Ref<Animation> animation;
+	int track = -1;
+	real_t key_ofs = 0.0;
+	bool use_fps = false;
+
+	EditorSpinSlider *spinner = nullptr;
+
+	struct KeyDataCache {
+		real_t time = 0.0;
+		float transition = 0.0;
+		Variant value;
+	} key_data_cache;
+
+	void _time_edit_entered();
+	void _time_edit_exited();
+
+public:
+	AnimationTrackKeyEditEditor(Ref<Animation> p_animation, int p_track, real_t p_key_ofs, bool p_use_fps);
+	~AnimationTrackKeyEditEditor();
+};
+
 #endif // ANIMATION_TRACK_EDITOR_H
 #endif // ANIMATION_TRACK_EDITOR_H

+ 6 - 6
editor/animation_track_editor_plugins.cpp

@@ -831,8 +831,8 @@ Rect2 AnimationTrackEditTypeAudio::get_key_rect(int p_index, float p_pixels_sec)
 
 
 	len -= end_ofs;
 	len -= end_ofs;
 	len -= start_ofs;
 	len -= start_ofs;
-	if (len <= 0.001) {
-		len = 0.001;
+	if (len <= 0.0001) {
+		len = 0.0001;
 	}
 	}
 
 
 	if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
 	if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
@@ -887,8 +887,8 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int
 	len -= end_ofs;
 	len -= end_ofs;
 	len -= start_ofs;
 	len -= start_ofs;
 
 
-	if (len <= 0.001) {
-		len = 0.001;
+	if (len <= 0.0001) {
+		len = 0.0001;
 	}
 	}
 
 
 	int pixel_len = len * p_pixels_sec;
 	int pixel_len = len * p_pixels_sec;
@@ -1014,8 +1014,8 @@ void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant
 
 
 			ofs = get_editor()->snap_time(ofs);
 			ofs = get_editor()->snap_time(ofs);
 
 
-			while (get_animation()->track_find_key(get_track(), ofs, true) != -1) { //make sure insertion point is valid
-				ofs += 0.001;
+			while (get_animation()->track_find_key(get_track(), ofs, Animation::FIND_MODE_APPROX) != -1) { //make sure insertion point is valid
+				ofs += 0.0001;
 			}
 			}
 
 
 			Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
 			Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();

+ 1 - 0
editor/editor_node.cpp

@@ -7262,6 +7262,7 @@ EditorNode::EditorNode() {
 	gui_base->add_child(disk_changed);
 	gui_base->add_child(disk_changed);
 
 
 	add_editor_plugin(memnew(AnimationPlayerEditorPlugin));
 	add_editor_plugin(memnew(AnimationPlayerEditorPlugin));
+	add_editor_plugin(memnew(AnimationTrackKeyEditEditorPlugin));
 	add_editor_plugin(memnew(CanvasItemEditorPlugin));
 	add_editor_plugin(memnew(CanvasItemEditorPlugin));
 	add_editor_plugin(memnew(Node3DEditorPlugin));
 	add_editor_plugin(memnew(Node3DEditorPlugin));
 	add_editor_plugin(memnew(ScriptEditorPlugin));
 	add_editor_plugin(memnew(ScriptEditorPlugin));

+ 13 - 14
editor/editor_properties.cpp

@@ -1384,10 +1384,11 @@ void EditorPropertyInteger::update_property() {
 void EditorPropertyInteger::_bind_methods() {
 void EditorPropertyInteger::_bind_methods() {
 }
 }
 
 
-void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {
+void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {
 	spin->set_min(p_min);
 	spin->set_min(p_min);
 	spin->set_max(p_max);
 	spin->set_max(p_max);
 	spin->set_step(p_step);
 	spin->set_step(p_step);
+	spin->set_hide_slider(p_hide_slider);
 	spin->set_allow_greater(p_allow_greater);
 	spin->set_allow_greater(p_allow_greater);
 	spin->set_allow_lesser(p_allow_lesser);
 	spin->set_allow_lesser(p_allow_lesser);
 	spin->set_suffix(p_suffix);
 	spin->set_suffix(p_suffix);
@@ -2242,12 +2243,11 @@ void EditorPropertyVector2i::_notification(int p_what) {
 	}
 	}
 }
 }
 
 
-void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_hide_slider, bool p_link, const String &p_suffix) {
+void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_link, const String &p_suffix) {
 	for (int i = 0; i < 2; i++) {
 	for (int i = 0; i < 2; i++) {
 		spin[i]->set_min(p_min);
 		spin[i]->set_min(p_min);
 		spin[i]->set_max(p_max);
 		spin[i]->set_max(p_max);
 		spin[i]->set_step(1);
 		spin[i]->set_step(1);
-		spin[i]->set_hide_slider(p_hide_slider);
 		spin[i]->set_allow_greater(true);
 		spin[i]->set_allow_greater(true);
 		spin[i]->set_allow_lesser(true);
 		spin[i]->set_allow_lesser(true);
 		spin[i]->set_suffix(p_suffix);
 		spin[i]->set_suffix(p_suffix);
@@ -2352,12 +2352,11 @@ void EditorPropertyRect2i::_notification(int p_what) {
 void EditorPropertyRect2i::_bind_methods() {
 void EditorPropertyRect2i::_bind_methods() {
 }
 }
 
 
-void EditorPropertyRect2i::setup(int p_min, int p_max, bool p_hide_slider, const String &p_suffix) {
+void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) {
 	for (int i = 0; i < 4; i++) {
 	for (int i = 0; i < 4; i++) {
 		spin[i]->set_min(p_min);
 		spin[i]->set_min(p_min);
 		spin[i]->set_max(p_max);
 		spin[i]->set_max(p_max);
 		spin[i]->set_step(1);
 		spin[i]->set_step(1);
-		spin[i]->set_hide_slider(p_hide_slider);
 		spin[i]->set_allow_greater(true);
 		spin[i]->set_allow_greater(true);
 		spin[i]->set_allow_lesser(true);
 		spin[i]->set_allow_lesser(true);
 		spin[i]->set_suffix(p_suffix);
 		spin[i]->set_suffix(p_suffix);
@@ -2496,12 +2495,12 @@ void EditorPropertyVector3i::_notification(int p_what) {
 void EditorPropertyVector3i::_bind_methods() {
 void EditorPropertyVector3i::_bind_methods() {
 }
 }
 
 
-void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_hide_slider, bool p_link, const String &p_suffix) {
+void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_link, const String &p_suffix) {
 	for (int i = 0; i < 3; i++) {
 	for (int i = 0; i < 3; i++) {
 		spin[i]->set_min(p_min);
 		spin[i]->set_min(p_min);
 		spin[i]->set_max(p_max);
 		spin[i]->set_max(p_max);
 		spin[i]->set_step(1);
 		spin[i]->set_step(1);
-		spin[i]->set_hide_slider(p_hide_slider);
+		spin[i]->set_hide_slider(false);
 		spin[i]->set_allow_greater(true);
 		spin[i]->set_allow_greater(true);
 		spin[i]->set_allow_lesser(true);
 		spin[i]->set_allow_lesser(true);
 		spin[i]->set_suffix(p_suffix);
 		spin[i]->set_suffix(p_suffix);
@@ -3004,11 +3003,11 @@ void EditorPropertyVector4i::_notification(int p_what) {
 void EditorPropertyVector4i::_bind_methods() {
 void EditorPropertyVector4i::_bind_methods() {
 }
 }
 
 
-void EditorPropertyVector4i::setup(double p_min, double p_max, bool p_hide_slider, const String &p_suffix) {
+void EditorPropertyVector4i::setup(double p_min, double p_max, const String &p_suffix) {
 	for (int i = 0; i < 4; i++) {
 	for (int i = 0; i < 4; i++) {
 		spin[i]->set_min(p_min);
 		spin[i]->set_min(p_min);
 		spin[i]->set_max(p_max);
 		spin[i]->set_max(p_max);
-		spin[i]->set_hide_slider(p_hide_slider);
+		spin[i]->set_step(1);
 		spin[i]->set_allow_greater(true);
 		spin[i]->set_allow_greater(true);
 		spin[i]->set_allow_lesser(true);
 		spin[i]->set_allow_lesser(true);
 		spin[i]->set_suffix(p_suffix);
 		spin[i]->set_suffix(p_suffix);
@@ -4347,7 +4346,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 				EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
 				EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
 
 
 				EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
 				EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
-				editor->setup(hint.min, hint.max, hint.step, hint.or_greater, hint.or_less, hint.suffix);
+				editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.or_greater, hint.or_less, hint.suffix);
 
 
 				return editor;
 				return editor;
 			}
 			}
@@ -4475,7 +4474,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 		case Variant::VECTOR2I: {
 		case Variant::VECTOR2I: {
 			EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));
 			EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
-			editor->setup(hint.min, hint.max, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
+			editor->setup(hint.min, hint.max, p_hint == PROPERTY_HINT_LINK, hint.suffix);
 			return editor;
 			return editor;
 
 
 		} break;
 		} break;
@@ -4488,7 +4487,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 		case Variant::RECT2I: {
 		case Variant::RECT2I: {
 			EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide));
 			EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide));
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
-			editor->setup(hint.min, hint.max, hint.hide_slider, hint.suffix);
+			editor->setup(hint.min, hint.max, hint.suffix);
 
 
 			return editor;
 			return editor;
 		} break;
 		} break;
@@ -4502,7 +4501,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 		case Variant::VECTOR3I: {
 		case Variant::VECTOR3I: {
 			EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));
 			EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
-			editor->setup(hint.min, hint.max, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
+			editor->setup(hint.min, hint.max, p_hint == PROPERTY_HINT_LINK, hint.suffix);
 			return editor;
 			return editor;
 
 
 		} break;
 		} break;
@@ -4516,7 +4515,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 		case Variant::VECTOR4I: {
 		case Variant::VECTOR4I: {
 			EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
 			EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
-			editor->setup(hint.min, hint.max, hint.hide_slider, hint.suffix);
+			editor->setup(hint.min, hint.max, hint.suffix);
 			return editor;
 			return editor;
 
 
 		} break;
 		} break;

+ 5 - 5
editor/editor_properties.h

@@ -378,7 +378,7 @@ protected:
 
 
 public:
 public:
 	virtual void update_property() override;
 	virtual void update_property() override;
-	void setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix = String());
+	void setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix = String());
 	EditorPropertyInteger();
 	EditorPropertyInteger();
 };
 };
 
 
@@ -566,7 +566,7 @@ protected:
 
 
 public:
 public:
 	virtual void update_property() override;
 	virtual void update_property() override;
-	void setup(int p_min, int p_max, bool p_hide_slider, bool p_link = false, const String &p_suffix = String());
+	void setup(int p_min, int p_max, bool p_link = false, const String &p_suffix = String());
 	EditorPropertyVector2i(bool p_force_wide = false);
 	EditorPropertyVector2i(bool p_force_wide = false);
 };
 };
 
 
@@ -583,7 +583,7 @@ protected:
 
 
 public:
 public:
 	virtual void update_property() override;
 	virtual void update_property() override;
-	void setup(int p_min, int p_max, bool p_hide_slider, const String &p_suffix = String());
+	void setup(int p_min, int p_max, const String &p_suffix = String());
 	EditorPropertyRect2i(bool p_force_wide = false);
 	EditorPropertyRect2i(bool p_force_wide = false);
 };
 };
 
 
@@ -608,7 +608,7 @@ protected:
 
 
 public:
 public:
 	virtual void update_property() override;
 	virtual void update_property() override;
-	void setup(int p_min, int p_max, bool p_hide_slider, bool p_link = false, const String &p_suffix = String());
+	void setup(int p_min, int p_max, bool p_link = false, const String &p_suffix = String());
 	EditorPropertyVector3i(bool p_force_wide = false);
 	EditorPropertyVector3i(bool p_force_wide = false);
 };
 };
 
 
@@ -693,7 +693,7 @@ protected:
 
 
 public:
 public:
 	virtual void update_property() override;
 	virtual void update_property() override;
-	void setup(double p_min, double p_max, bool p_hide_slider, const String &p_suffix = String());
+	void setup(double p_min, double p_max, const String &p_suffix = String());
 	EditorPropertyVector4i();
 	EditorPropertyVector4i();
 };
 };
 
 

+ 5 - 5
editor/editor_properties_array_dict.cpp

@@ -919,7 +919,7 @@ void EditorPropertyDictionary::update_property() {
 				} break;
 				} break;
 				case Variant::INT: {
 				case Variant::INT: {
 					EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
 					EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
-					editor->setup(-100000, 100000, 1, true, true);
+					editor->setup(-100000, 100000, 1, false, true, true);
 					prop = editor;
 					prop = editor;
 
 
 				} break;
 				} break;
@@ -942,7 +942,7 @@ void EditorPropertyDictionary::update_property() {
 				} break;
 				} break;
 				case Variant::VECTOR2I: {
 				case Variant::VECTOR2I: {
 					EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i);
 					EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i);
-					editor->setup(-100000, 100000, true);
+					editor->setup(-100000, 100000);
 					prop = editor;
 					prop = editor;
 
 
 				} break;
 				} break;
@@ -954,7 +954,7 @@ void EditorPropertyDictionary::update_property() {
 				} break;
 				} break;
 				case Variant::RECT2I: {
 				case Variant::RECT2I: {
 					EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i);
 					EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i);
-					editor->setup(-100000, 100000, true);
+					editor->setup(-100000, 100000);
 					prop = editor;
 					prop = editor;
 
 
 				} break;
 				} break;
@@ -966,7 +966,7 @@ void EditorPropertyDictionary::update_property() {
 				} break;
 				} break;
 				case Variant::VECTOR3I: {
 				case Variant::VECTOR3I: {
 					EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i);
 					EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i);
-					editor->setup(-100000, 100000, true);
+					editor->setup(-100000, 100000);
 					prop = editor;
 					prop = editor;
 
 
 				} break;
 				} break;
@@ -978,7 +978,7 @@ void EditorPropertyDictionary::update_property() {
 				} break;
 				} break;
 				case Variant::VECTOR4I: {
 				case Variant::VECTOR4I: {
 					EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
 					EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
-					editor->setup(-100000, 100000, true);
+					editor->setup(-100000, 100000);
 					prop = editor;
 					prop = editor;
 
 
 				} break;
 				} break;

+ 77 - 66
editor/editor_spin_slider.cpp

@@ -76,6 +76,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
 					pre_grab_value = get_value();
 					pre_grab_value = get_value();
 					grabbing_spinner = false;
 					grabbing_spinner = false;
 					grabbing_spinner_mouse_pos = get_global_mouse_position();
 					grabbing_spinner_mouse_pos = get_global_mouse_position();
+					emit_signal("grabbed");
 				}
 				}
 			} else {
 			} else {
 				if (grabbing_spinner_attempt) {
 				if (grabbing_spinner_attempt) {
@@ -83,6 +84,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
 						Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
 						Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
 						Input::get_singleton()->warp_mouse(grabbing_spinner_mouse_pos);
 						Input::get_singleton()->warp_mouse(grabbing_spinner_mouse_pos);
 						queue_redraw();
 						queue_redraw();
+						emit_signal("ungrabbed");
 					} else {
 					} else {
 						_focus_entered();
 						_focus_entered();
 					}
 					}
@@ -178,9 +180,11 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) {
 				grabbing_ratio = get_as_ratio();
 				grabbing_ratio = get_as_ratio();
 				grabbing_from = grabber->get_transform().xform(mb->get_position()).x;
 				grabbing_from = grabber->get_transform().xform(mb->get_position()).x;
 			}
 			}
+			emit_signal("grabbed");
 		} else {
 		} else {
 			grabbing_grabber = false;
 			grabbing_grabber = false;
 			mousewheel_over_grabber = false;
 			mousewheel_over_grabber = false;
+			emit_signal("ungrabbed");
 		}
 		}
 	}
 	}
 
 
@@ -299,10 +303,6 @@ void EditorSpinSlider::_draw_spin_slider() {
 
 
 	Ref<Texture2D> updown = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
 	Ref<Texture2D> updown = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
 
 
-	if (get_step() == 1) {
-		number_width -= updown->get_width();
-	}
-
 	String numstr = get_text_value();
 	String numstr = get_text_value();
 
 
 	int vofs = (size.height - font->get_height(font_size)) / 2 + font->get_ascent(font_size);
 	int vofs = (size.height - font->get_height(font_size)) / 2 + font->get_ascent(font_size);
@@ -359,76 +359,79 @@ void EditorSpinSlider::_draw_spin_slider() {
 	}
 	}
 	TS->free_rid(num_rid);
 	TS->free_rid(num_rid);
 
 
-	if (get_step() == 1) {
-		Ref<Texture2D> updown2 = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
-		int updown_vofs = (size.height - updown2->get_height()) / 2;
-		if (rtl) {
-			updown_offset = sb->get_margin(SIDE_LEFT);
-		} else {
-			updown_offset = size.width - sb->get_margin(SIDE_RIGHT) - updown2->get_width();
-		}
-		Color c(1, 1, 1);
-		if (hover_updown) {
-			c *= Color(1.2, 1.2, 1.2);
-		}
-		draw_texture(updown2, Vector2(updown_offset, updown_vofs), c);
-		if (grabber->is_visible()) {
-			grabber->hide();
-		}
-	} else if (!hide_slider) {
-		const int grabber_w = 4 * EDSCALE;
-		const int width = size.width - sb->get_minimum_size().width - grabber_w;
-		const int ofs = sb->get_offset().x;
-		const int svofs = (size.height + vofs) / 2 - 1;
-		Color c = fc;
-
-		// Draw the horizontal slider's background.
-		c.a = 0.2;
-		draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c);
-
-		// Draw the horizontal slider's filled part on the left.
-		const int gofs = get_as_ratio() * width;
-		c.a = 0.45;
-		draw_rect(Rect2(ofs, svofs + 1, gofs, 2 * EDSCALE), c);
-
-		// Draw the horizontal slider's grabber.
-		c.a = 0.9;
-		const Rect2 grabber_rect = Rect2(ofs + gofs, svofs, grabber_w, 4 * EDSCALE);
-		draw_rect(grabber_rect, c);
-
-		grabbing_spinner_mouse_pos = get_global_position() + grabber_rect.get_center();
-
-		bool display_grabber = (grabbing_grabber || mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !(value_input_popup && value_input_popup->is_visible());
-		if (grabber->is_visible() != display_grabber) {
-			if (display_grabber) {
-				grabber->show();
+	if (!hide_slider) {
+		if (get_step() == 1) {
+			number_width -= updown->get_width();
+			Ref<Texture2D> updown2 = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
+			int updown_vofs = (size.height - updown2->get_height()) / 2;
+			if (rtl) {
+				updown_offset = sb->get_margin(SIDE_LEFT);
 			} else {
 			} else {
+				updown_offset = size.width - sb->get_margin(SIDE_RIGHT) - updown2->get_width();
+			}
+			Color c(1, 1, 1);
+			if (hover_updown) {
+				c *= Color(1.2, 1.2, 1.2);
+			}
+			draw_texture(updown2, Vector2(updown_offset, updown_vofs), c);
+			if (grabber->is_visible()) {
 				grabber->hide();
 				grabber->hide();
 			}
 			}
-		}
-
-		if (display_grabber) {
-			Ref<Texture2D> grabber_tex;
-			if (mouse_over_grabber) {
-				grabber_tex = get_theme_icon(SNAME("grabber_highlight"), SNAME("HSlider"));
-			} else {
-				grabber_tex = get_theme_icon(SNAME("grabber"), SNAME("HSlider"));
+		} else {
+			const int grabber_w = 4 * EDSCALE;
+			const int width = size.width - sb->get_minimum_size().width - grabber_w;
+			const int ofs = sb->get_offset().x;
+			const int svofs = (size.height + vofs) / 2 - 1;
+			Color c = fc;
+
+			// Draw the horizontal slider's background.
+			c.a = 0.2;
+			draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c);
+
+			// Draw the horizontal slider's filled part on the left.
+			const int gofs = get_as_ratio() * width;
+			c.a = 0.45;
+			draw_rect(Rect2(ofs, svofs + 1, gofs, 2 * EDSCALE), c);
+
+			// Draw the horizontal slider's grabber.
+			c.a = 0.9;
+			const Rect2 grabber_rect = Rect2(ofs + gofs, svofs, grabber_w, 4 * EDSCALE);
+			draw_rect(grabber_rect, c);
+
+			grabbing_spinner_mouse_pos = get_global_position() + grabber_rect.get_center();
+
+			bool display_grabber = (grabbing_grabber || mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !(value_input_popup && value_input_popup->is_visible());
+			if (grabber->is_visible() != display_grabber) {
+				if (display_grabber) {
+					grabber->show();
+				} else {
+					grabber->hide();
+				}
 			}
 			}
 
 
-			if (grabber->get_texture() != grabber_tex) {
-				grabber->set_texture(grabber_tex);
-			}
+			if (display_grabber) {
+				Ref<Texture2D> grabber_tex;
+				if (mouse_over_grabber) {
+					grabber_tex = get_theme_icon(SNAME("grabber_highlight"), SNAME("HSlider"));
+				} else {
+					grabber_tex = get_theme_icon(SNAME("grabber"), SNAME("HSlider"));
+				}
 
 
-			Vector2 scale = get_global_transform_with_canvas().get_scale();
-			grabber->set_scale(scale);
-			grabber->reset_size();
-			grabber->set_position(get_global_position() + (grabber_rect.get_center() - grabber->get_size() * 0.5) * scale);
+				if (grabber->get_texture() != grabber_tex) {
+					grabber->set_texture(grabber_tex);
+				}
 
 
-			if (mousewheel_over_grabber) {
-				Input::get_singleton()->warp_mouse(grabber->get_position() + grabber_rect.size);
-			}
+				Vector2 scale = get_global_transform_with_canvas().get_scale();
+				grabber->set_scale(scale);
+				grabber->reset_size();
+				grabber->set_position(get_global_position() + (grabber_rect.get_center() - grabber->get_size() * 0.5) * scale);
+
+				if (mousewheel_over_grabber) {
+					Input::get_singleton()->warp_mouse(grabber->get_position() + grabber_rect.size);
+				}
 
 
-			grabber_range = width;
+				grabber_range = width;
+			}
 		}
 		}
 	}
 	}
 }
 }
@@ -584,6 +587,8 @@ void EditorSpinSlider::_value_focus_exited() {
 		//enter, click, esc
 		//enter, click, esc
 		grab_focus();
 		grab_focus();
 	}
 	}
+
+	emit_signal("value_focus_exited");
 }
 }
 
 
 void EditorSpinSlider::_grabber_mouse_entered() {
 void EditorSpinSlider::_grabber_mouse_entered() {
@@ -627,6 +632,7 @@ void EditorSpinSlider::_focus_entered() {
 	value_input->call_deferred(SNAME("select_all"));
 	value_input->call_deferred(SNAME("select_all"));
 	value_input->set_focus_next(find_next_valid_focus()->get_path());
 	value_input->set_focus_next(find_next_valid_focus()->get_path());
 	value_input->set_focus_previous(find_prev_valid_focus()->get_path());
 	value_input->set_focus_previous(find_prev_valid_focus()->get_path());
+	emit_signal("value_focus_entered");
 }
 }
 
 
 void EditorSpinSlider::_bind_methods() {
 void EditorSpinSlider::_bind_methods() {
@@ -650,6 +656,11 @@ void EditorSpinSlider::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_slider"), "set_hide_slider", "is_hiding_slider");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_slider"), "set_hide_slider", "is_hiding_slider");
+
+	ADD_SIGNAL(MethodInfo("grabbed"));
+	ADD_SIGNAL(MethodInfo("ungrabbed"));
+	ADD_SIGNAL(MethodInfo("value_focus_entered"));
+	ADD_SIGNAL(MethodInfo("value_focus_exited"));
 }
 }
 
 
 void EditorSpinSlider::_ensure_input_popup() {
 void EditorSpinSlider::_ensure_input_popup() {

+ 26 - 3
editor/plugins/animation_player_editor_plugin.cpp

@@ -87,13 +87,13 @@ void AnimationPlayerEditor::_notification(int p_what) {
 				}
 				}
 				frame->set_value(player->get_current_animation_position());
 				frame->set_value(player->get_current_animation_position());
 				track_editor->set_anim_pos(player->get_current_animation_position());
 				track_editor->set_anim_pos(player->get_current_animation_position());
-
 			} else if (!player->is_valid()) {
 			} else if (!player->is_valid()) {
 				// Reset timeline when the player has been stopped externally
 				// Reset timeline when the player has been stopped externally
 				frame->set_value(0);
 				frame->set_value(0);
 			} else if (last_active) {
 			} else if (last_active) {
 				// Need the last frame after it stopped.
 				// Need the last frame after it stopped.
 				frame->set_value(player->get_current_animation_position());
 				frame->set_value(player->get_current_animation_position());
+				track_editor->set_anim_pos(player->get_current_animation_position());
 			}
 			}
 
 
 			last_active = player->is_playing();
 			last_active = player->is_playing();
@@ -423,7 +423,7 @@ void AnimationPlayerEditor::_select_anim_by_name(const String &p_anim) {
 	_animation_selected(idx);
 	_animation_selected(idx);
 }
 }
 
 
-double AnimationPlayerEditor::_get_editor_step() const {
+float AnimationPlayerEditor::_get_editor_step() const {
 	// Returns the effective snapping value depending on snapping modifiers, or 0 if snapping is disabled.
 	// Returns the effective snapping value depending on snapping modifiers, or 0 if snapping is disabled.
 	if (track_editor->is_snap_enabled()) {
 	if (track_editor->is_snap_enabled()) {
 		const String current = player->get_assigned_animation();
 		const String current = player->get_assigned_animation();
@@ -434,7 +434,7 @@ double AnimationPlayerEditor::_get_editor_step() const {
 		return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? anim->get_step() * 0.25 : anim->get_step();
 		return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? anim->get_step() * 0.25 : anim->get_step();
 	}
 	}
 
 
-	return 0.0;
+	return 0.0f;
 }
 }
 
 
 void AnimationPlayerEditor::_animation_name_edited() {
 void AnimationPlayerEditor::_animation_name_edited() {
@@ -1973,3 +1973,26 @@ AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin() {
 
 
 AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() {
 AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() {
 }
 }
+
+// AnimationTrackKeyEditEditorPlugin
+
+bool EditorInspectorPluginAnimationTrackKeyEdit::can_handle(Object *p_object) {
+	return Object::cast_to<AnimationTrackKeyEdit>(p_object) != nullptr;
+}
+
+void EditorInspectorPluginAnimationTrackKeyEdit::parse_begin(Object *p_object) {
+	AnimationTrackKeyEdit *atk = Object::cast_to<AnimationTrackKeyEdit>(p_object);
+	ERR_FAIL_COND(!atk);
+
+	atk_editor = memnew(AnimationTrackKeyEditEditor(atk->animation, atk->track, atk->key_ofs, atk->use_fps));
+	add_custom_control(atk_editor);
+}
+
+AnimationTrackKeyEditEditorPlugin::AnimationTrackKeyEditEditorPlugin() {
+	atk_plugin = memnew(EditorInspectorPluginAnimationTrackKeyEdit);
+	EditorInspector::add_inspector_plugin(atk_plugin);
+}
+
+bool AnimationTrackKeyEditEditorPlugin::handles(Object *p_object) const {
+	return p_object->is_class("AnimationTrackKeyEdit");
+}

+ 27 - 1
editor/plugins/animation_player_editor_plugin.h

@@ -162,7 +162,7 @@ class AnimationPlayerEditor : public VBoxContainer {
 	} onion;
 	} onion;
 
 
 	void _select_anim_by_name(const String &p_anim);
 	void _select_anim_by_name(const String &p_anim);
-	double _get_editor_step() const;
+	float _get_editor_step() const;
 	void _play_pressed();
 	void _play_pressed();
 	void _play_from_pressed();
 	void _play_from_pressed();
 	void _play_bw_pressed();
 	void _play_bw_pressed();
@@ -272,4 +272,30 @@ public:
 	~AnimationPlayerEditorPlugin();
 	~AnimationPlayerEditorPlugin();
 };
 };
 
 
+// AnimationTrackKeyEditEditorPlugin
+
+class EditorInspectorPluginAnimationTrackKeyEdit : public EditorInspectorPlugin {
+	GDCLASS(EditorInspectorPluginAnimationTrackKeyEdit, EditorInspectorPlugin);
+
+	AnimationTrackKeyEditEditor *atk_editor = nullptr;
+
+public:
+	virtual bool can_handle(Object *p_object) override;
+	virtual void parse_begin(Object *p_object) override;
+};
+
+class AnimationTrackKeyEditEditorPlugin : public EditorPlugin {
+	GDCLASS(AnimationTrackKeyEditEditorPlugin, EditorPlugin);
+
+	EditorInspectorPluginAnimationTrackKeyEdit *atk_plugin = nullptr;
+
+public:
+	bool has_main_screen() const override { return false; }
+	virtual bool handles(Object *p_object) const override;
+
+	virtual String get_name() const override { return "AnimationTrackKeyEdit"; }
+
+	AnimationTrackKeyEditEditorPlugin();
+};
+
 #endif // ANIMATION_PLAYER_EDITOR_PLUGIN_H
 #endif // ANIMATION_PLAYER_EDITOR_PLUGIN_H

+ 2 - 2
editor/plugins/font_config_plugin.cpp

@@ -483,7 +483,7 @@ void EditorPropertyOTVariation::update_property() {
 			Vector3i range = supported.get_value_at_index(i);
 			Vector3i range = supported.get_value_at_index(i);
 
 
 			EditorPropertyInteger *prop = memnew(EditorPropertyInteger);
 			EditorPropertyInteger *prop = memnew(EditorPropertyInteger);
-			prop->setup(range.x, range.y, 1, false, false);
+			prop->setup(range.x, range.y, false, 1, false, false);
 			prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag));
 			prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag));
 
 
 			String name = TS->tag_to_name(name_tag);
 			String name = TS->tag_to_name(name_tag);
@@ -762,7 +762,7 @@ void EditorPropertyOTFeatures::update_property() {
 					} break;
 					} break;
 					case Variant::INT: {
 					case Variant::INT: {
 						EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
 						EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
-						editor->setup(0, 255, 1, false, false);
+						editor->setup(0, 255, 1, false, false, false);
 						prop = editor;
 						prop = editor;
 					} break;
 					} break;
 					default: {
 					default: {

+ 4 - 4
editor/plugins/tiles/tile_proxies_manager_dialog.cpp

@@ -398,7 +398,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() {
 	source_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
 	source_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
 	source_from_property_editor->set_selectable(false);
 	source_from_property_editor->set_selectable(false);
 	source_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	source_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	source_from_property_editor->setup(-1, 99999, 1, true, false);
+	source_from_property_editor->setup(-1, 99999, 1, false, true, false);
 	vboxcontainer_from->add_child(source_from_property_editor);
 	vboxcontainer_from->add_child(source_from_property_editor);
 
 
 	coords_from_property_editor = memnew(EditorPropertyVector2i);
 	coords_from_property_editor = memnew(EditorPropertyVector2i);
@@ -417,7 +417,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() {
 	alternative_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
 	alternative_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
 	alternative_from_property_editor->set_selectable(false);
 	alternative_from_property_editor->set_selectable(false);
 	alternative_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	alternative_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	alternative_from_property_editor->setup(-1, 99999, 1, true, false);
+	alternative_from_property_editor->setup(-1, 99999, 1, false, true, false);
 	alternative_from_property_editor->hide();
 	alternative_from_property_editor->hide();
 	vboxcontainer_from->add_child(alternative_from_property_editor);
 	vboxcontainer_from->add_child(alternative_from_property_editor);
 
 
@@ -432,7 +432,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() {
 	source_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
 	source_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
 	source_to_property_editor->set_selectable(false);
 	source_to_property_editor->set_selectable(false);
 	source_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	source_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	source_to_property_editor->setup(-1, 99999, 1, true, false);
+	source_to_property_editor->setup(-1, 99999, 1, false, true, false);
 	vboxcontainer_to->add_child(source_to_property_editor);
 	vboxcontainer_to->add_child(source_to_property_editor);
 
 
 	coords_to_property_editor = memnew(EditorPropertyVector2i);
 	coords_to_property_editor = memnew(EditorPropertyVector2i);
@@ -451,7 +451,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() {
 	alternative_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
 	alternative_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed));
 	alternative_to_property_editor->set_selectable(false);
 	alternative_to_property_editor->set_selectable(false);
 	alternative_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	alternative_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	alternative_to_property_editor->setup(-1, 99999, 1, true, false);
+	alternative_to_property_editor->setup(-1, 99999, 1, false, true, false);
 	alternative_to_property_editor->hide();
 	alternative_to_property_editor->hide();
 	vboxcontainer_to->add_child(alternative_to_property_editor);
 	vboxcontainer_to->add_child(alternative_to_property_editor);
 
 

+ 4 - 4
scene/animation/animation_player.cpp

@@ -691,7 +691,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 						}
 						}
 					} else {
 					} else {
 						if (p_started) {
 						if (p_started) {
-							int first_key = a->track_find_key(i, p_prev_time, true);
+							int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT);
 							if (first_key >= 0) {
 							if (first_key >= 0) {
 								indices.push_back(first_key);
 								indices.push_back(first_key);
 							}
 							}
@@ -761,7 +761,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 					}
 					}
 				} else {
 				} else {
 					if (p_started) {
 					if (p_started) {
-						int first_key = a->track_find_key(i, p_prev_time, true);
+						int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT);
 						if (first_key >= 0) {
 						if (first_key >= 0) {
 							indices.push_back(first_key);
 							indices.push_back(first_key);
 						}
 						}
@@ -855,7 +855,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 					//find stuff to play
 					//find stuff to play
 					List<int> to_play;
 					List<int> to_play;
 					if (p_started) {
 					if (p_started) {
-						int first_key = a->track_find_key(i, p_prev_time, true);
+						int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT);
 						if (first_key >= 0) {
 						if (first_key >= 0) {
 							to_play.push_back(first_key);
 							to_play.push_back(first_key);
 						}
 						}
@@ -968,7 +968,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
 					//find stuff to play
 					//find stuff to play
 					List<int> to_play;
 					List<int> to_play;
 					if (p_started) {
 					if (p_started) {
-						int first_key = a->track_find_key(i, p_prev_time, true);
+						int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT);
 						if (first_key >= 0) {
 						if (first_key >= 0) {
 							to_play.push_back(first_key);
 							to_play.push_back(first_key);
 						}
 						}

+ 4 - 4
scene/animation/animation_tree.cpp

@@ -1383,7 +1383,7 @@ void AnimationTree::_process_graph(double p_delta) {
 							}
 							}
 						} else {
 						} else {
 							if (seeked) {
 							if (seeked) {
-								int idx = a->track_find_key(i, time, !is_external_seeking);
+								int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
 								if (idx < 0) {
 								if (idx < 0) {
 									continue;
 									continue;
 								}
 								}
@@ -1406,7 +1406,7 @@ void AnimationTree::_process_graph(double p_delta) {
 						TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
 						TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
 
 
 						if (seeked) {
 						if (seeked) {
-							int idx = a->track_find_key(i, time, !is_external_seeking);
+							int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
 							if (idx < 0) {
 							if (idx < 0) {
 								continue;
 								continue;
 							}
 							}
@@ -1440,7 +1440,7 @@ void AnimationTree::_process_graph(double p_delta) {
 
 
 						if (seeked) {
 						if (seeked) {
 							//find whatever should be playing
 							//find whatever should be playing
-							int idx = a->track_find_key(i, time, !is_external_seeking);
+							int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
 							if (idx < 0) {
 							if (idx < 0) {
 								continue;
 								continue;
 							}
 							}
@@ -1553,7 +1553,7 @@ void AnimationTree::_process_graph(double p_delta) {
 
 
 						if (seeked) {
 						if (seeked) {
 							//seek
 							//seek
-							int idx = a->track_find_key(i, time, !is_external_seeking);
+							int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
 							if (idx < 0) {
 							if (idx < 0) {
 								continue;
 								continue;
 							}
 							}

+ 27 - 23
scene/resources/animation.cpp

@@ -1319,7 +1319,7 @@ Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float
 }
 }
 
 
 void Animation::track_remove_key_at_time(int p_track, double p_time) {
 void Animation::track_remove_key_at_time(int p_track, double p_time) {
-	int idx = track_find_key(p_track, p_time, true);
+	int idx = track_find_key(p_track, p_time, FIND_MODE_APPROX);
 	ERR_FAIL_COND(idx < 0);
 	ERR_FAIL_COND(idx < 0);
 	track_remove_key(p_track, idx);
 	track_remove_key(p_track, idx);
 }
 }
@@ -1400,7 +1400,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
 	emit_changed();
 	emit_changed();
 }
 }
 
 
-int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
+int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) const {
 	ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
 	ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
 	Track *t = tracks[p_track];
 	Track *t = tracks[p_track];
 
 
@@ -1416,7 +1416,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 				uint32_t key_index;
 				uint32_t key_index;
 				bool fetch_compressed_success = _fetch_compressed<3>(tt->compressed_track, p_time, key, time, key_next, time_next, &key_index);
 				bool fetch_compressed_success = _fetch_compressed<3>(tt->compressed_track, p_time, key, time, key_next, time_next, &key_index);
 				ERR_FAIL_COND_V(!fetch_compressed_success, -1);
 				ERR_FAIL_COND_V(!fetch_compressed_success, -1);
-				if (p_exact && time != p_time) {
+				if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) {
 					return -1;
 					return -1;
 				}
 				}
 				return key_index;
 				return key_index;
@@ -1426,7 +1426,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= tt->positions.size()) {
 			if (k < 0 || k >= tt->positions.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (tt->positions[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(tt->positions[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && tt->positions[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -1443,7 +1443,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 				uint32_t key_index;
 				uint32_t key_index;
 				bool fetch_compressed_success = _fetch_compressed<3>(rt->compressed_track, p_time, key, time, key_next, time_next, &key_index);
 				bool fetch_compressed_success = _fetch_compressed<3>(rt->compressed_track, p_time, key, time, key_next, time_next, &key_index);
 				ERR_FAIL_COND_V(!fetch_compressed_success, -1);
 				ERR_FAIL_COND_V(!fetch_compressed_success, -1);
-				if (p_exact && time != p_time) {
+				if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) {
 					return -1;
 					return -1;
 				}
 				}
 				return key_index;
 				return key_index;
@@ -1453,7 +1453,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= rt->rotations.size()) {
 			if (k < 0 || k >= rt->rotations.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (rt->rotations[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(rt->rotations[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && rt->rotations[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -1470,7 +1470,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 				uint32_t key_index;
 				uint32_t key_index;
 				bool fetch_compressed_success = _fetch_compressed<3>(st->compressed_track, p_time, key, time, key_next, time_next, &key_index);
 				bool fetch_compressed_success = _fetch_compressed<3>(st->compressed_track, p_time, key, time, key_next, time_next, &key_index);
 				ERR_FAIL_COND_V(!fetch_compressed_success, -1);
 				ERR_FAIL_COND_V(!fetch_compressed_success, -1);
-				if (p_exact && time != p_time) {
+				if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) {
 					return -1;
 					return -1;
 				}
 				}
 				return key_index;
 				return key_index;
@@ -1480,7 +1480,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= st->scales.size()) {
 			if (k < 0 || k >= st->scales.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (st->scales[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(st->scales[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && st->scales[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -1497,7 +1497,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 				uint32_t key_index;
 				uint32_t key_index;
 				bool fetch_compressed_success = _fetch_compressed<1>(bst->compressed_track, p_time, key, time, key_next, time_next, &key_index);
 				bool fetch_compressed_success = _fetch_compressed<1>(bst->compressed_track, p_time, key, time, key_next, time_next, &key_index);
 				ERR_FAIL_COND_V(!fetch_compressed_success, -1);
 				ERR_FAIL_COND_V(!fetch_compressed_success, -1);
-				if (p_exact && time != p_time) {
+				if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) {
 					return -1;
 					return -1;
 				}
 				}
 				return key_index;
 				return key_index;
@@ -1507,7 +1507,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= bst->blend_shapes.size()) {
 			if (k < 0 || k >= bst->blend_shapes.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (bst->blend_shapes[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(bst->blend_shapes[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && bst->blend_shapes[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -1519,7 +1519,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= vt->values.size()) {
 			if (k < 0 || k >= vt->values.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (vt->values[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(vt->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && vt->values[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -1531,7 +1531,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= mt->methods.size()) {
 			if (k < 0 || k >= mt->methods.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (mt->methods[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(mt->methods[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && mt->methods[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -1543,7 +1543,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= bt->values.size()) {
 			if (k < 0 || k >= bt->values.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (bt->values[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(bt->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && bt->values[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -1555,7 +1555,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= at->values.size()) {
 			if (k < 0 || k >= at->values.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (at->values[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(at->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && at->values[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -1567,7 +1567,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
 			if (k < 0 || k >= at->values.size()) {
 			if (k < 0 || k >= at->values.size()) {
 				return -1;
 				return -1;
 			}
 			}
-			if (at->values[k].time != p_time && p_exact) {
+			if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(at->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && at->values[k].time != p_time)) {
 				return -1;
 				return -1;
 			}
 			}
 			return k;
 			return k;
@@ -2944,12 +2944,12 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
 			// Not from_time > to_time but most recent of looping...
 			// Not from_time > to_time but most recent of looping...
 			if (p_looped_flag != Animation::LOOPED_FLAG_NONE) {
 			if (p_looped_flag != Animation::LOOPED_FLAG_NONE) {
 				if (!is_backward && Math::is_equal_approx(from_time, 0)) {
 				if (!is_backward && Math::is_equal_approx(from_time, 0)) {
-					int edge = track_find_key(p_track, 0, true);
+					int edge = track_find_key(p_track, 0, FIND_MODE_EXACT);
 					if (edge >= 0) {
 					if (edge >= 0) {
 						p_indices->push_back(edge);
 						p_indices->push_back(edge);
 					}
 					}
 				} else if (is_backward && Math::is_equal_approx(to_time, length)) {
 				} else if (is_backward && Math::is_equal_approx(to_time, length)) {
-					int edge = track_find_key(p_track, length, true);
+					int edge = track_find_key(p_track, length, FIND_MODE_EXACT);
 					if (edge >= 0) {
 					if (edge >= 0) {
 						p_indices->push_back(edge);
 						p_indices->push_back(edge);
 					}
 					}
@@ -2971,7 +2971,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
 						const PositionTrack *tt = static_cast<const PositionTrack *>(t);
 						const PositionTrack *tt = static_cast<const PositionTrack *>(t);
 						if (tt->compressed_track >= 0) {
 						if (tt->compressed_track >= 0) {
 							_get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
 							_get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
-							_get_compressed_key_indices_in_range<3>(tt->compressed_track, CMP_EPSILON, to_time, p_indices);
+							_get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
 						} else {
 						} else {
 							_track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices, true);
 							_track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices, true);
 							_track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices, false);
 							_track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices, false);
@@ -2981,7 +2981,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
 						const RotationTrack *rt = static_cast<const RotationTrack *>(t);
 						const RotationTrack *rt = static_cast<const RotationTrack *>(t);
 						if (rt->compressed_track >= 0) {
 						if (rt->compressed_track >= 0) {
 							_get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
 							_get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
-							_get_compressed_key_indices_in_range<3>(rt->compressed_track, CMP_EPSILON, to_time, p_indices);
+							_get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
 						} else {
 						} else {
 							_track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices, true);
 							_track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices, true);
 							_track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices, false);
 							_track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices, false);
@@ -3072,7 +3072,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
 						const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
 						const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
 						if (bst->compressed_track >= 0) {
 						if (bst->compressed_track >= 0) {
 							_get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
 							_get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
-							_get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length - CMP_EPSILON, p_indices);
+							_get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length, p_indices);
 						} else {
 						} else {
 							_track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices, false);
 							_track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices, false);
 							_track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices, true);
 							_track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices, true);
@@ -3109,9 +3109,9 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
 
 
 			// The edge will be pingponged in the next frame and processed there, so let's ignore it now...
 			// The edge will be pingponged in the next frame and processed there, so let's ignore it now...
 			if (!is_backward && Math::is_equal_approx(to_time, length)) {
 			if (!is_backward && Math::is_equal_approx(to_time, length)) {
-				to_time = length - CMP_EPSILON;
+				to_time -= CMP_EPSILON;
 			} else if (is_backward && Math::is_equal_approx(from_time, 0)) {
 			} else if (is_backward && Math::is_equal_approx(from_time, 0)) {
-				from_time = CMP_EPSILON;
+				from_time += CMP_EPSILON;
 			}
 			}
 		} break;
 		} break;
 	}
 	}
@@ -3818,7 +3818,7 @@ void Animation::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count);
 	ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count);
 	ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value);
 	ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value);
 	ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time);
 	ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time);
-	ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "exact"), &Animation::track_find_key, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST));
 
 
 	ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type);
 	ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type);
 	ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type);
 	ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type);
@@ -3905,6 +3905,10 @@ void Animation::_bind_methods() {
 	BIND_ENUM_CONSTANT(LOOPED_FLAG_NONE);
 	BIND_ENUM_CONSTANT(LOOPED_FLAG_NONE);
 	BIND_ENUM_CONSTANT(LOOPED_FLAG_END);
 	BIND_ENUM_CONSTANT(LOOPED_FLAG_END);
 	BIND_ENUM_CONSTANT(LOOPED_FLAG_START);
 	BIND_ENUM_CONSTANT(LOOPED_FLAG_START);
+
+	BIND_ENUM_CONSTANT(FIND_MODE_NEAREST);
+	BIND_ENUM_CONSTANT(FIND_MODE_APPROX);
+	BIND_ENUM_CONSTANT(FIND_MODE_EXACT);
 }
 }
 
 
 void Animation::clear() {
 void Animation::clear() {

+ 8 - 1
scene/resources/animation.h

@@ -79,6 +79,12 @@ public:
 		LOOPED_FLAG_START,
 		LOOPED_FLAG_START,
 	};
 	};
 
 
+	enum FindMode {
+		FIND_MODE_NEAREST,
+		FIND_MODE_APPROX,
+		FIND_MODE_EXACT,
+	};
+
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	enum HandleMode {
 	enum HandleMode {
 		HANDLE_MODE_FREE,
 		HANDLE_MODE_FREE,
@@ -392,7 +398,7 @@ public:
 	void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition);
 	void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition);
 	void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
 	void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
 	void track_set_key_time(int p_track, int p_key_idx, double p_time);
 	void track_set_key_time(int p_track, int p_key_idx, double p_time);
-	int track_find_key(int p_track, double p_time, bool p_exact = false) const;
+	int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const;
 	void track_remove_key(int p_track, int p_idx);
 	void track_remove_key(int p_track, int p_idx);
 	void track_remove_key_at_time(int p_track, double p_time);
 	void track_remove_key_at_time(int p_track, double p_time);
 	int track_get_key_count(int p_track) const;
 	int track_get_key_count(int p_track) const;
@@ -489,6 +495,7 @@ VARIANT_ENUM_CAST(Animation::InterpolationType);
 VARIANT_ENUM_CAST(Animation::UpdateMode);
 VARIANT_ENUM_CAST(Animation::UpdateMode);
 VARIANT_ENUM_CAST(Animation::LoopMode);
 VARIANT_ENUM_CAST(Animation::LoopMode);
 VARIANT_ENUM_CAST(Animation::LoopedFlag);
 VARIANT_ENUM_CAST(Animation::LoopedFlag);
+VARIANT_ENUM_CAST(Animation::FindMode);
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 VARIANT_ENUM_CAST(Animation::HandleMode);
 VARIANT_ENUM_CAST(Animation::HandleMode);
 VARIANT_ENUM_CAST(Animation::HandleSetMode);
 VARIANT_ENUM_CAST(Animation::HandleSetMode);

Some files were not shown because too many files changed in this diff