VSE: Slip keyframes with strip content

The "slip strip contents" operator in the VSE now can move the strip
keyframes. There is a property to enable keyframe slipping.
The property is disabled by default, mainly because animation is
often used for fade in/out, which would be annoying if it moved with
content.

Pull Request: https://projects.blender.org/blender/blender/pulls/136386
This commit is contained in:
il4n 2025-03-25 21:24:07 +01:00 • committed by Richard Antalik
parent a7026e817c
commit 3e023fcf79
5 changed files with 49 additions and 19 deletions

View file

@ -548,7 +548,8 @@ static wmOperatorStatus sequencer_slip_invoke(bContext *C, wmOperator *op, const
return OPERATOR_RUNNING_MODAL;
}
static void sequencer_slip_strips(Scene *scene, SlipData *data, int delta, float subframe_delta)
static void sequencer_slip_strips(
Scene *scene, SlipData *data, int delta, float subframe_delta, bool slip_keyframes)
{
for (int i = data->num_seq - 1; i >= 0; i--) {
Strip *strip = data->strip_array[i];
@ -558,7 +559,7 @@ static void sequencer_slip_strips(Scene *scene, SlipData *data, int delta, float
continue;
}
seq::time_slip_strip(scene, strip, delta, subframe_delta);
seq::time_slip_strip(scene, strip, delta, subframe_delta, slip_keyframes);
}
for (int i = data->num_seq - 1; i >= 0; i--) {
@ -614,6 +615,7 @@ static wmOperatorStatus sequencer_slip_exec(bContext *C, wmOperator *op)
slip_add_sequences(ed->seqbasep, data->strip_array);
bool slip_keyframes = RNA_boolean_get(op->ptr, "slip_keyframes");
float offset_fl = RNA_float_get(op->ptr, "offset");
int offset = round_fl_to_int(offset_fl);
@ -624,7 +626,7 @@ static wmOperatorStatus sequencer_slip_exec(bContext *C, wmOperator *op)
}
sequencer_slip_apply_limits(scene, data, &offset);
sequencer_slip_strips(scene, data, offset, subframe_delta);
sequencer_slip_strips(scene, data, offset, subframe_delta, slip_keyframes);
MEM_freeN(data->strip_array);
MEM_freeN(data);
@ -679,7 +681,9 @@ static void handle_number_input(
data->subframe_restore += subframe_delta;
}
data->previous_subframe_offset = offset_fl;
sequencer_slip_strips(scene, data, delta_offset, subframe_delta);
bool slip_keyframes = RNA_boolean_get(op->ptr, "slip_keyframes");
sequencer_slip_strips(scene, data, delta_offset, subframe_delta, slip_keyframes);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
}
@ -738,7 +742,9 @@ static wmOperatorStatus sequencer_slip_modal(bContext *C, wmOperator *op, const
data->subframe_restore = 0.0f;
}
data->previous_subframe_offset = offset_fl;
sequencer_slip_strips(scene, data, delta_offset, subframe_delta);
bool slip_keyframes = RNA_boolean_get(op->ptr, "slip_keyframes");
sequencer_slip_strips(scene, data, delta_offset, subframe_delta, slip_keyframes);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
}
@ -764,7 +770,8 @@ static wmOperatorStatus sequencer_slip_modal(bContext *C, wmOperator *op, const
case RIGHTMOUSE: {
int offset = data->previous_offset;
float subframe_delta = data->subframe_restore;
sequencer_slip_strips(scene, data, -offset, -subframe_delta);
bool slip_keyframes = RNA_boolean_get(op->ptr, "slip_keyframes");
sequencer_slip_strips(scene, data, -offset, -subframe_delta, slip_keyframes);
MEM_freeN(data->strip_array);
MEM_freeN(data);
@ -834,6 +841,11 @@ void SEQUENCER_OT_slip(wmOperatorType *ot)
-FLT_MAX,
FLT_MAX);
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 100, 0);
RNA_def_boolean(ot->srna,
"slip_keyframes",
false,
"Slip Keyframes",
"Move the keyframes alongside the media");
}
/** \} */

View file

