From 1122a05cb67849ec64c624694fd687d358143018 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 25 Aug 2025 11:58:17 +0200 Subject: [PATCH] VSE: Scene Selector & Scene Time Synchronization Implements the proposed design (with some modifications) in #135058. ## Sequencer Scene This adds a new property called `sequencer_scene` to workspaces. This scene is used by the video sequence editors in the current workspace for their context. This is a first step towards "detaching" the VSE from the active scene in the window. Each sequencer timeline editor shows the sequencer scene that is being used. By default, when no sequencer scene is selected, the timeline and preview are empty. Pressing the "new" button will add a new scene and assign it to the sequencer scene for the current workspace. ## Contextual Playback Pressing `Space` (by default) for starting the animation playback is now contextual: depending on the context (where your mouse cursor is), the scene that is played back might be different. E.g. with a 3D Viewport and a Sequencer open, pressing "play" in the 3D Viewport will play the _active scene_ of the window, while pressing "play" in the sequencer will play the _sequencer scene_. ## Time & Scene Synchronization Additionally, this adds a toggle called "Sync Active Scene". With the property turned on, the active scene & scene time in the window will be synced with the time & scene of the current scene strip in the sequencer. Note that this is _not_ bi-directional. The sequencer can change the active scene and map time, but it's not possible the other way around since it one can have multiple strips using the same scene (+camera, and even time!). Currently this setting is exposed in the footer of the sequencer timeline as well as in the workspace settings. This allows for one of the core concepts that the story tools projects aims at: Working in a scene (e.g. in the 3D viewport) while also working with the edit (in the sequencer timeline). ## Some technical notes * Undoing while playback is running will now cancel playback. This is to avoid the timer, that points to the scene and viewlayer that are playing, to get de-synced after loading the memfile undo step. * When the sequencer scene is not the same as the active scene, we ensure it has a depsgraph. * Normally, when a `NC_SCENE` notifier points to a specific scene, the notifier is dropped if that scene doesn't match the active one in the window. We now also check that it doesn't match the sequencer scene in the active workspace. * When loading older files, we need to make sure that the active workspace in a window uses the active scene as the sequencer scene. This is to make sure that the file opens with the same sequences open. * Tool settings are stored per scene. To make sure the sequencer uses the tool settings for the sequencer scene, the "context.tool_settings" and `CTX_data_tool_settings` members are overridden in the sequence editors. Pull Request: https://projects.blender.org/blender/blender/pulls/140271 --- scripts/startup/bl_ui/properties_workspace.py | 2 + scripts/startup/bl_ui/space_sequencer.py | 114 +++++---- scripts/startup/bl_ui/space_time.py | 70 +++--- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/context.cc | 13 +- source/blender/blenkernel/intern/workspace.cc | 1 + .../blenloader/intern/versioning_500.cc | 14 ++ .../blender/editors/animation/anim_markers.cc | 101 +++++--- source/blender/editors/animation/anim_ops.cc | 35 ++- source/blender/editors/include/ED_screen.hh | 6 +- .../editors/include/ED_screen_types.hh | 7 + .../blender/editors/include/ED_sequencer.hh | 3 + .../editors/interface/view2d/view2d_draw.cc | 3 +- .../blender/editors/render/render_opengl.cc | 3 + source/blender/editors/scene/scene_edit.cc | 52 ++++ .../blender/editors/screen/screen_context.cc | 59 +++-- source/blender/editors/screen/screen_edit.cc | 10 +- source/blender/editors/screen/screen_ops.cc | 91 +++++-- .../blender/editors/screen/workspace_edit.cc | 1 + .../editors/space_sequencer/sequencer_add.cc | 2 +- .../sequencer_channels_draw.cc | 6 +- .../editors/space_sequencer/sequencer_edit.cc | 226 ++++++++++++++++-- .../space_sequencer/sequencer_preview_draw.cc | 2 +- .../space_sequencer/sequencer_retiming.cc | 16 +- .../space_sequencer/sequencer_text_edit.cc | 14 +- .../sequencer_timeline_draw.cc | 36 ++- .../space_sequencer/space_sequencer.cc | 31 ++- .../transform/transform_convert_sequencer.cc | 7 +- .../editors/transform/transform_generics.cc | 12 +- source/blender/editors/util/ed_util.cc | 10 + source/blender/makesdna/DNA_scene_defaults.h | 1 + source/blender/makesdna/DNA_workspace_types.h | 5 + source/blender/makesrna/RNA_enum_types.hh | 8 +- source/blender/makesrna/intern/rna_context.cc | 10 + source/blender/makesrna/intern/rna_scene.cc | 13 +- .../blender/makesrna/intern/rna_sequencer.cc | 11 +- .../blender/makesrna/intern/rna_workspace.cc | 19 ++ source/blender/sequencer/SEQ_iterator.hh | 11 + source/blender/sequencer/intern/iterator.cc | 25 ++ source/blender/sequencer/intern/sequencer.cc | 2 +- .../blender/windowmanager/intern/wm_draw.cc | 6 +- .../windowmanager/intern/wm_event_system.cc | 18 +- .../windowmanager/intern/wm_operators.cc | 12 +- tests/python/bl_blendfile_relationships.py | 2 +- 44 files changed, 866 insertions(+), 226 deletions(-) diff --git a/scripts/startup/bl_ui/properties_workspace.py b/scripts/startup/bl_ui/properties_workspace.py index a3760fb6382..50b401e9c03 100644 --- a/scripts/startup/bl_ui/properties_workspace.py +++ b/scripts/startup/bl_ui/properties_workspace.py @@ -36,6 +36,8 @@ class WORKSPACE_PT_main(WorkSpaceButtonsPanel, Panel): layout.prop(workspace, "use_pin_scene") layout.prop(workspace, "object_mode", text="Mode") + layout.prop(workspace, "sequencer_scene") + layout.prop(workspace, "use_scene_time_sync") class WORKSPACE_PT_addons(WorkSpaceButtonsPanel, Panel): diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index e47db0b31a2..b02458c2e6e 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -179,24 +179,29 @@ class SEQUENCER_HT_header(Header): layout.separator_spacer() tool_settings = context.tool_settings - sequencer_tool_settings = tool_settings.sequencer_tool_settings + sequencer_tool_settings = tool_settings.sequencer_tool_settings if tool_settings else None - if st.view_type == 'PREVIEW': + if st.view_type == 'SEQUENCER': + row = layout.row(align=True) + row.template_ID(context.workspace, "sequencer_scene", new="scene.new_sequencer_scene") + + if sequencer_tool_settings and st.view_type == 'PREVIEW': layout.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True) - if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: + if sequencer_tool_settings and st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: row = layout.row(align=True) row.prop(sequencer_tool_settings, "overlap_mode", text="") - row = layout.row(align=True) - row.prop(tool_settings, "use_snap_sequencer", text="") - sub = row.row(align=True) - sub.popover(panel="SEQUENCER_PT_snapping") - if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: + if tool_settings: row = layout.row(align=True) - row.prop(tool_settings, "use_snap_playhead", text="") + row.prop(tool_settings, "use_snap_sequencer", text="") sub = row.row(align=True) - sub.popover(panel="SEQUENCER_PT_playhead_snapping", text="") + sub.popover(panel="SEQUENCER_PT_snapping") + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: + row = layout.row(align=True) + row.prop(tool_settings, "use_snap_playhead", text="") + sub = row.row(align=True) + sub.popover(panel="SEQUENCER_PT_playhead_snapping", text="") layout.separator_spacer() if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: @@ -243,7 +248,7 @@ class SEQUENCER_MT_editor_menus(Menu): layout.menu("SEQUENCER_MT_view") layout.menu("SEQUENCER_MT_select") - if has_sequencer: + if has_sequencer and context.sequencer_scene: if st.show_markers: layout.menu("SEQUENCER_MT_marker") layout.menu("SEQUENCER_MT_add") @@ -295,10 +300,10 @@ class SEQUENCER_PT_preview_overlay(Panel): @classmethod def poll(cls, context): st = context.space_data - return st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} + return st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} and context.sequencer_scene def draw(self, context): - ed = context.scene.sequence_editor + ed = context.sequencer_scene.sequence_editor st = context.space_data overlay_settings = st.preview_overlay layout = self.layout @@ -501,12 +506,12 @@ class SEQUENCER_MT_view(Menu): # See above (#32595) layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.operator("sequencer.view_selected", text="Frame Selected") - if is_sequencer_view: + if is_sequencer_view and context.sequencer_scene: layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("sequencer.view_all") layout.operator( "anim.scene_range_frame", - text="Frame Preview Range" if context.scene.use_preview_range else "Frame Scene Range", + text="Frame Preview Range" if context.sequencer_scene.use_preview_range else "Frame Scene Range", ) layout.operator("sequencer.view_frame") layout.prop(st, "use_clamp_view") @@ -602,8 +607,9 @@ class SEQUENCER_MT_select(Menu): st = context.space_data has_sequencer, has_preview = _space_view_types(st) is_retiming = ( - context.scene.sequence_editor is not None and - context.scene.sequence_editor.selected_retiming_keys + context.sequencer_scene and + context.sequencer_scene.sequence_editor is not None and + context.sequencer_scene.sequence_editor.selected_retiming_keys ) if has_preview: layout.operator_context = 'INVOKE_REGION_PREVIEW' @@ -811,7 +817,7 @@ class SEQUENCER_MT_add_scene(Menu): layout.operator("sequencer.scene_strip_add", text="Scene...", icon='SCENE_DATA') elif bpy_data_scenes_len > 1: layout.label(text="Scenes", icon='NONE') - scene = context.scene + scene = context.sequencer_scene for sc_item in bpy.data.scenes: if sc_item == scene: continue @@ -1125,8 +1131,9 @@ class SEQUENCER_MT_strip_retiming(Menu): layout = self.layout is_retiming = ( - context.scene.sequence_editor is not None and - context.scene.sequence_editor.selected_retiming_keys + context.sequencer_scene and + context.sequencer_scene.sequence_editor is not None and + context.sequencer_scene.sequence_editor.selected_retiming_keys ) strip = context.active_strip @@ -1440,7 +1447,7 @@ class SEQUENCER_MT_context_menu(Menu): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' - if context.scene.sequence_editor.selected_retiming_keys: + if context.sequencer_scene.sequence_editor.selected_retiming_keys: layout.operator("sequencer.retiming_add_freeze_frame_slide") layout.operator("sequencer.retiming_add_transition_slide") layout.separator() @@ -1451,7 +1458,7 @@ class SEQUENCER_MT_context_menu(Menu): layout.operator("sequencer.retiming_key_delete", text="Delete Retiming Keys") def draw(self, context): - ed = context.scene.sequence_editor + ed = context.sequencer_scene.sequence_editor if ed.selected_retiming_keys: self.draw_retime(context) @@ -1482,12 +1489,13 @@ class SEQUENCER_MT_pivot_pie(Menu): layout = self.layout pie = layout.menu_pie() - sequencer_tool_settings = context.tool_settings.sequencer_tool_settings + if context.tool_settings: + sequencer_tool_settings = context.tool_settings.sequencer_tool_settings - pie.prop_enum(sequencer_tool_settings, "pivot_point", value='CENTER') - pie.prop_enum(sequencer_tool_settings, "pivot_point", value='CURSOR') - pie.prop_enum(sequencer_tool_settings, "pivot_point", value='INDIVIDUAL_ORIGINS') - pie.prop_enum(sequencer_tool_settings, "pivot_point", value='MEDIAN') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='CENTER') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='CURSOR') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='INDIVIDUAL_ORIGINS') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='MEDIAN') class SEQUENCER_MT_view_pie(Menu): @@ -1500,7 +1508,7 @@ class SEQUENCER_MT_view_pie(Menu): pie.operator("sequencer.view_all") pie.operator("sequencer.view_selected", text="Frame Selected", icon='ZOOM_SELECTED') pie.separator() - if context.scene.use_preview_range: + if context.sequencer_scene.use_preview_range: pie.operator("anim.scene_range_frame", text="Frame Preview Range") else: pie.operator("anim.scene_range_frame", text="Frame Scene Range") @@ -1989,7 +1997,7 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False - scene = context.scene + scene = context.sequencer_scene strip = context.active_strip strip_type = strip.type @@ -2273,7 +2281,7 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel): layout.use_property_split = False layout.use_property_decorate = False - scene = context.scene + scene = context.sequencer_scene frame_current = scene.frame_current strip = context.active_strip @@ -2449,7 +2457,7 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): layout.use_property_split = True col = layout.column() - audio_channels = context.scene.render.ffmpeg.audio_channels + audio_channels = context.sequencer_scene.render.ffmpeg.audio_channels pan_enabled = sound.use_mono and audio_channels != 'MONO' pan_text = "{:.2f}°".format(strip.pan * 90.0) @@ -2631,14 +2639,14 @@ class SEQUENCER_PT_cache_settings(SequencerButtonsPanel, Panel): @classmethod def poll(cls, context): - return cls.has_sequencer(context) and context.scene.sequence_editor + return cls.has_sequencer(context) and context.sequencer_scene and context.sequencer_scene.sequence_editor def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False - ed = context.scene.sequence_editor + ed = context.sequencer_scene.sequence_editor col = layout.column() if ed: @@ -2657,7 +2665,7 @@ class SEQUENCER_PT_cache_view_settings(SequencerButtonsPanel, Panel): @classmethod def poll(cls, context): - return cls.has_sequencer(context) and context.scene.sequence_editor + return cls.has_sequencer(context) and context.sequencer_scene and context.sequencer_scene.sequence_editor def draw_header(self, context): cache_settings = context.space_data.cache_overlay @@ -2670,7 +2678,7 @@ class SEQUENCER_PT_cache_view_settings(SequencerButtonsPanel, Panel): layout.use_property_decorate = False cache_settings = context.space_data.cache_overlay - ed = context.scene.sequence_editor + ed = context.sequencer_scene.sequence_editor layout.active = cache_settings.show_cache col = layout.column(heading="Cache", align=True) @@ -2713,14 +2721,14 @@ class SEQUENCER_PT_proxy_settings(SequencerButtonsPanel, Panel): @classmethod def poll(cls, context): - return cls.has_sequencer(context) and context.scene.sequence_editor + return cls.has_sequencer(context) and context.sequencer_scene and context.sequencer_scene.sequence_editor def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False - ed = context.scene.sequence_editor + ed = context.sequencer_scene.sequence_editor flow = layout.column_flow() flow.prop(ed, "proxy_storage", text="Storage") @@ -2738,7 +2746,7 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): @classmethod def poll(cls, context): - if not cls.has_sequencer(context) and context.scene.sequence_editor: + if not cls.has_sequencer(context) or not context.sequencer_scene or not context.sequencer_scene.sequence_editor: return False strip = context.active_strip @@ -2757,7 +2765,7 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False - ed = context.scene.sequence_editor + ed = context.sequencer_scene.sequence_editor strip = context.active_strip @@ -2800,12 +2808,16 @@ class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel): bl_options = {'DEFAULT_CLOSED'} bl_category = "View" + @classmethod + def poll(cls, context): + return SequencerButtonsPanel_Output.poll(context) and context.sequencer_scene + def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False - render = context.scene.render + render = context.sequencer_scene.render col = layout.column() col.prop(render, "sequencer_gl_preview", text="Shading") @@ -2867,12 +2879,12 @@ class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): @classmethod def poll(cls, context): - if not context.scene.sequence_editor: + if not context.sequencer_scene or not context.sequencer_scene.sequence_editor: return False return SequencerButtonsPanel_Output.poll(context) def draw_header(self, context): - scene = context.scene + scene = context.sequencer_scene ed = scene.sequence_editor self.layout.prop(ed, "show_overlay_frame", text="") @@ -2888,7 +2900,7 @@ class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): layout.use_property_decorate = False st = context.space_data - scene = context.scene + scene = context.sequencer_scene ed = scene.sequence_editor layout.active = ed.show_overlay_frame @@ -2908,7 +2920,7 @@ class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel): def poll(cls, context): st = context.space_data is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} - return is_preview and (st.display_mode == 'IMAGE') + return is_preview and (st.display_mode == 'IMAGE') and context.sequencer_scene def draw_header(self, context): overlay_settings = context.space_data.preview_overlay @@ -2918,7 +2930,7 @@ class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel): layout = self.layout layout.use_property_split = True overlay_settings = context.space_data.preview_overlay - safe_data = context.scene.safe_areas + safe_data = context.sequencer_scene.safe_areas layout.active = overlay_settings.show_safe_areas @@ -2935,6 +2947,10 @@ class SEQUENCER_PT_view_safe_areas_center_cut(SequencerButtonsPanel_Output, Pane bl_options = {'DEFAULT_CLOSED'} bl_category = "View" + @classmethod + def poll(cls, context): + return SequencerButtonsPanel_Output.poll(context) and context.sequencer_scene + def draw_header(self, context): layout = self.layout overlay_settings = context.space_data.preview_overlay @@ -2944,7 +2960,7 @@ class SEQUENCER_PT_view_safe_areas_center_cut(SequencerButtonsPanel_Output, Pane def draw(self, context): layout = self.layout layout.use_property_split = True - safe_data = context.scene.safe_areas + safe_data = context.sequencer_scene.safe_areas overlay_settings = context.space_data.preview_overlay layout.active = overlay_settings.show_safe_areas and overlay_settings.show_safe_center @@ -2963,7 +2979,7 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel): layout.use_property_split = True strip = context.active_strip - ed = context.scene.sequence_editor + ed = context.sequencer_scene.sequence_editor if strip.type == 'SOUND': sound = strip.sound else: @@ -3154,7 +3170,7 @@ class SEQUENCER_PT_preview_snapping(Panel): @classmethod def poll(cls, context): st = context.space_data - return st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} + return st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} and context.tool_settings def draw(self, context): tool_settings = context.tool_settings @@ -3179,7 +3195,7 @@ class SEQUENCER_PT_sequencer_snapping(Panel): @classmethod def poll(cls, context): st = context.space_data - return st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'} + return st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'} and context.tool_settings def draw(self, context): tool_settings = context.tool_settings diff --git a/scripts/startup/bl_ui/space_time.py b/scripts/startup/bl_ui/space_time.py index 70e2ba06abe..f40af0ce0c3 100644 --- a/scripts/startup/bl_ui/space_time.py +++ b/scripts/startup/bl_ui/space_time.py @@ -8,7 +8,10 @@ from bpy.app.translations import contexts as i18n_contexts def playback_controls(layout, context): - scene = context.scene + st = context.space_data + is_sequencer = st.type == 'SEQUENCE_EDITOR' and st.view_type == 'SEQUENCER' + + scene = context.scene if not is_sequencer else context.sequencer_scene tool_settings = context.tool_settings screen = context.screen @@ -23,16 +26,20 @@ def playback_controls(layout, context): text_ctxt=i18n_contexts.id_windowmanager, ) + if is_sequencer: + layout.prop(context.workspace, "use_scene_time_sync", text="Sync Scene Time") + layout.separator_spacer() - row = layout.row(align=True) - row.prop(tool_settings, "use_keyframe_insert_auto", text="", toggle=True) - sub = row.row(align=True) - sub.active = tool_settings.use_keyframe_insert_auto - sub.popover( - panel="TIME_PT_auto_keyframing", - text="", - ) + if tool_settings: + row = layout.row(align=True) + row.prop(tool_settings, "use_keyframe_insert_auto", text="", toggle=True) + sub = row.row(align=True) + sub.active = tool_settings.use_keyframe_insert_auto + sub.popover( + panel="TIME_PT_auto_keyframing", + text="", + ) row = layout.row(align=True) row.operator("screen.frame_jump", text="", icon='REW').end = False @@ -42,7 +49,7 @@ def playback_controls(layout, context): # if using JACK and A/V sync: # hide the play-reversed button # since JACK transport doesn't support reversed playback - if scene.sync_mode == 'AUDIO_SYNC' and context.preferences.system.audio_device == 'JACK': + if scene and scene.sync_mode == 'AUDIO_SYNC' and context.preferences.system.audio_device == 'JACK': row.scale_x = 2 row.operator("screen.animation_play", text="", icon='PLAY') row.scale_x = 1 @@ -59,24 +66,25 @@ def playback_controls(layout, context): layout.separator_spacer() - row = layout.row() - if scene.show_subframe: - row.scale_x = 1.15 - row.prop(scene, "frame_float", text="") - else: - row.scale_x = 0.95 - row.prop(scene, "frame_current", text="") + if scene: + row = layout.row() + if scene.show_subframe: + row.scale_x = 1.15 + row.prop(scene, "frame_float", text="") + else: + row.scale_x = 0.95 + row.prop(scene, "frame_current", text="") - row = layout.row(align=True) - row.prop(scene, "use_preview_range", text="", toggle=True) - sub = row.row(align=True) - sub.scale_x = 0.8 - if not scene.use_preview_range: - sub.prop(scene, "frame_start", text="Start") - sub.prop(scene, "frame_end", text="End") - else: - sub.prop(scene, "frame_preview_start", text="Start") - sub.prop(scene, "frame_preview_end", text="End") + row = layout.row(align=True) + row.prop(scene, "use_preview_range", text="", toggle=True) + sub = row.row(align=True) + sub.scale_x = 0.8 + if not scene.use_preview_range: + sub.prop(scene, "frame_start", text="Start") + sub.prop(scene, "frame_end", text="End") + else: + sub.prop(scene, "frame_preview_start", text="Start") + sub.prop(scene, "frame_preview_end", text="End") class TIME_MT_editor_menus(Menu): @@ -206,7 +214,9 @@ class TIME_PT_playback(TimelinePanelButtons, Panel): layout.use_property_decorate = False screen = context.screen - scene = context.scene + st = context.space_data + is_sequencer = st.type == 'SEQUENCE_EDITOR' and st.view_type == 'SEQUENCER' + scene = context.scene if not is_sequencer else context.sequencer_scene layout.prop(scene, "sync_mode", text="Sync") col = layout.column(heading="Audio") @@ -246,7 +256,9 @@ class TIME_PT_keyframing_settings(TimelinePanelButtons, Panel): def draw(self, context): layout = self.layout - scene = context.scene + st = context.space_data + is_sequencer = st.type == 'SEQUENCE_EDITOR' and st.view_type == 'SEQUENCER' + scene = context.scene if not is_sequencer else context.sequencer_scene tool_settings = context.tool_settings col = layout.column(align=True) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 705ba3eda30..7bd627e5c4d 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 62 +#define BLENDER_FILE_SUBVERSION 63 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/intern/context.cc b/source/blender/blenkernel/intern/context.cc index bd8df56cfc3..daa09e1f135 100644 --- a/source/blender/blenkernel/intern/context.cc +++ b/source/blender/blenkernel/intern/context.cc @@ -1164,8 +1164,11 @@ Scene *CTX_data_sequencer_scene(const bContext *C) if (ctx_data_pointer_verify(C, "sequencer_scene", (void **)&scene)) { return scene; } - /* TODO: Use sequencer scene. */ - return C->data.scene; + WorkSpace *workspace = CTX_wm_workspace(C); + if (workspace) { + return workspace->sequencer_scene; + } + return nullptr; } ViewLayer *CTX_data_view_layer(const bContext *C) @@ -1371,8 +1374,12 @@ void CTX_data_scene_set(bContext *C, Scene *scene) ToolSettings *CTX_data_tool_settings(const bContext *C) { - Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings; + if (ctx_data_pointer_verify(C, "tool_settings", (void **)&toolsettings)) { + return toolsettings; + } + Scene *scene = CTX_data_scene(C); if (scene) { return scene->toolsettings; } diff --git a/source/blender/blenkernel/intern/workspace.cc b/source/blender/blenkernel/intern/workspace.cc index 1e8736c094e..8f28ffa5bd9 100644 --- a/source/blender/blenkernel/intern/workspace.cc +++ b/source/blender/blenkernel/intern/workspace.cc @@ -71,6 +71,7 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) WorkSpace *workspace = (WorkSpace *)id; BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, workspace->pin_scene, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, workspace->sequencer_scene, IDWALK_CB_NOP); LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER); diff --git a/source/blender/blenloader/intern/versioning_500.cc b/source/blender/blenloader/intern/versioning_500.cc index 2f5f7ec9d7c..110e5f43c25 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -22,6 +22,8 @@ #include "DNA_rigidbody_types.h" #include "DNA_screen_types.h" #include "DNA_sequence_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "DNA_world_types.h" #include "BLI_listbase.h" @@ -59,6 +61,8 @@ #include "SEQ_modifier.hh" #include "SEQ_sequencer.hh" +#include "WM_api.hh" + #include "readfile.hh" #include "versioning_common.hh" @@ -1676,6 +1680,16 @@ void do_versions_after_linking_500(FileData *fd, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 63)) { + LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + Scene *scene = WM_window_get_active_scene(win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + workspace->sequencer_scene = scene; + } + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/editors/animation/anim_markers.cc b/source/blender/editors/animation/anim_markers.cc index c373230b296..f05f6d475a3 100644 --- a/source/blender/editors/animation/anim_markers.cc +++ b/source/blender/editors/animation/anim_markers.cc @@ -65,6 +65,9 @@ ListBase *ED_scene_markers_get(Scene *scene, ScrArea *area) { + if (!scene) { + return nullptr; + } /* local marker sets... */ if (area) { if (area->spacetype == SPACE_ACTION) { @@ -577,7 +580,9 @@ static int markers_frame_sort(const void *a, const void *b) void ED_markers_draw(const bContext *C, int flag) { - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); if (markers == nullptr || BLI_listbase_is_empty(markers)) { return; } @@ -688,7 +693,9 @@ void ED_markers_draw(const bContext *C, int flag) /* special poll() which checks if there are selected markers first */ static bool ed_markers_poll_selected_markers(bContext *C) { - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); if (!operator_markers_region_active(C)) { return false; @@ -705,7 +712,9 @@ static bool ed_markers_poll_selected_markers(bContext *C) static bool ed_markers_poll_selected_no_locked_markers(bContext *C) { - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); ToolSettings *ts = CTX_data_tool_settings(C); if (!operator_markers_region_active(C)) { @@ -729,7 +738,9 @@ static bool ed_markers_poll_selected_no_locked_markers(bContext *C) /* special poll() which checks if there are any markers at all first */ static bool ed_markers_poll_markers_exist(bContext *C) { - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); ToolSettings *ts = CTX_data_tool_settings(C); if (ts->lock_markers || !operator_markers_region_active(C)) { @@ -765,14 +776,17 @@ static bool ed_markers_poll_markers_exist_visible(bContext *C) /* add TimeMarker at current frame */ static wmOperatorStatus ed_marker_add_exec(bContext *C, wmOperator * /*op*/) { - ListBase *markers = ED_context_get_markers(C); - TimeMarker *marker; - int frame = CTX_data_scene(C)->r.cfra; + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); - if (markers == nullptr) { + if (markers == nullptr || scene == nullptr) { return OPERATOR_CANCELLED; } + const int frame = scene->r.cfra; + /* prefer not having 2 markers at the same place, * though the user can move them to overlap once added */ LISTBASE_FOREACH (TimeMarker *, marker, markers) { @@ -786,7 +800,7 @@ static wmOperatorStatus ed_marker_add_exec(bContext *C, wmOperator * /*op*/) marker->flag &= ~SELECT; } - marker = MEM_callocN("TimeMarker"); + TimeMarker *marker = MEM_callocN("TimeMarker"); marker->flag = SELECT; marker->frame = frame; SNPRINTF_UTF8(marker->name, "F_%02d", frame); @@ -868,7 +882,8 @@ static bool ed_marker_move_use_time(MarkerMove *mm) static void ed_marker_move_update_header(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); MarkerMove *mm = static_cast(op->customdata); TimeMarker *marker, *selmarker = nullptr; const int ofs = RNA_int_get(op->ptr, "frames"); @@ -916,8 +931,10 @@ static void ed_marker_move_update_header(bContext *C, wmOperator *op) /* return 0 if not OK */ static bool ed_marker_move_init(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); MarkerMove *mm; TimeMarker *marker; int a, totmark; @@ -982,7 +999,9 @@ static wmOperatorStatus ed_marker_move_invoke(bContext *C, wmOperator *op, const if (tweak) { ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); if (!region_position_is_over_marker(v2d, markers, event->xy[0] - region->winrct.xmin)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } @@ -1014,7 +1033,8 @@ static wmOperatorStatus ed_marker_move_invoke(bContext *C, wmOperator *op, const static void ed_marker_move_apply(bContext *C, wmOperator *op) { bScreen *screen = CTX_wm_screen(C); - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); Object *camera = scene->camera; MarkerMove *mm = static_cast(op->customdata); TimeMarker *marker; @@ -1052,7 +1072,8 @@ static void ed_marker_move_cancel(bContext *C, wmOperator *op) static wmOperatorStatus ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *event) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); MarkerMove *mm = static_cast(op->customdata); View2D *v2d = UI_view2d_fromcontext(C); const bool has_numinput = hasNumInput(&mm->num); @@ -1204,7 +1225,9 @@ static void MARKER_OT_move(wmOperatorType *ot) /* duplicate selected TimeMarkers */ static void ed_marker_duplicate_apply(bContext *C) { - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); if (markers == nullptr) { return; } @@ -1293,7 +1316,8 @@ static void select_marker_camera_switch( using namespace blender::ed; if (camera) { BLI_assert(CTX_data_mode_enum(C) == CTX_MODE_OBJECT); - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Base *base; int sel = 0; @@ -1344,7 +1368,9 @@ static wmOperatorStatus ed_marker_select(bContext *C, * The variables (`sel_op` & `deselect_all`) have been included so marker * selection can use identical checks to dope-sheet selection. */ - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); const View2D *v2d = UI_view2d_fromcontext(C); wmOperatorStatus ret_val = OPERATOR_FINISHED; TimeMarker *nearest_marker = region_position_is_over_marker(v2d, markers, mval[0]); @@ -1496,7 +1522,9 @@ static wmOperatorStatus ed_marker_box_select_invoke(bContext *C, ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); bool over_marker = region_position_is_over_marker( v2d, markers, event->xy[0] - region->winrct.xmin) != nullptr; @@ -1511,7 +1539,9 @@ static wmOperatorStatus ed_marker_box_select_invoke(bContext *C, static wmOperatorStatus ed_marker_box_select_exec(bContext *C, wmOperator *op) { View2D *v2d = UI_view2d_fromcontext(C); - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); rctf rect; WM_operator_properties_border_to_rctf(op, &rect); @@ -1574,7 +1604,9 @@ static void MARKER_OT_select_box(wmOperatorType *ot) static wmOperatorStatus ed_marker_select_all_exec(bContext *C, wmOperator *op) { - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); if (markers == nullptr) { return OPERATOR_CANCELLED; } @@ -1696,7 +1728,9 @@ static void MARKER_OT_select_leftright(wmOperatorType *ot) static wmOperatorStatus ed_marker_delete_exec(bContext *C, wmOperator * /*op*/) { - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); TimeMarker *marker, *nmarker; bool changed = false; @@ -1767,7 +1801,10 @@ static void MARKER_OT_delete(wmOperatorType *ot) static wmOperatorStatus ed_marker_rename_exec(bContext *C, wmOperator *op) { - TimeMarker *marker = ED_markers_get_first_selected(ED_context_get_markers(C)); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); + TimeMarker *marker = ED_markers_get_first_selected(markers); if (marker) { RNA_string_get(op->ptr, "name", marker->name); @@ -1783,8 +1820,11 @@ static wmOperatorStatus ed_marker_rename_exec(bContext *C, wmOperator *op) static wmOperatorStatus ed_marker_rename_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); /* must initialize the marker name first if there is a marker selected */ - TimeMarker *marker = ED_markers_get_first_selected(ED_context_get_markers(C)); + TimeMarker *marker = ED_markers_get_first_selected(markers); if (marker) { RNA_string_set(op->ptr, "name", marker->name); } @@ -1829,7 +1869,9 @@ static void MARKER_OT_rename(wmOperatorType *ot) static wmOperatorStatus ed_marker_make_links_scene_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - ListBase *markers = ED_context_get_markers(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); Scene *scene_to = static_cast( BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene"))); TimeMarker *marker_new; @@ -1895,9 +1937,14 @@ static void MARKER_OT_make_links_scene(wmOperatorType *ot) static wmOperatorStatus ed_marker_camera_bind_exec(bContext *C, wmOperator *op) { bScreen *screen = CTX_wm_screen(C); - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + if (!scene) { + return OPERATOR_CANCELLED; + } + ListBase *markers = is_sequencer ? ED_sequencer_context_get_markers(C) : + ED_context_get_markers(C); Object *ob = CTX_data_active_object(C); - ListBase *markers = ED_context_get_markers(C); TimeMarker *marker; /* Don't do anything if we don't have a camera selected */ diff --git a/source/blender/editors/animation/anim_ops.cc b/source/blender/editors/animation/anim_ops.cc index 45b420ac2cd..996edac5d28 100644 --- a/source/blender/editors/animation/anim_ops.cc +++ b/source/blender/editors/animation/anim_ops.cc @@ -527,7 +527,11 @@ static float apply_frame_snap(bContext *C, FrameChangeModalData &op_data, const /* Set the new frame number */ static void change_frame_apply(bContext *C, wmOperator *op, const bool always_update) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + if (!scene) { + return; + } float frame = RNA_float_get(op->ptr, "frame"); bool do_snap = RNA_boolean_get(op->ptr, "snap"); @@ -550,6 +554,8 @@ static void change_frame_apply(bContext *C, wmOperator *op, const bool always_up } FRAMENUMBER_MIN_CLAMP(scene->r.cfra); + blender::ed::vse::sync_active_scene_and_time_with_scene_strip(*C); + /* do updates */ const bool frame_changed = (old_frame != scene->r.cfra) || (old_subframe != scene->r.subframe); if (frame_changed || always_update) { @@ -574,7 +580,8 @@ static wmOperatorStatus change_frame_exec(bContext *C, wmOperator *op) static float frame_from_event(bContext *C, const wmEvent *event) { ARegion *region = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); float frame; /* convert from region coordinates to View2D 'tot' space */ @@ -625,7 +632,7 @@ static bool use_playhead_snapping(bContext *C) static bool sequencer_skip_for_handle_tweak(const bContext *C, const wmEvent *event) { - const Scene *scene = CTX_data_scene(C); + Scene *scene = CTX_data_sequencer_scene(C); if (!blender::seq::editing_get(scene)) { return false; } @@ -859,7 +866,8 @@ static bool anim_set_end_frames_poll(bContext *C) static wmOperatorStatus anim_set_sfra_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); int frame; if (scene == nullptr) { @@ -914,7 +922,8 @@ static void ANIM_OT_start_frame_set(wmOperatorType *ot) static wmOperatorStatus anim_set_efra_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); int frame; if (scene == nullptr) { @@ -975,7 +984,11 @@ static void ANIM_OT_end_frame_set(wmOperatorType *ot) static wmOperatorStatus previewrange_define_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + if (!scene) { + return OPERATOR_CANCELLED; + } ARegion *region = CTX_wm_region(C); float sfra, efra; rcti rect; @@ -1039,7 +1052,8 @@ static void ANIM_OT_previewrange_set(wmOperatorType *ot) static wmOperatorStatus previewrange_clear_exec(bContext *C, wmOperator * /*op*/) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); ScrArea *curarea = CTX_wm_area(C); /* sanity checks */ @@ -1134,10 +1148,13 @@ static void ANIM_OT_debug_channel_list(wmOperatorType *ot) static wmOperatorStatus scene_range_frame_exec(bContext *C, wmOperator * /*op*/) { + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + const Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + if (!scene) { + return OPERATOR_CANCELLED; + } ARegion *region = CTX_wm_region(C); - const Scene *scene = CTX_data_scene(C); BLI_assert(region); - BLI_assert(scene); View2D &v2d = region->v2d; v2d.cur.xmin = PSFRA; diff --git a/source/blender/editors/include/ED_screen.hh b/source/blender/editors/include/ED_screen.hh index 5fa45516f77..f607f37b5d6 100644 --- a/source/blender/editors/include/ED_screen.hh +++ b/source/blender/editors/include/ED_screen.hh @@ -342,7 +342,8 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen); * redraws: uses defines from `stime->redraws` * \param enable: 1 - forward on, -1 - backwards on, 0 - off. */ -void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable); +void ED_screen_animation_timer( + bContext *C, Scene *scene, ViewLayer *view_layer, int redraws, int sync, int enable); void ED_screen_animation_timer_update(bScreen *screen, int redraws); void ED_screen_restore_temp_type(bContext *C, ScrArea *area); ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type); @@ -547,6 +548,9 @@ bool ED_operator_regionactive(bContext *C); bool ED_operator_scene(bContext *C); bool ED_operator_scene_editable(bContext *C); +bool ED_operator_sequencer_scene(bContext *C); +bool ED_operator_sequencer_scene_editable(bContext *C); + bool ED_operator_objectmode(bContext *C); /** * Same as #ED_operator_objectmode() but additionally sets a "disabled hint". That is, a message diff --git a/source/blender/editors/include/ED_screen_types.hh b/source/blender/editors/include/ED_screen_types.hh index c28979cb18d..5e15363e8df 100644 --- a/source/blender/editors/include/ED_screen_types.hh +++ b/source/blender/editors/include/ED_screen_types.hh @@ -20,6 +20,13 @@ struct ARegion; struct ScreenAnimData { /** Do not read from this, only for comparing if region exists. */ ARegion *region; + + /* The Scene and the View Layer that the animation timer is playing. */ + Scene *scene; + ViewLayer *view_layer; + /* For sequencer scenes, account for scene syncing during playback. */ + bool do_scene_syncing; + short redraws; /** Flags for playback */ short flag; diff --git a/source/blender/editors/include/ED_sequencer.hh b/source/blender/editors/include/ED_sequencer.hh index ebdd4883467..016756764ef 100644 --- a/source/blender/editors/include/ED_sequencer.hh +++ b/source/blender/editors/include/ED_sequencer.hh @@ -81,4 +81,7 @@ StripSelection pick_strip_and_handle(const struct Scene *scene, bool can_select_handle(const Scene *scene, const Strip *strip, const View2D *v2d); bool handle_is_selected(const Strip *strip, eStripHandle handle); +bool is_scene_time_sync_needed(const bContext &C); +void sync_active_scene_and_time_with_scene_strip(bContext &C); + } // namespace blender::ed::vse diff --git a/source/blender/editors/interface/view2d/view2d_draw.cc b/source/blender/editors/interface/view2d/view2d_draw.cc index f4e7f2b1372..59d1bde8aa7 100644 --- a/source/blender/editors/interface/view2d/view2d_draw.cc +++ b/source/blender/editors/interface/view2d/view2d_draw.cc @@ -92,7 +92,8 @@ static float view2d_major_step_y__continuous(const View2D *v2d) static float view2d_major_step_x__time(const View2D *v2d, const Scene *scene) { - const double fps = scene->frames_per_second(); + /* If we don't have a scene available, pick an arbitrary framerate to show *something*. */ + const double fps = scene ? scene->frames_per_second() : 25; blender::Vector possible_distances; diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 6cc8f2c030d..e7c2f49d922 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -710,6 +710,9 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) const bool is_sequencer = RNA_boolean_get(op->ptr, "sequencer"); Scene *scene = !is_sequencer ? CTX_data_scene(C) : CTX_data_sequencer_scene(C); + if (!scene) { + return false; + } ScrArea *prev_area = CTX_wm_area(C); ARegion *prev_region = CTX_wm_region(C); GPUOffScreen *ofs; diff --git a/source/blender/editors/scene/scene_edit.cc b/source/blender/editors/scene/scene_edit.cc index ead9ae6ed6b..1b2b5ae77fc 100644 --- a/source/blender/editors/scene/scene_edit.cc +++ b/source/blender/editors/scene/scene_edit.cc @@ -35,6 +35,7 @@ #include "SEQ_relations.hh" #include "SEQ_select.hh" +#include "SEQ_sequencer.hh" #include "RNA_access.hh" #include "RNA_define.hh" @@ -112,6 +113,14 @@ bool ED_scene_delete(bContext *C, Main *bmain, Scene *scene) } } + /* Update scenes used by the sequencer. */ + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + if (workspace->sequencer_scene == scene) { + workspace->sequencer_scene = scene_new; + WM_event_add_notifier(C, NC_WINDOW, nullptr); + } + } + BKE_id_delete(bmain, scene); return true; @@ -351,6 +360,48 @@ static void SCENE_OT_new_sequencer(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name New Sequencer Scene Operator + * \{ */ + +static wmOperatorStatus new_sequencer_scene_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + wmWindow *win = CTX_wm_window(C); + Scene *scene_old = WM_window_get_active_scene(win); + int type = RNA_enum_get(op->ptr, "type"); + + Scene *new_scene = scene_add(bmain, scene_old, eSceneCopyMethod(type)); + blender::seq::editing_ensure(new_scene); + + WorkSpace *workspace = CTX_wm_workspace(C); + workspace->sequencer_scene = new_scene; + + WM_event_add_notifier(C, NC_WINDOW, nullptr); + return OPERATOR_FINISHED; +} + +static void SCENE_OT_new_sequencer_scene(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Sequencer Scene"; + ot->description = "Add new scene to be used by the sequencer"; + ot->idname = "SCENE_OT_new_sequencer_scene"; + + /* API callbacks. */ + ot->exec = new_sequencer_scene_exec; + ot->invoke = WM_menu_invoke; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", scene_new_items, SCE_COPY_NEW, "Type", ""); + RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_SCENE); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Scene Delete Operator * \{ */ @@ -444,6 +495,7 @@ void ED_operatortypes_scene() WM_operatortype_append(SCENE_OT_new); WM_operatortype_append(SCENE_OT_delete); WM_operatortype_append(SCENE_OT_new_sequencer); + WM_operatortype_append(SCENE_OT_new_sequencer_scene); WM_operatortype_append(SCENE_OT_drop_scene_asset); } diff --git a/source/blender/editors/screen/screen_context.cc b/source/blender/editors/screen/screen_context.cc index 0027a521913..548759c758e 100644 --- a/source/blender/editors/screen/screen_context.cc +++ b/source/blender/editors/screen/screen_context.cc @@ -117,6 +117,7 @@ const char *screen_context_dir[] = { "strips", "selected_strips", "selected_editable_strips", + "sequencer_scene", nullptr, }; @@ -673,8 +674,10 @@ static eContextResult screen_ctx_pose_object(const bContext *C, bContextDataResu static eContextResult screen_ctx_active_sequence_strip(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return CTX_RESULT_NO_DATA; + } Strip *strip = blender::seq::select_active_get(scene); if (strip) { CTX_data_pointer_set(result, &scene->id, &RNA_Strip, strip); @@ -684,8 +687,10 @@ static eContextResult screen_ctx_active_sequence_strip(const bContext *C, } static eContextResult screen_ctx_sequences(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return CTX_RESULT_NO_DATA; + } Editing *ed = blender::seq::editing_get(scene); if (ed) { LISTBASE_FOREACH (Strip *, strip, ed->current_strips()) { @@ -698,8 +703,10 @@ static eContextResult screen_ctx_sequences(const bContext *C, bContextDataResult } static eContextResult screen_ctx_selected_sequences(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return CTX_RESULT_NO_DATA; + } Editing *ed = blender::seq::editing_get(scene); if (ed) { LISTBASE_FOREACH (Strip *, strip, ed->current_strips()) { @@ -715,8 +722,10 @@ static eContextResult screen_ctx_selected_sequences(const bContext *C, bContextD static eContextResult screen_ctx_selected_editable_sequences(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return CTX_RESULT_NO_DATA; + } Editing *ed = blender::seq::editing_get(scene); if (ed == nullptr) { return CTX_RESULT_NO_DATA; @@ -1138,8 +1147,10 @@ static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult * static eContextResult screen_ctx_active_strip(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return CTX_RESULT_NO_DATA; + } Strip *strip = blender::seq::select_active_get(scene); if (strip) { CTX_data_pointer_set(result, &scene->id, &RNA_Strip, strip); @@ -1149,8 +1160,10 @@ static eContextResult screen_ctx_active_strip(const bContext *C, bContextDataRes } static eContextResult screen_ctx_strips(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return CTX_RESULT_NO_DATA; + } Editing *ed = blender::seq::editing_get(scene); if (ed) { LISTBASE_FOREACH (Strip *, strip, ed->current_strips()) { @@ -1163,8 +1176,10 @@ static eContextResult screen_ctx_strips(const bContext *C, bContextDataResult *r } static eContextResult screen_ctx_selected_strips(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return CTX_RESULT_NO_DATA; + } Editing *ed = blender::seq::editing_get(scene); if (ed) { LISTBASE_FOREACH (Strip *, strip, ed->current_strips()) { @@ -1180,8 +1195,10 @@ static eContextResult screen_ctx_selected_strips(const bContext *C, bContextData static eContextResult screen_ctx_selected_editable_strips(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return CTX_RESULT_NO_DATA; + } Editing *ed = blender::seq::editing_get(scene); if (ed == nullptr) { return CTX_RESULT_NO_DATA; @@ -1196,6 +1213,15 @@ static eContextResult screen_ctx_selected_editable_strips(const bContext *C, CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } +static eContextResult screen_ctx_sequencer_scene(const bContext *C, bContextDataResult *result) +{ + Scene *scene = CTX_data_sequencer_scene(C); + if (scene) { + CTX_data_id_pointer_set(result, &scene->id); + return CTX_RESULT_OK; + } + return CTX_RESULT_NO_DATA; +} /* Registry of context callback functions. */ @@ -1263,6 +1289,7 @@ ensure_ed_screen_context_functions() map.add("strips", screen_ctx_strips); map.add("selected_strips", screen_ctx_selected_strips); map.add("selected_editable_strips", screen_ctx_selected_editable_strips); + map.add("sequencer_scene", screen_ctx_sequencer_scene); return map; }(); return screen_context_functions; diff --git a/source/blender/editors/screen/screen_edit.cc b/source/blender/editors/screen/screen_edit.cc index a5cc26fc0eb..153f5b36f1d 100644 --- a/source/blender/editors/screen/screen_edit.cc +++ b/source/blender/editors/screen/screen_edit.cc @@ -40,6 +40,7 @@ #include "ED_node.hh" #include "ED_screen.hh" #include "ED_screen_types.hh" +#include "ED_sequencer.hh" #include "RNA_access.hh" #include "RNA_enum_types.hh" @@ -1948,12 +1949,12 @@ ScrArea *ED_screen_temp_space_open(bContext *C, return nullptr; } -void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable) +void ED_screen_animation_timer( + bContext *C, Scene *scene, ViewLayer *view_layer, int redraws, int sync, int enable) { bScreen *screen = CTX_wm_screen(C); wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); - Scene *scene = CTX_data_scene(C); bScreen *stopscreen = ED_screen_animation_playing(wm); if (stopscreen) { @@ -1967,6 +1968,11 @@ void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable) screen->animtimer = WM_event_timer_add(wm, win, TIMER0, (1.0 / scene->frames_per_second())); sad->region = CTX_wm_region(C); + sad->scene = scene; + sad->view_layer = view_layer; + + sad->do_scene_syncing = blender::ed::vse::is_scene_time_sync_needed(*C); + sad->sfra = scene->r.cfra; /* Make sure that were are inside the scene or preview frame range. */ CLAMP(scene->r.cfra, PSFRA, PEFRA); diff --git a/source/blender/editors/screen/screen_ops.cc b/source/blender/editors/screen/screen_ops.cc index df96b53677b..52516643b96 100644 --- a/source/blender/editors/screen/screen_ops.cc +++ b/source/blender/editors/screen/screen_ops.cc @@ -38,6 +38,7 @@ #include "BKE_fcurve.hh" #include "BKE_global.hh" #include "BKE_icons.h" +#include "BKE_layer.hh" #include "BKE_lib_id.hh" #include "BKE_library.hh" #include "BKE_main.hh" @@ -183,6 +184,15 @@ bool ED_operator_scene(bContext *C) return false; } +bool ED_operator_sequencer_scene(bContext *C) +{ + Scene *scene = CTX_data_sequencer_scene(C); + if (scene == nullptr || !BKE_id_is_editable(CTX_data_main(C), &scene->id)) { + return false; + } + return true; +} + bool ED_operator_scene_editable(bContext *C) { Scene *scene = CTX_data_scene(C); @@ -192,6 +202,15 @@ bool ED_operator_scene_editable(bContext *C) return true; } +bool ED_operator_sequencer_scene_editable(bContext *C) +{ + Scene *scene = CTX_data_sequencer_scene(C); + if (scene == nullptr || !BKE_id_is_editable(CTX_data_main(C), &scene->id)) { + return false; + } + return true; +} + bool ED_operator_objectmode(bContext *C) { Scene *scene = CTX_data_scene(C); @@ -380,12 +399,12 @@ bool ED_operator_graphedit_active(bContext *C) bool ED_operator_sequencer_active(bContext *C) { - return ed_spacetype_test(C, SPACE_SEQ); + return ed_spacetype_test(C, SPACE_SEQ) && CTX_data_sequencer_scene(C) != nullptr; } bool ED_operator_sequencer_active_editable(bContext *C) { - return ed_spacetype_test(C, SPACE_SEQ) && ED_operator_scene_editable(C); + return ed_spacetype_test(C, SPACE_SEQ) && ED_operator_sequencer_scene_editable(C); } bool ED_operator_image_active(bContext *C) @@ -3259,7 +3278,11 @@ void ED_areas_do_frame_follow(bContext *C, bool center_view) /* function to be called outside UI context, or for redo */ static wmOperatorStatus frame_offset_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + if (!scene) { + return OPERATOR_CANCELLED; + } int delta = RNA_int_get(op->ptr, "delta"); @@ -3274,6 +3297,8 @@ static wmOperatorStatus frame_offset_exec(bContext *C, wmOperator *op) ED_areas_do_frame_follow(C, false); + blender::ed::vse::sync_active_scene_and_time_with_scene_strip(*C); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -3306,7 +3331,11 @@ static void SCREEN_OT_frame_offset(wmOperatorType *ot) /* function to be called outside UI context, or for redo */ static wmOperatorStatus frame_jump_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + if (!scene) { + return OPERATOR_CANCELLED; + } wmTimer *animtimer = CTX_wm_screen(C)->animtimer; /* Don't change scene->r.cfra directly if animtimer is running as this can cause @@ -3335,6 +3364,8 @@ static wmOperatorStatus frame_jump_exec(bContext *C, wmOperator *op) ED_areas_do_frame_follow(C, true); + blender::ed::vse::sync_active_scene_and_time_with_scene_strip(*C); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -3547,7 +3578,11 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot) /* function to be called outside UI context, or for redo */ static wmOperatorStatus marker_jump_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + if (!scene) { + return OPERATOR_CANCELLED; + } int closest = scene->r.cfra; const bool next = RNA_boolean_get(op->ptr, "next"); bool found = false; @@ -3579,6 +3614,8 @@ static wmOperatorStatus marker_jump_exec(bContext *C, wmOperator *op) ED_areas_do_frame_follow(C, true); + blender::ed::vse::sync_active_scene_and_time_with_scene_strip(*C); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -5613,19 +5650,17 @@ static wmOperatorStatus screen_animation_step_invoke(bContext *C, return OPERATOR_PASS_THROUGH; } - wmWindow *win = CTX_wm_window(C); - #ifdef PROFILE_AUDIO_SYNC static int old_frame = 0; int newfra_int; #endif Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = WM_window_get_active_view_layer(win); + ScreenAnimData *sad = static_cast(wt->customdata); + Scene *scene = sad->scene; + ViewLayer *view_layer = sad->view_layer; Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); Scene *scene_eval = (depsgraph != nullptr) ? DEG_get_evaluated_scene(depsgraph) : nullptr; - ScreenAnimData *sad = static_cast(wt->customdata); wmWindowManager *wm = CTX_wm_manager(C); int sync; double time; @@ -5758,6 +5793,8 @@ static wmOperatorStatus screen_animation_step_invoke(bContext *C, #endif } + blender::ed::vse::sync_active_scene_and_time_with_scene_strip(*C); + /* Since we follow draw-flags, we can't send notifier but tag regions ourselves. */ if (depsgraph != nullptr) { ED_update_for_newframe(bmain, depsgraph); @@ -5894,17 +5931,41 @@ bScreen *ED_screen_animation_no_scrub(const wmWindowManager *wm) wmOperatorStatus ED_screen_animation_play(bContext *C, int sync, int mode) { bScreen *screen = CTX_wm_screen(C); - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); + if (!scene) { + return OPERATOR_CANCELLED; + } + Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = is_sequencer ? BKE_view_layer_default_render(scene) : + CTX_data_view_layer(C); + Depsgraph *depsgraph = is_sequencer ? BKE_scene_ensure_depsgraph(bmain, scene, view_layer) : + CTX_data_ensure_evaluated_depsgraph(C); Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Main *bmain = DEG_get_bmain(depsgraph); if (ED_screen_animation_playing(CTX_wm_manager(C))) { /* stop playback now */ - ED_screen_animation_timer(C, 0, 0, 0); + ED_screen_animation_timer(C, scene, view_layer, 0, 0, 0); ED_scene_fps_average_clear(scene); BKE_sound_stop_scene(scene_eval); + if (is_sequencer) { + /* Stop sound for active scene in window. */ + BKE_sound_stop_scene(DEG_get_evaluated_scene(CTX_data_ensure_evaluated_depsgraph(C))); + } + else { + /* Stop sound for sequencer scene. */ + WorkSpace *workspace = CTX_wm_workspace(C); + if (workspace->sequencer_scene) { + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph( + bmain, + workspace->sequencer_scene, + BKE_view_layer_default_render(workspace->sequencer_scene)); + Scene *seq_scene_eval = DEG_get_evaluated_scene(depsgraph); + BKE_sound_stop_scene(seq_scene_eval); + } + } + BKE_callback_exec_id_depsgraph( bmain, &scene->id, depsgraph, BKE_CB_EVT_ANIMATION_PLAYBACK_POST); @@ -5923,7 +5984,7 @@ wmOperatorStatus ED_screen_animation_play(bContext *C, int sync, int mode) BKE_sound_play_scene(scene_eval); } - ED_screen_animation_timer(C, screen->redraws_flag, sync, mode); + ED_screen_animation_timer(C, scene, view_layer, screen->redraws_flag, sync, mode); ED_scene_fps_average_clear(scene); if (screen->animtimer) { diff --git a/source/blender/editors/screen/workspace_edit.cc b/source/blender/editors/screen/workspace_edit.cc index 21c9693eb9e..1d4cd67b9a2 100644 --- a/source/blender/editors/screen/workspace_edit.cc +++ b/source/blender/editors/screen/workspace_edit.cc @@ -244,6 +244,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo workspace_new->flags = workspace_old->flags; workspace_new->pin_scene = workspace_old->pin_scene; + workspace_new->sequencer_scene = workspace_old->sequencer_scene; workspace_new->object_mode = workspace_old->object_mode; workspace_new->order = workspace_old->order; BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids); diff --git a/source/blender/editors/space_sequencer/sequencer_add.cc b/source/blender/editors/space_sequencer/sequencer_add.cc index c829889d3e7..68584ab8873 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.cc +++ b/source/blender/editors/space_sequencer/sequencer_add.cc @@ -691,7 +691,7 @@ void SEQUENCER_OT_scene_strip_add(wmOperatorType *ot) sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_MOVE); prop = RNA_def_enum(ot->srna, "scene", rna_enum_dummy_NULL_items, 0, "Scene", ""); - RNA_def_enum_funcs(prop, RNA_scene_without_active_itemf); + RNA_def_enum_funcs(prop, RNA_scene_without_sequencer_scene_itemf); RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); ot->prop = prop; } diff --git a/source/blender/editors/space_sequencer/sequencer_channels_draw.cc b/source/blender/editors/space_sequencer/sequencer_channels_draw.cc index 838585b0f98..b87815f9682 100644 --- a/source/blender/editors/space_sequencer/sequencer_channels_draw.cc +++ b/source/blender/editors/space_sequencer/sequencer_channels_draw.cc @@ -327,8 +327,12 @@ void channel_draw_context_init(const bContext *C, void draw_channels(const bContext *C, ARegion *region) { draw_background(); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return; + } - Editing *ed = seq::editing_get(CTX_data_sequencer_scene(C)); + Editing *ed = seq::editing_get(scene); if (ed == nullptr) { return; } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.cc b/source/blender/editors/space_sequencer/sequencer_edit.cc index f3940af02ff..0c80896ffbd 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.cc +++ b/source/blender/editors/space_sequencer/sequencer_edit.cc @@ -65,6 +65,7 @@ #include "ED_numinput.hh" #include "ED_scene.hh" #include "ED_screen.hh" +#include "ED_screen_types.hh" #include "ED_sequencer.hh" #include "UI_interface_layout.hh" @@ -160,7 +161,11 @@ bool has_playback_animation(const Scene *scene) bool sequencer_edit_poll(bContext *C) { - return (seq::editing_get(CTX_data_sequencer_scene(C)) != nullptr); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return false; + } + return (seq::editing_get(scene) != nullptr); } bool sequencer_edit_with_channel_region_poll(bContext *C) @@ -191,7 +196,7 @@ bool sequencer_strip_poll(bContext *C) bool sequencer_strip_editable_poll(bContext *C) { Scene *scene = CTX_data_sequencer_scene(C); - if (!ID_IS_EDITABLE(&scene->id)) { + if (!scene || !ID_IS_EDITABLE(&scene->id)) { return false; } Editing *ed = seq::editing_get(scene); @@ -200,19 +205,28 @@ bool sequencer_strip_editable_poll(bContext *C) bool sequencer_strip_has_path_poll(bContext *C) { - Editing *ed; - Strip *strip; - return (((ed = seq::editing_get(CTX_data_sequencer_scene(C))) != nullptr) && - ((strip = ed->act_strip) != nullptr) && STRIP_HAS_PATH(strip)); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return false; + } + Editing *ed = seq::editing_get(scene); + if (!ed) { + return false; + } + Strip *strip = ed->act_strip; + if (!strip) { + return false; + } + return STRIP_HAS_PATH(strip); } bool sequencer_view_has_preview_poll(bContext *C) { - SpaceSeq *sseq = CTX_wm_space_seq(C); - if (sseq == nullptr) { + if (!sequencer_edit_poll(C)) { return false; } - if (seq::editing_get(CTX_data_sequencer_scene(C)) == nullptr) { + SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq == nullptr) { return false; } if (!(ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW) && @@ -230,11 +244,16 @@ bool sequencer_view_has_preview_poll(bContext *C) bool sequencer_view_preview_only_poll(const bContext *C) { - SpaceSeq *sseq = CTX_wm_space_seq(C); - if (sseq == nullptr) { + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { return false; } - if (seq::editing_get(CTX_data_sequencer_scene(C)) == nullptr) { + Editing *ed = seq::editing_get(scene); + if (!ed) { + return false; + } + SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq == nullptr) { return false; } if (!(ELEM(sseq->view, SEQ_VIEW_PREVIEW) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF))) { @@ -266,6 +285,9 @@ bool sequencer_view_strips_poll(bContext *C) static bool sequencer_effect_poll(bContext *C) { + if (!sequencer_edit_poll(C)) { + return false; + } Scene *scene = CTX_data_sequencer_scene(C); Editing *ed = seq::editing_get(scene); @@ -281,6 +303,9 @@ static bool sequencer_effect_poll(bContext *C) static bool sequencer_swap_inputs_poll(bContext *C) { + if (!sequencer_edit_poll(C)) { + return false; + } Scene *scene = CTX_data_sequencer_scene(C); Strip *active_strip = seq::select_active_get(scene); @@ -293,6 +318,142 @@ static bool sequencer_swap_inputs_poll(bContext *C) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Scene syncing with current scene strip + * \{ */ + +bool is_scene_time_sync_needed(const bContext &C) +{ + WorkSpace *workspace = CTX_wm_workspace(&C); + if (!workspace || !workspace->sequencer_scene) { + return false; + } + if ((workspace->flags & WORKSPACE_SYNC_SCENE_TIME) == 0) { + return false; + } + SpaceSeq *sseq = CTX_wm_space_seq(&C); + if (!sseq) { + /* We only want to start syncing the time when we're in a sequence editor. + * Changing time in any other editor should just affect the active scene. */ + return false; + } + return true; +} + +static Scene *get_sequencer_scene_for_time_sync(const bContext &C) +{ + wmWindowManager *wm = CTX_wm_manager(&C); + bScreen *screen = ED_screen_animation_playing(wm); + if (screen && screen->animtimer) { + wmTimer *wt = screen->animtimer; + ScreenAnimData *sad = static_cast(wt->customdata); + if (sad->do_scene_syncing) { + return sad->scene; + } + /* If we're playing a scene that's not a sequence scene, don't try and sync. */ + return nullptr; + } + if (is_scene_time_sync_needed(C)) { + return CTX_data_sequencer_scene(&C); + } + return nullptr; +} + +void sync_active_scene_and_time_with_scene_strip(bContext &C) +{ + using namespace blender; + Scene *sequence_scene = get_sequencer_scene_for_time_sync(C); + if (!sequence_scene) { + return; + } + + wmWindow *win = CTX_wm_window(&C); + Scene *active_scene = WM_window_get_active_scene(win); + + Editing *ed = seq::editing_get(sequence_scene); + if (!ed) { + return; + } + ListBase *seqbase = seq::active_seqbase_get(ed); + const ListBase *channels = seq::channels_displayed_get(ed); + VectorSet query_strips = seq::query_strips_recursive_at_frame( + sequence_scene, seqbase, sequence_scene->r.cfra); + /* Ignore effect strips, sound strips and muted strips. */ + query_strips.remove_if([&](const Strip *strip) { + return (strip->type & STRIP_TYPE_EFFECT) != 0 || strip->type == STRIP_TYPE_SOUND_RAM || + seq::render_is_muted(channels, strip); + }); + Vector strips = query_strips.extract_vector(); + /* Sort strips by channel. */ + std::sort(strips.begin(), strips.end(), [](const Strip *a, const Strip *b) { + return a->channel > b->channel; + }); + /* Get the top-most scene strip. */ + const Strip *scene_strip = [&]() -> const Strip * { + for (const Strip *strip : strips) { + if (strip->type == STRIP_TYPE_SCENE) { + return strip; + } + } + return nullptr; + }(); + if (!scene_strip || !scene_strip->scene) { + /* No scene strip with scene found. Switch to pinned scene. */ + Main *bmain = CTX_data_main(&C); + WM_window_set_active_scene(bmain, &C, win, sequence_scene); + return; + } + if (active_scene != scene_strip->scene) { + /* Sync active scene in window. */ + Main *bmain = CTX_data_main(&C); + WM_window_set_active_scene(bmain, &C, win, scene_strip->scene); + active_scene = scene_strip->scene; + } + Object *camera = [&]() -> Object * { + if (scene_strip->scene_camera) { + return scene_strip->scene_camera; + } + return scene_strip->scene->camera; + }(); + if (camera) { + /* Sync camera in any 3D view that uses camera view. */ + PointerRNA camera_ptr = RNA_id_pointer_create(&camera->id); + bScreen *screen = WM_window_get_active_screen(win); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype != SPACE_VIEW3D) { + continue; + } + View3D *view3d = reinterpret_cast(sl); + if (view3d->camera == camera) { + continue; + } + PointerRNA view3d_ptr = RNA_pointer_create_discrete(&screen->id, &RNA_SpaceView3D, view3d); + RNA_pointer_set(&view3d_ptr, "camera", camera_ptr); + } + } + } + + /* Compute the scene time based on the scene strip. */ + const float frame_index = seq::give_frame_index( + sequence_scene, scene_strip, sequence_scene->r.cfra); + if (active_scene->r.flag & SCER_SHOW_SUBFRAME) { + active_scene->r.cfra = int(frame_index); + active_scene->r.subframe = frame_index - int(frame_index); + } + else { + active_scene->r.cfra = round_fl_to_int(frame_index); + active_scene->r.subframe = 0.0f; + } + FRAMENUMBER_MIN_CLAMP(active_scene->r.cfra); + + DEG_id_tag_update(&active_scene->id, ID_RECALC_FRAME_CHANGE); + WM_event_add_notifier(&C, NC_WINDOW, nullptr); + WM_event_add_notifier(&C, NC_SCENE | ND_FRAME, nullptr); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Remove Gaps Operator * \{ */ @@ -685,7 +846,8 @@ static wmOperatorStatus sequencer_slip_invoke(bContext *C, wmOperator *op, const return OPERATOR_RUNNING_MODAL; } -static void slip_strips_delta(wmOperator *op, Scene *scene, SlipData *data, const float delta) +static void slip_strips_delta( + bContext *C, wmOperator *op, Scene *scene, SlipData *data, const float delta) { float new_offset = data->prev_offset + delta; /* Calculate rounded whole frames between offsets, which cannot be determined from `delta` alone. @@ -719,6 +881,8 @@ static void slip_strips_delta(wmOperator *op, Scene *scene, SlipData *data, cons } } + sync_active_scene_and_time_with_scene_strip(*C); + RNA_float_set(op->ptr, "offset", new_offset); data->prev_offset = new_offset; } @@ -803,13 +967,14 @@ static wmOperatorStatus sequencer_slip_exec(bContext *C, wmOperator *op) float offset = RNA_float_get(op->ptr, "offset"); slip_apply_clamp(scene, data, &offset); - slip_strips_delta(op, scene, data, offset); + slip_strips_delta(C, op, scene, data, offset); + slip_cleanup(C, op, scene); return OPERATOR_FINISHED; } static void slip_handle_num_input( - const bContext *C, wmOperator *op, ScrArea *area, SlipData *data, Scene *scene) + bContext *C, wmOperator *op, ScrArea *area, SlipData *data, Scene *scene) { float offset; applyNumInput(&data->num_input, &offset); @@ -818,7 +983,7 @@ static void slip_handle_num_input( slip_update_header(scene, area, data, offset); RNA_float_set(op->ptr, "offset", offset); - slip_strips_delta(op, scene, data, offset_delta); + slip_strips_delta(C, op, scene, data, offset_delta); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); } @@ -843,7 +1008,7 @@ static wmOperatorStatus sequencer_slip_modal(bContext *C, wmOperator *op, const return OPERATOR_FINISHED; } case SLIP_MODAL_CANCEL: { - slip_strips_delta(op, scene, data, -data->prev_offset); + slip_strips_delta(C, op, scene, data, -data->prev_offset); slip_cleanup(C, op, scene); return OPERATOR_CANCELLED; } @@ -864,7 +1029,7 @@ static wmOperatorStatus sequencer_slip_modal(bContext *C, wmOperator *op, const /* If we exit precision mode, make sure we undo the fractional adjustments and align the * virtual mouse pointer. */ float to_nearest_frame = -(data->prev_offset - round_fl_to_int(data->prev_offset)); - slip_strips_delta(op, scene, data, to_nearest_frame); + slip_strips_delta(C, op, scene, data, to_nearest_frame); data->virtual_mval_x += to_nearest_frame * UI_view2d_scale_get_x(v2d); } break; @@ -899,7 +1064,7 @@ static wmOperatorStatus sequencer_slip_modal(bContext *C, wmOperator *op, const /* Also adjust virtual mouse pointer after clamp is applied. */ data->virtual_mval_x += (clamped_offset - offset) * UI_view2d_scale_get_x(v2d); - slip_strips_delta(op, scene, data, clamped_offset_delta); + slip_strips_delta(C, op, scene, data, clamped_offset_delta); slip_update_header(scene, area, data, clamped_offset); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -973,6 +1138,8 @@ static wmOperatorStatus sequencer_mute_exec(bContext *C, wmOperator *op) } } + sync_active_scene_and_time_with_scene_strip(*C); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1033,6 +1200,8 @@ static wmOperatorStatus sequencer_unmute_exec(bContext *C, wmOperator *op) } } + sync_active_scene_and_time_with_scene_strip(*C); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -3059,7 +3228,7 @@ void SEQUENCER_OT_change_scene(wmOperatorType *ot) /* Properties. */ prop = RNA_def_enum(ot->srna, "scene", rna_enum_dummy_NULL_items, 0, "Scene", ""); - RNA_def_enum_funcs(prop, RNA_scene_without_active_itemf); + RNA_def_enum_funcs(prop, RNA_scene_without_sequencer_scene_itemf); RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); ot->prop = prop; } @@ -3203,10 +3372,19 @@ static wmOperatorStatus sequencer_export_subtitles_exec(bContext *C, wmOperator static bool sequencer_strip_is_text_poll(bContext *C) { - Editing *ed; - Strip *strip; - return (((ed = seq::editing_get(CTX_data_sequencer_scene(C))) != nullptr) && - ((strip = ed->act_strip) != nullptr) && (strip->type == STRIP_TYPE_TEXT)); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return false; + } + Editing *ed = seq::editing_get(scene); + if (!ed) { + return false; + } + Strip *strip = ed->act_strip; + if (!strip) { + return false; + } + return strip->type == STRIP_TYPE_TEXT; } void SEQUENCER_OT_export_subtitles(wmOperatorType *ot) diff --git a/source/blender/editors/space_sequencer/sequencer_preview_draw.cc b/source/blender/editors/space_sequencer/sequencer_preview_draw.cc index abde9d92424..8647bffee02 100644 --- a/source/blender/editors/space_sequencer/sequencer_preview_draw.cc +++ b/source/blender/editors/space_sequencer/sequencer_preview_draw.cc @@ -1644,7 +1644,7 @@ void sequencer_preview_region_draw(const bContext *C, ARegion *region) const SpaceSeq &space_sequencer = *static_cast(area->spacedata.first); const Scene *scene = CTX_data_sequencer_scene(C); - if (!scene->ed || space_sequencer.render_size == SEQ_RENDER_SIZE_NONE) { + if (!scene || !scene->ed || space_sequencer.render_size == SEQ_RENDER_SIZE_NONE) { sequencer_preview_draw_empty(*region); return; } diff --git a/source/blender/editors/space_sequencer/sequencer_retiming.cc b/source/blender/editors/space_sequencer/sequencer_retiming.cc index 832cd7e9c85..e2d6cc80815 100644 --- a/source/blender/editors/space_sequencer/sequencer_retiming.cc +++ b/source/blender/editors/space_sequencer/sequencer_retiming.cc @@ -42,8 +42,12 @@ namespace blender::ed::vse { bool sequencer_retiming_mode_is_active(const bContext *C) { - Editing *ed = seq::editing_get(CTX_data_sequencer_scene(C)); - if (ed == nullptr) { + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return false; + } + Editing *ed = seq::editing_get(scene); + if (!ed) { return false; } return seq::retiming_selection_get(ed).size() > 0; @@ -129,8 +133,12 @@ void SEQUENCER_OT_retiming_show(wmOperatorType *ot) static bool retiming_poll(bContext *C) { - const Editing *ed = seq::editing_get(CTX_data_sequencer_scene(C)); - if (ed == nullptr) { + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return false; + } + Editing *ed = seq::editing_get(scene); + if (!ed) { return false; } Strip *strip = ed->act_strip; diff --git a/source/blender/editors/space_sequencer/sequencer_text_edit.cc b/source/blender/editors/space_sequencer/sequencer_text_edit.cc index 3afad031734..8addb855dbf 100644 --- a/source/blender/editors/space_sequencer/sequencer_text_edit.cc +++ b/source/blender/editors/space_sequencer/sequencer_text_edit.cc @@ -42,8 +42,12 @@ static bool sequencer_text_editing_poll(bContext *C) if (!sequencer_editing_initialized_and_active(C)) { return false; } + const Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return false; + } - const Strip *strip = seq::select_active_get(CTX_data_sequencer_scene(C)); + const Strip *strip = seq::select_active_get(scene); if (strip == nullptr || strip->type != STRIP_TYPE_TEXT || !seq::effects_can_render_text(strip)) { return false; } @@ -58,7 +62,11 @@ static bool sequencer_text_editing_poll(bContext *C) bool sequencer_text_editing_active_poll(bContext *C) { - const Strip *strip = seq::select_active_get(CTX_data_sequencer_scene(C)); + const Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return false; + } + const Strip *strip = seq::select_active_get(scene); if (strip == nullptr || !sequencer_text_editing_poll(C)) { return false; } @@ -67,8 +75,6 @@ bool sequencer_text_editing_active_poll(bContext *C) return false; } - const Scene *scene = CTX_data_sequencer_scene(C); - if (!seq::time_strip_intersects_frame(scene, strip, BKE_scene_frame_get(scene))) { return false; } diff --git a/source/blender/editors/space_sequencer/sequencer_timeline_draw.cc b/source/blender/editors/space_sequencer/sequencer_timeline_draw.cc index 95c545b9ea2..a5ec919f044 100644 --- a/source/blender/editors/space_sequencer/sequencer_timeline_draw.cc +++ b/source/blender/editors/space_sequencer/sequencer_timeline_draw.cc @@ -121,7 +121,7 @@ static TimelineDrawContext timeline_draw_context_get(const bContext *C, SeqQuads ctx.sseq = CTX_wm_space_seq(C); ctx.v2d = UI_view2d_fromcontext(C); - ctx.ed = seq::editing_get(ctx.scene); + ctx.ed = ctx.scene ? seq::editing_get(ctx.scene) : nullptr; ctx.channels = ctx.ed ? seq::channels_displayed_get(ctx.ed) : nullptr; ctx.viewport = WM_draw_region_get_viewport(ctx.region); @@ -1595,6 +1595,9 @@ static void draw_seq_strips(TimelineDrawContext *timeline_ctx, StripsDrawBatch & static void draw_timeline_sfra_efra(TimelineDrawContext *ctx) { const Scene *scene = ctx->scene; + if (!scene) { + return; + } const View2D *v2d = ctx->v2d; const Editing *ed = seq::editing_get(scene); const int frame_sta = scene->r.sfra; @@ -1834,6 +1837,9 @@ static void draw_timeline_markers(TimelineDrawContext *ctx) if ((ctx->sseq->flag & SEQ_SHOW_MARKERS) == 0) { return; } + if (ctx->scene == nullptr) { + return; + } UI_view2d_view_orthoSpecial(ctx->region, ctx->v2d, true); ED_markers_draw(ctx->C, DRAW_MARKERS_MARGIN); @@ -1880,17 +1886,26 @@ void draw_timeline_seq(const bContext *C, ARegion *region) draw_timeline_markers(&ctx); } UI_view2d_view_ortho(ctx.v2d); - ANIM_draw_previewrange(ctx.scene, ctx.v2d, 1); + if (ctx.scene) { + ANIM_draw_previewrange(ctx.scene, ctx.v2d, 1); + } draw_timeline_gizmos(&ctx); draw_timeline_post_view_callbacks(&ctx); - ED_time_scrub_draw(region, ctx.scene, !(ctx.sseq->flag & SEQ_DRAWFRAMES), true); + if (ctx.scene) { + ED_time_scrub_draw(region, ctx.scene, !(ctx.sseq->flag & SEQ_DRAWFRAMES), true); + } - seq_prefetch_wm_notify(C, ctx.scene); + if (ctx.scene) { + seq_prefetch_wm_notify(C, ctx.scene); + } } void draw_timeline_seq_display(const bContext *C, ARegion *region) { const Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return; + } const SpaceSeq *sseq = CTX_wm_space_seq(C); View2D *v2d = ®ion->v2d; @@ -1907,11 +1922,14 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region) region, scene, !(sseq->flag & SEQ_DRAWFRAMES), region->winy >= UI_ANIM_MINY); if (region->winy > UI_ANIM_MINY) { - const ListBase *seqbase = seq::active_seqbase_get(seq::editing_get(scene)); - seq::timeline_boundbox(scene, seqbase, &v2d->tot); - const rcti scroller_mask = ED_time_scrub_clamp_scroller_mask(v2d->mask); - region->v2d.scroll |= V2D_SCROLL_BOTTOM; - UI_view2d_scrollers_draw(v2d, &scroller_mask); + const Editing *ed = seq::editing_get(scene); + if (ed) { + const ListBase *seqbase = seq::active_seqbase_get(ed); + seq::timeline_boundbox(scene, seqbase, &v2d->tot); + const rcti scroller_mask = ED_time_scrub_clamp_scroller_mask(v2d->mask); + region->v2d.scroll |= V2D_SCROLL_BOTTOM; + UI_view2d_scrollers_draw(v2d, &scroller_mask); + } } else { region->v2d.scroll &= ~V2D_SCROLL_BOTTOM; diff --git a/source/blender/editors/space_sequencer/space_sequencer.cc b/source/blender/editors/space_sequencer/space_sequencer.cc index 3320fc498f5..c8c1dc3020e 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.cc +++ b/source/blender/editors/space_sequencer/space_sequencer.cc @@ -26,6 +26,7 @@ #include "BLF_api.hh" #include "BKE_global.hh" +#include "BKE_layer.hh" #include "BKE_lib_query.hh" #include "BKE_lib_remap.hh" #include "BKE_screen.hh" @@ -321,7 +322,7 @@ static void sequencer_listener(const wmSpaceTypeListenerParams *params) /* DO NOT make this static, this hides the symbol and breaks API generation script. */ extern "C" const char *sequencer_context_dir[]; /* Quiet warning. */ -const char *sequencer_context_dir[] = {"edit_mask", nullptr}; +const char *sequencer_context_dir[] = {"edit_mask", "tool_settings", nullptr}; static int /*eContextResult*/ sequencer_context(const bContext *C, const char *member, @@ -334,12 +335,20 @@ static int /*eContextResult*/ sequencer_context(const bContext *C, return CTX_RESULT_OK; } - if (CTX_data_equals(member, "edit_mask")) { - Mask *mask = seq::active_mask_get(scene); - if (mask) { - CTX_data_id_pointer_set(result, &mask->id); + if (CTX_data_equals(member, "tool_settings")) { + if (scene) { + CTX_data_pointer_set(result, &scene->id, &RNA_ToolSettings, scene->toolsettings); + return CTX_RESULT_OK; + } + } + if (CTX_data_equals(member, "edit_mask")) { + if (scene) { + Mask *mask = seq::active_mask_get(scene); + if (mask) { + CTX_data_id_pointer_set(result, &mask->id); + } + return CTX_RESULT_OK; } - return CTX_RESULT_OK; } return CTX_RESULT_MEMBER_NOT_FOUND; @@ -476,6 +485,9 @@ static void sequencer_main_clamp_view(const bContext *C, ARegion *region) View2D *v2d = ®ion->v2d; Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return; + } /* Transformation uses edge panning to move view. Also if smooth view is running, don't apply * clamping to prevent overriding this functionality. */ @@ -687,7 +699,12 @@ static void sequencer_main_cursor(wmWindow *win, ScrArea *area, ARegion *region) UI_view2d_region_to_view( ®ion->v2d, mouse_co_region[0], mouse_co_region[1], &mouse_co_view[0], &mouse_co_view[1]); - const Scene *scene = win->scene; + const WorkSpace *workspace = WM_window_get_active_workspace(win); + const Scene *scene = workspace->sequencer_scene; + if (!scene) { + WM_cursor_set(win, wmcursor); + return; + } const Editing *ed = seq::editing_get(scene); if (STREQ(tref->idname, "builtin.blade")) { diff --git a/source/blender/editors/transform/transform_convert_sequencer.cc b/source/blender/editors/transform/transform_convert_sequencer.cc index 55c48dd036f..797b83e7eb1 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.cc +++ b/source/blender/editors/transform/transform_convert_sequencer.cc @@ -560,9 +560,12 @@ static void create_trans_seq_clamp_data(TransInfo *t, const Scene *scene) } } -static void createTransSeqData(bContext * /*C*/, TransInfo *t) +static void createTransSeqData(bContext *C, TransInfo *t) { - Scene *scene = CTX_data_sequencer_scene(t->context); + Scene *scene = CTX_data_sequencer_scene(C); + if (!scene) { + return; + } Editing *ed = seq::editing_get(scene); TransData *td = nullptr; TransData2D *td2d = nullptr; diff --git a/source/blender/editors/transform/transform_generics.cc b/source/blender/editors/transform/transform_generics.cc index 510ae7720d1..22a4bddcd64 100644 --- a/source/blender/editors/transform/transform_generics.cc +++ b/source/blender/editors/transform/transform_generics.cc @@ -128,13 +128,21 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve ToolSettings *ts = CTX_data_tool_settings(C); ARegion *region = CTX_wm_region(C); ScrArea *area = CTX_wm_area(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + if (!is_sequencer) { + t->scene = sce; + t->view_layer = view_layer; + } + else { + t->scene = CTX_data_sequencer_scene(C); + t->view_layer = t->scene ? BKE_view_layer_default_render(t->scene) : nullptr; + } PropertyRNA *prop; t->mbus = CTX_wm_message_bus(C); t->depsgraph = CTX_data_depsgraph_pointer(C); - t->scene = sce; - t->view_layer = view_layer; + t->area = area; t->region = region; t->settings = ts; diff --git a/source/blender/editors/util/ed_util.cc b/source/blender/editors/util/ed_util.cc index a99cd8949bb..ce46f591ff3 100644 --- a/source/blender/editors/util/ed_util.cc +++ b/source/blender/editors/util/ed_util.cc @@ -41,6 +41,7 @@ #include "ED_object.hh" #include "ED_paint.hh" #include "ED_screen.hh" +#include "ED_screen_types.hh" #include "ED_sculpt.hh" #include "ED_space_api.hh" #include "ED_util.hh" @@ -70,6 +71,15 @@ void ED_editors_init_for_undo(Main *bmain) ED_paint_proj_mesh_data_check(*scene, *ob, nullptr, nullptr, nullptr, nullptr); } + /* Stop animation from playing. + * TODO: There might be a way to keep the animation from playing, but sad->scene and + * sad->view_layer pointers are outdated and would need to be updated somehow. */ + bScreen *animscreen = ED_screen_animation_playing(wm); + if (animscreen && animscreen->animtimer) { + WM_event_timer_remove(wm, win, animscreen->animtimer); + animscreen->animtimer = nullptr; + } + /* UI Updates. */ /* Flag local View3D's to check and exit if they are empty. */ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 4ce3a24a134..c5b33a90dc0 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -388,6 +388,7 @@ .snap_angle_increment_3d_precision = DEG2RADF(1.0f), \ .snap_angle_increment_2d_precision = DEG2RADF(1.0f), \ \ + .snap_flag_seq = SCE_SNAP, \ /* Weight Paint */ \ .weightuser = OB_DRAW_GROUPUSER_ACTIVE, \ \ diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index 5f45c8f5e42..58c08ca5c51 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -166,6 +166,9 @@ typedef struct WorkSpace { * link/append. */ struct Scene *pin_scene; + /* Scene that is used by the sequence editors in this workspace. */ + struct Scene *sequencer_scene; + char _pad[4]; int object_mode; @@ -248,4 +251,6 @@ typedef struct WorkSpaceInstanceHook { typedef enum eWorkSpaceFlags { WORKSPACE_USE_FILTER_BY_ORIGIN = (1 << 1), WORKSPACE_USE_PIN_SCENE = (1 << 2), + /* Used for syncing time between sequencer scene strips and the active scene. */ + WORKSPACE_SYNC_SCENE_TIME = (1 << 3), } eWorkSpaceFlags; diff --git a/source/blender/makesrna/RNA_enum_types.hh b/source/blender/makesrna/RNA_enum_types.hh index dc84c171f4e..db047425665 100644 --- a/source/blender/makesrna/RNA_enum_types.hh +++ b/source/blender/makesrna/RNA_enum_types.hh @@ -97,10 +97,10 @@ const EnumPropertyItem *RNA_scene_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *prop, bool *r_free); -const EnumPropertyItem *RNA_scene_without_active_itemf(bContext *C, - PointerRNA *ptr, - PropertyRNA *prop, - bool *r_free); +const EnumPropertyItem *RNA_scene_without_sequencer_scene_itemf(bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + bool *r_free); const EnumPropertyItem *RNA_scene_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *prop, diff --git a/source/blender/makesrna/intern/rna_context.cc b/source/blender/makesrna/intern/rna_context.cc index 3a1f2a3b935..59bfe24e190 100644 --- a/source/blender/makesrna/intern/rna_context.cc +++ b/source/blender/makesrna/intern/rna_context.cc @@ -192,6 +192,16 @@ static PointerRNA rna_Context_layer_collection_get(PointerRNA *ptr) static PointerRNA rna_Context_tool_settings_get(PointerRNA *ptr) { bContext *C = (bContext *)ptr->data; + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; + if (is_sequencer) { + Scene *scene = CTX_data_sequencer_scene(C); + if (scene) { + ToolSettings *toolsettings = scene->toolsettings; + return RNA_pointer_create_id_subdata( + *reinterpret_cast(scene), &RNA_ToolSettings, toolsettings); + } + return PointerRNA_NULL; + } return RNA_pointer_create_id_subdata( *reinterpret_cast(CTX_data_scene(C)), &RNA_ToolSettings, CTX_data_tool_settings(C)); } diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 7bfbe340866..78e9c7b16b4 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -775,6 +775,7 @@ static const EnumPropertyItem eevee_resolution_scale_items[] = { # include "ED_node.hh" # include "ED_render.hh" # include "ED_scene.hh" +# include "ED_sequencer.hh" # include "ED_uvedit.hh" # include "ED_view3d.hh" @@ -1149,6 +1150,14 @@ static void rna_Scene_show_subframe_update(Main * /*bmain*/, scene->r.subframe = 0.0f; } +static void rna_Scene_frame_update_context(bContext *C, PointerRNA *ptr) +{ + Scene *scene = (Scene *)ptr->owner_id; + blender::ed::vse::sync_active_scene_and_time_with_scene_strip(*C); + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); + WM_main_add_notifier(NC_SCENE | ND_FRAME, scene); +} + static void rna_Scene_frame_update(Main * /*bmain*/, Scene * /*current_scene*/, PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; @@ -3599,7 +3608,6 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_DEG_SYNC_ONLY); RNA_def_property_ui_text(prop, "Use Snapping", "Snap strips during transform"); RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1); - RNA_def_property_boolean_default(prop, true); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* Publish message-bus. */ prop = RNA_def_property(srna, "use_snap_uv", PROP_BOOLEAN, PROP_NONE); @@ -8657,7 +8665,8 @@ void RNA_def_scene(BlenderRNA *brna) prop, "Current Frame", "Current frame, to update animation data from Python frame_set() instead"); - RNA_def_property_update(prop, NC_SCENE | ND_FRAME, "rna_Scene_frame_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_FRAME, "rna_Scene_frame_update_context"); prop = RNA_def_property(srna, "frame_subframe", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, nullptr, "r.subframe"); diff --git a/source/blender/makesrna/intern/rna_sequencer.cc b/source/blender/makesrna/intern/rna_sequencer.cc index 31e9c1a0e60..f2b75b11b74 100644 --- a/source/blender/makesrna/intern/rna_sequencer.cc +++ b/source/blender/makesrna/intern/rna_sequencer.cc @@ -135,6 +135,8 @@ const EnumPropertyItem rna_enum_strip_scale_method_items[] = { # include "MOV_read.hh" +# include "ED_sequencer.hh" + # include "SEQ_add.hh" # include "SEQ_channels.hh" # include "SEQ_edit.hh" @@ -202,6 +204,12 @@ static void rna_Strip_invalidate_preprocessed_update(Main * /*bmain*/, } } +static void rna_Strip_mute_update(bContext *C, PointerRNA *ptr) +{ + blender::ed::vse::sync_active_scene_and_time_with_scene_strip(*C); + rna_Strip_invalidate_raw_update(nullptr, nullptr, ptr); +} + static void UNUSED_FUNCTION(rna_Strip_invalidate_composite_update)(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) @@ -2234,7 +2242,8 @@ static void rna_def_strip(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1); RNA_def_property_ui_text( prop, "Mute", "Disable strip so that it cannot be viewed in the output"); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Strip_invalidate_raw_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Strip_mute_update"); prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", SEQ_LOCK); diff --git a/source/blender/makesrna/intern/rna_workspace.cc b/source/blender/makesrna/intern/rna_workspace.cc index 4ad91216c22..1017cbb0545 100644 --- a/source/blender/makesrna/intern/rna_workspace.cc +++ b/source/blender/makesrna/intern/rna_workspace.cc @@ -33,6 +33,7 @@ # include "ED_asset.hh" # include "ED_paint.hh" +# include "ED_sequencer.hh" # include "RNA_access.hh" @@ -245,6 +246,11 @@ static int rna_WorkSpaceTool_widget_length(PointerRNA *ptr) return tref->runtime ? strlen(tref->runtime->gizmo_group) : 0; } +static void rna_workspace_sync_scene_time_update(bContext *C, PointerRNA * /*ptr*/) +{ + blender::ed::vse::sync_active_scene_and_time_with_scene_strip(*C); +} + #else /* RNA_RUNTIME */ static void rna_def_workspace_owner(BlenderRNA *brna) @@ -485,6 +491,19 @@ static void rna_def_workspace(BlenderRNA *brna) "(which has its own active asset library)"); RNA_def_property_update(prop, NC_ASSET | ND_ASSET_LIST_READING, nullptr); + prop = RNA_def_property(srna, "sequencer_scene", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "sequencer_scene"); + RNA_def_property_ui_text(prop, "Sequencer Scene", ""); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP); + RNA_def_property_update(prop, 0, "rna_window_update_all"); + + prop = RNA_def_property(srna, "use_scene_time_sync", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", WORKSPACE_SYNC_SCENE_TIME); + RNA_def_property_ui_text( + prop, "Sync Active Scene", "Set the active scene and time based on the current scene strip"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_WINDOW, "rna_workspace_sync_scene_time_update"); + RNA_api_workspace(srna); } diff --git a/source/blender/sequencer/SEQ_iterator.hh b/source/blender/sequencer/SEQ_iterator.hh index 38b11cdb3ea..16bc072b687 100644 --- a/source/blender/sequencer/SEQ_iterator.hh +++ b/source/blender/sequencer/SEQ_iterator.hh @@ -96,6 +96,17 @@ blender::VectorSet query_all_strips(ListBase *seqbase); */ blender::VectorSet query_all_strips_recursive(const ListBase *seqbase); +/** + * Query strips at \a timeline_frame in seqbase and nested meta strips. + * + * \param seqbase: ListBase in which strips are queried + * \param timeline_frame: viewed frame + * \return set of strips + */ +blender::VectorSet query_strips_recursive_at_frame(const Scene *scene, + const ListBase *seqbase, + int timeline_frame); + /** * Query all effect strips that are directly or indirectly connected to strip_reference. * This includes all effects of strip_reference, strips used by another inputs and their effects, diff --git a/source/blender/sequencer/intern/iterator.cc b/source/blender/sequencer/intern/iterator.cc index cadbb17c759..9cb21d2b93b 100644 --- a/source/blender/sequencer/intern/iterator.cc +++ b/source/blender/sequencer/intern/iterator.cc @@ -115,6 +115,31 @@ VectorSet query_all_strips_recursive(const ListBase *seqbase) return strips; } +static void query_strips_recursive_at_frame(const Scene *scene, + const ListBase *seqbase, + const int timeline_frame, + VectorSet &strips) +{ + LISTBASE_FOREACH (Strip *, strip, seqbase) { + if (!time_strip_intersects_frame(scene, strip, timeline_frame)) { + continue; + } + if (strip->type == STRIP_TYPE_META) { + query_strips_recursive_at_frame(scene, &strip->seqbase, timeline_frame, strips); + } + strips.add(strip); + } +} + +VectorSet query_strips_recursive_at_frame(const Scene *scene, + const ListBase *seqbase, + const int timeline_frame) +{ + VectorSet strips; + query_strips_recursive_at_frame(scene, seqbase, timeline_frame, strips); + return strips; +} + VectorSet query_all_strips(ListBase *seqbase) { VectorSet strips; diff --git a/source/blender/sequencer/intern/sequencer.cc b/source/blender/sequencer/intern/sequencer.cc index a813b94dd9e..20846db8343 100644 --- a/source/blender/sequencer/intern/sequencer.cc +++ b/source/blender/sequencer/intern/sequencer.cc @@ -354,7 +354,7 @@ SequencerToolSettings *tool_settings_init() tool_settings->snap_mode = SEQ_SNAP_TO_STRIPS | SEQ_SNAP_TO_CURRENT_FRAME | SEQ_SNAP_TO_STRIP_HOLD | SEQ_SNAP_TO_MARKERS | SEQ_SNAP_TO_RETIMING | SEQ_SNAP_TO_PREVIEW_BORDERS | SEQ_SNAP_TO_PREVIEW_CENTER | - SEQ_SNAP_TO_STRIPS_PREVIEW; + SEQ_SNAP_TO_STRIPS_PREVIEW | SEQ_SNAP_TO_FRAME_RANGE; tool_settings->snap_distance = 15; tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE; tool_settings->pivot_point = V3D_AROUND_LOCAL_ORIGINS; diff --git a/source/blender/windowmanager/intern/wm_draw.cc b/source/blender/windowmanager/intern/wm_draw.cc index 54cbcca2247..f2ebad05b13 100644 --- a/source/blender/windowmanager/intern/wm_draw.cc +++ b/source/blender/windowmanager/intern/wm_draw.cc @@ -990,8 +990,10 @@ static void wm_draw_area_offscreen(bContext *C, wmWindow *win, ScrArea *area, bo if ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) { if (area->spacetype == SPACE_SEQ) { Scene *scene = CTX_data_sequencer_scene(C); - WM_toolsystem_update_from_context( - C, CTX_wm_workspace(C), scene, BKE_view_layer_default_render(scene), area); + if (scene) { + WM_toolsystem_update_from_context( + C, CTX_wm_workspace(C), scene, BKE_view_layer_default_render(scene), area); + } } else { WM_toolsystem_update_from_context( diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 794ecb62e5d..c2d379152a9 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -40,6 +40,7 @@ #include "BKE_customdata.hh" #include "BKE_global.hh" #include "BKE_idprop.hh" +#include "BKE_layer.hh" #include "BKE_lib_remap.hh" #include "BKE_library.hh" #include "BKE_main.hh" @@ -510,6 +511,19 @@ void wm_event_do_depsgraph(bContext *C, bool is_after_open_file) Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); Main *bmain = CTX_data_main(C); + + /* Update dependency graph of sequencer scene. */ + Scene *sequencer_scene = CTX_data_sequencer_scene(C); + if (sequencer_scene && sequencer_scene != scene) { + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph( + bmain, sequencer_scene, BKE_view_layer_default_render(sequencer_scene)); + if (is_after_open_file) { + DEG_graph_relations_update(depsgraph); + DEG_tag_on_visible_update(bmain, depsgraph); + } + BKE_scene_graph_update_tagged(depsgraph, bmain); + } + /* Copied to set's in #scene_update_tagged_recursive(). */ scene->customdata_mask = win_combine_v3d_datamask; /* XXX, hack so operators can enforce data-masks #26482, GPU render. */ @@ -731,7 +745,9 @@ void wm_event_do_notifiers(bContext *C) { /* Pass. */ } - else if (note->category == NC_SCENE && note->reference && note->reference != scene) { + else if (note->category == NC_SCENE && note->reference && + (note->reference != scene && note->reference != workspace->sequencer_scene)) + { /* Pass. */ } else { diff --git a/source/blender/windowmanager/intern/wm_operators.cc b/source/blender/windowmanager/intern/wm_operators.cc index 4e858f04248..03eb1ec9078 100644 --- a/source/blender/windowmanager/intern/wm_operators.cc +++ b/source/blender/windowmanager/intern/wm_operators.cc @@ -4578,17 +4578,17 @@ const EnumPropertyItem *RNA_scene_local_itemf(bContext *C, return rna_id_itemf( r_free, C ? (ID *)CTX_data_main(C)->scenes.first : nullptr, true, nullptr, nullptr); } -const EnumPropertyItem *RNA_scene_without_active_itemf(bContext *C, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) +const EnumPropertyItem *RNA_scene_without_sequencer_scene_itemf(bContext *C, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + bool *r_free) { - Scene *scene_active = C ? CTX_data_scene(C) : nullptr; + Scene *sequencer_scene = C ? CTX_data_sequencer_scene(C) : nullptr; return rna_id_itemf(r_free, C ? (ID *)CTX_data_main(C)->scenes.first : nullptr, false, rna_id_enum_filter_single, - scene_active); + sequencer_scene); } const EnumPropertyItem *RNA_movieclip_itemf(bContext *C, PointerRNA * /*ptr*/, diff --git a/tests/python/bl_blendfile_relationships.py b/tests/python/bl_blendfile_relationships.py index 5de8787ab3e..c4859ec9660 100644 --- a/tests/python/bl_blendfile_relationships.py +++ b/tests/python/bl_blendfile_relationships.py @@ -46,7 +46,7 @@ class TestBlendUserMap(TestBlendLibLinkHelper): expected_map = { bpy.data.images[0]: {bpy.data.materials[0]}, bpy.data.materials[0]: {bpy.data.meshes[0]}, - bpy.data.scenes[0]: {bpy.data.window_managers[0]}, + bpy.data.scenes[0]: {bpy.data.window_managers[0], bpy.data.workspaces['Layout']}, bpy.data.collections[0]: {bpy.data.scenes[0]}, bpy.data.libraries[0]: set(), bpy.data.meshes[0]: {bpy.data.objects[0]},