diff --git a/scripts/modules/keyingsets_utils.py b/scripts/modules/keyingsets_utils.py index c4724bbf4e9..3669c76a0f9 100644 --- a/scripts/modules/keyingsets_utils.py +++ b/scripts/modules/keyingsets_utils.py @@ -44,6 +44,9 @@ def path_add_property(path, prop): # selected objects (active object must be in object mode) def RKS_POLL_selected_objects(_ksi, context): + if context.area.type == 'SEQUENCE_EDITOR': + return False + ob = context.active_object if ob: return ob.mode == 'OBJECT' @@ -53,6 +56,9 @@ def RKS_POLL_selected_objects(_ksi, context): # selected bones def RKS_POLL_selected_bones(_ksi, context): + if context.area.type == 'SEQUENCE_EDITOR': + return False + # we must be in Pose Mode, and there must be some bones selected ob = context.active_object if ob and ob.mode == 'POSE': @@ -62,9 +68,27 @@ def RKS_POLL_selected_bones(_ksi, context): # nothing selected return False +# selected vse strip + + +def RKS_POLL_selected_strip(_ksi, context): + if context.active_strip or context.selected_strips: + return True + + # nothing selected + return False + + +# selected bones, objects or strips +def RKS_POLL_selected_items(ksi, context): + return (RKS_POLL_selected_bones(ksi, context) or + RKS_POLL_selected_objects(ksi, context) or + RKS_POLL_selected_strip(ksi, context)) # selected bones or objects -def RKS_POLL_selected_items(ksi, context): + + +def RKS_POLL_selected_bones_or_objects(ksi, context): return (RKS_POLL_selected_bones(ksi, context) or RKS_POLL_selected_objects(ksi, context)) @@ -74,11 +98,17 @@ def RKS_POLL_selected_items(ksi, context): # All selected objects or pose bones, depending on which we've got. def RKS_ITER_selected_item(ksi, context, ks): + if context.area.type == 'SEQUENCE_EDITOR': + if context.selected_strips: + for strip in context.selected_strips: + ksi.generate(context, ks, strip) + return + ob = context.active_object if ob and ob.mode == 'POSE': for bone in context.selected_pose_bones: ksi.generate(context, ks, bone) - else: + elif context.selected_objects: for ob in context.selected_objects: ksi.generate(context, ks, ob) @@ -165,6 +195,17 @@ def RKS_GEN_location(_ksi, _context, ks, data): # get id-block and path info id_block, base_path, grouping = get_transform_generators_base_info(data) + if isinstance(data, bpy.types.Strip): + path_x = path_add_property(base_path, "transform.offset_x") + path_y = path_add_property(base_path, "transform.offset_y") + if grouping: + ks.paths.add(id_block, path_x, group_method='NAMED', group_name=grouping) + ks.paths.add(id_block, path_y, group_method='NAMED', group_name=grouping) + else: + ks.paths.add(id_block, path_x) + ks.paths.add(id_block, path_y) + return + # add the property name to the base path path = path_add_property(base_path, "location") @@ -181,6 +222,13 @@ def RKS_GEN_rotation(_ksi, _context, ks, data): id_block, base_path, grouping = get_transform_generators_base_info(data) # add the property name to the base path + if isinstance(data, bpy.types.Strip): + path = path_add_property(base_path, "transform.rotation") + if grouping: + ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping) + else: + ks.paths.add(id_block, path) + return # rotation mode affects the property used if data.rotation_mode == 'QUATERNION': path = path_add_property(base_path, "rotation_quaternion") @@ -201,6 +249,16 @@ def RKS_GEN_scaling(_ksi, _context, ks, data): # get id-block and path info id_block, base_path, grouping = get_transform_generators_base_info(data) + if isinstance(data, bpy.types.Strip): + path_x = path_add_property(base_path, "transform.scale_x") + path_y = path_add_property(base_path, "transform.scale_y") + if grouping: + ks.paths.add(id_block, path_x, group_method='NAMED', group_name=grouping) + ks.paths.add(id_block, path_y, group_method='NAMED', group_name=grouping) + else: + ks.paths.add(id_block, path_x) + ks.paths.add(id_block, path_y) + return # add the property name to the base path path = path_add_property(base_path, "scale") diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index f25914a8569..64025affcdc 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3234,6 +3234,11 @@ def km_sequencer_preview(params): ("sequencer.delete", {"type": 'X', "value": 'PRESS'}, None), ("sequencer.delete", {"type": 'DEL', "value": 'PRESS'}, None), + # Animation + ("anim.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None), + ("anim.keying_set_active_set", {"type": 'K', "value": 'PRESS', "shift": True}, None), + ("anim.keyframe_insert_menu", {"type": 'K', "value": 'PRESS'}, {"properties": [("always_prompt", True)]}), + *_template_items_context_menu("SEQUENCER_MT_preview_context_menu", params.context_menu_event), ]) diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index be2b67b44b6..3c37b9f186e 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -975,6 +975,17 @@ class SEQUENCER_MT_strip_show_hide(Menu): layout.operator("sequencer.mute", text="Hide Unselected").unselected = True +class SEQUENCER_MT_strip_animation(Menu): + bl_label = "Animation" + + def draw(self, _context): + layout = self.layout + + layout.operator("anim.keyframe_insert", text="Insert Keyframe") + 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...") + + class SEQUENCER_MT_strip_input(Menu): bl_label = "Inputs" @@ -1131,6 +1142,8 @@ class SEQUENCER_MT_strip(Menu): layout.separator() layout.operator("sequencer.preview_duplicate_move", text="Duplicate") layout.separator() + layout.menu("SEQUENCER_MT_strip_animation") + layout.separator() layout.menu("SEQUENCER_MT_strip_show_hide") layout.separator() if strip and strip.type == 'TEXT': @@ -3177,6 +3190,7 @@ classes = ( SEQUENCER_MT_strip_retiming, SEQUENCER_MT_strip_text, SEQUENCER_MT_strip_show_hide, + SEQUENCER_MT_strip_animation, SEQUENCER_MT_strip_input, SEQUENCER_MT_strip_lock_mute, SEQUENCER_MT_image, diff --git a/scripts/startup/keyingsets_builtins.py b/scripts/startup/keyingsets_builtins.py index 3e5b662cc8e..77c1937eede 100644 --- a/scripts/startup/keyingsets_builtins.py +++ b/scripts/startup/keyingsets_builtins.py @@ -207,7 +207,7 @@ class BUILTIN_KSI_VisualLoc(KeyingSetInfo): bl_options = {'INSERTKEY_VISUAL'} # poll - use predefined callback for selected bones/objects - poll = keyingsets_utils.RKS_POLL_selected_items + poll = keyingsets_utils.RKS_POLL_selected_bones_or_objects # iterator - use callback for selected bones/objects iterator = keyingsets_utils.RKS_ITER_selected_item @@ -225,7 +225,7 @@ class BUILTIN_KSI_VisualRot(KeyingSetInfo): bl_options = {'INSERTKEY_VISUAL'} # poll - use predefined callback for selected bones/objects - poll = keyingsets_utils.RKS_POLL_selected_items + poll = keyingsets_utils.RKS_POLL_selected_bones_or_objects # iterator - use callback for selected bones/objects iterator = keyingsets_utils.RKS_ITER_selected_item @@ -243,7 +243,7 @@ class BUILTIN_KSI_VisualScaling(KeyingSetInfo): bl_options = {'INSERTKEY_VISUAL'} # poll - use predefined callback for selected bones/objects - poll = keyingsets_utils.RKS_POLL_selected_items + poll = keyingsets_utils.RKS_POLL_selected_bones_or_objects # iterator - use callback for selected bones/objects iterator = keyingsets_utils.RKS_ITER_selected_item @@ -261,7 +261,7 @@ class BUILTIN_KSI_VisualLocRot(KeyingSetInfo): bl_options = {'INSERTKEY_VISUAL'} # poll - use predefined callback for selected bones/objects - poll = keyingsets_utils.RKS_POLL_selected_items + poll = keyingsets_utils.RKS_POLL_selected_bones_or_objects # iterator - use callback for selected bones/objects iterator = keyingsets_utils.RKS_ITER_selected_item @@ -283,7 +283,7 @@ class BUILTIN_KSI_VisualLocScale(KeyingSetInfo): bl_options = {'INSERTKEY_VISUAL'} # poll - use predefined callback for selected bones/objects - poll = keyingsets_utils.RKS_POLL_selected_items + poll = keyingsets_utils.RKS_POLL_selected_bones_or_objects # iterator - use callback for selected bones/objects iterator = keyingsets_utils.RKS_ITER_selected_item @@ -305,7 +305,7 @@ class BUILTIN_KSI_VisualLocRotScale(KeyingSetInfo): bl_options = {'INSERTKEY_VISUAL'} # poll - use predefined callback for selected bones/objects - poll = keyingsets_utils.RKS_POLL_selected_items + poll = keyingsets_utils.RKS_POLL_selected_bones_or_objects # iterator - use callback for selected bones/objects iterator = keyingsets_utils.RKS_ITER_selected_item @@ -329,7 +329,7 @@ class BUILTIN_KSI_VisualRotScale(KeyingSetInfo): bl_options = {'INSERTKEY_VISUAL'} # poll - use predefined callback for selected bones/objects - poll = keyingsets_utils.RKS_POLL_selected_items + poll = keyingsets_utils.RKS_POLL_selected_bones_or_objects # iterator - use callback for selected bones/objects iterator = keyingsets_utils.RKS_ITER_selected_item diff --git a/source/blender/editors/animation/keyframing.cc b/source/blender/editors/animation/keyframing.cc index fa7f5428ac3..5bdb021c50b 100644 --- a/source/blender/editors/animation/keyframing.cc +++ b/source/blender/editors/animation/keyframing.cc @@ -10,6 +10,8 @@ #include +#include "DNA_sequence_types.h" +#include "ED_sequencer.hh" #include "MEM_guardedalloc.h" #include "BLI_string.h" @@ -207,6 +209,7 @@ static wmOperatorStatus insert_key_with_keyingset(bContext *C, wmOperator *op, K if (num_channels > 0) { /* send notifiers that keyframes have been changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); } if (confirm) { @@ -231,6 +234,25 @@ static blender::Vector construct_rna_paths(PointerRNA *ptr) eRotationModes rotation_mode; blender::Vector paths; + if (ptr->type == &RNA_Strip || RNA_struct_is_a(ptr->type, &RNA_Strip)) { + eKeyInsertChannels insert_channel_flags = eKeyInsertChannels(U.key_insert_channels); + if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_LOCATION) { + paths.append({"transform.offset_x"}); + paths.append({"transform.offset_y"}); + } + if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_ROTATION) { + paths.append({"transform.rotation"}); + } + if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_SCALE) { + paths.append({"transform.scale_x"}); + paths.append({"transform.scale_y"}); + } + if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES) { + paths.extend(blender::animrig::get_keyable_id_property_paths(*ptr)); + } + return paths; + } + if (ptr->type == &RNA_PoseBone) { bPoseChannel *pchan = static_cast(ptr->data); rotation_mode = eRotationModes(pchan->rotmode); @@ -284,6 +306,17 @@ static blender::Vector construct_rna_paths(PointerRNA *ptr) static bool get_selection(bContext *C, blender::Vector *r_selection) { const eContextObjectMode context_mode = CTX_data_mode_enum(C); + ScrArea *area = CTX_wm_area(C); + + if (area && area->spacetype == SPACE_SEQ) { + blender::VectorSet strips = blender::ed::vse::selected_strips_from_context(C); + for (Strip *strip : strips) { + PointerRNA ptr; + ptr = RNA_pointer_create_discrete(&CTX_data_scene(C)->id, &RNA_Strip, strip); + r_selection->append(ptr); + } + return true; + } switch (context_mode) { case CTX_MODE_OBJECT: { @@ -366,6 +399,7 @@ static wmOperatorStatus insert_key(bContext *C, wmOperator *op) } WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; }