@ -16,13 +16,13 @@ struct ListBase;
struct Scene;
struct Strip;
struct SeqAnimationBackup;
namespace blender::seq {
bool animation_keyframes_exist(Scene *scene);
bool animation_keyframes_exist(const Scene *scene);
bool animation_drivers_exist(Scene *scene);
void free_animdata(Scene *scene, Strip *strip);
void offset_animdata(Scene *scene, Strip *strip, int ofs);
void offset_animdata(const Scene *scene, Strip *strip, float ofs);
/**
* Return whether the fcurve targets the given sequence.
*/

View file

@ -125,7 +125,8 @@ void time_update_meta_strip_range(const Scene *scene, Strip *strip_meta);
/**
* Move contents of a strip without moving the strip handles.
*/
void time_slip_strip(const Scene *scene, Strip *strip, int delta, float subframe_delta);
void time_slip_strip(
const Scene *scene, Strip *strip, int delta, float subframe_delta, bool slip_keyframes);
/**
* Get difference between scene and movie strip frame-rate.
*/

View file

@ -22,7 +22,7 @@
namespace blender::seq {
bool animation_keyframes_exist(Scene *scene)
bool animation_keyframes_exist(const Scene *scene)
{
return scene->adt != nullptr && scene->adt->action != nullptr &&
scene->adt->action->wrap().has_keyframes(scene->adt->slot_handle);
@ -39,9 +39,9 @@ bool fcurve_matches(const Strip &strip, const FCurve &fcurve)
fcurve, "sequence_editor.strips_all[", strip.name + 2);
}
void offset_animdata(Scene *scene, Strip *strip, int ofs)
void offset_animdata(const Scene *scene, Strip *strip, float ofs)
{
if (!animation_keyframes_exist(scene) || ofs == 0) {
if (!animation_keyframes_exist(scene) || ofs == 0.0f) {
return;
}

View file

@ -23,6 +23,7 @@
#include "MOV_read.hh"
#include "SEQ_animation.hh"
#include "SEQ_channels.hh"
#include "SEQ_iterator.hh"
#include "SEQ_render.hh"
@ -551,14 +552,18 @@ void strip_time_translate_handles(const Scene *scene, Strip *strip, const int of
time_update_meta_strip_range(scene, lookup_meta_by_strip(scene->ed, strip));
}
static void strip_time_slip_strip_ex(
const Scene *scene, Strip *strip, int delta, float subframe_delta, bool recursed)
static void strip_time_slip_strip_ex(const Scene *scene,
Strip *strip,
int delta,
float subframe_delta,
bool slip_keyframes,
bool recursed)
{
if (strip->type == STRIP_TYPE_SOUND_RAM && subframe_delta != 0.0f) {
strip->sound_offset += subframe_delta / FPS;
}
if (delta == 0) {
if (delta == 0 && (!slip_keyframes || subframe_delta == 0.0f)) {
return;
}
@ -580,12 +585,23 @@ static void strip_time_slip_strip_ex(
if (BLI_listbase_is_empty(&strip->seqbase)) {
return;
}
LISTBASE_FOREACH (Strip *, strip_child, &strip->seqbase) {
strip_time_slip_strip_ex(scene, strip_child, delta, subframe_delta, true);
/* The keyframes of strips inside meta strips should always be moved. */
strip_time_slip_strip_ex(scene, strip_child, delta, subframe_delta, true, true);
}
}
strip->start = strip->start + delta;
if (slip_keyframes) {
float anim_offset = delta;
if (strip->type == STRIP_TYPE_SOUND_RAM) {
anim_offset += subframe_delta;
}
offset_animdata(scene, strip, anim_offset);
}
if (!recursed) {
strip->startofs = strip->startofs - delta;
strip->endofs = strip->endofs + delta;
@ -599,9 +615,10 @@ static void strip_time_slip_strip_ex(
strip_time_update_effects_strip_range(scene, effects);
}
void time_slip_strip(const Scene *scene, Strip *strip, int delta, float subframe_delta)
void time_slip_strip(
const Scene *scene, Strip *strip, int delta, float subframe_delta, bool slip_keyframes)
{
strip_time_slip_strip_ex(scene, strip, delta, subframe_delta, false);
strip_time_slip_strip_ex(scene, strip, delta, subframe_delta, slip_keyframes, false);
}
int time_get_rounded_sound_offset(const Strip *strip, const float frames_per_second)