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]},