From d388b807a38857fd0b8dd7bc91dd5c28fee02ef6 Mon Sep 17 00:00:00 2001 From: Ramon Klauck Date: Fri, 15 Aug 2025 05:37:39 +0200 Subject: [PATCH] VSE: Clear Strip Keyframes from Preview This PR makes it easier to clear keyframes from strips in preview. It is the similar to the feature in 3D viewport. Just navigate to "Strip" -> "Animation" -> "Clear Keyframes...". This then deletes all keyframes form the selected strips. Pull Request: https://projects.blender.org/blender/blender/pulls/141106 --- scripts/startup/bl_ui/space_sequencer.py | 1 + .../blender/editors/animation/anim_intern.hh | 1 + source/blender/editors/animation/anim_ops.cc | 1 + .../blender/editors/animation/keyframing.cc | 88 +++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index 52b629db9bd..d8677ed64e1 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -991,6 +991,7 @@ class SEQUENCER_MT_strip_animation(Menu): layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe with Keying Set").always_prompt = True layout.operator("anim.keying_set_active_set", text="Change Keying Set...") layout.operator("anim.keyframe_delete_vse", text="Delete Keyframes...") + layout.operator("anim.keyframe_clear_vse", text="Clear Keyframes...") class SEQUENCER_MT_strip_input(Menu): diff --git a/source/blender/editors/animation/anim_intern.hh b/source/blender/editors/animation/anim_intern.hh index 043df9e71e3..f7b9225a761 100644 --- a/source/blender/editors/animation/anim_intern.hh +++ b/source/blender/editors/animation/anim_intern.hh @@ -47,6 +47,7 @@ void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot); void ANIM_OT_keyframe_delete_v3d(wmOperatorType *ot); void ANIM_OT_keyframe_delete_vse(wmOperatorType *ot); void ANIM_OT_keyframe_clear_v3d(wmOperatorType *ot); +void ANIM_OT_keyframe_clear_vse(wmOperatorType *ot); /** \} */ diff --git a/source/blender/editors/animation/anim_ops.cc b/source/blender/editors/animation/anim_ops.cc index 4dc3000f7aa..45b420ac2cd 100644 --- a/source/blender/editors/animation/anim_ops.cc +++ b/source/blender/editors/animation/anim_ops.cc @@ -1376,6 +1376,7 @@ void ED_operatortypes_anim() WM_operatortype_append(ANIM_OT_keyframe_delete_v3d); WM_operatortype_append(ANIM_OT_keyframe_delete_vse); WM_operatortype_append(ANIM_OT_keyframe_clear_v3d); + WM_operatortype_append(ANIM_OT_keyframe_clear_vse); WM_operatortype_append(ANIM_OT_keyframe_insert_button); WM_operatortype_append(ANIM_OT_keyframe_delete_button); WM_operatortype_append(ANIM_OT_keyframe_clear_button); diff --git a/source/blender/editors/animation/keyframing.cc b/source/blender/editors/animation/keyframing.cc index 95f936edb59..c6e3c1195c5 100644 --- a/source/blender/editors/animation/keyframing.cc +++ b/source/blender/editors/animation/keyframing.cc @@ -849,6 +849,94 @@ static bool fcurve_belongs_to_strip(const FCurve &fcurve, const std::string &str return fcurve.rna_path && std::strncmp(fcurve.rna_path, strip_path.c_str(), strip_path.length()) == 0; } + +static wmOperatorStatus clear_anim_vse_exec(bContext *C, wmOperator *op) +{ + using namespace blender::animrig; + bool changed = false; + + Scene *scene = CTX_data_scene(C); + + blender::Vector selection; + blender::Vector selected_strips_rna_paths; + get_selection(C, &selection); + selected_strips_rna_paths = get_selected_strips_rna_paths(selection); + + if (selected_strips_rna_paths.is_empty()) { + BKE_reportf(op->reports, RPT_WARNING, "No strips selected"); + return OPERATOR_CANCELLED; + } + + if (!scene->adt || !scene->adt->action || (scene->adt->slot_handle == Slot::unassigned)) { + BKE_reportf(op->reports, RPT_ERROR, "Scene has no animation data or active action"); + return OPERATOR_CANCELLED; + } + + AnimData *adt = scene->adt; + bAction *dna_action = adt->action; + + Action &action = dna_action->wrap(); + blender::Vector fcurves_to_delete; + foreach_fcurve_in_action_slot(action, adt->slot_handle, [&](FCurve &fcurve) { + for (const std::string &strip_path : selected_strips_rna_paths) { + if (fcurve_belongs_to_strip(fcurve, strip_path)) { + fcurves_to_delete.append(&fcurve); + break; + } + } + }); + for (FCurve *fcurve : fcurves_to_delete) { + action_fcurve_remove(action, *fcurve); + changed = true; + } + + if (!changed) { + return OPERATOR_CANCELLED; + } + + if (scene->adt->action) { + DEG_id_tag_update(&scene->adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH); + } + invalidate_strip_caches(selection, scene); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + WM_event_add_notifier(C, NC_ANIMATION, nullptr); + + return OPERATOR_FINISHED; +} + +static wmOperatorStatus clear_anim_vse_invoke(bContext *C, + wmOperator *op, + const wmEvent * /*event*/) +{ + if (RNA_boolean_get(op->ptr, "confirm")) { + return WM_operator_confirm_ex(C, + op, + IFACE_("Remove animation from selected strips?"), + nullptr, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove"), + ALERT_ICON_NONE, + false); + } + return clear_anim_vse_exec(C, op); +} +void ANIM_OT_keyframe_clear_vse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Animation"; + ot->description = "Remove all keyframe animation for selected strips"; + ot->idname = "ANIM_OT_keyframe_clear_vse"; + + /* callbacks */ + ot->invoke = clear_anim_vse_invoke; + ot->exec = clear_anim_vse_exec; + + ot->poll = ED_operator_areaactive; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + WM_operator_properties_confirm_or_exec(ot); +} + static bool can_delete_key(FCurve *fcu, Object *ob, ReportList *reports) { /* don't touch protected F-Curves */