diff --git a/editor/animation/animation_track_editor.cpp b/editor/animation/animation_track_editor.cpp index e618cf1ddb0..17c3da3c1d0 100644 --- a/editor/animation/animation_track_editor.cpp +++ b/editor/animation/animation_track_editor.cpp @@ -3198,11 +3198,14 @@ void AnimationTrackEdit::gui_input(const Ref &p_event) { menu->add_icon_item(get_editor_theme_icon(SNAME("ActionPaste")), TTR("Paste Key(s)"), MENU_KEY_PASTE); } if (selected || editor->is_selection_active()) { - AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player(); - if ((!player->has_animation(SceneStringName(RESET)) || animation != player->get_animation(SceneStringName(RESET))) && editor->can_add_reset_key()) { - menu->add_icon_item(get_editor_theme_icon(SNAME("Reload")), TTR("Add RESET Value(s)"), MENU_KEY_ADD_RESET); + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + AnimationPlayer *ap = ape->get_player(); + if (ap && editor->can_add_reset_key() && animation != ap->get_animation(SceneStringName(RESET))) { + menu->add_separator(); + menu->add_icon_item(get_editor_theme_icon(SNAME("MoveUp")), TTR("Send Key(s) to RESET"), MENU_KEY_ADD_RESET); + } } - menu->add_separator(); menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete Key(s)"), MENU_KEY_DELETE); } @@ -3611,11 +3614,9 @@ void AnimationTrackEdit::_menu_selected(int p_index) { } break; case MENU_KEY_ADD_RESET: { emit_signal(SNAME("create_reset_request")); - } break; case MENU_KEY_DELETE: { emit_signal(SNAME("delete_request")); - } break; case MENU_KEY_LOOKUP: { _lookup_key(lookup_key_idx); @@ -4263,6 +4264,10 @@ static bool track_type_is_resettable(Animation::TrackType p_type) { } } +bool AnimationTrackEditor::is_read_only() const { + return read_only; +} + void AnimationTrackEditor::make_insert_queue() { insert_data.clear(); insert_queue = true; @@ -4271,7 +4276,7 @@ void AnimationTrackEditor::make_insert_queue() { void AnimationTrackEditor::commit_insert_queue() { bool reset_allowed = true; AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player(); - if (player->has_animation(SceneStringName(RESET)) && player->get_animation(SceneStringName(RESET)) == animation) { + if (is_global_library_read_only() || (player->has_animation(SceneStringName(RESET)) && player->get_animation(SceneStringName(RESET)) == animation)) { // Avoid messing with the reset animation itself. reset_allowed = false; } else { @@ -4353,6 +4358,8 @@ void AnimationTrackEditor::commit_insert_queue() { } void AnimationTrackEditor::_query_insert(const InsertData &p_id) { + ERR_FAIL_COND_EDMSG(read_only, "Animation is read-only."); // Should have been prevented by dialog, but for safety. + if (!insert_queue) { insert_data.clear(); } @@ -4399,6 +4406,11 @@ void AnimationTrackEditor::_insert_track(bool p_reset_wanted, bool p_create_bezi } void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant &p_value) { + if (read_only) { + popup_read_only_dialog(); + return; + } + ERR_FAIL_NULL(root); ERR_FAIL_COND_MSG( (p_type != Animation::TYPE_POSITION_3D && p_type != Animation::TYPE_ROTATION_3D && p_type != Animation::TYPE_SCALE_3D), @@ -4464,6 +4476,11 @@ bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const } void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant &p_value) { + if (read_only) { + popup_read_only_dialog(); + return; + } + String path = String(p_path); // Animation property is a special case, always creates an animation track. @@ -4498,6 +4515,11 @@ void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant } void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_property, bool p_only_if_exists, bool p_advance) { + if (read_only) { + popup_read_only_dialog(); + return; + } + ERR_FAIL_NULL(root); // Let's build a node path. @@ -4612,6 +4634,11 @@ float AnimationTrackEditor::get_marker_moving_selection_offset() const { } void AnimationTrackEditor::insert_value_key(const String &p_property, bool p_advance) { + if (read_only) { + popup_read_only_dialog(); + return; + } + EditorSelectionHistory *history = EditorNode::get_singleton()->get_editor_selection_history(); ERR_FAIL_NULL(root); @@ -4641,6 +4668,29 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, bool p_adv } } +bool AnimationTrackEditor::is_global_library_read_only() const { + Ref al; + AnimationMixer *mixer = AnimationPlayerEditor::get_singleton()->fetch_mixer_for_library(); + if (mixer) { + if (!mixer->has_animation_library("")) { + return false; + } else { + al = mixer->get_animation_library(""); + } + } + if (al.is_valid()) { + String base = al->get_path(); + int srpos = base.find("::"); + if (srpos != -1) { + base = base.substr(0, srpos); + } + if (FileAccess::exists(base + ".import")) { + return true; + } + } + return false; +} + Ref AnimationTrackEditor::_create_and_get_reset_animation() { AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player(); if (player->has_animation(SceneStringName(RESET))) { @@ -4987,6 +5037,9 @@ bool AnimationTrackEditor::is_bezier_editor_active() const { } bool AnimationTrackEditor::can_add_reset_key() const { + if (is_global_library_read_only()) { + return false; + } for (const KeyValue &E : selection) { const Animation::TrackType track_type = animation->track_get_type(E.key.track); if (track_type != Animation::TYPE_ANIMATION && track_type != Animation::TYPE_AUDIO && track_type != Animation::TYPE_METHOD) { @@ -5405,6 +5458,7 @@ void AnimationTrackEditor::_notification(int p_what) { dummy_player_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); inactive_player_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); + edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_ADD_RESET_KEY), get_editor_theme_icon(SNAME("MoveUp"))); edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), get_editor_theme_icon(SNAME("Reload"))); auto_fit->set_button_icon(get_editor_theme_icon(SNAME("AnimationAutoFit"))); auto_fit_bezier->set_button_icon(get_editor_theme_icon(SNAME("AnimationAutoFitBezier"))); @@ -5750,6 +5804,11 @@ int AnimationTrackEditor::_get_track_selected() { } void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { + if (read_only) { + popup_read_only_dialog(); + return; + } + ERR_FAIL_INDEX(p_track, animation->get_track_count()); if (snap_keys->is_pressed() && step->get_value() != 0) { @@ -6678,6 +6737,7 @@ bool AnimationTrackEditor::_is_track_compatible(int p_target_track_idx, Variant: void AnimationTrackEditor::_edit_menu_about_to_popup() { AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player(); + edit->get_popup()->set_item_disabled(edit->get_popup()->get_item_index(EDIT_ADD_RESET_KEY), !can_add_reset_key() || animation == player->get_animation(SceneStringName(RESET))); edit->get_popup()->set_item_disabled(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), !player->can_apply_reset()); bool has_length = false; @@ -7843,6 +7903,10 @@ void AnimationTrackEditor::_pick_track_select_recursive(TreeItem *p_item, const } } +void AnimationTrackEditor::popup_read_only_dialog() { + read_only_dialog->popup_centered(Size2(200, 100) * EDSCALE); +} + AnimationTrackEditor::AnimationTrackEditor() { main_panel = memnew(PanelContainer); main_panel->set_focus_mode(FOCUS_ALL); // Allow panel to have focus so that shortcuts work as expected. @@ -8148,7 +8212,7 @@ AnimationTrackEditor::AnimationTrackEditor() { edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/cut_selected_keys", TTRC("Cut Selected Keys"), KeyModifierMask::CMD_OR_CTRL | Key::X), EDIT_CUT_KEYS); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/copy_selected_keys", TTRC("Copy Selected Keys"), KeyModifierMask::CMD_OR_CTRL | Key::C), EDIT_COPY_KEYS); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/paste_keys", TTRC("Paste Keys"), KeyModifierMask::CMD_OR_CTRL | Key::V), EDIT_PASTE_KEYS); - edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/add_reset_value", TTRC("Add RESET Value(s)"))); + edit->get_popup()->add_separator(); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/move_first_selected_key_to_cursor", TTRC("Move First Selected Key to Cursor"), Key::BRACKETLEFT), EDIT_MOVE_FIRST_SELECTED_KEY_TO_CURSOR); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/move_last_selected_key_to_cursor", TTRC("Move Last Selected Key to Cursor"), Key::BRACKETRIGHT), EDIT_MOVE_LAST_SELECTED_KEY_TO_CURSOR); @@ -8162,7 +8226,9 @@ AnimationTrackEditor::AnimationTrackEditor() { edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/go_to_next_keyframe", TTRC("Go to Next Keyframe"), KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::D), EDIT_GOTO_NEXT_KEYFRAME); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/go_to_previous_keyframe", TTRC("Go to Previous Keyframe"), KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::A), EDIT_GOTO_PREV_KEYFRAME); edit->get_popup()->add_separator(); - edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/apply_reset", TTRC("Apply Reset")), EDIT_APPLY_RESET); + + edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/add_reset_value", TTRC("Add tracks to RESET")), EDIT_ADD_RESET_KEY); + edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/apply_reset", TTRC("Apply RESET")), EDIT_APPLY_RESET); edit->get_popup()->add_separator(); edit->get_popup()->add_item(TTR("Bake Animation..."), EDIT_BAKE_ANIMATION); edit->get_popup()->add_item(TTR("Optimize Animation (no undo)..."), EDIT_OPTIMIZE_ANIMATION); @@ -8401,6 +8467,11 @@ AnimationTrackEditor::AnimationTrackEditor() { track_copy_select->set_hide_root(true); track_copy_vbox->add_child(track_copy_select); track_copy_dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_COPY_TRACKS_CONFIRM)); + + read_only_dialog = memnew(AcceptDialog); + read_only_dialog->set_title(TTRC("Key Insertion Error")); + read_only_dialog->set_text(TTRC("Imported Animation cannot be edited!")); + add_child(read_only_dialog); } AnimationTrackEditor::~AnimationTrackEditor() { diff --git a/editor/animation/animation_track_editor.h b/editor/animation/animation_track_editor.h index 59de3fbed9a..e1603edac04 100644 --- a/editor/animation/animation_track_editor.h +++ b/editor/animation/animation_track_editor.h @@ -596,6 +596,8 @@ class AnimationTrackEditor : public VBoxContainer { bool read_only = false; Node *root = nullptr; + AcceptDialog *read_only_dialog = nullptr; + MenuButton *edit = nullptr; PanelContainer *main_panel = nullptr; @@ -986,6 +988,10 @@ public: /** If `p_from_mouse_event` is `true`, handle Shift key presses for precise snapping. */ void goto_next_step(bool p_from_mouse_event, bool p_timeline_only = false); + bool is_read_only() const; + bool is_global_library_read_only() const; + void popup_read_only_dialog(); + MenuButton *get_edit_menu(); AnimationTrackEditor(); ~AnimationTrackEditor(); diff --git a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp index 0e995af029f..47deb0c1c2c 100644 --- a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp @@ -601,6 +601,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory path_string = path_string + String(":") + anim->track_get_path(i).get_concatenated_subnames(); } anim->track_set_path(i, path_string); + anim->track_set_imported(i, true); } } } diff --git a/editor/scene/3d/skeleton_3d_editor_plugin.cpp b/editor/scene/3d/skeleton_3d_editor_plugin.cpp index ed36aa63956..df8997c4690 100644 --- a/editor/scene/3d/skeleton_3d_editor_plugin.cpp +++ b/editor/scene/3d/skeleton_3d_editor_plugin.cpp @@ -435,6 +435,12 @@ void Skeleton3DEditor::reset_pose(const bool p_all_bones) { } void Skeleton3DEditor::insert_keys(const bool p_all_bones) { + AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor(); + bool is_read_only = te->is_read_only(); + if (is_read_only) { + te->popup_read_only_dialog(); + return; + } if (!skeleton) { return; } @@ -447,7 +453,6 @@ void Skeleton3DEditor::insert_keys(const bool p_all_bones) { Node *root = EditorNode::get_singleton()->get_tree()->get_root(); String path = String(root->get_path_to(skeleton)); - AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor(); te->make_insert_queue(); for (int i = 0; i < bone_len; i++) { const String name = skeleton->get_bone_name(i); diff --git a/editor/scene/canvas_item_editor_plugin.cpp b/editor/scene/canvas_item_editor_plugin.cpp index 03deba3d30f..07dc27c7729 100644 --- a/editor/scene/canvas_item_editor_plugin.cpp +++ b/editor/scene/canvas_item_editor_plugin.cpp @@ -4668,6 +4668,11 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor(); ERR_FAIL_COND_MSG(te->get_current_animation().is_null(), "Cannot insert animation key. No animation selected."); + bool is_read_only = te->is_read_only(); + if (is_read_only) { + te->popup_read_only_dialog(); + return; + } te->make_insert_queue(); for (const KeyValue &E : selection) { CanvasItem *ci = Object::cast_to(E.key);