diff options
Diffstat (limited to 'editor')
63 files changed, 2352 insertions, 1520 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index c376d5434f..a1e4b5fde4 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -35,6 +35,7 @@ #include "editor/input_event_configuration_dialog.h" #include "scene/gui/check_button.h" #include "scene/gui/tree.h" +#include "scene/scene_string_names.h" static bool _is_action_name_valid(const String &p_name) { const char32_t *cstr = p_name.get_data(); @@ -362,6 +363,8 @@ void ActionMapEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("action_removed", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("action_renamed", PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name"))); ADD_SIGNAL(MethodInfo("action_reordered", PropertyInfo(Variant::STRING, "action_name"), PropertyInfo(Variant::STRING, "relative_to"), PropertyInfo(Variant::BOOL, "before"))); + ADD_SIGNAL(MethodInfo(SNAME("filter_focused"))); + ADD_SIGNAL(MethodInfo(SNAME("filter_unfocused"))); } LineEdit *ActionMapEditor::get_search_box() const { @@ -492,6 +495,14 @@ void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) { action_list_search->connect("text_changed", callable_mp(this, &ActionMapEditor::_search_term_updated)); } +void ActionMapEditor::_on_filter_focused() { + emit_signal(SNAME("filter_focused")); +} + +void ActionMapEditor::_on_filter_unfocused() { + emit_signal(SNAME("filter_unfocused")); +} + ActionMapEditor::ActionMapEditor() { // Main Vbox Container VBoxContainer *main_vbox = memnew(VBoxContainer); @@ -512,6 +523,8 @@ ActionMapEditor::ActionMapEditor() { action_list_search_by_event->set_h_size_flags(Control::SIZE_EXPAND_FILL); action_list_search_by_event->set_stretch_ratio(0.75); action_list_search_by_event->connect("event_changed", callable_mp(this, &ActionMapEditor::_search_by_event)); + action_list_search_by_event->connect(SceneStringNames::get_singleton()->focus_entered, callable_mp(this, &ActionMapEditor::_on_filter_focused)); + action_list_search_by_event->connect(SceneStringNames::get_singleton()->focus_exited, callable_mp(this, &ActionMapEditor::_on_filter_unfocused)); top_hbox->add_child(action_list_search_by_event); Button *clear_all_search = memnew(Button); diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h index ad9980c4ef..1908805b17 100644 --- a/editor/action_map_editor.h +++ b/editor/action_map_editor.h @@ -106,6 +106,9 @@ private: bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + void _on_filter_focused(); + void _on_filter_unfocused(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 530708f3e5..7b5a7b7046 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -575,7 +575,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { ep.point_rect.size = bezier_icon->get_size(); if (selection.has(IntPair(i, j))) { 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); } else { Color track_color = Color(1, 1, 1, 1); @@ -812,7 +812,7 @@ void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int 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); selection.insert(IntPair(p_track, idx)); @@ -1168,8 +1168,8 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { new_point[4] = 0; 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(); @@ -1179,7 +1179,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { undo_redo->commit_action(); //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); _clear_selection(); 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()) { 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) { 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(); - 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; } @@ -1599,7 +1599,7 @@ void AnimationBezierTrackEdit::duplicate_selection() { 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 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_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; 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) { continue; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 2ee06a0dbd..74e0db967d 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -48,1356 +48,1182 @@ #include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" -class AnimationTrackKeyEdit : public Object { - GDCLASS(AnimationTrackKeyEdit, Object); - -public: - bool setting = false; - bool animation_read_only = false; +void AnimationTrackKeyEdit::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); + ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed); + ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationTrackKeyEdit::_hide_script_from_inspector); + ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationTrackKeyEdit::_hide_metadata_from_inspector); + ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationTrackKeyEdit::get_root_path); + ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationTrackKeyEdit::_dont_undo_redo); + ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationTrackKeyEdit::_is_read_only); +} - bool _hide_script_from_inspector() { return true; } - bool _hide_metadata_from_inspector() { return true; } - bool _dont_undo_redo() { return true; } +void AnimationTrackKeyEdit::_fix_node_path(Variant &value) { + NodePath np = value; - bool _is_read_only() { - return animation_read_only; + if (np == NodePath()) { + return; } - static void _bind_methods() { - ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); - ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed); - ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationTrackKeyEdit::_hide_script_from_inspector); - ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationTrackKeyEdit::_hide_metadata_from_inspector); - ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationTrackKeyEdit::get_root_path); - ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationTrackKeyEdit::_dont_undo_redo); - ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationTrackKeyEdit::_is_read_only); - } + Node *root = EditorNode::get_singleton()->get_tree()->get_root(); - void _fix_node_path(Variant &value) { - NodePath np = value; + Node *np_node = root->get_node(np); + ERR_FAIL_COND(!np_node); - if (np == NodePath()) { - return; - } + Node *edited_node = root->get_node(base); + ERR_FAIL_COND(!edited_node); - Node *root = EditorNode::get_singleton()->get_tree()->get_root(); + value = edited_node->get_path_to(np_node); +} - Node *np_node = root->get_node(np); - ERR_FAIL_COND(!np_node); +void AnimationTrackKeyEdit::_update_obj(const Ref<Animation> &p_anim) { + if (setting || animation != p_anim) { + return; + } - Node *edited_node = root->get_node(base); - ERR_FAIL_COND(!edited_node); + notify_change(); +} - value = edited_node->get_path_to(np_node); +void AnimationTrackKeyEdit::_key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) { + if (animation != p_anim || from != key_ofs) { + return; } - void _update_obj(const Ref<Animation> &p_anim) { - if (setting || animation != p_anim) { - return; - } + key_ofs = to; - notify_change(); + if (setting) { + return; } - void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) { - if (animation != p_anim || from != key_ofs) { - return; - } + notify_change(); +} - key_ofs = to; +bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_value) { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND_V(key == -1, false); - if (setting) { - return; - } + String name = p_name; + if (name == "easing") { + float val = p_value; + float prev_val = animation->track_get_key_transition(track, key); + setting = true; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Animation 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); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - notify_change(); + setting = false; + return true; } - bool _set(const StringName &p_name, const Variant &p_value) { - 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; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + Variant old = animation->track_get_key_value(track, key); + setting = true; + String chan; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + chan = "Position3D"; + break; + case Animation::TYPE_ROTATION_3D: + chan = "Rotation3D"; + break; + case Animation::TYPE_SCALE_3D: + chan = "Scale3D"; + break; + default: { + } } - new_time /= fps; - } - if (new_time == key_ofs) { + undo_redo->create_action(vformat(TTR("Animation Change %s"), chan)); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); + 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; } - int existing = animation->track_find_key(track, new_time, true); + } break; + case Animation::TYPE_BLEND_SHAPE: + case Animation::TYPE_VALUE: { + if (name == "value") { + Variant value = p_value; - setting = true; - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - undo_redo->create_action(TTR("Animation Change Keyframe Time"), UndoRedo::MERGE_ENDS); + if (value.get_type() == Variant::NODE_PATH) { + _fix_node_path(value); + } - Variant val = animation->track_get_key_value(track, key); - float trans = animation->track_get_key_transition(track, key); + setting = true; + undo_redo->create_action(TTR("Animation 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); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - 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_time", 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); + setting = false; + return true; + } + } break; + case Animation::TYPE_METHOD: { + Dictionary d_old = animation->track_get_key_value(track, key); + Dictionary d_new = d_old.duplicate(); + + bool change_notify_deserved = false; + 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()) { + Callable::CallError err; + if (Variant::can_convert(args[idx].get_type(), t)) { + Variant old = args[idx]; + Variant *ptrs[1] = { &old }; + Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); + } else { + Variant::construct(t, args.write[idx], nullptr, 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); + } - 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); + args.write[idx] = value; + d_new["args"] = args; + mergeable = true; + } } - undo_redo->commit_action(); - setting = false; - return true; - } + if (mergeable) { + undo_redo->create_action(TTR("Animation Change Call"), UndoRedo::MERGE_ENDS); + } else { + undo_redo->create_action(TTR("Animation Change Call")); + } - if (name == "easing") { - float val = p_value; - float prev_val = animation->track_get_key_transition(track, key); setting = true; - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - undo_redo->create_action(TTR("Animation 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); + 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); 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; - } - - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - case Animation::TYPE_ROTATION_3D: - case Animation::TYPE_SCALE_3D: { - if (name == "position" || name == "rotation" || name == "scale") { - Variant old = animation->track_get_key_value(track, key); - setting = true; - String chan; - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - chan = "Position3D"; - break; - case Animation::TYPE_ROTATION_3D: - chan = "Rotation3D"; - break; - case Animation::TYPE_SCALE_3D: - chan = "Scale3D"; - break; - default: { - } - } - - undo_redo->create_action(vformat(TTR("Anim Change %s"), chan)); - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); + } break; + case Animation::TYPE_BEZIER: { + if (name == "value") { + const Variant &value = p_value; - setting = false; - return true; - } + setting = true; + undo_redo->create_action(TTR("Animation 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); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - } break; - case Animation::TYPE_BLEND_SHAPE: - case Animation::TYPE_VALUE: { - if (name == "value") { - Variant value = p_value; + setting = false; + return true; + } - if (value.get_type() == Variant::NODE_PATH) { - _fix_node_path(value); - } + if (name == "in_handle") { + const Variant &value = p_value; - setting = true; - undo_redo->create_action(TTR("Animation 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); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); + setting = true; + undo_redo->create_action(TTR("Animation 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); + 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; - case Animation::TYPE_METHOD: { - Dictionary d_old = animation->track_get_key_value(track, key); - Dictionary d_new = d_old.duplicate(); + setting = false; + return true; + } - bool change_notify_deserved = false; - bool mergeable = false; + if (name == "out_handle") { + const Variant &value = p_value; - 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()) { - Callable::CallError err; - if (Variant::can_convert(args[idx].get_type(), t)) { - Variant old = args[idx]; - Variant *ptrs[1] = { &old }; - Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); - } else { - Variant::construct(t, args.write[idx], nullptr, 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); - } + setting = true; + undo_redo->create_action(TTR("Animation 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); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - args.write[idx] = value; - d_new["args"] = args; - mergeable = true; - } - } + setting = false; + return true; + } - if (mergeable) { - undo_redo->create_action(TTR("Animation Change Call"), UndoRedo::MERGE_ENDS); - } else { - undo_redo->create_action(TTR("Animation Change Call")); - } + if (name == "handle_mode") { + const Variant &value = p_value; 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); + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + int prev = animation->bezier_track_get_key_handle_mode(track, key); + undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); 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; - } break; - case Animation::TYPE_BEZIER: { - if (name == "value") { - const Variant &value = p_value; - - setting = true; - undo_redo->create_action(TTR("Animation 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); - 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; + case Animation::TYPE_AUDIO: { + if (name == "stream") { + Ref<AudioStream> stream = p_value; - if (name == "in_handle") { - const Variant &value = p_value; + setting = true; + undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); + Ref<Resource> 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); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - setting = true; - undo_redo->create_action(TTR("Animation 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); - 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; - } + setting = false; + return true; + } - if (name == "out_handle") { - const Variant &value = p_value; + if (name == "start_offset") { + float value = p_value; - setting = true; - undo_redo->create_action(TTR("Animation 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); - 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; - } + setting = true; + undo_redo->create_action(TTR("Animation 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); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - if (name == "handle_mode") { - const Variant &value = p_value; + setting = false; + return true; + } - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - int prev = animation->bezier_track_get_key_handle_mode(track, key); - undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); - undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); - 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; - case Animation::TYPE_AUDIO: { - if (name == "stream") { - Ref<AudioStream> stream = p_value; + if (name == "end_offset") { + float value = p_value; - setting = true; - undo_redo->create_action(TTR("Animation Change Keyframe Value"), UndoRedo::MERGE_ENDS); - Ref<Resource> 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); - 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; - } + setting = true; + undo_redo->create_action(TTR("Animation 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); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - if (name == "start_offset") { - float value = p_value; + setting = false; + return true; + } + } break; + case Animation::TYPE_ANIMATION: { + if (name == "animation") { + StringName anim_name = p_value; - setting = true; - undo_redo->create_action(TTR("Animation 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); - 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; - } + setting = true; + undo_redo->create_action(TTR("Animation 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); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); - if (name == "end_offset") { - float value = p_value; + setting = false; + return true; + } + } break; + } - setting = true; - undo_redo->create_action(TTR("Animation 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); - 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; - case Animation::TYPE_ANIMATION: { - if (name == "animation") { - StringName anim_name = p_value; + return false; +} - setting = true; - undo_redo->create_action(TTR("Animation 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); - 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; - } +bool AnimationTrackKeyEdit::_get(const StringName &p_name, Variant &r_ret) const { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND_V(key == -1, false); - return false; + String name = p_name; + if (name == "easing") { + r_ret = animation->track_get_key_transition(track, key); + return true; } - bool _get(const StringName &p_name, Variant &r_ret) const { - int key = animation->track_find_key(track, key_ofs, true); - ERR_FAIL_COND_V(key == -1, false); - - 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; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + r_ret = animation->track_get_key_value(track, key); + return true; + } + } break; + case Animation::TYPE_BLEND_SHAPE: + case Animation::TYPE_VALUE: { + if (name == "value") { + r_ret = animation->track_get_key_value(track, key); + return true; } - r_ret = key_ofs * fps; - return true; - } - if (name == "easing") { - r_ret = animation->track_get_key_transition(track, key); - return true; - } + } break; + case Animation::TYPE_METHOD: { + Dictionary d = animation->track_get_key_value(track, key); - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - case Animation::TYPE_ROTATION_3D: - case Animation::TYPE_SCALE_3D: { - if (name == "position" || name == "rotation" || name == "scale") { - r_ret = animation->track_get_key_value(track, key); - return true; - } - } break; - case Animation::TYPE_BLEND_SHAPE: - case Animation::TYPE_VALUE: { - if (name == "value") { - r_ret = animation->track_get_key_value(track, key); - return true; - } + if (name == "name") { + ERR_FAIL_COND_V(!d.has("method"), false); + r_ret = d["method"]; + return true; + } - } break; - case Animation::TYPE_METHOD: { - Dictionary d = animation->track_get_key_value(track, key); + ERR_FAIL_COND_V(!d.has("args"), false); - if (name == "name") { - ERR_FAIL_COND_V(!d.has("method"), false); - r_ret = d["method"]; - return true; - } + Vector<Variant> args = d["args"]; - ERR_FAIL_COND_V(!d.has("args"), false); + if (name == "arg_count") { + r_ret = args.size(); + return true; + } - Vector<Variant> args = d["args"]; + if (name.begins_with("args/")) { + int idx = name.get_slice("/", 1).to_int(); + ERR_FAIL_INDEX_V(idx, args.size(), false); - if (name == "arg_count") { - r_ret = args.size(); + String what = name.get_slice("/", 2); + if (what == "type") { + r_ret = args[idx].get_type(); 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); + if (what == "value") { + r_ret = args[idx]; return true; } + } - if (name == "in_handle") { - r_ret = animation->bezier_track_get_key_in_handle(track, key); - return true; - } + } break; + case Animation::TYPE_BEZIER: { + if (name == "value") { + r_ret = animation->bezier_track_get_key_value(track, key); + return true; + } - if (name == "out_handle") { - r_ret = animation->bezier_track_get_key_out_handle(track, key); - return true; - } + if (name == "in_handle") { + r_ret = animation->bezier_track_get_key_in_handle(track, key); + return true; + } - if (name == "handle_mode") { - r_ret = animation->bezier_track_get_key_handle_mode(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 == "handle_mode") { + r_ret = animation->bezier_track_get_key_handle_mode(track, key); + return true; + } - if (name == "start_offset") { - r_ret = animation->audio_track_get_key_start_offset(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 == "end_offset") { - r_ret = animation->audio_track_get_key_end_offset(track, key); - return true; - } + if (name == "start_offset") { + r_ret = animation->audio_track_get_key_start_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; - } + if (name == "end_offset") { + r_ret = animation->audio_track_get_key_end_offset(track, key); + return true; + } - } break; - } + } break; + case Animation::TYPE_ANIMATION: { + if (name == "animation") { + r_ret = animation->animation_track_get_key_animation(track, key); + return true; + } - return false; + } break; } - void _get_property_list(List<PropertyInfo> *p_list) const { - if (animation.is_null()) { - return; - } - ERR_FAIL_INDEX(track, animation->get_track_count()); - int key = animation->track_find_key(track, key_ofs, true); - ERR_FAIL_COND(key == -1); + return false; +} - if (use_fps && animation->get_step() > 0) { - float max_frame = animation->get_length() / animation->get_step(); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("frame"), PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",1")); - } else { - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("time"), PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01")); - } +void AnimationTrackKeyEdit::_get_property_list(List<PropertyInfo> *p_list) const { + if (animation.is_null()) { + return; + } - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, PNAME("position"))); - } break; - case Animation::TYPE_ROTATION_3D: { - p_list->push_back(PropertyInfo(Variant::QUATERNION, PNAME("rotation"))); - } break; - case Animation::TYPE_SCALE_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, PNAME("scale"))); - } break; - case Animation::TYPE_BLEND_SHAPE: { - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); - } break; - case Animation::TYPE_VALUE: { - Variant v = animation->track_get_key_value(track, key); + ERR_FAIL_INDEX(track, animation->get_track_count()); + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND(key == -1); - if (hint.type != Variant::NIL) { - PropertyInfo pi = hint; - pi.name = PNAME("value"); - p_list->push_back(pi); - } else { - PropertyHint val_hint = PROPERTY_HINT_NONE; - String val_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()) { - val_hint = PROPERTY_HINT_RESOURCE_TYPE; - val_hint_string = res->get_class(); - } - } + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, PNAME("position"))); + } break; + case Animation::TYPE_ROTATION_3D: { + p_list->push_back(PropertyInfo(Variant::QUATERNION, PNAME("rotation"))); + } break; + case Animation::TYPE_SCALE_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, PNAME("scale"))); + } break; + case Animation::TYPE_BLEND_SHAPE: { + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); + } break; + case Animation::TYPE_VALUE: { + Variant v = animation->track_get_key_value(track, key); - if (v.get_type() != Variant::NIL) { - p_list->push_back(PropertyInfo(v.get_type(), PNAME("value"), val_hint, val_hint_string)); + if (hint.type != Variant::NIL) { + PropertyInfo pi = hint; + pi.name = PNAME("value"); + p_list->push_back(pi); + } else { + PropertyHint val_hint = PROPERTY_HINT_NONE; + String val_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()) { + val_hint = PROPERTY_HINT_RESOURCE_TYPE; + val_hint_string = res->get_class(); } } - } break; - case Animation::TYPE_METHOD: { - p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("name"))); - p_list->push_back(PropertyInfo(Variant::INT, PNAME("arg_count"), PROPERTY_HINT_RANGE, "0,32,1,or_greater")); - - Dictionary d = animation->track_get_key_value(track, 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)); + if (v.get_type() != Variant::NIL) { + p_list->push_back(PropertyInfo(v.get_type(), PNAME("value"), val_hint, val_hint_string)); } + } - for (int i = 0; i < args.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%d/%s", PNAME("args"), i, PNAME("type")), PROPERTY_HINT_ENUM, vtypes)); - if (args[i].get_type() != Variant::NIL) { - p_list->push_back(PropertyInfo(args[i].get_type(), vformat("%s/%d/%s", PNAME("args"), i, PNAME("value")))); - } + } break; + case Animation::TYPE_METHOD: { + p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("name"))); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("arg_count"), PROPERTY_HINT_RANGE, "0,32,1,or_greater")); + + Dictionary d = animation->track_get_key_value(track, 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)); + } - } break; - case Animation::TYPE_BEZIER: { - Animation::HandleMode hm = animation->bezier_track_get_key_handle_mode(track, key); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); - if (hm == Animation::HANDLE_MODE_LINEAR) { - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); - } else { - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"))); - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"))); + for (int i = 0; i < args.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%d/%s", PNAME("args"), i, PNAME("type")), PROPERTY_HINT_ENUM, vtypes)); + if (args[i].get_type() != Variant::NIL) { + p_list->push_back(PropertyInfo(args[i].get_type(), vformat("%s/%d/%s", PNAME("args"), i, PNAME("value")))); } - p_list->push_back(PropertyInfo(Variant::INT, PNAME("handle_mode"), PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); - - } break; - case Animation::TYPE_AUDIO: { - p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("stream"), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("start_offset"), PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater")); - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("end_offset"), PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater")); + } - } break; - case Animation::TYPE_ANIMATION: { - String animations; + } break; + case Animation::TYPE_BEZIER: { + Animation::HandleMode hm = animation->bezier_track_get_key_handle_mode(track, key); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); + if (hm == Animation::HANDLE_MODE_LINEAR) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); + } else { + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"))); + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"))); + } + p_list->push_back(PropertyInfo(Variant::INT, PNAME("handle_mode"), PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); - if (root_path && root_path->has_node(animation->track_get_path(track))) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(track))); - if (ap) { - List<StringName> anims; - ap->get_animation_list(&anims); - for (const StringName &E : anims) { - if (!animations.is_empty()) { - animations += ","; - } + } break; + case Animation::TYPE_AUDIO: { + p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("stream"), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("start_offset"), PROPERTY_HINT_RANGE, "0,3600,0.0001,or_greater")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("end_offset"), PROPERTY_HINT_RANGE, "0,3600,0.0001,or_greater")); - animations += String(E); + } break; + case Animation::TYPE_ANIMATION: { + String animations; + + if (root_path && root_path->has_node(animation->track_get_path(track))) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(track))); + if (ap) { + List<StringName> anims; + ap->get_animation_list(&anims); + for (const StringName &E : anims) { + if (!animations.is_empty()) { + animations += ","; } + + animations += String(E); } } + } - if (!animations.is_empty()) { - animations += ","; - } - animations += "[stop]"; + if (!animations.is_empty()) { + animations += ","; + } + animations += "[stop]"; - p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("animation"), PROPERTY_HINT_ENUM, animations)); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("animation"), PROPERTY_HINT_ENUM, animations)); - } break; - } + } break; + } - if (animation->track_get_type(track) == Animation::TYPE_VALUE) { - p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("easing"), PROPERTY_HINT_EXP_EASING)); - } + if (animation->track_get_type(track) == Animation::TYPE_VALUE) { + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("easing"), PROPERTY_HINT_EXP_EASING)); } +} - Ref<Animation> animation; - int track = -1; - float key_ofs = 0; - Node *root_path = nullptr; +void AnimationTrackKeyEdit::notify_change() { + notify_property_list_changed(); +} - PropertyInfo hint; - NodePath base; - bool use_fps = false; +Node *AnimationTrackKeyEdit::get_root_path() { + return root_path; +} - void notify_change() { - notify_property_list_changed(); - } +void AnimationTrackKeyEdit::set_use_fps(bool p_enable) { + use_fps = p_enable; + notify_property_list_changed(); +} - Node *get_root_path() { - return root_path; - } +void AnimationMultiTrackKeyEdit::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationMultiTrackKeyEdit::_update_obj); + ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationMultiTrackKeyEdit::_key_ofs_changed); + ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_script_from_inspector); + ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_metadata_from_inspector); + ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationMultiTrackKeyEdit::get_root_path); + ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiTrackKeyEdit::_dont_undo_redo); + ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiTrackKeyEdit::_is_read_only); +} - void set_use_fps(bool p_enable) { - use_fps = p_enable; - notify_property_list_changed(); +void AnimationMultiTrackKeyEdit::_fix_node_path(Variant &value, NodePath &base) { + NodePath np = value; + + if (np == NodePath()) { + return; } -}; -class AnimationMultiTrackKeyEdit : public Object { - GDCLASS(AnimationMultiTrackKeyEdit, Object); + Node *root = EditorNode::get_singleton()->get_tree()->get_root(); -public: - bool setting = false; - bool animation_read_only = false; + Node *np_node = root->get_node(np); + ERR_FAIL_COND(!np_node); - bool _hide_script_from_inspector() { return true; } - bool _hide_metadata_from_inspector() { return true; } - bool _dont_undo_redo() { return true; } + Node *edited_node = root->get_node(base); + ERR_FAIL_COND(!edited_node); - bool _is_read_only() { - return animation_read_only; - } + value = edited_node->get_path_to(np_node); +} - static void _bind_methods() { - ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationMultiTrackKeyEdit::_update_obj); - ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationMultiTrackKeyEdit::_key_ofs_changed); - ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_script_from_inspector); - ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_metadata_from_inspector); - ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationMultiTrackKeyEdit::get_root_path); - ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiTrackKeyEdit::_dont_undo_redo); - ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiTrackKeyEdit::_is_read_only); +void AnimationMultiTrackKeyEdit::_update_obj(const Ref<Animation> &p_anim) { + if (setting || animation != p_anim) { + return; } - void _fix_node_path(Variant &value, NodePath &base) { - NodePath np = value; + notify_change(); +} - if (np == NodePath()) { - return; - } +void AnimationMultiTrackKeyEdit::_key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) { + if (animation != p_anim) { + return; + } - Node *root = EditorNode::get_singleton()->get_tree()->get_root(); + for (const KeyValue<int, List<float>> &E : key_ofs_map) { + int key = 0; + for (const float &key_ofs : E.value) { + if (from != key_ofs) { + key++; + continue; + } - Node *np_node = root->get_node(np); - ERR_FAIL_COND(!np_node); + int track = E.key; + key_ofs_map[track][key] = to; - Node *edited_node = root->get_node(base); - ERR_FAIL_COND(!edited_node); + if (setting) { + return; + } - value = edited_node->get_path_to(np_node); - } + notify_change(); - 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 (const KeyValue<int, List<float>> &E : key_ofs_map) { - int key = 0; - for (const float &key_ofs : E.value) { - if (from != key_ofs) { - key++; - continue; - } +bool AnimationMultiTrackKeyEdit::_set(const StringName &p_name, const Variant &p_value) { + bool update_obj = false; + bool change_notify_deserved = false; + for (const KeyValue<int, List<float>> &E : key_ofs_map) { + int track = E.key; + for (const float &key_ofs : E.value) { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND_V(key == -1, false); - int track = E.key; - key_ofs_map[track][key] = to; + String name = p_name; + if (name == "easing") { + float val = p_value; + float prev_val = animation->track_get_key_transition(track, key); - if (setting) { - return; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Transition"), UndoRedo::MERGE_ENDS); } - - notify_change(); - - return; + 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; } - } - } - bool _set(const StringName &p_name, const Variant &p_value) { - bool update_obj = false; - bool change_notify_deserved = false; - for (const KeyValue<int, List<float>> &E : key_ofs_map) { - int track = E.key; - for (const float &key_ofs : E.value) { - 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; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + Variant old = animation->track_get_key_value(track, key); + if (!setting) { + String chan; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + chan = "Position3D"; + break; + case Animation::TYPE_ROTATION_3D: + chan = "Rotation3D"; + break; + case Animation::TYPE_SCALE_3D: + chan = "Scale3D"; + break; + default: { + } } - new_time /= fps; - } - - int existing = animation->track_find_key(track, new_time, true); - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - if (!setting) { setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Time"), UndoRedo::MERGE_ENDS); + undo_redo->create_action(vformat(TTR("Animation Multi Change %s"), chan)); } + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); + update_obj = true; + } break; + case Animation::TYPE_BLEND_SHAPE: + case Animation::TYPE_VALUE: { + if (name == "value") { + Variant value = p_value; - 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_time", 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 (value.get_type() == Variant::NODE_PATH) { + _fix_node_path(value, base_map[track]); + } - 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); + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation 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; } - } else if (name == "easing") { - float val = p_value; - float prev_val = animation->track_get_key_transition(track, key); + } break; + case Animation::TYPE_METHOD: { + Dictionary d_old = animation->track_get_key_value(track, key); + Dictionary d_new = d_old.duplicate(); - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation 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; - } + bool mergeable = false; - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - case Animation::TYPE_ROTATION_3D: - case Animation::TYPE_SCALE_3D: { - Variant old = animation->track_get_key_value(track, key); - if (!setting) { - String chan; - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - chan = "Position3D"; - break; - case Animation::TYPE_ROTATION_3D: - chan = "Rotation3D"; - break; - case Animation::TYPE_SCALE_3D: - chan = "Scale3D"; - break; - default: { + 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()) { + Callable::CallError err; + if (Variant::can_convert(args[idx].get_type(), t)) { + Variant old = args[idx]; + Variant *ptrs[1] = { &old }; + Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); + } else { + Variant::construct(t, args.write[idx], nullptr, 0, err); } + change_notify_deserved = true; + d_new["args"] = args; } - - setting = true; - undo_redo->create_action(vformat(TTR("Animation Multi Change %s"), chan)); - } - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); - update_obj = true; - } break; - case Animation::TYPE_BLEND_SHAPE: - case Animation::TYPE_VALUE: { - if (name == "value") { + } else if (what == "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("Animation 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; + args.write[idx] = value; + d_new["args"] = args; + mergeable = 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; + Variant prev = animation->track_get_key_value(track, key); - 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()) { - Callable::CallError err; - if (Variant::can_convert(args[idx].get_type(), t)) { - Variant old = args[idx]; - Variant *ptrs[1] = { &old }; - Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); - } else { - Variant::construct(t, args.write[idx], nullptr, 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]); - } + if (!setting) { + if (mergeable) { + undo_redo->create_action(TTR("Animation Multi Change Call"), UndoRedo::MERGE_ENDS); + } else { + undo_redo->create_action(TTR("Animation Multi Change Call")); + } - args.write[idx] = value; - d_new["args"] = args; - mergeable = true; - } + 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("Animation 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; - Variant prev = animation->track_get_key_value(track, key); + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); + } + Vector2 prev = animation->bezier_track_get_key_in_handle(track, key); + undo_redo->add_do_method(this, "_bezier_track_set_key_in_handle", track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_in_handle", track, key, prev); + update_obj = true; + } else if (name == "out_handle") { + const Variant &value = p_value; if (!setting) { - if (mergeable) { - undo_redo->create_action(TTR("Animation Multi Change Call"), UndoRedo::MERGE_ENDS); - } else { - undo_redo->create_action(TTR("Animation Multi Change Call")); - } + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); + } + Vector2 prev = animation->bezier_track_get_key_out_handle(track, key); + undo_redo->add_do_method(this, "_bezier_track_set_key_out_handle", track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_out_handle", track, key, prev); + update_obj = true; + } else if (name == "handle_mode") { + const Variant &value = p_value; + if (!setting) { setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } + int prev = animation->bezier_track_get_key_handle_mode(track, key); + undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); + update_obj = true; + } + } break; + case Animation::TYPE_AUDIO: { + if (name == "stream") { + Ref<AudioStream> stream = p_value; - 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); + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); + } + Ref<Resource> 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; - } break; - case Animation::TYPE_BEZIER: { - if (name == "value") { - const Variant &value = p_value; + } else if (name == "start_offset") { + float value = p_value; - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation 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("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - Vector2 prev = animation->bezier_track_get_key_in_handle(track, key); - undo_redo->add_do_method(this, "_bezier_track_set_key_in_handle", track, key, value); - undo_redo->add_undo_method(this, "_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("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - Vector2 prev = animation->bezier_track_get_key_out_handle(track, key); - undo_redo->add_do_method(this, "_bezier_track_set_key_out_handle", track, key, value); - undo_redo->add_undo_method(this, "_bezier_track_set_key_out_handle", track, key, prev); - update_obj = true; - } else if (name == "handle_mode") { - const Variant &value = p_value; - - if (!setting) { - setting = true; - undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - int prev = animation->bezier_track_get_key_handle_mode(track, key); - undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); - undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); - update_obj = true; + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } - } break; - case Animation::TYPE_AUDIO: { - if (name == "stream") { - Ref<AudioStream> stream = p_value; + 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("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); - } - Ref<Resource> 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("Animation 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("Animation 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; + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } - } break; - case Animation::TYPE_ANIMATION: { - if (name == "animation") { - StringName anim_name = p_value; + 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("Animation 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; + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Animation Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } - } break; - } + 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; } } + } - Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - 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; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + if (setting) { + if (update_obj) { + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + } - if (change_notify_deserved) { - notify_change(); - } + undo_redo->commit_action(); + setting = false; - return true; + if (change_notify_deserved) { + notify_change(); } - return false; + return true; } - bool _get(const StringName &p_name, Variant &r_ret) const { - for (const KeyValue<int, List<float>> &E : key_ofs_map) { - int track = E.key; - for (const float &key_ofs : E.value) { - int key = animation->track_find_key(track, key_ofs, true); - ERR_CONTINUE(key == -1); + return false; +} - String name = p_name; - if (name == "time") { - r_ret = key_ofs; - return true; - } +bool AnimationMultiTrackKeyEdit::_get(const StringName &p_name, Variant &r_ret) const { + for (const KeyValue<int, List<float>> &E : key_ofs_map) { + int track = E.key; + for (const float &key_ofs : E.value) { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + ERR_CONTINUE(key == -1); - if (name == "frame") { - float fps = animation->get_step(); - if (fps > 0) { - fps = 1.0 / fps; + String name = p_name; + if (name == "easing") { + r_ret = animation->track_get_key_transition(track, key); + return true; + } + + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + r_ret = animation->track_get_key_value(track, key); + return true; } - r_ret = key_ofs * fps; - return true; - } - if (name == "easing") { - r_ret = animation->track_get_key_transition(track, key); - return true; - } + } break; + case Animation::TYPE_BLEND_SHAPE: + case Animation::TYPE_VALUE: { + if (name == "value") { + r_ret = animation->track_get_key_value(track, key); + return true; + } - switch (animation->track_get_type(track)) { - case Animation::TYPE_POSITION_3D: - case Animation::TYPE_ROTATION_3D: - case Animation::TYPE_SCALE_3D: { - if (name == "position" || name == "rotation" || name == "scale") { - 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); - } break; - case Animation::TYPE_BLEND_SHAPE: - case Animation::TYPE_VALUE: { - if (name == "value") { - r_ret = animation->track_get_key_value(track, key); - return true; - } + if (name == "name") { + ERR_FAIL_COND_V(!d.has("method"), false); + r_ret = d["method"]; + return true; + } - } break; - case Animation::TYPE_METHOD: { - Dictionary d = animation->track_get_key_value(track, key); + ERR_FAIL_COND_V(!d.has("args"), false); - if (name == "name") { - ERR_FAIL_COND_V(!d.has("method"), false); - r_ret = d["method"]; - return true; - } + Vector<Variant> args = d["args"]; - ERR_FAIL_COND_V(!d.has("args"), false); + if (name == "arg_count") { + r_ret = args.size(); + return true; + } - Vector<Variant> args = d["args"]; + if (name.begins_with("args/")) { + int idx = name.get_slice("/", 1).to_int(); + ERR_FAIL_INDEX_V(idx, args.size(), false); - if (name == "arg_count") { - r_ret = args.size(); + String what = name.get_slice("/", 2); + if (what == "type") { + r_ret = args[idx].get_type(); 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); + if (what == "value") { + r_ret = args[idx]; return true; } + } - if (name == "in_handle") { - r_ret = animation->bezier_track_get_key_in_handle(track, key); - return true; - } + } break; + case Animation::TYPE_BEZIER: { + if (name == "value") { + r_ret = animation->bezier_track_get_key_value(track, key); + return true; + } - if (name == "out_handle") { - r_ret = animation->bezier_track_get_key_out_handle(track, key); - return true; - } + if (name == "in_handle") { + r_ret = animation->bezier_track_get_key_in_handle(track, key); + return true; + } - if (name == "handle_mode") { - r_ret = animation->bezier_track_get_key_handle_mode(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 == "handle_mode") { + r_ret = animation->bezier_track_get_key_handle_mode(track, key); + return true; + } - if (name == "start_offset") { - r_ret = animation->audio_track_get_key_start_offset(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 == "end_offset") { - r_ret = animation->audio_track_get_key_end_offset(track, key); - return true; - } + if (name == "start_offset") { + r_ret = animation->audio_track_get_key_start_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; - } + if (name == "end_offset") { + r_ret = animation->audio_track_get_key_end_offset(track, key); + return true; + } - } break; - } + } break; + case Animation::TYPE_ANIMATION: { + if (name == "animation") { + r_ret = animation->animation_track_get_key_animation(track, key); + return true; + } + + } break; } } + } - return false; + return false; +} + +void AnimationMultiTrackKeyEdit::_get_property_list(List<PropertyInfo> *p_list) const { + if (animation.is_null()) { + return; } - void _get_property_list(List<PropertyInfo> *p_list) const { - if (animation.is_null()) { - return; - } - int first_track = -1; - float first_key = -1.0; + int first_track = -1; + float first_key = -1.0; - bool show_time = true; - bool same_track_type = true; - bool same_key_type = true; - for (const KeyValue<int, List<float>> &E : key_ofs_map) { - int track = E.key; - ERR_FAIL_INDEX(track, animation->get_track_count()); + bool same_track_type = true; + bool same_key_type = true; + for (const KeyValue<int, List<float>> &E : key_ofs_map) { + int track = E.key; + ERR_FAIL_INDEX(track, animation->get_track_count()); - if (first_track < 0) { - first_track = track; - } + 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; } - 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 (const float &F : E.value) { + int key = animation->track_find_key(track, F, Animation::FIND_MODE_APPROX); + ERR_FAIL_COND(key == -1); + if (first_key < 0) { + first_key = key; } - for (const float &F : E.value) { - int key = animation->track_find_key(track, F, 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 (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::FLOAT, "frame", PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",1")); - } else { - p_list->push_back(PropertyInfo(Variant::FLOAT, "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_POSITION_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); - } break; - case Animation::TYPE_ROTATION_3D: { - p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale")); - } break; - case Animation::TYPE_SCALE_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); - } break; - case Animation::TYPE_BLEND_SHAPE: { - p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); - } break; - case Animation::TYPE_VALUE: { - if (same_key_type) { - Variant v = animation->track_get_key_value(first_track, first_key); + if (same_track_type) { + switch (animation->track_get_type(first_track)) { + case Animation::TYPE_POSITION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); + } break; + case Animation::TYPE_ROTATION_3D: { + p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale")); + } break; + case Animation::TYPE_SCALE_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); + } break; + case Animation::TYPE_BLEND_SHAPE: { + p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); + } break; + case Animation::TYPE_VALUE: { + if (same_key_type) { + 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 val_hint = PROPERTY_HINT_NONE; - String val_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()) { - val_hint = PROPERTY_HINT_RESOURCE_TYPE; - val_hint_string = res->get_class(); - } + if (hint.type != Variant::NIL) { + PropertyInfo pi = hint; + pi.name = "value"; + p_list->push_back(pi); + } else { + PropertyHint val_hint = PROPERTY_HINT_NONE; + String val_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()) { + val_hint = PROPERTY_HINT_RESOURCE_TYPE; + val_hint_string = res->get_class(); } + } - if (v.get_type() != Variant::NIL) { - p_list->push_back(PropertyInfo(v.get_type(), "value", val_hint, val_hint_string)); - } + if (v.get_type() != Variant::NIL) { + p_list->push_back(PropertyInfo(v.get_type(), "value", val_hint, val_hint_string)); } } + } - p_list->push_back(PropertyInfo(Variant::FLOAT, "easing", PROPERTY_HINT_EXP_EASING)); - } break; - case Animation::TYPE_METHOD: { - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name")); + p_list->push_back(PropertyInfo(Variant::FLOAT, "easing", PROPERTY_HINT_EXP_EASING)); + } break; + case Animation::TYPE_METHOD: { + p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name")); - p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,32,1,or_greater")); + p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,32,1,or_greater")); - 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)); + 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::FLOAT, "value")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle")); - p_list->push_back(PropertyInfo(Variant::INT, "handle_mode", PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); - } break; - case Animation::TYPE_AUDIO: { - p_list->push_back(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "start_offset", PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "end_offset", PROPERTY_HINT_RANGE, "0,3600,0.01,or_greater")); - } break; - case Animation::TYPE_ANIMATION: { - if (key_ofs_map.size() > 1) { - break; + 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::FLOAT, "value")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle")); + p_list->push_back(PropertyInfo(Variant::INT, "handle_mode", PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); + } break; + case Animation::TYPE_AUDIO: { + p_list->push_back(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); + p_list->push_back(PropertyInfo(Variant::FLOAT, "start_offset", PROPERTY_HINT_RANGE, "0,3600,0.0001,or_greater")); + p_list->push_back(PropertyInfo(Variant::FLOAT, "end_offset", PROPERTY_HINT_RANGE, "0,3600,0.0001,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.is_empty()) { - animations += ","; - } + String animations; - animations += String(G->get()); + 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.is_empty()) { + animations += ","; } + + animations += String(G->get()); } } + } - if (!animations.is_empty()) { - animations += ","; - } - animations += "[stop]"; + if (!animations.is_empty()) { + animations += ","; + } + animations += "[stop]"; - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "animation", PROPERTY_HINT_ENUM, animations)); - } break; - } + p_list->push_back(PropertyInfo(Variant::STRING_NAME, "animation", PROPERTY_HINT_ENUM, animations)); + } break; } } +} - 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; - - void notify_change() { - notify_property_list_changed(); - } +void AnimationMultiTrackKeyEdit::notify_change() { + notify_property_list_changed(); +} - Node *get_root_path() { - return root_path; - } +Node *AnimationMultiTrackKeyEdit::get_root_path() { + return root_path; +} - void set_use_fps(bool p_enable) { - use_fps = p_enable; - notify_property_list_changed(); - } -}; +void AnimationMultiTrackKeyEdit::set_use_fps(bool p_enable) { + use_fps = p_enable; + notify_property_list_changed(); +} void AnimationTimelineEdit::_zoom_changed(double) { queue_redraw(); @@ -1420,7 +1246,7 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) { return; } - p_new_len = MAX(0.001, p_new_len); + p_new_len = MAX(0.0001, p_new_len); if (use_fps && animation->get_step() > 0) { p_new_len *= animation->get_step(); } @@ -1539,7 +1365,7 @@ void AnimationTimelineEdit::_notification(int p_what) { float l = animation->get_length(); if (l <= 0) { - l = 0.001; // Avoid crashor. + l = 0.0001; // Avoid crashor. } Ref<Texture2D> hsize_icon = get_theme_icon(SNAME("Hsize"), SNAME("EditorIcons")); @@ -1600,7 +1426,7 @@ void AnimationTimelineEdit::_notification(int p_what) { end_px = zoomw; } - draw_rect(Rect2(Point2(get_name_limit() + begin_px, 0), Point2(end_px - begin_px - 1, h)), timecolor); + draw_rect(Rect2(Point2(get_name_limit() + begin_px, 0), Point2(end_px - begin_px, h)), timecolor); } } @@ -1759,7 +1585,7 @@ void AnimationTimelineEdit::update_values() { } } else { length->set_value(animation->get_length()); - length->set_step(0.001); + length->set_step(0.0001); length->set_tooltip_text(TTR("Animation length (seconds)")); time_icon->set_tooltip_text(TTR("Animation length (seconds)")); } @@ -1950,9 +1776,9 @@ AnimationTimelineEdit::AnimationTimelineEdit() { time_icon->set_tooltip_text(TTR("Animation length (seconds)")); len_hb->add_child(time_icon); length = memnew(EditorSpinSlider); - length->set_min(0.001); + length->set_min(0.0001); length->set_max(36000); - length->set_step(0.001); + length->set_step(0.0001); length->set_allow_greater(true); length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0)); length->set_hide_slider(true); @@ -2058,7 +1884,7 @@ void AnimationTrackEdit::_notification(int p_what) { } else if (animation->track_get_type(track) == Animation::TYPE_AUDIO) { text = TTR("Audio Clips:"); } else if (animation->track_get_type(track) == Animation::TYPE_ANIMATION) { - text = TTR("Anim Clips:"); + text = TTR("Animation Clips:"); } else { text += anim_path.get_concatenated_subnames(); } @@ -2670,7 +2496,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { } if (key_idx != -1) { - String text = TTR("Time (s):") + " " + rtos(animation->track_get_key_time(track, key_idx)) + "\n"; + String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->track_get_key_time(track, key_idx), 0.0001))) + "\n"; switch (animation->track_get_type(track)) { case Animation::TYPE_POSITION_3D: { Vector3 t = animation->track_get_key_value(track, key_idx); @@ -4390,7 +4216,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD p_next_tracks.normal++; } else { undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_id.track_idx, time); - int existing = animation->track_find_key(p_id.track_idx, time, true); + int existing = animation->track_find_key(p_id.track_idx, time, Animation::FIND_MODE_APPROX); if (existing != -1) { Variant v = animation->track_get_key_value(p_id.track_idx, existing); float trans = animation->track_get_key_transition(p_id.track_idx, existing); @@ -5005,8 +4831,8 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { if (snap->is_pressed() && step->get_value() != 0) { p_ofs = snap_time(p_ofs); } - while (animation->track_find_key(p_track, p_ofs, true) != -1) { // Make sure insertion point is valid. - p_ofs += 0.001; + while (animation->track_find_key(p_track, p_ofs, Animation::FIND_MODE_APPROX) != -1) { // Make sure insertion point is valid. + p_ofs += 0.0001; } Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); @@ -5338,7 +5164,7 @@ void AnimationTrackEditor::_select_at_anim(const Ref<Animation> &p_anim, int p_t 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); SelectedKey sk; @@ -5365,7 +5191,7 @@ void AnimationTrackEditor::_move_selection_commit() { // 2 - Remove overlapped keys. for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { float newtime = snap_time(E->get().pos + motion); - int idx = animation->track_find_key(E->key().track, newtime, true); + int idx = animation->track_find_key(E->key().track, newtime, Animation::FIND_MODE_APPROX); if (idx == -1) { continue; } @@ -5625,7 +5451,7 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) { continue; } - int existing_idx = animation->track_find_key(dst_track, dst_time, true); + int existing_idx = animation->track_find_key(dst_track, dst_time, Animation::FIND_MODE_APPROX); undo_redo->add_do_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key)); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", dst_track, dst_time); @@ -5916,7 +5742,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { // 2 - Remove overlapped keys. for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { float newtime = (E->get().pos - from_t) * s + from_t; - int idx = animation->track_find_key(E->key().track, newtime, true); + int idx = animation->track_find_key(E->key().track, newtime, Animation::FIND_MODE_APPROX); if (idx == -1) { continue; } @@ -6127,7 +5953,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { undo_redo->add_do_method(reset.ptr(), "track_set_path", dst_track, path); undo_redo->add_undo_method(reset.ptr(), "remove_track", dst_track); } else { - existing_idx = reset->track_find_key(dst_track, 0, true); + existing_idx = reset->track_find_key(dst_track, 0, Animation::FIND_MODE_APPROX); } undo_redo->add_do_method(reset.ptr(), "track_insert_key", dst_track, 0, animation->track_get_key_value(sk.track, sk.key), animation->track_get_key_transition(sk.track, sk.key)); @@ -6656,7 +6482,7 @@ AnimationTrackEditor::AnimationTrackEditor() { step = memnew(EditorSpinSlider); step->set_min(0); step->set_max(1000000); - step->set_step(0.001); + step->set_step(0.0001); step->set_hide_slider(true); step->set_custom_minimum_size(Size2(100, 0) * EDSCALE); step->set_tooltip_text(TTR("Animation step value.")); @@ -6946,3 +6772,103 @@ AnimationTrackEditor::~AnimationTrackEditor() { memdelete(multi_key_edit); } } + +// AnimationTrackKeyEditEditorPlugin + +void AnimationTrackKeyEditEditor::_time_edit_entered() { + int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); + if (key == -1) { + return; + } + key_data_cache.time = animation->track_get_key_time(track, key); + key_data_cache.transition = animation->track_get_key_transition(track, key); + key_data_cache.value = animation->track_get_key_value(track, key); +} + +void AnimationTrackKeyEditEditor::_time_edit_exited() { + real_t new_time = spinner->get_value(); + + if (use_fps) { + real_t fps = animation->get_step(); + if (fps > 0) { + fps = 1.0 / fps; + } + new_time /= fps; + } + + if (Math::is_equal_approx(new_time, key_data_cache.time)) { + return; // No change. + } + + int existing = animation->track_find_key(track, new_time, Animation::FIND_MODE_APPROX); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Animation Change Keyframe Time"), UndoRedo::MERGE_ENDS); + + if (existing != -1) { + undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, animation->track_get_key_time(track, existing)); + } + undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, key_data_cache.time); + undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, key_data_cache.value, key_data_cache.transition); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time); + undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_data_cache.time, key_data_cache.value, key_data_cache.transition); + if (existing != -1) { + undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, animation->track_get_key_time(track, existing), animation->track_get_key_value(track, existing), animation->track_get_key_transition(track, existing)); + } + + // Reselect key. + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + AnimationTrackEditor *ate = ape->get_track_editor(); + if (ate) { + undo_redo->add_do_method(ate, "_clear_selection_for_anim", animation); + undo_redo->add_undo_method(ate, "_clear_selection_for_anim", animation); + undo_redo->add_do_method(ate, "_select_at_anim", animation, track, new_time); + undo_redo->add_undo_method(ate, "_select_at_anim", animation, track, key_data_cache.time); + } + } + + undo_redo->commit_action(); +} + +AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animation, int p_track, real_t p_key_ofs, bool p_use_fps) { + if (!p_animation.is_valid()) { + return; + } + + animation = p_animation; + track = p_track; + key_ofs = p_key_ofs; + use_fps = p_use_fps; + + set_label("Time"); + + spinner = memnew(EditorSpinSlider); + spinner->set_focus_mode(Control::FOCUS_CLICK); + spinner->set_min(0); + spinner->set_allow_greater(true); + spinner->set_allow_lesser(true); + + if (use_fps) { + spinner->set_step(1); + spinner->set_hide_slider(true); + real_t fps = animation->get_step(); + if (fps > 0) { + fps = 1.0 / fps; + } + spinner->set_value(key_ofs * fps); + } else { + spinner->set_step(0.0001); + spinner->set_value(key_ofs); + spinner->set_max(animation->get_length()); + } + + add_child(spinner); + + spinner->connect("grabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED); + spinner->connect("ungrabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED); + spinner->connect("value_focus_entered", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED); + spinner->connect("value_focus_exited", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED); +} + +AnimationTrackKeyEditEditor::~AnimationTrackKeyEditEditor() { +} diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 4b50424f39..5ae826bd5c 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -50,9 +50,83 @@ #include "scene/resources/animation.h" #include "scene_tree_editor.h" +class AnimationTrackEditor; class AnimationTrackEdit; 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 { GDCLASS(AnimationTimelineEdit, Range); @@ -129,8 +203,6 @@ public: AnimationTimelineEdit(); }; -class AnimationTrackEditor; - class AnimationTrackEdit : public Control { GDCLASS(AnimationTrackEdit, Control); friend class AnimationTimelineEdit; @@ -592,4 +664,30 @@ public: ~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 diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index 704935e163..0ad62710eb 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/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 -= 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) { @@ -887,8 +887,8 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int len -= end_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; @@ -1014,8 +1014,8 @@ void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant 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(); diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp index 8b31781c5e..351af4508f 100644 --- a/editor/debugger/editor_debugger_tree.cpp +++ b/editor/debugger/editor_debugger_tree.cpp @@ -118,6 +118,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou item_menu->add_icon_item(get_theme_icon(SNAME("CreateNewSceneFrom"), SNAME("EditorIcons")), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE); item_menu->add_icon_item(get_theme_icon(SNAME("CopyNodePath"), SNAME("EditorIcons")), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH); item_menu->set_position(get_screen_position() + get_local_mouse_position()); + item_menu->reset_size(); item_menu->popup(); } @@ -323,6 +324,8 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) { file_dialog->add_filter("*." + extensions[i], extensions[i].to_upper()); } + String filename = get_selected_path().get_file() + "." + extensions.front()->get().to_lower(); + file_dialog->set_current_path(filename); file_dialog->popup_file_dialog(); } break; case ITEM_MENU_COPY_NODE_PATH: { diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 35666a5566..0f8b2f36be 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -106,7 +106,7 @@ void EditorProfiler::clear() { seeking = false; // Ensure button text (start, stop) is correct - _set_button_text(); + _update_button_text(); emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } @@ -376,7 +376,7 @@ void EditorProfiler::_update_frame() { updating_frame = false; } -void EditorProfiler::_set_button_text() { +void EditorProfiler::_update_button_text() { if (activate->is_pressed()) { activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); activate->set_text(TTR("Stop")); @@ -387,7 +387,7 @@ void EditorProfiler::_set_button_text() { } void EditorProfiler::_activate_pressed() { - _set_button_text(); + _update_button_text(); if (activate->is_pressed()) { _clear_pressed(); @@ -510,13 +510,17 @@ void EditorProfiler::_bind_methods() { } void EditorProfiler::set_enabled(bool p_enable, bool p_clear) { - activate->set_pressed(false); activate->set_disabled(!p_enable); if (p_clear) { clear(); } } +void EditorProfiler::set_pressed(bool p_pressed) { + activate->set_pressed(p_pressed); + _update_button_text(); +} + bool EditorProfiler::is_profiling() { return activate->is_pressed(); } @@ -595,6 +599,7 @@ EditorProfiler::EditorProfiler() { add_child(hb); activate = memnew(Button); activate->set_toggle_mode(true); + activate->set_disabled(true); activate->set_text(TTR("Start")); activate->connect("pressed", callable_mp(this, &EditorProfiler::_activate_pressed)); hb->add_child(activate); diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index e9ecc285ed..d0dd67688b 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -122,7 +122,7 @@ private: Timer *frame_delay = nullptr; Timer *plot_delay = nullptr; - void _set_button_text(); + void _update_button_text(); void _update_frame(); void _activate_pressed(); @@ -155,6 +155,7 @@ protected: public: void add_frame_metric(const Metric &p_metric, bool p_final = false); void set_enabled(bool p_enable, bool p_clear = true); + void set_pressed(bool p_pressed); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index b8bc712ba6..fe425220c9 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -66,6 +66,7 @@ void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) { } updating_frame = true; + clear_button->set_disabled(false); cursor_metric_edit->set_max(frame_metrics[last_metric].frame_number); cursor_metric_edit->set_min(MAX(frame_metrics[last_metric].frame_number - frame_metrics.size(), 0u)); @@ -408,6 +409,7 @@ void EditorVisualProfiler::_activate_pressed() { activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); activate->set_text(TTR("Stop")); _clear_pressed(); //always clear on start + clear_button->set_disabled(false); } else { activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); activate->set_text(TTR("Start")); @@ -416,6 +418,7 @@ void EditorVisualProfiler::_activate_pressed() { } void EditorVisualProfiler::_clear_pressed() { + clear_button->set_disabled(true); clear(); _update_plot(); } @@ -647,10 +650,25 @@ void EditorVisualProfiler::_bind_methods() { ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable"))); } +void EditorVisualProfiler::_update_button_text() { + if (activate->is_pressed()) { + activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); + activate->set_text(TTR("Stop")); + } else { + activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); + activate->set_text(TTR("Start")); + } +} + void EditorVisualProfiler::set_enabled(bool p_enable) { activate->set_disabled(!p_enable); } +void EditorVisualProfiler::set_pressed(bool p_pressed) { + activate->set_pressed(p_pressed); + _update_button_text(); +} + bool EditorVisualProfiler::is_profiling() { return activate->is_pressed(); } @@ -714,12 +732,14 @@ EditorVisualProfiler::EditorVisualProfiler() { add_child(hb); activate = memnew(Button); activate->set_toggle_mode(true); + activate->set_disabled(true); activate->set_text(TTR("Start")); activate->connect("pressed", callable_mp(this, &EditorVisualProfiler::_activate_pressed)); hb->add_child(activate); clear_button = memnew(Button); clear_button->set_text(TTR("Clear")); + clear_button->set_disabled(true); clear_button->connect("pressed", callable_mp(this, &EditorVisualProfiler::_clear_pressed)); hb->add_child(clear_button); diff --git a/editor/debugger/editor_visual_profiler.h b/editor/debugger/editor_visual_profiler.h index 8aa9e7b308..8180d354e8 100644 --- a/editor/debugger/editor_visual_profiler.h +++ b/editor/debugger/editor_visual_profiler.h @@ -101,6 +101,8 @@ private: Timer *frame_delay = nullptr; Timer *plot_delay = nullptr; + void _update_button_text(); + void _update_frame(bool p_focus_selected = false); void _activate_pressed(); @@ -133,6 +135,7 @@ protected: public: void add_frame_metric(const Metric &p_metric); void set_enabled(bool p_enable); + void set_pressed(bool p_pressed); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index deca638f3b..f6b00b83b0 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -319,6 +319,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da tabs->set_current_tab(0); } profiler->set_enabled(false, false); + visual_profiler->set_enabled(false); inspector->clear_cache(); // Take a chance to force remote objects update. } else if (p_msg == "debug_exit") { @@ -328,8 +329,12 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da _update_buttons_state(); _set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS); emit_signal(SNAME("breaked"), false, false, "", false); + profiler->set_enabled(true, false); profiler->disable_seeking(); + + visual_profiler->set_enabled(true); + } else if (p_msg == "set_pid") { ERR_FAIL_COND(p_data.size() < 1); remote_pid = p_data[0]; @@ -751,7 +756,16 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType reason->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); } reason->set_text(p_reason); - reason->set_tooltip_text(p_reason.word_wrap(80)); + + const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80); + PackedStringArray lines; + for (int i = 0; i < boundaries.size(); i += 2) { + const int start = boundaries[i]; + const int end = boundaries[i + 1]; + lines.append(p_reason.substr(start, end - start + 1)); + } + + reason->set_tooltip_text(String("\n").join(lines)); } void ScriptEditorDebugger::_notification(int p_what) { @@ -892,6 +906,7 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { stop(); profiler->set_enabled(true, true); + visual_profiler->set_enabled(true); peer = p_peer; ERR_FAIL_COND(p_peer.is_null()); @@ -948,7 +963,11 @@ void ScriptEditorDebugger::stop() { res_path_cache.clear(); profiler_signature.clear(); - profiler->set_enabled(true, false); + profiler->set_enabled(false, false); + profiler->set_pressed(false); + + visual_profiler->set_enabled(false); + visual_profiler->set_pressed(false); inspector->edit(nullptr); _update_buttons_state(); diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index f15b874c45..e593d652ad 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -947,9 +947,10 @@ Variant EditorData::script_class_instance(const String &p_class) { if (ScriptServer::is_global_class(p_class)) { Ref<Script> script = script_class_load_script(p_class); if (script.is_valid()) { - Object *obj = ClassDB::instantiate(script->get_instance_base_type()); + // Store in a variant to initialize the refcount if needed. + Variant obj = ClassDB::instantiate(script->get_instance_base_type()); if (obj) { - obj->set_script(script); + obj.operator Object *()->set_script(script); } return obj; } diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 55c512f77d..a9c46d4d94 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -1500,7 +1500,7 @@ void EditorFileDialog::_go_back() { } void EditorFileDialog::_go_forward() { - if (local_history_pos == local_history.size() - 1) { + if (local_history_pos >= local_history.size() - 1) { return; } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index a5397a8e6a..9a3eb75416 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1172,6 +1172,36 @@ void EditorInspectorSection::_test_unfold() { } } +Ref<Texture2D> EditorInspectorSection::_get_arrow() { + Ref<Texture2D> arrow; + if (foldable) { + if (object->editor_is_section_unfolded(section)) { + arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); + } else { + if (is_layout_rtl()) { + arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree")); + } else { + arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree")); + } + } + } + return arrow; +} + +int EditorInspectorSection::_get_header_height() { + Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); + + int header_height = font->get_height(font_size); + Ref<Texture2D> arrow = _get_arrow(); + if (arrow.is_valid()) { + header_height = MAX(header_height, arrow->get_height()); + } + header_height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); + + return header_height; +} + void EditorInspectorSection::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { @@ -1182,30 +1212,6 @@ void EditorInspectorSection::_notification(int p_what) { if (!vbox_added) { return; } - // Get the section header font. - Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); - int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); - - // Get the right direction arrow texture, if the section is foldable. - Ref<Texture2D> arrow; - if (foldable) { - if (object->editor_is_section_unfolded(section)) { - arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); - } else { - if (is_layout_rtl()) { - arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree")); - } else { - arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree")); - } - } - } - - // Compute the height of the section header. - int header_height = font->get_height(font_size); - if (arrow.is_valid()) { - header_height = MAX(header_height, arrow->get_height()); - } - header_height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); int inspector_margin = get_theme_constant(SNAME("inspector_margin"), SNAME("Editor")); int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection")); @@ -1218,6 +1224,7 @@ void EditorInspectorSection::_notification(int p_what) { } Size2 size = get_size() - Vector2(inspector_margin, 0); + int header_height = _get_header_height(); Vector2 offset = Vector2(is_layout_rtl() ? 0 : inspector_margin, header_height); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -1233,36 +1240,6 @@ void EditorInspectorSection::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - // Get the section header font. - Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); - int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); - Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); - - // Get the right direction arrow texture, if the section is foldable. - Ref<Texture2D> arrow; - bool folded = foldable; - if (foldable) { - if (object->editor_is_section_unfolded(section)) { - arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); - folded = false; - } else { - if (is_layout_rtl()) { - arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree")); - } else { - arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree")); - } - } - } - - bool rtl = is_layout_rtl(); - - // Compute the height and width of the section header. - int header_height = font->get_height(font_size); - if (arrow.is_valid()) { - header_height = MAX(header_height, arrow->get_height()); - } - header_height += get_theme_constant(SNAME("v_separation"), SNAME("Tree")); - int section_indent = 0; int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection")); if (indent_depth > 0 && section_indent_size > 0) { @@ -1275,11 +1252,13 @@ void EditorInspectorSection::_notification(int p_what) { int header_width = get_size().width - section_indent; int header_offset_x = 0.0; + bool rtl = is_layout_rtl(); if (!rtl) { header_offset_x += section_indent; } // Draw header area. + int header_height = _get_header_height(); Rect2 header_rect = Rect2(Vector2(header_offset_x, 0.0), Vector2(header_width, header_height)); Color c = bg_color; c.a *= 0.4; @@ -1288,7 +1267,7 @@ void EditorInspectorSection::_notification(int p_what) { } draw_rect(header_rect, c); - // Draw header title, folding arrow and coutn of revertable properties. + // Draw header title, folding arrow and count of revertable properties. { int separation = Math::round(2 * EDSCALE); @@ -1296,6 +1275,7 @@ void EditorInspectorSection::_notification(int p_what) { int margin_end = separation; // - Arrow. + Ref<Texture2D> arrow = _get_arrow(); if (arrow.is_valid()) { Point2 arrow_position; if (rtl) { @@ -1313,6 +1293,13 @@ void EditorInspectorSection::_notification(int p_what) { // - Count of revertable properties. String num_revertable_str; int num_revertable_width = 0; + + bool folded = foldable && !object->editor_is_section_unfolded(section); + + Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); + Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); + if (folded && revertable_properties.size()) { int label_width = font->get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, available, font_size, TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS).x; @@ -1481,10 +1468,12 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { - Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree")); - int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree")); - if (mb->get_position().y > font->get_height(font_size)) { //clicked outside - return; + if (object->editor_is_section_unfolded(section)) { + int header_height = _get_header_height(); + + if (mb->get_position().y >= header_height) { + return; + } } accept_event(); @@ -2597,7 +2586,7 @@ bool EditorInspector::_is_property_disabled_by_feature_profile(const StringName return false; } -void EditorInspector::update_tree() { +void EditorInspector::_update_tree() { //to update properly if all is refreshed StringName current_selected = property_selected; int current_focusable = -1; @@ -3316,6 +3305,10 @@ void EditorInspector::update_tree() { } } +void EditorInspector::update_tree() { + update_tree_pending = true; +} + void EditorInspector::update_property(const String &p_prop) { if (!editor_property_map.has(p_prop)) { return; @@ -3372,7 +3365,10 @@ void EditorInspector::set_keying(bool p_active) { return; } keying = p_active; - update_tree(); + // Propagate the keying state to its editor properties. + Array args; + args.append(keying); + main_vbox->propagate_call(SNAME("set_keying"), args, true); } void EditorInspector::set_read_only(bool p_read_only) { @@ -3910,10 +3906,9 @@ void EditorInspector::_notification(int p_what) { changing++; if (update_tree_pending) { - update_tree(); update_tree_pending = false; pending.clear(); - + _update_tree(); } else { while (pending.size()) { StringName prop = *pending.begin(); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 41651494ce..53490f0880 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -280,6 +280,8 @@ class EditorInspectorSection : public Container { HashSet<StringName> revertable_properties; void _test_unfold(); + int _get_header_height(); + Ref<Texture2D> _get_arrow(); protected: Object *object = nullptr; @@ -540,6 +542,8 @@ class EditorInspector : public ScrollContainer { void _show_add_meta_dialog(); void _check_meta_name(const String &p_name); + void _update_tree(); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/editor_layouts_dialog.cpp b/editor/editor_layouts_dialog.cpp index 886e29a504..19b137c7fd 100644 --- a/editor/editor_layouts_dialog.cpp +++ b/editor/editor_layouts_dialog.cpp @@ -33,6 +33,7 @@ #include "core/io/config_file.h" #include "core/object/class_db.h" #include "core/os/keyboard.h" +#include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "scene/gui/item_list.h" #include "scene/gui/line_edit.h" @@ -97,6 +98,11 @@ void EditorLayoutsDialog::_post_popup() { for (const String &E : layouts) { layout_names->add_item(E); } + if (name->is_visible()) { + name->grab_focus(); + } else { + layout_names->grab_focus(); + } } EditorLayoutsDialog::EditorLayoutsDialog() { @@ -106,7 +112,9 @@ EditorLayoutsDialog::EditorLayoutsDialog() { makevb->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, -5); layout_names = memnew(ItemList); - makevb->add_child(layout_names); + layout_names->set_auto_height(true); + makevb->add_margin_child(TTR("Select existing layout:"), layout_names); + layout_names->set_custom_minimum_size(Size2(300 * EDSCALE, 1)); layout_names->set_visible(true); layout_names->set_offset(SIDE_TOP, 5); layout_names->set_anchor_and_offset(SIDE_LEFT, Control::ANCHOR_BEGIN, 5); @@ -116,8 +124,10 @@ EditorLayoutsDialog::EditorLayoutsDialog() { layout_names->set_allow_rmb_select(true); name = memnew(LineEdit); + name->set_placeholder("Or enter new layout name"); makevb->add_child(name); name->set_offset(SIDE_TOP, 5); + name->set_custom_minimum_size(Size2(300 * EDSCALE, 1)); name->set_anchor_and_offset(SIDE_LEFT, Control::ANCHOR_BEGIN, 5); name->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, -5); name->connect("gui_input", callable_mp(this, &EditorLayoutsDialog::_line_gui_input)); diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 84284a7f31..a232c83783 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -286,7 +286,7 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { // Remove last line if replacing, as it will be replace by the next added line. // Why "- 2"? RichTextLabel is weird. When you add a line with add_newline(), it also adds an element to the list of lines which is null/blank, // but it still counts as a line. So if you remove the last line (count - 1) you are actually removing nothing... - log->remove_line(log->get_paragraph_count() - 2); + log->remove_paragraph(log->get_paragraph_count() - 2); } switch (p_message.type) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 02cfd3e873..01e605880c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -96,6 +96,7 @@ #include "editor/export/editor_export.h" #include "editor/export/export_template_manager.h" #include "editor/export/project_export.h" +#include "editor/fbx_importer_manager.h" #include "editor/filesystem_dock.h" #include "editor/history_dock.h" #include "editor/import/audio_stream_import_settings.h" @@ -2981,7 +2982,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case SETTINGS_MANAGE_EXPORT_TEMPLATES: { export_template_manager->popup_manager(); - + } break; + case SETTINGS_MANAGE_FBX_IMPORTER: { +#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) + fbx_importer_manager->show_dialog(); +#endif } break; case SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE: { custom_build_manage_templates->hide(); @@ -5189,15 +5194,15 @@ void EditorNode::_layout_menu_option(int p_id) { current_menu_option = p_id; layout_dialog->set_title(TTR("Save Layout")); layout_dialog->set_ok_button_text(TTR("Save")); - layout_dialog->popup_centered(); layout_dialog->set_name_line_enabled(true); + layout_dialog->popup_centered(); } break; case SETTINGS_LAYOUT_DELETE: { current_menu_option = p_id; layout_dialog->set_title(TTR("Delete Layout")); layout_dialog->set_ok_button_text(TTR("Delete")); - layout_dialog->popup_centered(); layout_dialog->set_name_line_enabled(false); + layout_dialog->popup_centered(); } break; case SETTINGS_LAYOUT_DEFAULT: { _load_docks_from_config(default_layout, "docks"); @@ -6624,6 +6629,11 @@ EditorNode::EditorNode() { gui_base->add_child(about); feature_profile_manager->connect("current_feature_profile_changed", callable_mp(this, &EditorNode::_feature_profile_changed)); +#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) + fbx_importer_manager = memnew(FBXImporterManager); + gui_base->add_child(fbx_importer_manager); +#endif + warning = memnew(AcceptDialog); warning->add_button(TTR("Copy Text"), true, "copy"); gui_base->add_child(warning); @@ -6796,6 +6806,9 @@ EditorNode::EditorNode() { #ifndef ANDROID_ENABLED settings_menu->add_item(TTR("Manage Export Templates..."), SETTINGS_MANAGE_EXPORT_TEMPLATES); #endif +#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) + settings_menu->add_item(TTR("Configure FBX Importer..."), SETTINGS_MANAGE_FBX_IMPORTER); +#endif help_menu = memnew(PopupMenu); help_menu->set_name(TTR("Help")); @@ -7249,6 +7262,7 @@ EditorNode::EditorNode() { gui_base->add_child(disk_changed); add_editor_plugin(memnew(AnimationPlayerEditorPlugin)); + add_editor_plugin(memnew(AnimationTrackKeyEditEditorPlugin)); add_editor_plugin(memnew(CanvasItemEditorPlugin)); add_editor_plugin(memnew(Node3DEditorPlugin)); add_editor_plugin(memnew(ScriptEditorPlugin)); diff --git a/editor/editor_node.h b/editor/editor_node.h index c430662c7f..950970379d 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -77,6 +77,7 @@ class EditorSettingsDialog; class EditorToaster; class EditorUndoRedoManager; class ExportTemplateManager; +class FBXImporterManager; class FileDialog; class FileSystemDock; class HistoryDock; @@ -209,6 +210,7 @@ private: SETTINGS_EDITOR_DATA_FOLDER, SETTINGS_EDITOR_CONFIG_FOLDER, SETTINGS_MANAGE_EXPORT_TEMPLATES, + SETTINGS_MANAGE_FBX_IMPORTER, SETTINGS_MANAGE_FEATURE_PROFILES, SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE, SETTINGS_PICK_MAIN_SCENE, @@ -285,6 +287,8 @@ private: ProjectExportDialog *project_export = nullptr; ProjectSettingsEditor *project_settings_editor = nullptr; + FBXImporterManager *fbx_importer_manager = nullptr; + Vector<EditorPlugin *> editor_plugins; bool _initializing_plugins = false; HashMap<String, EditorPlugin *> addon_name_to_plugin; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index fb3bf46c05..8c5db444ab 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1384,10 +1384,11 @@ void EditorPropertyInteger::update_property() { 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_max(p_max); spin->set_step(p_step); + spin->set_hide_slider(p_hide_slider); spin->set_allow_greater(p_allow_greater); spin->set_allow_lesser(p_allow_lesser); 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++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); spin[i]->set_step(1); - spin[i]->set_hide_slider(p_hide_slider); spin[i]->set_allow_greater(true); spin[i]->set_allow_lesser(true); spin[i]->set_suffix(p_suffix); @@ -2352,12 +2352,11 @@ void EditorPropertyRect2i::_notification(int p_what) { 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++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); spin[i]->set_step(1); - spin[i]->set_hide_slider(p_hide_slider); spin[i]->set_allow_greater(true); spin[i]->set_allow_lesser(true); spin[i]->set_suffix(p_suffix); @@ -2496,12 +2495,12 @@ void EditorPropertyVector3i::_notification(int p_what) { 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++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); 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_lesser(true); spin[i]->set_suffix(p_suffix); @@ -3004,11 +3003,11 @@ void EditorPropertyVector4i::_notification(int p_what) { 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++) { spin[i]->set_min(p_min); 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_lesser(true); spin[i]->set_suffix(p_suffix); @@ -4122,6 +4121,8 @@ void EditorPropertyResource::update_property() { if (use_editor) { // Open editor directly and hide other such editors which are currently open. + // The opened editor is the one that edits the sub-resource, so keying state will be toggled to false. + sub_inspector->set_keying(false); _open_editor_pressed(); if (is_inside_tree()) { get_tree()->call_deferred(SNAME("call_group"), "_editor_resource_properties", "_fold_other_editors", this); @@ -4347,7 +4348,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ EditorPropertyInteger *editor = memnew(EditorPropertyInteger); 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; } @@ -4475,7 +4476,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR2I: { EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide)); 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; } break; @@ -4488,7 +4489,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::RECT2I: { EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide)); 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; } break; @@ -4502,7 +4503,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR3I: { EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide)); 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; } break; @@ -4516,7 +4517,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::VECTOR4I: { EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i); 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; } break; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index f38e33d9e3..042b94130b 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -378,7 +378,7 @@ protected: public: 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(); }; @@ -566,7 +566,7 @@ protected: public: 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); }; @@ -583,7 +583,7 @@ protected: public: 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); }; @@ -608,7 +608,7 @@ protected: public: 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); }; @@ -693,7 +693,7 @@ protected: public: 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(); }; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index edda6c5d7b..451cb7cfee 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -919,7 +919,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::INT: { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); - editor->setup(-100000, 100000, 1, true, true); + editor->setup(-100000, 100000, 1, false, true, true); prop = editor; } break; @@ -942,7 +942,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::VECTOR2I: { EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i); - editor->setup(-100000, 100000, true); + editor->setup(-100000, 100000); prop = editor; } break; @@ -954,7 +954,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::RECT2I: { EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i); - editor->setup(-100000, 100000, true); + editor->setup(-100000, 100000); prop = editor; } break; @@ -966,7 +966,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::VECTOR3I: { EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i); - editor->setup(-100000, 100000, true); + editor->setup(-100000, 100000); prop = editor; } break; @@ -978,7 +978,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::VECTOR4I: { EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i); - editor->setup(-100000, 100000, true); + editor->setup(-100000, 100000); prop = editor; } break; diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index 9587e65cad..e8a0912d66 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -231,6 +231,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["tcp"] = "TCP"; capitalize_string_remaps["tls"] = "TLS"; capitalize_string_remaps["ui"] = "UI"; + capitalize_string_remaps["uri"] = "URI"; capitalize_string_remaps["url"] = "URL"; capitalize_string_remaps["urls"] = "URLs"; capitalize_string_remaps["us"] = String::utf8("(µs)"); // Unit. diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 9128143619..60c46835fe 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -76,6 +76,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) { pre_grab_value = get_value(); grabbing_spinner = false; grabbing_spinner_mouse_pos = get_global_mouse_position(); + emit_signal("grabbed"); } } else { 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()->warp_mouse(grabbing_spinner_mouse_pos); queue_redraw(); + emit_signal("ungrabbed"); } else { _focus_entered(); } @@ -178,9 +180,11 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { grabbing_ratio = get_as_ratio(); grabbing_from = grabber->get_transform().xform(mb->get_position()).x; } + emit_signal("grabbed"); } else { grabbing_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")); - if (get_step() == 1) { - number_width -= updown->get_width(); - } - String numstr = get_text_value(); 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); - 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 { + 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(); } - } - - 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 grab_focus(); } + + emit_signal("value_focus_exited"); } void EditorSpinSlider::_grabber_mouse_entered() { @@ -627,6 +632,7 @@ void EditorSpinSlider::_focus_entered() { value_input->call_deferred(SNAME("select_all")); value_input->set_focus_next(find_next_valid_focus()->get_path()); value_input->set_focus_previous(find_prev_valid_focus()->get_path()); + emit_signal("value_focus_entered"); } 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, "flat"), "set_flat", "is_flat"); 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() { diff --git a/editor/editor_vcs_interface.cpp b/editor/editor_vcs_interface.cpp index d22c7bd149..3a6516fbfa 100644 --- a/editor/editor_vcs_interface.cpp +++ b/editor/editor_vcs_interface.cpp @@ -57,7 +57,7 @@ void EditorVCSInterface::set_credentials(String p_username, String p_password, S } List<String> EditorVCSInterface::get_remotes() { - TypedArray<Dictionary> result; + TypedArray<String> result; if (!GDVIRTUAL_CALL(_get_remotes, result)) { UNIMPLEMENTED(); return {}; @@ -137,7 +137,7 @@ List<EditorVCSInterface::Commit> EditorVCSInterface::get_previous_commits(int p_ } List<String> EditorVCSInterface::get_branch_list() { - TypedArray<Dictionary> result; + TypedArray<String> result; if (!GDVIRTUAL_CALL(_get_branch_list, result)) { UNIMPLEMENTED(); return {}; diff --git a/editor/editor_vcs_interface.h b/editor/editor_vcs_interface.h index 5d4901cefa..c93fe4a672 100644 --- a/editor/editor_vcs_interface.h +++ b/editor/editor_vcs_interface.h @@ -117,8 +117,8 @@ protected: GDVIRTUAL0R(bool, _shut_down); GDVIRTUAL0R(String, _get_vcs_name); GDVIRTUAL1R(TypedArray<Dictionary>, _get_previous_commits, int); - GDVIRTUAL0R(TypedArray<Dictionary>, _get_branch_list); - GDVIRTUAL0R(TypedArray<Dictionary>, _get_remotes); + GDVIRTUAL0R(TypedArray<String>, _get_branch_list); + GDVIRTUAL0R(TypedArray<String>, _get_remotes); GDVIRTUAL1(_create_branch, String); GDVIRTUAL1(_remove_branch, String); GDVIRTUAL2(_create_remote, String, String); diff --git a/editor/fbx_importer_manager.cpp b/editor/fbx_importer_manager.cpp new file mode 100644 index 0000000000..b258fe7e09 --- /dev/null +++ b/editor/fbx_importer_manager.cpp @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* fbx_importer_manager.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "fbx_importer_manager.h" + +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "scene/gui/link_button.h" + +void FBXImporterManager::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + connect("confirmed", callable_mp(this, &FBXImporterManager::_path_confirmed)); + } break; + } +} + +void FBXImporterManager::show_dialog(bool p_exclusive) { + String fbx2gltf_path = EDITOR_GET("filesystem/import/fbx/fbx2gltf_path"); + fbx_path->set_text(fbx2gltf_path); + _validate_path(fbx2gltf_path); + + set_flag(Window::FLAG_BORDERLESS, p_exclusive); // Avoid closing accidentally . + set_close_on_escape(!p_exclusive); + + popup_centered(); +} + +void FBXImporterManager::_validate_path(const String &p_path) { + String error; + bool success = false; + + if (p_path == "") { + error = TTR("Path to FBX2glTF executable is empty."); + } else if (!FileAccess::exists(p_path)) { + error = TTR("Path to FBX2glTF executable is invalid."); + } else { + List<String> args; + args.push_back("--version"); + int exitcode; + Error err = OS::get_singleton()->execute(p_path, args, nullptr, &exitcode); + + if (err == OK && exitcode == 0) { + success = true; + } else { + error = TTR("Error executing this file (wrong version or architecture)."); + } + } + + if (success) { + path_status->set_text(TTR("FBX2glTF executable is valid.")); + path_status->add_theme_color_override("font_color", path_status->get_theme_color(SNAME("success_color"), SNAME("Editor"))); + get_ok_button()->set_disabled(false); + } else { + path_status->set_text(error); + path_status->add_theme_color_override("font_color", path_status->get_theme_color(SNAME("error_color"), SNAME("Editor"))); + get_ok_button()->set_disabled(true); + } +} + +void FBXImporterManager::_select_file(const String &p_path) { + fbx_path->set_text(p_path); + _validate_path(p_path); +} + +void FBXImporterManager::_path_confirmed() { + String path = fbx_path->get_text(); + EditorSettings::get_singleton()->set("filesystem/import/fbx/fbx2gltf_path", path); + EditorSettings::get_singleton()->save(); +} + +void FBXImporterManager::_browse_install() { + if (fbx_path->get_text() != String()) { + browse_dialog->set_current_file(fbx_path->get_text()); + } + + browse_dialog->popup_centered_ratio(); +} + +FBXImporterManager *FBXImporterManager::singleton = nullptr; + +FBXImporterManager::FBXImporterManager() { + singleton = this; + + set_title(TTR("Configure FBX Importer")); + + VBoxContainer *vb = memnew(VBoxContainer); + vb->add_child(memnew(Label(TTR("FBX2glTF is required for importing FBX files.\nPlease download it and provide a valid path to the binary:")))); + LinkButton *lb = memnew(LinkButton); + lb->set_text(TTR("Click this link to download FBX2glTF")); + lb->set_uri("https://godotengine.org/fbx-import"); + vb->add_child(lb); + + HBoxContainer *hb = memnew(HBoxContainer); + + fbx_path = memnew(LineEdit); + fbx_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->add_child(fbx_path); + fbx_path_browse = memnew(Button); + hb->add_child(fbx_path_browse); + fbx_path_browse->set_text(TTR("Browse")); + fbx_path_browse->connect("pressed", callable_mp(this, &FBXImporterManager::_browse_install)); + hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->set_custom_minimum_size(Size2(400 * EDSCALE, 0)); + + vb->add_child(hb); + + path_status = memnew(Label); + vb->add_child(path_status); + + add_child(vb); + + fbx_path->connect("text_changed", callable_mp(this, &FBXImporterManager::_validate_path)); + + get_ok_button()->set_text(TTR("Confirm Path")); + + browse_dialog = memnew(EditorFileDialog); + browse_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + browse_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); +#if defined(X11_ENABLED) + browse_dialog->add_filter("FBX2glTF-linux-x86_64"); +#elif defined(OSX_ENABLED) + browse_dialog->add_filter("FBX2glTF-macos-x86_64"); +#elif defined(WINDOWS_ENABLED) + browse_dialog->add_filter("FBX2glTF-windows-x86_64"); +#endif + + browse_dialog->connect("file_selected", callable_mp(this, &FBXImporterManager::_select_file)); + + add_child(browse_dialog); +} diff --git a/editor/fbx_importer_manager.h b/editor/fbx_importer_manager.h new file mode 100644 index 0000000000..28de761519 --- /dev/null +++ b/editor/fbx_importer_manager.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* fbx_importer_manager.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FBX_IMPORTER_MANAGER_H +#define FBX_IMPORTER_MANAGER_H + +#include "editor/editor_file_dialog.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/line_edit.h" + +class FBXImporterManager : public ConfirmationDialog { + GDCLASS(FBXImporterManager, ConfirmationDialog) + + Label *message = nullptr; + LineEdit *fbx_path = nullptr; + Button *fbx_path_browse = nullptr; + EditorFileDialog *browse_dialog = nullptr; + Label *path_status = nullptr; + + void _validate_path(const String &p_path); + void _select_file(const String &p_path); + void _path_confirmed(); + void _browse_install(); + void _link_clicked(); + + static FBXImporterManager *singleton; + +protected: + void _notification(int p_what); + +public: + static FBXImporterManager *get_singleton() { return singleton; } + + void show_dialog(bool p_exclusive = false); + + FBXImporterManager(); +}; + +#endif // FBX_IMPORTER_MANAGER_H diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index b7e7200b11..3a6c891142 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -569,8 +569,6 @@ FindInFilesPanel::FindInFilesPanel() { hbc->add_child(find_label); _search_text_label = memnew(Label); - _search_text_label->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("source"), SNAME("EditorFonts"))); - _search_text_label->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts"))); hbc->add_child(_search_text_label); _progress_bar = memnew(ProgressBar); @@ -598,8 +596,6 @@ FindInFilesPanel::FindInFilesPanel() { } _results_display = memnew(Tree); - _results_display->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("source"), SNAME("EditorFonts"))); - _results_display->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts"))); _results_display->set_v_size_flags(SIZE_EXPAND_FILL); _results_display->connect("item_selected", callable_mp(this, &FindInFilesPanel::_on_result_selected)); _results_display->connect("item_edited", callable_mp(this, &FindInFilesPanel::_on_item_edited)); @@ -688,13 +684,20 @@ void FindInFilesPanel::stop_search() { void FindInFilesPanel::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_PROCESS: { - _progress_bar->set_as_ratio(_finder->get_progress()); - } break; - case NOTIFICATION_THEME_CHANGED: { _search_text_label->add_theme_font_override("font", get_theme_font(SNAME("source"), SNAME("EditorFonts"))); + _search_text_label->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts"))); _results_display->add_theme_font_override("font", get_theme_font(SNAME("source"), SNAME("EditorFonts"))); + _results_display->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts"))); + + // Rebuild search tree. + if (!_finder->get_search_text().is_empty()) { + start_search(); + } + } break; + + case NOTIFICATION_PROCESS: { + _progress_bar->set_as_ratio(_finder->get_progress()); } break; } } diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp index f635c74547..92d4397c68 100644 --- a/editor/import/audio_stream_import_settings.cpp +++ b/editor/import/audio_stream_import_settings.cpp @@ -465,12 +465,17 @@ void AudioStreamImportSettings::_settings_changed() { updating_settings = true; stream->call("set_loop", loop->is_pressed()); stream->call("set_loop_offset", loop_offset->get_value()); + if (loop->is_pressed()) { + loop_offset->set_editable(true); + } else { + loop_offset->set_editable(false); + } + if (bpm_enabled->is_pressed()) { stream->call("set_bpm", bpm_edit->get_value()); - beats_enabled->show(); - beats_edit->show(); - bar_beats_label->show(); - bar_beats_edit->show(); + beats_enabled->set_disabled(false); + beats_edit->set_editable(true); + bar_beats_edit->set_editable(true); double bpm = bpm_edit->get_value(); if (bpm > 0) { float beat_size = 60 / float(bpm); @@ -486,10 +491,9 @@ void AudioStreamImportSettings::_settings_changed() { } else { stream->call("set_bpm", 0); stream->call("set_bar_beats", 4); - beats_enabled->hide(); - beats_edit->hide(); - bar_beats_label->hide(); - bar_beats_edit->hide(); + beats_enabled->set_disabled(true); + beats_edit->set_editable(false); + bar_beats_edit->set_editable(false); } if (bpm_enabled->is_pressed() && beats_enabled->is_pressed()) { stream->call("set_beat_count", beats_edit->get_value()); @@ -552,17 +556,8 @@ AudioStreamImportSettings::AudioStreamImportSettings() { bpm_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(bpm_edit); interactive_hb->add_spacer(); - bar_beats_label = memnew(Label(TTR("Beats/Bar:"))); - interactive_hb->add_child(bar_beats_label); - bar_beats_edit = memnew(SpinBox); - bar_beats_edit->set_tooltip_text(TTR("Configure the Beats Per Bar. This used for music-aware transitions between AudioStreams.")); - bar_beats_edit->set_min(2); - bar_beats_edit->set_max(32); - bar_beats_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); - interactive_hb->add_child(bar_beats_edit); - interactive_hb->add_spacer(); beats_enabled = memnew(CheckBox); - beats_enabled->set_text(TTR("Length (in beats):")); + beats_enabled->set_text(TTR("Beat Count:")); beats_enabled->connect("toggled", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(beats_enabled); beats_edit = memnew(SpinBox); @@ -570,6 +565,15 @@ AudioStreamImportSettings::AudioStreamImportSettings() { beats_edit->set_max(99999); beats_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(beats_edit); + bar_beats_label = memnew(Label(TTR("Bar Beats:"))); + interactive_hb->add_child(bar_beats_label); + bar_beats_edit = memnew(SpinBox); + bar_beats_edit->set_tooltip_text(TTR("Configure the Beats Per Bar. This used for music-aware transitions between AudioStreams.")); + bar_beats_edit->set_min(2); + bar_beats_edit->set_max(32); + bar_beats_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); + interactive_hb->add_child(bar_beats_edit); + interactive_hb->add_spacer(); main_vbox->add_margin_child(TTR("Music Playback:"), interactive_hb); color_rect = memnew(ColorRect); diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 730aa3bd61..380a4e370c 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -555,6 +555,7 @@ void SceneImportSettings::open_settings(const String &p_path, bool p_for_animati material_set.clear(); mesh_set.clear(); + animation_map.clear(); material_map.clear(); mesh_map.clear(); node_map.clear(); @@ -957,15 +958,15 @@ void SceneImportSettings::_notification(int p_what) { void SceneImportSettings::_menu_callback(int p_id) { switch (p_id) { case ACTION_EXTRACT_MATERIALS: { - save_path->set_text(TTR("Select folder to extract material resources")); + save_path->set_title(TTR("Select folder to extract material resources")); external_extension_type->select(0); } break; case ACTION_CHOOSE_MESH_SAVE_PATHS: { - save_path->set_text(TTR("Select folder where mesh resources will save on import")); + save_path->set_title(TTR("Select folder where mesh resources will save on import")); external_extension_type->select(1); } break; case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: { - save_path->set_text(TTR("Select folder where animations will save on import")); + save_path->set_title(TTR("Select folder where animations will save on import")); external_extension_type->select(1); } break; } diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index affb31945d..1f350f1199 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -219,6 +219,7 @@ PluginConfigDialog::PluginConfigDialog() { GridContainer *grid = memnew(GridContainer); grid->set_columns(3); + grid->set_v_size_flags(Control::SIZE_EXPAND_FILL); vbox->add_child(grid); // Plugin Name @@ -234,6 +235,7 @@ PluginConfigDialog::PluginConfigDialog() { name_edit = memnew(LineEdit); name_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); name_edit->set_placeholder("MyPlugin"); + name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(name_edit); // Subfolder @@ -248,6 +250,7 @@ PluginConfigDialog::PluginConfigDialog() { subfolder_edit = memnew(LineEdit); subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin"); + subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); subfolder_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); grid->add_child(subfolder_edit); @@ -263,6 +266,8 @@ PluginConfigDialog::PluginConfigDialog() { desc_edit = memnew(TextEdit); desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE); desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); + desc_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); + desc_edit->set_v_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(desc_edit); // Author @@ -276,6 +281,7 @@ PluginConfigDialog::PluginConfigDialog() { author_edit = memnew(LineEdit); author_edit->set_placeholder("Godette"); + author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(author_edit); // Version @@ -289,6 +295,7 @@ PluginConfigDialog::PluginConfigDialog() { version_edit = memnew(LineEdit); version_edit->set_placeholder("1.0"); + version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(version_edit); // Language dropdown @@ -326,6 +333,7 @@ PluginConfigDialog::PluginConfigDialog() { script_edit = memnew(LineEdit); script_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd"); + script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(script_edit); // Activate now checkbox diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 5183a738ae..344a800241 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/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()); track_editor->set_anim_pos(player->get_current_animation_position()); - } else if (!player->is_valid()) { // Reset timeline when the player has been stopped externally frame->set_value(0); } else if (last_active) { // Need the last frame after it stopped. frame->set_value(player->get_current_animation_position()); + track_editor->set_anim_pos(player->get_current_animation_position()); } last_active = player->is_playing(); @@ -423,7 +423,7 @@ void AnimationPlayerEditor::_select_anim_by_name(const String &p_anim) { _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. if (track_editor->is_snap_enabled()) { 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 0.0; + return 0.0f; } void AnimationPlayerEditor::_animation_name_edited() { @@ -1922,10 +1922,12 @@ void AnimationPlayerEditorPlugin::_notification(int p_what) { } void AnimationPlayerEditorPlugin::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) { - if (!anim_editor->get_track_editor()->has_keying()) { + AnimationTrackEditor *te = anim_editor->get_track_editor(); + if (!te || !te->has_keying()) { return; } - anim_editor->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance); + te->_clear_selection(); + te->insert_value_key(p_keyed, p_value, p_advance); } void AnimationPlayerEditorPlugin::_transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key) { @@ -1971,3 +1973,26 @@ 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"); +} diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 53d460fc9e..8539d450e6 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -162,7 +162,7 @@ class AnimationPlayerEditor : public VBoxContainer { } onion; void _select_anim_by_name(const String &p_anim); - double _get_editor_step() const; + float _get_editor_step() const; void _play_pressed(); void _play_from_pressed(); void _play_bw_pressed(); @@ -272,4 +272,30 @@ public: ~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 diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 66a0c746d9..7b8a5d06f8 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1097,7 +1097,8 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action Ref<AnimationNodeStateMachineTransition> tr; tr.instantiate(); - tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected())); + tr->set_advance_mode(auto_advance->is_pressed() ? AnimationNodeStateMachineTransition::AdvanceMode::ADVANCE_MODE_AUTO : AnimationNodeStateMachineTransition::AdvanceMode::ADVANCE_MODE_ENABLED); + tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected())); Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); if (!p_nested_action) { @@ -1326,7 +1327,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } - _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()), true, false, false, false, false); + _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, false, false); } Ref<Texture2D> tr_reference_icon = get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")); @@ -1349,8 +1350,8 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { tl.to = (state_machine->get_node_position(local_to) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); - tl.disabled = tr->is_disabled(); - tl.auto_advance = tr->has_auto_advance(); + tl.disabled = bool(tr->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED); + tl.auto_advance = bool(tr->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO); tl.advance_condition_name = tr->get_advance_condition_name(); tl.advance_condition_state = false; tl.mode = tr->get_switch_mode(); @@ -1590,10 +1591,12 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { tool_create->set_icon(get_theme_icon(SNAME("ToolAddNode"), SNAME("EditorIcons"))); tool_connect->set_icon(get_theme_icon(SNAME("ToolConnect"), SNAME("EditorIcons"))); - transition_mode->clear(); - transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionImmediate"), SNAME("EditorIcons")), TTR("Immediate")); - transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionSync"), SNAME("EditorIcons")), TTR("Sync")); - transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionEnd"), SNAME("EditorIcons")), TTR("At End")); + switch_mode->clear(); + switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionImmediate"), SNAME("EditorIcons")), TTR("Immediate")); + switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionSync"), SNAME("EditorIcons")), TTR("Sync")); + switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionEnd"), SNAME("EditorIcons")), TTR("At End")); + + auto_advance->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons"))); tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); tool_group->set_icon(get_theme_icon(SNAME("Group"), SNAME("EditorIcons"))); @@ -1652,12 +1655,12 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { break; } - if (transition_lines[i].disabled != state_machine->get_transition(tidx)->is_disabled()) { + if (transition_lines[i].disabled != bool(state_machine->get_transition(tidx)->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED)) { state_machine_draw->queue_redraw(); break; } - if (transition_lines[i].auto_advance != state_machine->get_transition(tidx)->has_auto_advance()) { + if (transition_lines[i].auto_advance != bool(state_machine->get_transition(tidx)->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO)) { state_machine_draw->queue_redraw(); break; } @@ -1904,7 +1907,7 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action void AnimationNodeStateMachineEditor::_update_mode() { if (tool_select->is_pressed()) { - tool_erase_hb->show(); + selection_tools_hb->show(); bool nothing_selected = selected_nodes.is_empty() && selected_transition_from == StringName() && selected_transition_to == StringName(); bool start_end_selected = selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node); tool_erase->set_disabled(nothing_selected || start_end_selected || read_only); @@ -1927,7 +1930,13 @@ void AnimationNodeStateMachineEditor::_update_mode() { } } } else { - tool_erase_hb->hide(); + selection_tools_hb->hide(); + } + + if (tool_connect->is_pressed()) { + transition_tools_hb->show(); + } else { + transition_tools_hb->hide(); } } @@ -1978,35 +1987,48 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { tool_connect->set_tooltip_text(TTR("Connect nodes.")); tool_connect->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_update_mode), CONNECT_DEFERRED); - tool_erase_hb = memnew(HBoxContainer); - top_hb->add_child(tool_erase_hb); - tool_erase_hb->add_child(memnew(VSeparator)); + // Context-sensitive selection tools: + selection_tools_hb = memnew(HBoxContainer); + top_hb->add_child(selection_tools_hb); + selection_tools_hb->add_child(memnew(VSeparator)); tool_group = memnew(Button); tool_group->set_flat(true); tool_group->set_tooltip_text(TTR("Group Selected Node(s)") + " (Ctrl+G)"); tool_group->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_group_selected_nodes)); tool_group->set_disabled(true); - tool_erase_hb->add_child(tool_group); + selection_tools_hb->add_child(tool_group); tool_ungroup = memnew(Button); tool_ungroup->set_flat(true); tool_ungroup->set_tooltip_text(TTR("Ungroup Selected Node") + " (Ctrl+Shift+G)"); tool_ungroup->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_ungroup_selected_nodes)); tool_ungroup->set_visible(false); - tool_erase_hb->add_child(tool_ungroup); + selection_tools_hb->add_child(tool_ungroup); tool_erase = memnew(Button); tool_erase->set_flat(true); tool_erase->set_tooltip_text(TTR("Remove selected node or transition.")); tool_erase->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_erase_selected).bind(false)); tool_erase->set_disabled(true); - tool_erase_hb->add_child(tool_erase); + selection_tools_hb->add_child(tool_erase); + + transition_tools_hb = memnew(HBoxContainer); + top_hb->add_child(transition_tools_hb); + transition_tools_hb->add_child(memnew(VSeparator)); + + transition_tools_hb->add_child(memnew(Label(TTR("Transition:")))); + switch_mode = memnew(OptionButton); + transition_tools_hb->add_child(switch_mode); + + auto_advance = memnew(Button); + auto_advance->set_flat(true); + auto_advance->set_tooltip_text(TTR("New Transitions Should Auto Advance")); + auto_advance->set_toggle_mode(true); + auto_advance->set_pressed(true); + transition_tools_hb->add_child(auto_advance); - top_hb->add_child(memnew(VSeparator)); - top_hb->add_child(memnew(Label(TTR("Transition:")))); - transition_mode = memnew(OptionButton); - top_hb->add_child(transition_mode); + // top_hb->add_spacer(); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 5edf803c41..28b5f0cbcc 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -52,15 +52,18 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { Button *tool_select = nullptr; Button *tool_create = nullptr; Button *tool_connect = nullptr; - Button *tool_group = nullptr; - Button *tool_ungroup = nullptr; Popup *name_edit_popup = nullptr; LineEdit *name_edit = nullptr; - HBoxContainer *tool_erase_hb = nullptr; + HBoxContainer *selection_tools_hb = nullptr; + Button *tool_group = nullptr; + Button *tool_ungroup = nullptr; Button *tool_erase = nullptr; - OptionButton *transition_mode = nullptr; + HBoxContainer *transition_tools_hb = nullptr; + OptionButton *switch_mode = nullptr; + Button *auto_advance = nullptr; + OptionButton *play_mode = nullptr; PanelContainer *panel = nullptr; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 7c3ecd5d4e..6483a18a4d 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -43,6 +43,11 @@ #include "editor/project_settings_editor.h" #include "scene/gui/menu_button.h" +#include "modules/modules_enabled.gen.h" // For svg. +#ifdef MODULE_SVG_ENABLED +#include "modules/svg/image_loader_svg.h" +#endif + static inline void setup_http_request(HTTPRequest *request) { request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); @@ -751,13 +756,30 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PackedB uint8_t png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; uint8_t jpg_signature[3] = { 255, 216, 255 }; + uint8_t webp_signature[4] = { 82, 73, 70, 70 }; + uint8_t bmp_signature[2] = { 66, 77 }; if (r) { if ((memcmp(&r[0], &png_signature[0], 8) == 0) && Image::_png_mem_loader_func) { image->copy_internals_from(Image::_png_mem_loader_func(r, len)); } else if ((memcmp(&r[0], &jpg_signature[0], 3) == 0) && Image::_jpg_mem_loader_func) { image->copy_internals_from(Image::_jpg_mem_loader_func(r, len)); + } else if ((memcmp(&r[0], &webp_signature[0], 4) == 0) && Image::_webp_mem_loader_func) { + image->copy_internals_from(Image::_webp_mem_loader_func(r, len)); + } else if ((memcmp(&r[0], &bmp_signature[0], 2) == 0) && Image::_bmp_mem_loader_func) { + image->copy_internals_from(Image::_bmp_mem_loader_func(r, len)); } +#ifdef MODULE_SVG_ENABLED + else { + ImageLoaderSVG svg_loader; + Ref<Image> img = Ref<Image>(memnew(Image)); + Error err = svg_loader.create_image_from_utf8_buffer(img, image_data, 1.0, false); + + if (err == OK) { + image->copy_internals_from(img); + } + } +#endif } if (!image->is_empty()) { diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp new file mode 100644 index 0000000000..60d949cdf0 --- /dev/null +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -0,0 +1,280 @@ +/*************************************************************************/ +/* audio_stream_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "audio_stream_editor_plugin.h" + +#include "core/core_string_names.h" +#include "editor/audio_stream_preview.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "scene/resources/audio_stream_wav.h" + +// AudioStreamEditor + +void AudioStreamEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + AudioStreamPreviewGenerator::get_singleton()->connect(SNAME("preview_updated"), callable_mp(this, &AudioStreamEditor::_preview_changed)); + } break; + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_ENTER_TREE: { + Ref<Font> font = get_theme_font(SNAME("status_source"), SNAME("EditorFonts")); + + _current_label->add_theme_font_override(SNAME("font"), font); + _duration_label->add_theme_font_override(SNAME("font"), font); + + _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + _stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); + _preview->set_color(get_theme_color(SNAME("dark_color_2"), SNAME("Editor"))); + + set_color(get_theme_color(SNAME("dark_color_1"), SNAME("Editor"))); + + _indicator->queue_redraw(); + _preview->queue_redraw(); + } break; + case NOTIFICATION_PROCESS: { + _current = _player->get_playback_position(); + _indicator->queue_redraw(); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible_in_tree()) { + _stop(); + } + } break; + default: { + } break; + } +} + +void AudioStreamEditor::_draw_preview() { + Rect2 rect = _preview->get_rect(); + Size2 size = get_size(); + + Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream); + float preview_len = preview->get_length(); + + Vector<Vector2> lines; + lines.resize(size.width * 2); + + for (int i = 0; i < size.width; i++) { + float ofs = i * preview_len / size.width; + float ofs_n = (i + 1) * preview_len / size.width; + float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5; + float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5; + + int idx = i; + lines.write[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y); + lines.write[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y); + } + + RS::get_singleton()->canvas_item_add_multiline(_preview->get_canvas_item(), lines, { get_theme_color(SNAME("contrast_color_2"), SNAME("Editor")) }); +} + +void AudioStreamEditor::_preview_changed(ObjectID p_which) { + if (stream.is_valid() && stream->get_instance_id() == p_which) { + _preview->queue_redraw(); + } +} + +void AudioStreamEditor::_stream_changed() { + if (!is_visible()) { + return; + } + queue_redraw(); +} + +void AudioStreamEditor::_play() { + if (_player->is_playing()) { + _pausing = true; + _player->stop(); + _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + set_process(false); + } else { + _pausing = false; + _player->play(_current); + _play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons"))); + set_process(true); + } +} + +void AudioStreamEditor::_stop() { + _player->stop(); + _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + _current = 0; + _indicator->queue_redraw(); + set_process(false); +} + +void AudioStreamEditor::_on_finished() { + _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + if (!_pausing) { + _current = 0; + _indicator->queue_redraw(); + } else { + _pausing = false; + } + set_process(false); +} + +void AudioStreamEditor::_draw_indicator() { + if (stream.is_null()) { + return; + } + + Rect2 rect = _preview->get_rect(); + float len = stream->get_length(); + float ofs_x = _current / len * rect.size.width; + const Color col = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + Ref<Texture2D> icon = get_theme_icon(SNAME("TimelineIndicator"), SNAME("EditorIcons")); + _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), col, Math::round(2 * EDSCALE)); + _indicator->draw_texture( + icon, + Point2(ofs_x - icon->get_width() * 0.5, 0), + col); + + _current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /"); +} + +void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) { + const Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { + if (mb->is_pressed()) { + _seek_to(mb->get_position().x); + } + _dragging = mb->is_pressed(); + } + + const Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + if (_dragging) { + _seek_to(mm->get_position().x); + } + } +} + +void AudioStreamEditor::_seek_to(real_t p_x) { + _current = p_x / _preview->get_rect().size.x * stream->get_length(); + _current = CLAMP(_current, 0, stream->get_length()); + _player->seek(_current); + _indicator->queue_redraw(); +} + +void AudioStreamEditor::set_stream(const Ref<AudioStream> &p_stream) { + if (stream.is_valid()) { + stream->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed)); + } + + stream = p_stream; + if (stream.is_null()) { + hide(); + return; + } + stream->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed)); + + _player->set_stream(stream); + _current = 0; + + String text = String::num(stream->get_length(), 2).pad_decimals(2) + "s"; + _duration_label->set_text(text); + + queue_redraw(); +} + +AudioStreamEditor::AudioStreamEditor() { + set_custom_minimum_size(Size2(1, 100) * EDSCALE); + + _player = memnew(AudioStreamPlayer); + _player->connect(SNAME("finished"), callable_mp(this, &AudioStreamEditor::_on_finished)); + add_child(_player); + + VBoxContainer *vbox = memnew(VBoxContainer); + vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + add_child(vbox); + + _preview = memnew(ColorRect); + _preview->set_v_size_flags(SIZE_EXPAND_FILL); + _preview->connect(SNAME("draw"), callable_mp(this, &AudioStreamEditor::_draw_preview)); + vbox->add_child(_preview); + + _indicator = memnew(Control); + _indicator->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + _indicator->connect(SNAME("draw"), callable_mp(this, &AudioStreamEditor::_draw_indicator)); + _indicator->connect(SNAME("gui_input"), callable_mp(this, &AudioStreamEditor::_on_input_indicator)); + _preview->add_child(_indicator); + + HBoxContainer *hbox = memnew(HBoxContainer); + hbox->add_theme_constant_override("separation", 0); + vbox->add_child(hbox); + + _play_button = memnew(Button); + hbox->add_child(_play_button); + _play_button->set_flat(true); + _play_button->set_focus_mode(Control::FOCUS_NONE); + _play_button->connect(SNAME("pressed"), callable_mp(this, &AudioStreamEditor::_play)); + _play_button->set_shortcut(ED_SHORTCUT("audio_stream_editor/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), Key::SPACE)); + + _stop_button = memnew(Button); + hbox->add_child(_stop_button); + _stop_button->set_flat(true); + _stop_button->set_focus_mode(Control::FOCUS_NONE); + _stop_button->connect(SNAME("pressed"), callable_mp(this, &AudioStreamEditor::_stop)); + + _current_label = memnew(Label); + _current_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + _current_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + _current_label->set_modulate(Color(1, 1, 1, 0.5)); + hbox->add_child(_current_label); + + _duration_label = memnew(Label); + hbox->add_child(_duration_label); +} + +// EditorInspectorPluginAudioStream + +bool EditorInspectorPluginAudioStream::can_handle(Object *p_object) { + return Object::cast_to<AudioStreamWAV>(p_object) != nullptr; +} + +void EditorInspectorPluginAudioStream::parse_begin(Object *p_object) { + AudioStream *stream = Object::cast_to<AudioStream>(p_object); + + editor = memnew(AudioStreamEditor); + editor->set_stream(Ref<AudioStream>(stream)); + + add_custom_control(editor); +} + +// AudioStreamEditorPlugin + +AudioStreamEditorPlugin::AudioStreamEditorPlugin() { + Ref<EditorInspectorPluginAudioStream> plugin; + plugin.instantiate(); + add_inspector_plugin(plugin); +} diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h new file mode 100644 index 0000000000..13e52929ee --- /dev/null +++ b/editor/plugins/audio_stream_editor_plugin.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* audio_stream_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef AUDIO_STREAM_EDITOR_PLUGIN_H +#define AUDIO_STREAM_EDITOR_PLUGIN_H + +#include "editor/editor_inspector.h" +#include "editor/editor_plugin.h" +#include "scene/audio/audio_stream_player.h" +#include "scene/gui/button.h" +#include "scene/gui/color_rect.h" +#include "scene/gui/label.h" + +class AudioStreamEditor : public ColorRect { + GDCLASS(AudioStreamEditor, ColorRect); + + Ref<AudioStream> stream; + + AudioStreamPlayer *_player = nullptr; + ColorRect *_preview = nullptr; + Control *_indicator = nullptr; + Label *_current_label = nullptr; + Label *_duration_label = nullptr; + + Button *_play_button = nullptr; + Button *_stop_button = nullptr; + + float _current = 0; + bool _dragging = false; + bool _pausing = false; + +protected: + void _notification(int p_what); + void _preview_changed(ObjectID p_which); + void _play(); + void _stop(); + void _on_finished(); + void _draw_preview(); + void _draw_indicator(); + void _on_input_indicator(Ref<InputEvent> p_event); + void _seek_to(real_t p_x); + void _stream_changed(); + +public: + void set_stream(const Ref<AudioStream> &p_stream); + + AudioStreamEditor(); +}; + +class EditorInspectorPluginAudioStream : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginAudioStream, EditorInspectorPlugin); + AudioStreamEditor *editor = nullptr; + +public: + virtual bool can_handle(Object *p_object) override; + virtual void parse_begin(Object *p_object) override; +}; + +class AudioStreamEditorPlugin : public EditorPlugin { + GDCLASS(AudioStreamEditorPlugin, EditorPlugin); + +public: + AudioStreamEditorPlugin(); +}; + +#endif // AUDIO_STREAM_EDITOR_PLUGIN_H diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp index e21a50a434..61b7683a05 100644 --- a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp +++ b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp @@ -81,7 +81,7 @@ void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_und if (p_from_index < 0) { undo_redo_man->add_undo_method(randomizer, "remove_stream", p_to_pos < 0 ? randomizer->get_streams_count() : p_to_pos); } else if (p_to_pos < 0) { - undo_redo_man->add_undo_method(randomizer, "add_stream", p_from_index); + undo_redo_man->add_undo_method(randomizer, "add_stream", p_from_index, Ref<AudioStream>()); } List<PropertyInfo> properties; @@ -107,7 +107,7 @@ void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_und #undef ADD_UNDO if (p_from_index < 0) { - undo_redo_man->add_do_method(randomizer, "add_stream", p_to_pos); + undo_redo_man->add_do_method(randomizer, "add_stream", p_to_pos, Ref<AudioStream>()); } else if (p_to_pos < 0) { undo_redo_man->add_do_method(randomizer, "remove_stream", p_from_index); } else { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index aab64587ce..30322a94f1 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -135,7 +135,7 @@ public: label->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid_step_x = memnew(SpinBox); - grid_step_x->set_min(0.01); + grid_step_x->set_min(1); grid_step_x->set_max(SPIN_BOX_GRID_RANGE); grid_step_x->set_allow_greater(true); grid_step_x->set_suffix("px"); @@ -144,7 +144,7 @@ public: child_container->add_child(grid_step_x); grid_step_y = memnew(SpinBox); - grid_step_y->set_min(0.01); + grid_step_y->set_min(1); grid_step_y->set_max(SPIN_BOX_GRID_RANGE); grid_step_y->set_allow_greater(true); grid_step_y->set_suffix("px"); @@ -4977,7 +4977,7 @@ CanvasItemEditor::CanvasItemEditor() { SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created)); SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position)); - EditorNode::get_singleton()->call_deferred(SNAME("connect"), callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true)); + EditorNode::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true)); EditorNode::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false)); // A fluid container for all toolbars. diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 5f9446c3b1..cbe30f7e0f 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -436,6 +436,7 @@ EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() { } EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(sphere); RS::get_singleton()->free(sphere_instance); RS::get_singleton()->free(viewport); @@ -767,6 +768,7 @@ EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() { } EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); //RS::get_singleton()->free(sphere); RS::get_singleton()->free(mesh_instance); RS::get_singleton()->free(viewport); @@ -867,6 +869,7 @@ EditorFontPreviewPlugin::EditorFontPreviewPlugin() { } EditorFontPreviewPlugin::~EditorFontPreviewPlugin() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(canvas_item); RS::get_singleton()->free(canvas); RS::get_singleton()->free(viewport); diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index efb2c80cfd..0f8ed1c857 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -70,10 +70,12 @@ public: }; class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorPackedScenePreviewPlugin, EditorResourcePreviewGenerator); + public: - virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; - virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const; + virtual bool handles(const String &p_type) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; + virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const override; EditorPackedScenePreviewPlugin(); }; @@ -107,17 +109,21 @@ public: }; class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorScriptPreviewPlugin, EditorResourcePreviewGenerator); + public: - virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; + virtual bool handles(const String &p_type) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorScriptPreviewPlugin(); }; class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorAudioStreamPreviewPlugin, EditorResourcePreviewGenerator); + public: - virtual bool handles(const String &p_type) const; - virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const; + virtual bool handles(const String &p_type) const override; + virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override; EditorAudioStreamPreviewPlugin(); }; diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp index 4370d013be..6b3db095d4 100644 --- a/editor/plugins/font_config_plugin.cpp +++ b/editor/plugins/font_config_plugin.cpp @@ -483,7 +483,7 @@ void EditorPropertyOTVariation::update_property() { Vector3i range = supported.get_value_at_index(i); 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)); String name = TS->tag_to_name(name_tag); @@ -762,7 +762,7 @@ void EditorPropertyOTFeatures::update_property() { } break; case Variant::INT: { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); - editor->setup(0, 255, 1, false, false); + editor->setup(0, 255, 1, false, false, false); prop = editor; } break; default: { diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 4a8e5d9979..4b31e99f0e 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -160,23 +160,23 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, p_library->set_item_shapes(id, collisions); - Ref<NavigationMesh> navmesh; - Transform3D navmesh_transform; + Ref<NavigationMesh> navigation_mesh; + Transform3D navigation_mesh_transform; for (int j = 0; j < mi->get_child_count(); j++) { Node *child2 = mi->get_child(j); if (!Object::cast_to<NavigationRegion3D>(child2)) { continue; } NavigationRegion3D *sb = Object::cast_to<NavigationRegion3D>(child2); - navmesh = sb->get_navigation_mesh(); - navmesh_transform = sb->get_transform(); - if (!navmesh.is_null()) { + navigation_mesh = sb->get_navigation_mesh(); + navigation_mesh_transform = sb->get_transform(); + if (!navigation_mesh.is_null()) { break; } } - if (!navmesh.is_null()) { - p_library->set_item_navmesh(id, navmesh); - p_library->set_item_navmesh_transform(id, navmesh_transform); + if (!navigation_mesh.is_null()) { + p_library->set_item_navigation_mesh(id, navigation_mesh); + p_library->set_item_navigation_mesh_transform(id, navigation_mesh_transform); } } diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index c8b80db334..0df53bddd1 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -100,6 +100,7 @@ bool EditorNode3DGizmo::is_editable() const { } void EditorNode3DGizmo::clear() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); for (int i = 0; i < instances.size(); i++) { if (instances[i].instance.is_valid()) { RS::get_singleton()->free(instances[i].instance); @@ -809,6 +810,7 @@ void EditorNode3DGizmo::transform() { } void EditorNode3DGizmo::free() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); ERR_FAIL_COND(!spatial_node); ERR_FAIL_COND(!valid); @@ -1016,8 +1018,9 @@ Ref<StandardMaterial3D> EditorNode3DGizmoPlugin::get_material(const String &p_na } String EditorNode3DGizmoPlugin::get_gizmo_name() const { - if (get_script_instance() && get_script_instance()->has_method("_get_gizmo_name")) { - return get_script_instance()->call("_get_gizmo_name"); + String ret; + if (GDVIRTUAL_CALL(_get_gizmo_name, ret)) { + return ret; } WARN_PRINT_ONCE("A 3D editor gizmo has no name defined (it will appear as \"Unnamed Gizmo\" in the \"View > Gizmos\" menu). To resolve this, override the `_get_gizmo_name()` function to return a String in the script that extends EditorNode3DGizmoPlugin."); @@ -1025,8 +1028,9 @@ String EditorNode3DGizmoPlugin::get_gizmo_name() const { } int EditorNode3DGizmoPlugin::get_priority() const { - if (get_script_instance() && get_script_instance()->has_method("_get_priority")) { - return get_script_instance()->call("_get_priority"); + int ret; + if (GDVIRTUAL_CALL(_get_priority, ret)) { + return ret; } return 0; } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 363ad273a8..8e2b9b4bce 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2606,7 +2606,7 @@ void Node3DEditorViewport::_project_settings_changed() { const bool use_taa = GLOBAL_GET("rendering/anti_aliasing/quality/use_taa"); viewport->set_use_taa(use_taa); - const bool transparent_background = GLOBAL_GET("rendering/transparent_background"); + const bool transparent_background = GLOBAL_GET("rendering/viewport/transparent_background"); viewport->set_transparent_background(transparent_background); const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding"); @@ -3557,6 +3557,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) { } void Node3DEditorViewport::_finish_gizmo_instances() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); for (int i = 0; i < 3; i++) { RS::get_singleton()->free(move_gizmo_instance[i]); RS::get_singleton()->free(move_plane_gizmo_instance[i]); @@ -5536,6 +5537,7 @@ Node3DEditorViewportContainer::Node3DEditorViewportContainer() { Node3DEditor *Node3DEditor::singleton = nullptr; Node3DEditorSelectedItem::~Node3DEditorSelectedItem() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (sbox_instance.is_valid()) { RenderingServer::get_singleton()->free(sbox_instance); } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index bb5491fcb5..562eddbc64 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3003,6 +3003,7 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) { _go_to_tab(script_list->get_item_metadata(next_tab)); _update_script_names(); } + accept_event(); } if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) { if (script_list->get_item_count() > 1) { @@ -3011,12 +3012,15 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) { _go_to_tab(script_list->get_item_metadata(next_tab)); _update_script_names(); } + accept_event(); } if (ED_IS_SHORTCUT("script_editor/window_move_up", p_event)) { _menu_option(WINDOW_MOVE_UP); + accept_event(); } if (ED_IS_SHORTCUT("script_editor/window_move_down", p_event)) { _menu_option(WINDOW_MOVE_DOWN); + accept_event(); } } diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index f79a001efb..d2dadd884b 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -153,9 +153,10 @@ void BoneTransformEditor::set_target(const String &p_prop) { void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) { AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor(); - if (!te->has_keying()) { + if (!te || !te->has_keying()) { return; } + te->_clear_selection(); PackedStringArray split = p_path.split("/"); if (split.size() == 3 && split[0] == "bones") { int bone_idx = split[1].to_int(); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 57f9b3135a..1fdf940a82 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -309,7 +309,22 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { case ROTATE_LEFT: case FLIP_HORIZONTALLY: case FLIP_VERTICALLY: { - undo_redo->create_action(TTR("Rotate Polygons Left")); + switch (p_item_pressed) { + case ROTATE_RIGHT: { + undo_redo->create_action(TTR("Rotate Polygons Right")); + } break; + case ROTATE_LEFT: { + undo_redo->create_action(TTR("Rotate Polygons Left")); + } break; + case FLIP_HORIZONTALLY: { + undo_redo->create_action(TTR("Flip Polygons Horizontally")); + } break; + case FLIP_VERTICALLY: { + undo_redo->create_action(TTR("Flip Polygons Vertically")); + } break; + default: + break; + } for (unsigned int i = 0; i < polygons.size(); i++) { Vector<Point2> new_polygon; for (int point_index = 0; point_index < polygons[i].size(); point_index++) { diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 4131c06745..414c3a2759 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -924,6 +924,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over Color modulate = tile_data->get_modulate(); Color self_modulate = tile_map->get_self_modulate(); modulate *= self_modulate; + modulate *= tile_map->get_layer_modulate(tile_map_layer); // Draw the tile. p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping()); @@ -1487,13 +1488,15 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { } // Selection if needed. - for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { + for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E;) { + RBSet<TileMapCell>::Element *N = E->next(); const TileMapCell *selected = &(E->get()); if (!tile_set->has_source(selected->source_id) || !tile_set->get_source(selected->source_id)->has_tile(selected->get_atlas_coords()) || !tile_set->get_source(selected->source_id)->has_alternative_tile(selected->get_atlas_coords(), selected->alternative_tile)) { tile_set_selection.erase(E); } + E = N; } if (!tile_map_selection.is_empty()) { diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp index b31fb1aa58..2868c14452 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp +++ b/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->set_selectable(false); 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); 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->set_selectable(false); 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(); 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->set_selectable(false); 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); 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->set_selectable(false); 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(); vboxcontainer_to->add_child(alternative_to_property_editor); diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 369a59c443..e9c6184d73 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -47,43 +47,7 @@ VersionControlEditorPlugin *VersionControlEditorPlugin::singleton = nullptr; void VersionControlEditorPlugin::_bind_methods() { - ClassDB::bind_method(D_METHOD("_initialize_vcs"), &VersionControlEditorPlugin::_initialize_vcs); - ClassDB::bind_method(D_METHOD("_set_credentials"), &VersionControlEditorPlugin::_set_credentials); - ClassDB::bind_method(D_METHOD("_update_set_up_warning"), &VersionControlEditorPlugin::_update_set_up_warning); - ClassDB::bind_method(D_METHOD("_commit"), &VersionControlEditorPlugin::_commit); - ClassDB::bind_method(D_METHOD("_refresh_stage_area"), &VersionControlEditorPlugin::_refresh_stage_area); - ClassDB::bind_method(D_METHOD("_move_all"), &VersionControlEditorPlugin::_move_all); - ClassDB::bind_method(D_METHOD("_load_diff"), &VersionControlEditorPlugin::_load_diff); - ClassDB::bind_method(D_METHOD("_display_diff"), &VersionControlEditorPlugin::_display_diff); - ClassDB::bind_method(D_METHOD("_item_activated"), &VersionControlEditorPlugin::_item_activated); - ClassDB::bind_method(D_METHOD("_update_branch_create_button"), &VersionControlEditorPlugin::_update_branch_create_button); - ClassDB::bind_method(D_METHOD("_update_remote_create_button"), &VersionControlEditorPlugin::_update_remote_create_button); - ClassDB::bind_method(D_METHOD("_update_commit_button"), &VersionControlEditorPlugin::_update_commit_button); - ClassDB::bind_method(D_METHOD("_refresh_branch_list"), &VersionControlEditorPlugin::_refresh_branch_list); - ClassDB::bind_method(D_METHOD("_set_commit_list_size"), &VersionControlEditorPlugin::_set_commit_list_size); - ClassDB::bind_method(D_METHOD("_refresh_commit_list"), &VersionControlEditorPlugin::_refresh_commit_list); - ClassDB::bind_method(D_METHOD("_refresh_remote_list"), &VersionControlEditorPlugin::_refresh_remote_list); - ClassDB::bind_method(D_METHOD("_ssh_public_key_selected"), &VersionControlEditorPlugin::_ssh_public_key_selected); - ClassDB::bind_method(D_METHOD("_ssh_private_key_selected"), &VersionControlEditorPlugin::_ssh_private_key_selected); - ClassDB::bind_method(D_METHOD("_commit_message_gui_input"), &VersionControlEditorPlugin::_commit_message_gui_input); - ClassDB::bind_method(D_METHOD("_cell_button_pressed"), &VersionControlEditorPlugin::_cell_button_pressed); - ClassDB::bind_method(D_METHOD("_discard_all"), &VersionControlEditorPlugin::_discard_all); - ClassDB::bind_method(D_METHOD("_create_branch"), &VersionControlEditorPlugin::_create_branch); - ClassDB::bind_method(D_METHOD("_create_remote"), &VersionControlEditorPlugin::_create_remote); - ClassDB::bind_method(D_METHOD("_remove_branch"), &VersionControlEditorPlugin::_remove_branch); - ClassDB::bind_method(D_METHOD("_remove_remote"), &VersionControlEditorPlugin::_remove_remote); - ClassDB::bind_method(D_METHOD("_branch_item_selected"), &VersionControlEditorPlugin::_branch_item_selected); - ClassDB::bind_method(D_METHOD("_remote_selected"), &VersionControlEditorPlugin::_remote_selected); - ClassDB::bind_method(D_METHOD("_fetch"), &VersionControlEditorPlugin::_fetch); - ClassDB::bind_method(D_METHOD("_pull"), &VersionControlEditorPlugin::_pull); - ClassDB::bind_method(D_METHOD("_push"), &VersionControlEditorPlugin::_push); - ClassDB::bind_method(D_METHOD("_extra_option_selected"), &VersionControlEditorPlugin::_extra_option_selected); - ClassDB::bind_method(D_METHOD("_update_extra_options"), &VersionControlEditorPlugin::_update_extra_options); - ClassDB::bind_method(D_METHOD("_popup_branch_remove_confirm"), &VersionControlEditorPlugin::_popup_branch_remove_confirm); - ClassDB::bind_method(D_METHOD("_popup_remote_remove_confirm"), &VersionControlEditorPlugin::_popup_remote_remove_confirm); - ClassDB::bind_method(D_METHOD("_popup_file_dialog"), &VersionControlEditorPlugin::_popup_file_dialog); - - ClassDB::bind_method(D_METHOD("popup_vcs_set_up_dialog"), &VersionControlEditorPlugin::popup_vcs_set_up_dialog); + // No binds required so far. } void VersionControlEditorPlugin::_create_vcs_metadata_files() { @@ -94,12 +58,10 @@ void VersionControlEditorPlugin::_create_vcs_metadata_files() { void VersionControlEditorPlugin::_notification(int p_what) { if (p_what == NOTIFICATION_READY) { String installed_plugin = GLOBAL_DEF("editor/version_control/plugin_name", ""); - String project_path = GLOBAL_DEF("editor/version_control/project_path", OS::get_singleton()->get_resource_dir()); - project_path_input->set_text(project_path); bool has_autoload_enable = GLOBAL_DEF("editor/version_control/autoload_on_startup", false); if (installed_plugin != "" && has_autoload_enable) { - if (_load_plugin(installed_plugin, project_path)) { + if (_load_plugin(installed_plugin)) { _set_credentials(); } } @@ -144,18 +106,15 @@ void VersionControlEditorPlugin::_initialize_vcs() { const int id = set_up_choice->get_selected_id(); String selected_plugin = set_up_choice->get_item_text(id); - if (_load_plugin(selected_plugin, project_path_input->get_text())) { + if (_load_plugin(selected_plugin)) { ProjectSettings::get_singleton()->set("editor/version_control/autoload_on_startup", true); ProjectSettings::get_singleton()->set("editor/version_control/plugin_name", selected_plugin); - ProjectSettings::get_singleton()->set("editor/version_control/project_path", project_path_input->get_text()); ProjectSettings::get_singleton()->save(); } } void VersionControlEditorPlugin::_set_vcs_ui_state(bool p_enabled) { - select_project_path_button->set_disabled(p_enabled); set_up_dialog->get_ok_button()->set_disabled(!p_enabled); - project_path_input->set_editable(!p_enabled); set_up_choice->set_disabled(p_enabled); toggle_vcs_choice->set_pressed_no_signal(p_enabled); } @@ -181,14 +140,14 @@ void VersionControlEditorPlugin::_set_credentials() { EditorSettings::get_singleton()->set_setting("version_control/ssh_private_key_path", ssh_private_key); } -bool VersionControlEditorPlugin::_load_plugin(String p_name, String p_project_path) { +bool VersionControlEditorPlugin::_load_plugin(String p_name) { Object *extension_instance = ClassDB::instantiate(p_name); ERR_FAIL_NULL_V_MSG(extension_instance, false, "Received a nullptr VCS extension instance during construction."); EditorVCSInterface *vcs_plugin = Object::cast_to<EditorVCSInterface>(extension_instance); ERR_FAIL_NULL_V_MSG(vcs_plugin, false, vformat("Could not cast VCS extension instance to %s.", EditorVCSInterface::get_class_static())); - String res_dir = project_path_input->get_text(); + String res_dir = OS::get_singleton()->get_resource_dir(); ERR_FAIL_COND_V_MSG(!vcs_plugin->initialize(res_dir), false, "Could not initialize " + p_name); @@ -435,6 +394,10 @@ void VersionControlEditorPlugin::_discard_file(String p_file_path, EditorVCSInte EditorFileSystem::get_singleton()->update_file(p_file_path); } +void VersionControlEditorPlugin::_confirm_discard_all() { + discard_all_confirm->popup_centered(); +} + void VersionControlEditorPlugin::_discard_all() { TreeItem *file_entry = unstaged_files->get_root()->get_first_child(); while (file_entry) { @@ -631,7 +594,6 @@ void VersionControlEditorPlugin::_display_diff(int p_idx) { diff->pop(); diff->pop(); - diff->add_newline(); diff->push_font(EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("status_source"), SNAME("EditorFonts"))); for (int j = 0; j < diff_file.diff_hunks.size(); j++) { EditorVCSInterface::DiffHunk hunk = diff_file.diff_hunks[j]; @@ -641,6 +603,7 @@ void VersionControlEditorPlugin::_display_diff(int p_idx) { String old_lines = String::num_int64(hunk.old_lines); String new_lines = String::num_int64(hunk.new_lines); + diff->add_newline(); diff->append_text("[center]@@ " + old_start + "," + old_lines + " " + new_start + "," + new_lines + " @@[/center]"); diff->add_newline(); @@ -653,7 +616,6 @@ void VersionControlEditorPlugin::_display_diff(int p_idx) { break; } diff->add_newline(); - diff->add_newline(); } diff->pop(); @@ -721,12 +683,10 @@ void VersionControlEditorPlugin::_display_diff_split_view(List<EditorVCSInterfac if (diff_line.old_line_no >= 0) { diff->push_cell(); - diff->push_indent(1); diff->push_color(has_change ? red : white); diff->add_text(String::num_int64(diff_line.old_line_no)); diff->pop(); diff->pop(); - diff->pop(); diff->push_cell(); diff->push_color(has_change ? red : white); @@ -753,12 +713,10 @@ void VersionControlEditorPlugin::_display_diff_split_view(List<EditorVCSInterfac if (diff_line.new_line_no >= 0) { diff->push_cell(); - diff->push_indent(1); diff->push_color(has_change ? green : white); diff->add_text(String::num_int64(diff_line.new_line_no)); diff->pop(); diff->pop(); - diff->pop(); diff->push_cell(); diff->push_color(has_change ? green : white); @@ -943,10 +901,6 @@ void VersionControlEditorPlugin::_toggle_vcs_integration(bool p_toggled) { } } -void VersionControlEditorPlugin::_project_path_selected(String p_project_path) { - project_path_input->set_text(p_project_path); -} - void VersionControlEditorPlugin::fetch_available_vcs_plugin_names() { available_plugins.clear(); ClassDB::get_direct_inheriters_from_class(EditorVCSInterface::get_class_static(), &available_plugins); @@ -1040,34 +994,6 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_choice->set_h_size_flags(Control::SIZE_EXPAND_FILL); set_up_hbc->add_child(set_up_choice); - HBoxContainer *project_path_hbc = memnew(HBoxContainer); - project_path_hbc->set_h_size_flags(Control::SIZE_FILL); - set_up_vbc->add_child(project_path_hbc); - - Label *project_path_label = memnew(Label); - project_path_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); - project_path_label->set_text(TTR("VCS Project Path")); - project_path_hbc->add_child(project_path_label); - - project_path_input = memnew(LineEdit); - project_path_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); - project_path_input->set_text(OS::get_singleton()->get_resource_dir()); - project_path_hbc->add_child(project_path_input); - - FileDialog *select_project_path_file_dialog = memnew(FileDialog); - select_project_path_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM); - select_project_path_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_DIR); - select_project_path_file_dialog->set_show_hidden_files(true); - select_project_path_file_dialog->set_current_dir(OS::get_singleton()->get_resource_dir()); - select_project_path_file_dialog->connect(SNAME("dir_selected"), callable_mp(this, &VersionControlEditorPlugin::_project_path_selected)); - project_path_hbc->add_child(select_project_path_file_dialog); - - select_project_path_button = memnew(Button); - select_project_path_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Folder", "EditorIcons")); - select_project_path_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_file_dialog).bind(select_project_path_file_dialog)); - select_project_path_button->set_tooltip_text(TTR("Select VCS project path")); - project_path_hbc->add_child(select_project_path_button); - HBoxContainer *toggle_vcs_hbc = memnew(HBoxContainer); toggle_vcs_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); set_up_vbc->add_child(toggle_vcs_hbc); @@ -1240,10 +1166,21 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { refresh_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_remote_list)); unstage_title->add_child(refresh_button); + discard_all_confirm = memnew(AcceptDialog); + discard_all_confirm->set_title(TTR("Discard all changes")); + discard_all_confirm->set_min_size(Size2i(400, 50)); + discard_all_confirm->set_text(TTR("This operation is IRREVERSIBLE. Your changes will be deleted FOREVER.")); + discard_all_confirm->set_hide_on_ok(true); + discard_all_confirm->set_ok_button_text(TTR("Permanentally delete my changes")); + discard_all_confirm->add_cancel_button(); + version_commit_dock->add_child(discard_all_confirm); + + discard_all_confirm->get_ok_button()->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_discard_all)); + discard_all_button = memnew(Button); discard_all_button->set_tooltip_text(TTR("Discard all changes")); discard_all_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Close"), SNAME("EditorIcons"))); - discard_all_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_discard_all)); + discard_all_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_confirm_discard_all)); discard_all_button->set_flat(true); unstage_title->add_child(discard_all_button); diff --git a/editor/plugins/version_control_editor_plugin.h b/editor/plugins/version_control_editor_plugin.h index 3340384a92..d73588a1bf 100644 --- a/editor/plugins/version_control_editor_plugin.h +++ b/editor/plugins/version_control_editor_plugin.h @@ -73,8 +73,6 @@ private: AcceptDialog *set_up_dialog = nullptr; CheckButton *toggle_vcs_choice = nullptr; OptionButton *set_up_choice = nullptr; - LineEdit *project_path_input = nullptr; - Button *select_project_path_button = nullptr; VBoxContainer *set_up_vbc = nullptr; VBoxContainer *set_up_settings_vbc = nullptr; LineEdit *set_up_username = nullptr; @@ -86,6 +84,8 @@ private: FileDialog *set_up_ssh_private_key_file_dialog = nullptr; Label *set_up_warning_text = nullptr; + AcceptDialog *discard_all_confirm = nullptr; + OptionButton *commit_list_size_button = nullptr; AcceptDialog *branch_create_confirm = nullptr; @@ -150,13 +150,14 @@ private: void _update_opened_tabs(); void _update_extra_options(); - bool _load_plugin(String p_name, String p_project_path); + bool _load_plugin(String p_name); void _pull(); void _push(); void _force_push(); void _fetch(); void _commit(); + void _confirm_discard_all(); void _discard_all(); void _refresh_stage_area(); void _refresh_branch_list(); @@ -193,7 +194,6 @@ private: void _create_vcs_metadata_files(); void _popup_file_dialog(Variant p_file_dialog_variant); void _toggle_vcs_integration(bool p_toggled); - void _project_path_selected(String p_project_path); friend class EditorVCSInterface; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index cf811067c9..3d0c1acff8 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1263,7 +1263,7 @@ Dictionary VisualShaderEditor::get_custom_node_data(Ref<VisualShaderNodeCustom> void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { Ref<Script> scr = Ref<Script>(p_resource.ptr()); - if (scr.is_null() || scr->get_instance_base_type() != String("VisualShaderNodeCustom")) { + if (scr.is_null() || scr->get_instance_base_type() != "VisualShaderNodeCustom") { return; } @@ -5383,6 +5383,10 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Binormal", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal", "BINORMAL"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Color", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color", "COLOR"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Custom0", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom0", "CUSTOM0"), { "custom0" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Custom1", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom1", "CUSTOM1"), { "custom1" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Custom2", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom2", "CUSTOM2"), { "custom2" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Custom3", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom3", "CUSTOM3"), { "custom3" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("InstanceId", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id", "INSTANCE_ID"), { "instance_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("InstanceCustom", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom", "INSTANCE_CUSTOM"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("ModelViewMatrix", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "modelview_matrix", "MODELVIEW_MATRIX"), { "modelview_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 1b169076c6..0d93c8a95e 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -447,7 +447,7 @@ private: } else if (renderer_type == "gl_compatibility") { renderer_info->set_text( String::utf8("• ") + TTR("Supports desktop, mobile + web platforms.") + - String::utf8("\n• ") + TTR("Least advanced 3D graphics.") + + String::utf8("\n• ") + TTR("Least advanced 3D graphics (currently work-in-progress).") + String::utf8("\n• ") + TTR("Intended for low-end/older devices.") + String::utf8("\n• ") + TTR("Uses OpenGL 3 backend (OpenGL 3.3/ES 3.0/WebGL2).") + String::utf8("\n• ") + TTR("Fastest rendering of simple scenes.")); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index b406b2a1ce..b99a83a546 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -553,6 +553,14 @@ void ProjectSettingsEditor::_update_theme() { } } +void ProjectSettingsEditor::_input_filter_focused() { + set_close_on_escape(false); +} + +void ProjectSettingsEditor::_input_filter_unfocused() { + set_close_on_escape(true); +} + void ProjectSettingsEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -683,6 +691,8 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { action_map_editor->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed)); action_map_editor->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed)); action_map_editor->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered)); + action_map_editor->connect(SNAME("filter_focused"), callable_mp(this, &ProjectSettingsEditor::_input_filter_focused)); + action_map_editor->connect(SNAME("filter_unfocused"), callable_mp(this, &ProjectSettingsEditor::_input_filter_unfocused)); tab_container->add_child(action_map_editor); localization_editor = memnew(LocalizationEditor); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 7f6dd1b692..1687be47fb 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -107,6 +107,9 @@ class ProjectSettingsEditor : public AcceptDialog { void _update_action_map_editor(); void _update_theme(); + void _input_filter_focused(); + void _input_filter_unfocused(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 247d5e1717..8ac0aa6fe3 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -48,6 +48,7 @@ #include "editor/import/editor_import_plugin.h" #include "editor/import/resource_importer_scene.h" #include "editor/plugins/animation_tree_editor_plugin.h" +#include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/audio_stream_randomizer_editor_plugin.h" #include "editor/plugins/bit_map_editor_plugin.h" #include "editor/plugins/bone_map_editor_plugin.h" @@ -155,6 +156,7 @@ void register_editor_types() { // This list is alphabetized, and plugins that depend on Node2D are in their own section below. EditorPlugins::add_by_type<AnimationTreeEditorPlugin>(); + EditorPlugins::add_by_type<AudioStreamEditorPlugin>(); EditorPlugins::add_by_type<AudioStreamRandomizerEditorPlugin>(); EditorPlugins::add_by_type<BitMapEditorPlugin>(); EditorPlugins::add_by_type<BoneMapEditorPlugin>(); diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 40683e2938..5243916bd2 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -338,7 +338,7 @@ void RenameDialog::_bind_methods() { } void RenameDialog::_update_substitute() { - LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner()); + LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(get_viewport()->gui_get_focus_owner()); bool is_main_field = _is_main_field(focus_owner_line_edit); but_insert_name->set_disabled(!is_main_field); @@ -636,7 +636,7 @@ bool RenameDialog::_is_main_field(LineEdit *line_edit) { } void RenameDialog::_insert_text(String text) { - LineEdit *focus_owner = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner()); + LineEdit *focus_owner = Object::cast_to<LineEdit>(get_viewport()->gui_get_focus_owner()); if (_is_main_field(focus_owner)) { focus_owner->selection_delete(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 96688a3614..4b04fd1589 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -134,6 +134,8 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) { _tool_selected(TOOL_MOVE_DOWN); } else if (ED_IS_SHORTCUT("scene_tree/reparent", p_event)) { _tool_selected(TOOL_REPARENT); + } else if (ED_IS_SHORTCUT("scene_tree/reparent_to_new_node", p_event)) { + _tool_selected(TOOL_REPARENT_TO_NEW_NODE); } else if (ED_IS_SHORTCUT("scene_tree/save_branch_as_scene", p_event)) { _tool_selected(TOOL_NEW_SCENE_FROM); } else if (ED_IS_SHORTCUT("scene_tree/delete_no_confirm", p_event)) { @@ -1965,37 +1967,21 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { } Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); - if (selected.size() == 1) { - Node *node = selected.front()->get(); - Ref<Script> existing = node->get_script(); - - undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, node); - undo_redo->add_do_method(InspectorDock::get_singleton(), "store_script_properties", node); - undo_redo->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", node); - undo_redo->add_do_method(node, "set_script", p_script); - undo_redo->add_undo_method(node, "set_script", existing); - undo_redo->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", node); - undo_redo->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", node); + undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, selected.front()->get()); + for (Node *E : selected) { + Ref<Script> existing = E->get_script(); + undo_redo->add_do_method(InspectorDock::get_singleton(), "store_script_properties", E); + undo_redo->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", E); + undo_redo->add_do_method(E, "set_script", p_script); + undo_redo->add_undo_method(E, "set_script", existing); + undo_redo->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", E); + undo_redo->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", E); undo_redo->add_do_method(this, "_update_script_button"); undo_redo->add_undo_method(this, "_update_script_button"); - undo_redo->commit_action(); - } else { - undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, selected.front()->get()); - for (Node *E : selected) { - Ref<Script> existing = E->get_script(); - undo_redo->add_do_method(InspectorDock::get_singleton(), "store_script_properties", E); - undo_redo->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", E); - undo_redo->add_do_method(E, "set_script", p_script); - undo_redo->add_undo_method(E, "set_script", existing); - undo_redo->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", E); - undo_redo->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", E); - undo_redo->add_do_method(this, "_update_script_button"); - undo_redo->add_undo_method(this, "_update_script_button"); - } - undo_redo->commit_action(); } + undo_redo->commit_action(); - _push_item(p_script.operator->()); + _push_item(p_script.ptr()); _update_script_button(); } @@ -3233,6 +3219,7 @@ List<Node *> SceneTreeDock::paste_nodes() { if (!paste_parent) { paste_parent = dup; owner = dup; + dup->set_scene_file_path(String()); // Make sure the scene path is empty, to avoid accidental references. ur->add_do_method(EditorNode::get_singleton(), "set_edited_scene", dup); } else { ur->add_do_method(paste_parent, "add_child", dup, true); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 092ef30678..30a9dc5bbf 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -132,8 +132,16 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i if (config_err.is_empty()) { return; } - config_err = config_err.word_wrap(80); - warning->set_text(config_err); + + const PackedInt32Array boundaries = TS->string_get_word_breaks(config_err, "", 80); + PackedStringArray lines; + for (int i = 0; i < boundaries.size(); i += 2) { + const int start = boundaries[i]; + const int end = boundaries[i + 1]; + lines.append(config_err.substr(start, end - start + 1)); + } + + warning->set_text(String("\n").join(lines)); warning->popup_centered(); } else if (p_id == BUTTON_SIGNALS) { |