From f385327442e93727c410e4e62f208663c5ec5a60 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 3 Oct 2025 17:33:17 +0200 Subject: [PATCH] VSE: Move Strip properties to Properties editor Goal of this PR is mainly to improve flexibility and of VSE layout and screen space efficiency. Previously, strip properties were displayed in timeline sidebar. This was limiting, because if you want to display as much properties as possible, the timeline area had to be taller, but this was at the expense of space available for preview. However there is plenty of space in properties editor, which is mostly unused in VSE. Therefore strip properties were moved to the properties editor. ID pinning and path displayed in top section aren't drawn, since strip is not an ID and can not be pinned. Since there is more space for properties, various panels are changed to be open by default. Mainly transform and time panels, since they are used often. There is one minor change: The waveform display property of sound strip was previously hidden, if timeline area had per strip waveform display overlay set. This is no longer possible to do and the property is always visible. Pull Request: https://projects.blender.org/blender/blender/pulls/140395 --- scripts/startup/bl_ui/__init__.py | 2 + scripts/startup/bl_ui/properties_strip.py | 1056 ++++++++++++++++ .../bl_ui/properties_strip_modifier.py | 56 + scripts/startup/bl_ui/space_properties.py | 6 +- scripts/startup/bl_ui/space_sequencer.py | 1067 ----------------- .../blenloader/intern/versioning_defaults.cc | 25 + .../editors/space_buttons/CMakeLists.txt | 1 + .../editors/space_buttons/buttons_context.cc | 78 +- .../editors/space_buttons/space_buttons.cc | 51 +- .../space_sequencer/space_sequencer.cc | 10 - source/blender/makesdna/DNA_space_enums.h | 2 + .../blender/makesrna/intern/rna_sequencer.cc | 1 + source/blender/makesrna/intern/rna_space.cc | 8 + .../sequencer/intern/modifiers/modifier.cc | 8 +- 14 files changed, 1265 insertions(+), 1106 deletions(-) create mode 100644 scripts/startup/bl_ui/properties_strip.py create mode 100644 scripts/startup/bl_ui/properties_strip_modifier.py diff --git a/scripts/startup/bl_ui/__init__.py b/scripts/startup/bl_ui/__init__.py index bdc032ee7d9..678e32e8a0d 100644 --- a/scripts/startup/bl_ui/__init__.py +++ b/scripts/startup/bl_ui/__init__.py @@ -60,6 +60,8 @@ _modules = [ "properties_texture", "properties_world", "properties_collection", + "properties_strip", + "properties_strip_modifier", "generic_ui_list", # Generic Space Modules diff --git a/scripts/startup/bl_ui/properties_strip.py b/scripts/startup/bl_ui/properties_strip.py new file mode 100644 index 00000000000..cc3e4301541 --- /dev/null +++ b/scripts/startup/bl_ui/properties_strip.py @@ -0,0 +1,1056 @@ +# SPDX-FileCopyrightText: 2009-2023 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import bpy +from bpy.types import ( + Panel, +) +from bpy.app.translations import ( + contexts as i18n_contexts, + pgettext_rpt as rpt_, +) +from rna_prop_ui import PropertyPanel + + +class StripButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "strip" + + @classmethod + def poll(cls, context): + return context.active_strip is not None + + +class StripColorTagPicker: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "none" # Used as popover + + @classmethod + def poll(cls, context): + return context.active_strip is not None + + +class STRIP_PT_color_tag_picker(StripColorTagPicker, Panel): + bl_label = "Color Tag" + bl_options = {'HIDE_HEADER'} + + def draw(self, _context): + layout = self.layout + + row = layout.row(align=True) + row.operator("sequencer.strip_color_tag_set", icon='X').color = 'NONE' + for i in range(1, 10): + icon = 'STRIP_COLOR_{:02d}'.format(i) + row.operator("sequencer.strip_color_tag_set", icon=icon).color = 'COLOR_{:02d}'.format(i) + + +class STRIP_PT_strip(StripButtonsPanel, Panel): + bl_label = "" + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + strip = context.active_strip + strip_type = strip.type + + if strip_type in { + 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER', 'MULTIPLY', + 'GLOW', 'SPEED', 'MULTICAM', 'GAUSSIAN_BLUR', 'COLORMIX', + }: + icon_header = 'SHADERFX' + elif strip_type in { + 'CROSS', 'GAMMA_CROSS', 'WIPE', + }: + icon_header = 'ARROW_LEFTRIGHT' + elif strip_type == 'SCENE': + icon_header = 'SCENE_DATA' + elif strip_type == 'MOVIECLIP': + icon_header = 'TRACKER' + elif strip_type == 'MASK': + icon_header = 'MOD_MASK' + elif strip_type == 'MOVIE': + icon_header = 'FILE_MOVIE' + elif strip_type == 'SOUND': + icon_header = 'FILE_SOUND' + elif strip_type == 'IMAGE': + icon_header = 'FILE_IMAGE' + elif strip_type == 'COLOR': + icon_header = 'COLOR' + elif strip_type == 'TEXT': + icon_header = 'FONT_DATA' + elif strip_type == 'ADJUSTMENT': + icon_header = 'COLOR' + elif strip_type == 'META': + icon_header = 'SEQ_STRIP_META' + else: + icon_header = 'SEQ_SEQUENCER' + + row = layout.row(align=True) + row.use_property_decorate = False + row.label(text="", icon=icon_header) + row.separator() + row.prop(strip, "name", text="") + + sub = row.row(align=True) + if strip.color_tag == 'NONE': + sub.popover(panel="STRIP_PT_color_tag_picker", text="", icon='COLOR') + else: + icon = 'STRIP_' + strip.color_tag + sub.popover(panel="STRIP_PT_color_tag_picker", text="", icon=icon) + + row.separator() + row.prop(strip, "mute", toggle=True, icon_only=True, emboss=False) + + +class STRIP_PT_adjust_crop(StripButtonsPanel, Panel): + bl_label = "Crop" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type != 'SOUND' + + def draw(self, context): + strip = context.active_strip + layout = self.layout + layout.use_property_split = True + layout.active = not strip.mute + + col = layout.column(align=True) + col.prop(strip.crop, "min_x") + col.prop(strip.crop, "max_x") + col.prop(strip.crop, "max_y") + col.prop(strip.crop, "min_y") + + +class STRIP_PT_effect(StripButtonsPanel, Panel): + bl_label = "Effect Strip" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type in { + 'ADD', + 'SUBTRACT', + 'ALPHA_OVER', + 'ALPHA_UNDER', + 'CROSS', + 'GAMMA_CROSS', + 'MULTIPLY', + 'WIPE', + 'GLOW', + 'COLOR', + 'SPEED', + 'MULTICAM', + 'GAUSSIAN_BLUR', + 'TEXT', + 'COLORMIX', + } + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + strip = context.active_strip + + layout.active = not strip.mute + + if strip.input_count > 0: + col = layout.column() + row = col.row() + row.prop(strip, "input_1") + + if strip.input_count > 1: + row.operator("sequencer.swap_inputs", text="", icon='SORT_ASC') + row = col.row() + row.prop(strip, "input_2") + row.operator("sequencer.swap_inputs", text="", icon='SORT_DESC') + + strip_type = strip.type + + if strip_type == 'COLOR': + layout.template_color_picker(strip, "color", value_slider=True, cubic=True) + layout.prop(strip, "color", text="") + + elif strip_type == 'WIPE': + col = layout.column() + col.prop(strip, "transition_type") + col.alignment = 'RIGHT' + col.row().prop(strip, "direction", expand=True) + + col = layout.column() + col.prop(strip, "blur_width", slider=True) + if strip.transition_type in {'SINGLE', 'DOUBLE'}: + col.prop(strip, "angle") + + elif strip_type == 'GLOW': + flow = layout.column_flow() + flow.prop(strip, "threshold", slider=True) + flow.prop(strip, "clamp", slider=True) + flow.prop(strip, "boost_factor") + flow.prop(strip, "blur_radius") + flow.prop(strip, "quality", slider=True) + flow.prop(strip, "use_only_boost") + + elif strip_type == 'SPEED': + col = layout.column(align=True) + col.prop(strip, "speed_control", text="Speed Control") + if strip.speed_control == 'MULTIPLY': + col.prop(strip, "speed_factor", text=" ") + elif strip.speed_control == 'LENGTH': + col.prop(strip, "speed_length", text=" ") + elif strip.speed_control == 'FRAME_NUMBER': + col.prop(strip, "speed_frame_number", text=" ") + + row = layout.row(align=True) + row.enabled = strip.speed_control != 'STRETCH' + row = layout.row(align=True, heading="Interpolation") + row.prop(strip, "use_frame_interpolate", text="") + + elif strip_type == 'MULTICAM': + col = layout.column(align=True) + strip_channel = strip.channel + + col.prop(strip, "multicam_source", text="Source Channel") + + # The multicam strip needs at least 2 strips to be useful + if strip_channel > 2: + BT_ROW = 4 + col.label(text="Cut To") + row = col.row() + + for i in range(1, strip_channel): + if (i % BT_ROW) == 1: + row = col.row(align=True) + + # Workaround - .enabled has to have a separate UI block to work + if i == strip.multicam_source: + sub = row.row(align=True) + sub.enabled = False + sub.operator("sequencer.split_multicam", text="{:d}".format(i), translate=False).camera = i + else: + sub_1 = row.row(align=True) + sub_1.enabled = True + sub_1.operator("sequencer.split_multicam", text="{:d}".format(i), translate=False).camera = i + + if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW: + for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)): + row.label(text="") + else: + col.separator() + col.label(text="Two or more channels are needed below this strip", icon='INFO') + + elif strip_type == 'TEXT': + layout = self.layout + col = layout.column() + col.scale_x = 1.3 + col.scale_y = 1.3 + col.use_property_split = False + col.prop(strip, "text", text="") + col.use_property_split = True + layout.prop(strip, "wrap_width", text="Wrap Width") + + col = layout.column(align=True) + if strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER'}: + col.prop(strip, "use_default_fade", text="Default Fade") + if not strip.use_default_fade: + col.prop(strip, "effect_fader", text="Effect Fader") + elif strip_type == 'GAUSSIAN_BLUR': + col = layout.column(align=True) + col.prop(strip, "size_x", text="Size X") + col.prop(strip, "size_y", text="Y") + elif strip_type == 'COLORMIX': + layout.prop(strip, "blend_effect", text="Blend Mode") + row = layout.row(align=True) + row.prop(strip, "factor", slider=True) + + +class STRIP_PT_effect_text_layout(StripButtonsPanel, Panel): + bl_label = "Layout" + bl_parent_id = "STRIP_PT_effect" + + @classmethod + def poll(cls, context): + strip = context.active_strip + return strip.type == 'TEXT' + + def draw(self, context): + strip = context.active_strip + layout = self.layout + layout.use_property_split = True + col = layout.column() + col.prop(strip, "location", text="Location") + col.prop(strip, "alignment_x", text="Alignment X") + col.prop(strip, "anchor_x", text="Anchor X") + col.prop(strip, "anchor_y", text="Y") + + +class STRIP_PT_effect_text_style(StripButtonsPanel, Panel): + bl_label = "Style" + bl_parent_id = "STRIP_PT_effect" + + @classmethod + def poll(cls, context): + strip = context.active_strip + return strip.type == 'TEXT' + + def draw(self, context): + strip = context.active_strip + layout = self.layout + layout.use_property_split = True + col = layout.column() + + row = col.row(align=True) + row.use_property_decorate = False + row.template_ID(strip, "font", open="font.open", unlink="font.unlink") + row.prop(strip, "use_bold", text="", icon='BOLD') + row.prop(strip, "use_italic", text="", icon='ITALIC') + + col.prop(strip, "font_size") + col.prop(strip, "color") + + +class STRIP_PT_effect_text_outline(StripButtonsPanel, Panel): + bl_label = "Outline" + bl_options = {'DEFAULT_CLOSED'} + + bl_parent_id = "STRIP_PT_effect_text_style" + + @classmethod + def poll(cls, context): + strip = context.active_strip + return strip.type == 'TEXT' + + def draw_header(self, context): + strip = context.active_strip + layout = self.layout + layout.prop(strip, "use_outline", text="") + + def draw(self, context): + strip = context.active_strip + layout = self.layout + layout.use_property_split = True + + col = layout.column() + col.prop(strip, "outline_color", text="Color") + col.prop(strip, "outline_width", text="Width") + col.active = strip.use_outline and (not strip.mute) + + +class STRIP_PT_effect_text_shadow(StripButtonsPanel, Panel): + bl_label = "Shadow" + bl_options = {'DEFAULT_CLOSED'} + + bl_parent_id = "STRIP_PT_effect_text_style" + + @classmethod + def poll(cls, context): + strip = context.active_strip + return strip.type == 'TEXT' + + def draw_header(self, context): + strip = context.active_strip + layout = self.layout + layout.prop(strip, "use_shadow", text="") + + def draw(self, context): + strip = context.active_strip + layout = self.layout + layout.use_property_split = True + + col = layout.column() + col.prop(strip, "shadow_color", text="Color") + col.prop(strip, "shadow_angle", text="Angle") + col.prop(strip, "shadow_offset", text="Offset") + col.prop(strip, "shadow_blur", text="Blur") + col.active = strip.use_shadow and (not strip.mute) + + +class STRIP_PT_effect_text_box(StripButtonsPanel, Panel): + bl_label = "Box" + bl_translation_context = i18n_contexts.id_sequence + bl_options = {'DEFAULT_CLOSED'} + + bl_parent_id = "STRIP_PT_effect_text_style" + + @classmethod + def poll(cls, context): + strip = context.active_strip + return strip.type == 'TEXT' + + def draw_header(self, context): + strip = context.active_strip + layout = self.layout + layout.prop(strip, "use_box", text="") + + def draw(self, context): + strip = context.active_strip + layout = self.layout + layout.use_property_split = True + + col = layout.column() + col.prop(strip, "box_color", text="Color") + col.prop(strip, "box_margin", text="Margin") + col.prop(strip, "box_roundness", text="Roundness") + col.active = strip.use_box and (not strip.mute) + + +class STRIP_PT_source(StripButtonsPanel, Panel): + bl_label = "Source" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type in {'MOVIE', 'IMAGE', 'SOUND'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + scene = context.sequencer_scene + strip = context.active_strip + strip_type = strip.type + + layout.active = not strip.mute + + # Draw a filename if we have one. + if strip_type == 'SOUND': + sound = strip.sound + layout.template_ID(strip, "sound", open="sound.open") + if sound is not None: + + col = layout.column() + col.prop(sound, "filepath", text="") + + col.alignment = 'RIGHT' + sub = col.column(align=True) + split = sub.split(factor=0.5, align=True) + split.alignment = 'RIGHT' + if sound.packed_file: + split.label(text="Unpack") + split.operator("sound.unpack", icon='PACKAGE', text="") + else: + split.label(text="Pack") + split.operator("sound.pack", icon='UGLYPACKAGE', text="") + + layout.prop(sound, "use_memory_cache") + + col = layout.box() + col = col.column(align=True) + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="Sample Rate") + split.alignment = 'LEFT' + if sound.samplerate <= 0: + split.label(text="Unknown") + else: + split.label(text="{:d} Hz".format(sound.samplerate), translate=False) + + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="Channels") + split.alignment = 'LEFT' + + # FIXME(@campbellbarton): this is ugly, we may want to support a way of showing a label from an enum. + channel_enum_items = sound.bl_rna.properties["channels"].enum_items + split.label(text=channel_enum_items[channel_enum_items.find(sound.channels)].name) + del channel_enum_items + else: + if strip_type == 'IMAGE': + col = layout.column() + col.prop(strip, "directory", text="") + + # Current element for the filename. + elem = strip.strip_elem_from_frame(scene.frame_current) + if elem: + col.prop(elem, "filename", text="") # strip.elements[0] could be a fallback + + col.prop(strip.colorspace_settings, "name", text="Color Space") + + col.prop(strip, "alpha_mode", text="Alpha") + sub = col.column(align=True) + sub.operator("sequencer.change_path", text="Change Data/Files", icon='FILEBROWSER').filter_image = True + else: # elif strip_type == 'MOVIE': + elem = strip.elements[0] + + col = layout.column() + col.prop(strip, "filepath", text="") + col.prop(strip.colorspace_settings, "name", text="Color Space") + col.prop(strip, "stream_index") + col.prop(strip, "use_deinterlace") + + if scene.render.use_multiview: + layout.prop(strip, "use_multiview") + + col = layout.column() + col.active = strip.use_multiview + + col.row().prop(strip, "views_format", expand=True) + + box = col.box() + box.active = strip.views_format == 'STEREO_3D' + box.template_image_stereo_3d(strip.stereo_3d_format) + + # Resolution. + col = layout.box() + col = col.column(align=True) + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="Resolution") + size = (elem.orig_width, elem.orig_height) if elem else (0, 0) + if size[0] and size[1]: + split.alignment = 'LEFT' + split.label(text="{:d}x{:d}".format(*size), translate=False) + else: + split.label(text="None") + # FPS + if elem.orig_fps: + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="FPS") + split.alignment = 'LEFT' + split.label(text="{:.2f}".format(elem.orig_fps), translate=False) + + +class STRIP_PT_movie_clip(StripButtonsPanel, Panel): + bl_label = "Movie Clip" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type == 'MOVIECLIP' + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + strip = context.active_strip + + layout.active = not strip.mute + layout.template_ID(strip, "clip") + + if strip.type == 'MOVIECLIP': + col = layout.column(heading="Use") + col.prop(strip, "stabilize2d", text="2D Stabilized Clip") + col.prop(strip, "undistort", text="Undistorted Clip") + + clip = strip.clip + if clip: + sta = clip.frame_start + end = clip.frame_start + clip.frame_duration + layout.label( + text=rpt_("Original frame range: {:d}-{:d} ({:d})").format(sta, end, end - sta + 1), + translate=False, + ) + + +class STRIP_PT_scene(StripButtonsPanel, Panel): + bl_label = "Scene" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return (strip.type == 'SCENE') + + def draw(self, context): + strip = context.active_strip + scene = strip.scene + + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + layout.active = not strip.mute + + layout.template_ID(strip, "scene", text="Scene", new="scene.new_sequencer") + layout.prop(strip, "scene_input", text="Input") + + if strip.scene_input == 'CAMERA': + layout.template_ID(strip, "scene_camera", text="Camera") + + if strip.scene_input == 'CAMERA': + layout = layout.column(heading="Show") + layout.prop(strip, "use_annotations", text="Annotations") + if scene: + # Warning, this is not a good convention to follow. + # Expose here because setting the alpha from the "Render" menu is very inconvenient. + layout.prop(scene.render, "film_transparent") + + +class STRIP_PT_scene_sound(StripButtonsPanel, Panel): + bl_label = "Sound" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return (strip.type == 'SCENE') + + def draw(self, context): + strip = context.active_strip + + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + layout.active = not strip.mute + + col = layout.column() + + col.use_property_decorate = True + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Strip Volume", text_ctxt=i18n_contexts.id_sound) + split.prop(strip, "volume", text="") + col.use_property_decorate = False + + +class STRIP_PT_mask(StripButtonsPanel, Panel): + bl_label = "Mask" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return (strip.type == 'MASK') + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + strip = context.active_strip + + layout.active = not strip.mute + + layout.template_ID(strip, "mask") + + mask = strip.mask + + if mask: + sta = mask.frame_start + end = mask.frame_end + layout.label( + text=rpt_("Original frame range: {:d}-{:d} ({:d})").format(sta, end, end - sta + 1), + translate=False, + ) + + +class STRIP_PT_time(StripButtonsPanel, Panel): + bl_label = "Time" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type + + def draw_header_preset(self, context): + layout = self.layout + layout.alignment = 'RIGHT' + strip = context.active_strip + + layout.prop(strip, "lock", text="", icon_only=True, emboss=False) + + def draw(self, context): + from bpy.utils import smpte_from_frame + + layout = self.layout + layout.use_property_split = False + layout.use_property_decorate = False + + scene = context.sequencer_scene + frame_current = scene.frame_current + strip = context.active_strip + + is_effect = isinstance(strip, bpy.types.EffectStrip) + + # Get once. + frame_start = strip.frame_start + frame_final_start = strip.frame_final_start + frame_final_end = strip.frame_final_end + frame_final_duration = strip.frame_final_duration + frame_offset_start = strip.frame_offset_start + frame_offset_end = strip.frame_offset_end + + length_list = ( + str(round(frame_start, 0)), + str(round(frame_final_end, 0)), + str(round(frame_final_duration, 0)), + str(round(frame_offset_start, 0)), + str(round(frame_offset_end, 0)), + ) + + if not is_effect: + length_list = length_list + ( + str(round(strip.animation_offset_start, 0)), + str(round(strip.animation_offset_end, 0)), + ) + + max_length = max(len(x) for x in length_list) + max_factor = (1.9 - max_length) / 30 + factor = 0.45 + + layout.enabled = not strip.lock + layout.active = not strip.mute + + sub = layout.row(align=True) + split = sub.split(factor=factor + max_factor) + split.alignment = 'RIGHT' + split.label(text="") + split.prop(strip, "show_retiming_keys") + + sub = layout.row(align=True) + split = sub.split(factor=factor + max_factor) + split.alignment = 'RIGHT' + split.label(text="Channel") + split.prop(strip, "channel", text="") + + sub = layout.column(align=True) + split = sub.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="Start") + split.prop(strip, "frame_start", text=smpte_from_frame(frame_start)) + + split = sub.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="Duration") + split.prop(strip, "frame_final_duration", text=smpte_from_frame(frame_final_duration)) + + # Use label, editing this value from the UI allows negative values, + # users can adjust duration. + split = sub.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="End") + split = split.split(factor=factor + 0.3 + max_factor, align=True) + split.label(text="{:>14s}".format(smpte_from_frame(frame_final_end)), translate=False) + split.alignment = 'RIGHT' + split.label(text=str(frame_final_end) + " ") + + if not is_effect: + + layout.alignment = 'RIGHT' + sub = layout.column(align=True) + + split = sub.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="Strip Offset Start") + split.prop(strip, "frame_offset_start", text=smpte_from_frame(frame_offset_start)) + + split = sub.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="End") + split.prop(strip, "frame_offset_end", text=smpte_from_frame(frame_offset_end)) + + layout.alignment = 'RIGHT' + sub = layout.column(align=True) + + split = sub.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="Hold Offset Start") + split.prop(strip, "animation_offset_start", text=smpte_from_frame(strip.animation_offset_start)) + + split = sub.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="End") + split.prop(strip, "animation_offset_end", text=smpte_from_frame(strip.animation_offset_end)) + + if strip.type == 'SOUND': + sub2 = layout.column(align=True) + split = sub2.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="Sound Offset", text_ctxt=i18n_contexts.id_sound) + split.prop(strip, "sound_offset", text="") + + col = layout.column(align=True) + col = col.box() + col.active = ( + (frame_current >= frame_final_start) and + (frame_current <= frame_final_start + frame_final_duration) + ) + + split = col.split(factor=factor + max_factor, align=True) + split.alignment = 'RIGHT' + split.label(text="Current Frame") + split = split.split(factor=factor + 0.3 + max_factor, align=True) + frame_display = frame_current - frame_final_start + split.label(text="{:>14s}".format(smpte_from_frame(frame_display)), translate=False) + split.alignment = 'RIGHT' + split.label(text=str(frame_display) + " ") + + if strip.type == 'SCENE': + scene = strip.scene + + if scene: + sta = scene.frame_start + end = scene.frame_end + split = col.split(factor=factor + max_factor) + split.alignment = 'RIGHT' + split.label(text="Original Frame Range") + split.alignment = 'LEFT' + split.label(text="{:d}-{:d} ({:d})".format(sta, end, end - sta + 1), translate=False) + + +class STRIP_PT_adjust_sound(StripButtonsPanel, Panel): + bl_label = "Sound" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type == 'SOUND' + + def draw(self, context): + layout = self.layout + + strip = context.active_strip + sound = strip.sound + + layout.active = not strip.mute + + if sound is not None: + layout.use_property_split = True + col = layout.column() + + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Volume", text_ctxt=i18n_contexts.id_sound) + split.prop(strip, "volume", text="") + + layout.use_property_split = False + col = layout.column() + + split = col.split(factor=0.4) + split.label(text="") + split.prop(sound, "use_mono") + + layout.use_property_split = True + col = layout.column() + + 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) + + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Pan", text_ctxt=i18n_contexts.id_sound) + split.prop(strip, "pan", text="") + split.enabled = pan_enabled + + if audio_channels not in {'MONO', 'STEREO'}: + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Pan Angle") + split.enabled = pan_enabled + subsplit = split.row() + subsplit.alignment = 'CENTER' + subsplit.label(text=pan_text) + subsplit.label(text=" ") # Compensate for no decorate. + subsplit.enabled = pan_enabled + + layout.use_property_split = False + col = layout.column() + + split = col.split(factor=0.4) + split.label(text="") + split.prop(strip, "pitch_correction") + + split = col.split(factor=0.4) + split.label(text="") + split.prop(strip, "show_waveform") + + +class STRIP_PT_adjust_comp(StripButtonsPanel, Panel): + bl_label = "Compositing" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type != 'SOUND' + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + strip = context.active_strip + + layout.active = not strip.mute + + col = layout.column() + col.prop(strip, "blend_type", text="Blend") + col.prop(strip, "blend_alpha", text="Opacity", slider=True) + + +class STRIP_PT_adjust_transform(StripButtonsPanel, Panel): + bl_label = "Transform" + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type != 'SOUND' + + def draw(self, context): + strip = context.active_strip + layout = self.layout + layout.use_property_split = True + layout.active = not strip.mute + + col = layout.column(align=True) + col.prop(strip.transform, "filter", text="Filter") + + col = layout.column(align=True) + col.prop(strip.transform, "offset_x", text="Position X") + col.prop(strip.transform, "offset_y", text="Y") + + col = layout.column(align=True) + col.prop(strip.transform, "scale_x", text="Scale X") + col.prop(strip.transform, "scale_y", text="Y") + + col = layout.column(align=True) + col.prop(strip.transform, "rotation", text="Rotation") + + col = layout.column(align=True) + col.prop(strip.transform, "origin") + + col = layout.column(heading="Mirror", align=True, heading_ctxt=i18n_contexts.id_image) + col.prop(strip, "use_flip_x", text="X", toggle=True) + col.prop(strip, "use_flip_y", text="Y", toggle=True) + + +class STRIP_PT_adjust_video(StripButtonsPanel, Panel): + bl_label = "Video" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type in { + 'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK', + 'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER', + 'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', + 'WIPE', 'GLOW', 'COLOR', 'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX', + } + + def draw(self, context): + layout = self.layout + + layout.use_property_split = True + + col = layout.column() + + strip = context.active_strip + + layout.active = not strip.mute + + col.prop(strip, "strobe") + col.prop(strip, "use_reverse_frames") + + +class STRIP_PT_adjust_color(StripButtonsPanel, Panel): + bl_label = "Color" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + strip = context.active_strip + if not strip: + return False + + return strip.type in { + 'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK', + 'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER', + 'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', + 'WIPE', 'GLOW', 'COLOR', 'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX', + } + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + strip = context.active_strip + + layout.active = not strip.mute + + col = layout.column() + col.prop(strip, "color_saturation", text="Saturation") + col.prop(strip, "color_multiply", text="Multiply") + col.prop(strip, "multiply_alpha") + col.prop(strip, "use_float", text="Convert to Float") + + +class STRIP_PT_custom_props(StripButtonsPanel, PropertyPanel, Panel): + COMPAT_ENGINES = { + 'BLENDER_RENDER', + 'BLENDER_WORKBENCH', + } + _context_path = "active_strip" + _property_type = (bpy.types.Strip,) + + +classes = ( + STRIP_PT_color_tag_picker, + + STRIP_PT_strip, + STRIP_PT_effect, + STRIP_PT_scene, + STRIP_PT_scene_sound, + STRIP_PT_mask, + STRIP_PT_effect_text_style, + STRIP_PT_effect_text_outline, + STRIP_PT_effect_text_shadow, + STRIP_PT_effect_text_box, + STRIP_PT_effect_text_layout, + STRIP_PT_movie_clip, + + STRIP_PT_adjust_comp, + STRIP_PT_adjust_transform, + STRIP_PT_adjust_crop, + STRIP_PT_adjust_video, + STRIP_PT_adjust_color, + STRIP_PT_adjust_sound, + + STRIP_PT_time, + STRIP_PT_source, + + STRIP_PT_custom_props, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/scripts/startup/bl_ui/properties_strip_modifier.py b/scripts/startup/bl_ui/properties_strip_modifier.py new file mode 100644 index 00000000000..36c564ca98f --- /dev/null +++ b/scripts/startup/bl_ui/properties_strip_modifier.py @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: 2009-2023 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import bpy +from bpy.types import ( + Panel, +) +from bpy.app.translations import ( + contexts as i18n_contexts, + pgettext_rpt as rpt_, +) +from rna_prop_ui import PropertyPanel + + +class StripModButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "strip_modifier" + + @classmethod + def poll(cls, context): + return context.active_strip is not None + + +class STRIP_PT_modifiers(StripModButtonsPanel, Panel): + bl_label = "Modifiers" + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + strip = context.active_strip + ed = context.sequencer_scene.sequence_editor + if strip.type == 'SOUND': + sound = strip.sound + else: + sound = None + + if sound is None: + layout.prop(strip, "use_linear_modifiers", text="Linear Modifiers") + + layout.operator("wm.call_menu", text="Add Modifier", icon='ADD').name = "SEQUENCER_MT_modifier_add" + + layout.template_strip_modifiers() + + +classes = ( + STRIP_PT_modifiers, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/scripts/startup/bl_ui/space_properties.py b/scripts/startup/bl_ui/space_properties.py index e8fea7c2ea0..c2f85316580 100644 --- a/scripts/startup/bl_ui/space_properties.py +++ b/scripts/startup/bl_ui/space_properties.py @@ -32,7 +32,9 @@ class PROPERTIES_HT_header(Header): space.show_properties_bone or space.show_properties_bone_constraints or space.show_properties_material or - space.show_properties_texture + space.show_properties_texture or + space.show_properties_strip or + space.show_properties_strip_modifier ) def draw(self, context): @@ -122,6 +124,8 @@ class PROPERTIES_PT_options(Panel): ("show_properties_bone_constraints", "Bone Constraints", 'CONSTRAINT_BONE'), ("show_properties_material", "Material", 'MATERIAL'), ("show_properties_texture", "Texture", 'TEXTURE'), + ("show_properties_strip", "Strip", 'SEQ_SEQUENCER'), + ("show_properties_strip_modifier", "Strip Modifiers", 'SEQ_STRIP_MODIFIER') ] col = layout.column(align=True) diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index 8ea667b06ec..82b78256de8 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -1617,7 +1617,6 @@ class SequencerColorTagPicker: class SEQUENCER_PT_color_tag_picker(SequencerColorTagPicker, Panel): bl_label = "Color Tag" - bl_category = "Strip" bl_options = {'HIDE_HEADER', 'INSTANCED'} def draw(self, _context): @@ -1640,1022 +1639,6 @@ class SEQUENCER_MT_color_tag_picker(SequencerColorTagPicker, Menu): row.operator_enum("sequencer.strip_color_tag_set", "color", icon_only=True) -class SEQUENCER_PT_strip(SequencerButtonsPanel, Panel): - bl_label = "" - bl_options = {'HIDE_HEADER'} - bl_category = "Strip" - - def draw(self, context): - layout = self.layout - strip = context.active_strip - strip_type = strip.type - - if strip_type in { - 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER', 'MULTIPLY', - 'GLOW', 'SPEED', 'MULTICAM', 'GAUSSIAN_BLUR', 'COLORMIX', - }: - icon_header = 'SHADERFX' - elif strip_type in { - 'CROSS', 'GAMMA_CROSS', 'WIPE', - }: - icon_header = 'ARROW_LEFTRIGHT' - elif strip_type == 'SCENE': - icon_header = 'SCENE_DATA' - elif strip_type == 'MOVIECLIP': - icon_header = 'TRACKER' - elif strip_type == 'MASK': - icon_header = 'MOD_MASK' - elif strip_type == 'MOVIE': - icon_header = 'FILE_MOVIE' - elif strip_type == 'SOUND': - icon_header = 'FILE_SOUND' - elif strip_type == 'IMAGE': - icon_header = 'FILE_IMAGE' - elif strip_type == 'COLOR': - icon_header = 'COLOR' - elif strip_type == 'TEXT': - icon_header = 'FONT_DATA' - elif strip_type == 'ADJUSTMENT': - icon_header = 'COLOR' - elif strip_type == 'META': - icon_header = 'SEQ_STRIP_META' - else: - icon_header = 'SEQ_SEQUENCER' - - row = layout.row(align=True) - row.use_property_decorate = False - row.label(text="", icon=icon_header) - row.separator() - row.prop(strip, "name", text="") - - sub = row.row(align=True) - if strip.color_tag == 'NONE': - sub.popover(panel="SEQUENCER_PT_color_tag_picker", text="", icon='COLOR') - else: - icon = 'STRIP_' + strip.color_tag - sub.popover(panel="SEQUENCER_PT_color_tag_picker", text="", icon=icon) - - row.separator() - row.prop(strip, "mute", toggle=True, icon_only=True, emboss=False) - - -class SEQUENCER_PT_adjust_crop(SequencerButtonsPanel, Panel): - bl_label = "Crop" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type != 'SOUND' - - def draw(self, context): - strip = context.active_strip - layout = self.layout - layout.use_property_split = True - layout.active = not strip.mute - - col = layout.column(align=True) - col.prop(strip.crop, "min_x") - col.prop(strip.crop, "max_x") - col.prop(strip.crop, "max_y") - col.prop(strip.crop, "min_y") - - -class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): - bl_label = "Effect Strip" - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type in { - 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER', - 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'WIPE', 'GLOW', 'COLOR', 'SPEED', - 'MULTICAM', 'GAUSSIAN_BLUR', 'TEXT', 'COLORMIX', - } - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - strip = context.active_strip - - layout.active = not strip.mute - - if strip.input_count > 0: - col = layout.column() - row = col.row() - row.prop(strip, "input_1") - - if strip.input_count > 1: - row.operator("sequencer.swap_inputs", text="", icon='SORT_ASC') - row = col.row() - row.prop(strip, "input_2") - row.operator("sequencer.swap_inputs", text="", icon='SORT_DESC') - - strip_type = strip.type - - if strip_type == 'COLOR': - layout.template_color_picker(strip, "color", value_slider=True, cubic=True) - layout.prop(strip, "color", text="") - - elif strip_type == 'WIPE': - col = layout.column() - col.prop(strip, "transition_type") - col.alignment = 'RIGHT' - col.row().prop(strip, "direction", expand=True) - - col = layout.column() - col.prop(strip, "blur_width", slider=True) - if strip.transition_type in {'SINGLE', 'DOUBLE'}: - col.prop(strip, "angle") - - elif strip_type == 'GLOW': - flow = layout.column_flow() - flow.prop(strip, "threshold", slider=True) - flow.prop(strip, "clamp", slider=True) - flow.prop(strip, "boost_factor") - flow.prop(strip, "blur_radius") - flow.prop(strip, "quality", slider=True) - flow.prop(strip, "use_only_boost") - - elif strip_type == 'SPEED': - col = layout.column(align=True) - col.prop(strip, "speed_control", text="Speed Control") - if strip.speed_control == 'MULTIPLY': - col.prop(strip, "speed_factor", text=" ") - elif strip.speed_control == 'LENGTH': - col.prop(strip, "speed_length", text=" ") - elif strip.speed_control == 'FRAME_NUMBER': - col.prop(strip, "speed_frame_number", text=" ") - - row = layout.row(align=True) - row.enabled = strip.speed_control != 'STRETCH' - row = layout.row(align=True, heading="Interpolation") - row.prop(strip, "use_frame_interpolate", text="") - - elif strip_type == 'MULTICAM': - col = layout.column(align=True) - strip_channel = strip.channel - - col.prop(strip, "multicam_source", text="Source Channel") - - # The multicam strip needs at least 2 strips to be useful - if strip_channel > 2: - BT_ROW = 4 - col.label(text="Cut To") - row = col.row() - - for i in range(1, strip_channel): - if (i % BT_ROW) == 1: - row = col.row(align=True) - - # Workaround - .enabled has to have a separate UI block to work - if i == strip.multicam_source: - sub = row.row(align=True) - sub.enabled = False - sub.operator("sequencer.split_multicam", text="{:d}".format(i), translate=False).camera = i - else: - sub_1 = row.row(align=True) - sub_1.enabled = True - sub_1.operator("sequencer.split_multicam", text="{:d}".format(i), translate=False).camera = i - - if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW: - for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)): - row.label(text="") - else: - col.separator() - col.label(text="Two or more channels are needed below this strip", icon='INFO') - - elif strip_type == 'TEXT': - layout = self.layout - col = layout.column() - col.scale_x = 1.3 - col.scale_y = 1.3 - col.use_property_split = False - col.prop(strip, "text", text="") - col.use_property_split = True - layout.prop(strip, "wrap_width", text="Wrap Width") - - col = layout.column(align=True) - if strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER'}: - col.prop(strip, "use_default_fade", text="Default Fade") - if not strip.use_default_fade: - col.prop(strip, "effect_fader", text="Effect Fader") - elif strip_type == 'GAUSSIAN_BLUR': - col = layout.column(align=True) - col.prop(strip, "size_x", text="Size X") - col.prop(strip, "size_y", text="Y") - elif strip_type == 'COLORMIX': - layout.prop(strip, "blend_effect", text="Blend Mode") - row = layout.row(align=True) - row.prop(strip, "factor", slider=True) - - -class SEQUENCER_PT_effect_text_layout(SequencerButtonsPanel, Panel): - bl_label = "Layout" - bl_parent_id = "SEQUENCER_PT_effect" - bl_category = "Strip" - - @classmethod - def poll(cls, context): - strip = context.active_strip - return strip.type == 'TEXT' - - def draw(self, context): - strip = context.active_strip - layout = self.layout - layout.use_property_split = True - col = layout.column() - col.prop(strip, "location", text="Location") - col.prop(strip, "alignment_x", text="Alignment X") - col.prop(strip, "anchor_x", text="Anchor X") - col.prop(strip, "anchor_y", text="Y") - - -class SEQUENCER_PT_effect_text_style(SequencerButtonsPanel, Panel): - bl_label = "Style" - bl_parent_id = "SEQUENCER_PT_effect" - bl_category = "Strip" - - @classmethod - def poll(cls, context): - strip = context.active_strip - return strip.type == 'TEXT' - - def draw(self, context): - strip = context.active_strip - layout = self.layout - layout.use_property_split = True - col = layout.column() - - row = col.row(align=True) - row.use_property_decorate = False - row.template_ID(strip, "font", open="font.open", unlink="font.unlink") - row.prop(strip, "use_bold", text="", icon='BOLD') - row.prop(strip, "use_italic", text="", icon='ITALIC') - - col.prop(strip, "font_size") - col.prop(strip, "color") - - -class SEQUENCER_PT_effect_text_outline(SequencerButtonsPanel, Panel): - bl_label = "Outline" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - bl_parent_id = "SEQUENCER_PT_effect_text_style" - - @classmethod - def poll(cls, context): - strip = context.active_strip - return strip.type == 'TEXT' - - def draw_header(self, context): - strip = context.active_strip - layout = self.layout - layout.prop(strip, "use_outline", text="") - - def draw(self, context): - strip = context.active_strip - layout = self.layout - layout.use_property_split = True - - col = layout.column() - col.prop(strip, "outline_color", text="Color") - col.prop(strip, "outline_width", text="Width") - col.active = strip.use_outline and (not strip.mute) - - -class SEQUENCER_PT_effect_text_shadow(SequencerButtonsPanel, Panel): - bl_label = "Shadow" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - bl_parent_id = "SEQUENCER_PT_effect_text_style" - - @classmethod - def poll(cls, context): - strip = context.active_strip - return strip.type == 'TEXT' - - def draw_header(self, context): - strip = context.active_strip - layout = self.layout - layout.prop(strip, "use_shadow", text="") - - def draw(self, context): - strip = context.active_strip - layout = self.layout - layout.use_property_split = True - - col = layout.column() - col.prop(strip, "shadow_color", text="Color") - col.prop(strip, "shadow_angle", text="Angle") - col.prop(strip, "shadow_offset", text="Offset") - col.prop(strip, "shadow_blur", text="Blur") - col.active = strip.use_shadow and (not strip.mute) - - -class SEQUENCER_PT_effect_text_box(SequencerButtonsPanel, Panel): - bl_label = "Box" - bl_translation_context = i18n_contexts.id_sequence - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - bl_parent_id = "SEQUENCER_PT_effect_text_style" - - @classmethod - def poll(cls, context): - strip = context.active_strip - return strip.type == 'TEXT' - - def draw_header(self, context): - strip = context.active_strip - layout = self.layout - layout.prop(strip, "use_box", text="") - - def draw(self, context): - strip = context.active_strip - layout = self.layout - layout.use_property_split = True - - col = layout.column() - col.prop(strip, "box_color", text="Color") - col.prop(strip, "box_margin", text="Margin") - col.prop(strip, "box_roundness", text="Roundness") - col.active = strip.use_box and (not strip.mute) - - -class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): - bl_label = "Source" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type in {'MOVIE', 'IMAGE', 'SOUND'} - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - scene = context.sequencer_scene - strip = context.active_strip - strip_type = strip.type - - layout.active = not strip.mute - - # Draw a filename if we have one. - if strip_type == 'SOUND': - sound = strip.sound - layout.template_ID(strip, "sound", open="sound.open") - if sound is not None: - - col = layout.column() - col.prop(sound, "filepath", text="") - - col.alignment = 'RIGHT' - sub = col.column(align=True) - split = sub.split(factor=0.5, align=True) - split.alignment = 'RIGHT' - if sound.packed_file: - split.label(text="Unpack") - split.operator("sound.unpack", icon='PACKAGE', text="") - else: - split.label(text="Pack") - split.operator("sound.pack", icon='UGLYPACKAGE', text="") - - layout.prop(sound, "use_memory_cache") - - col = layout.box() - col = col.column(align=True) - split = col.split(factor=0.5, align=False) - split.alignment = 'RIGHT' - split.label(text="Sample Rate") - split.alignment = 'LEFT' - if sound.samplerate <= 0: - split.label(text="Unknown") - else: - split.label(text="{:d} Hz".format(sound.samplerate), translate=False) - - split = col.split(factor=0.5, align=False) - split.alignment = 'RIGHT' - split.label(text="Channels") - split.alignment = 'LEFT' - - # FIXME(@campbellbarton): this is ugly, we may want to support a way of showing a label from an enum. - channel_enum_items = sound.bl_rna.properties["channels"].enum_items - split.label(text=channel_enum_items[channel_enum_items.find(sound.channels)].name) - del channel_enum_items - else: - if strip_type == 'IMAGE': - col = layout.column() - col.prop(strip, "directory", text="") - - # Current element for the filename. - elem = strip.strip_elem_from_frame(scene.frame_current) - if elem: - col.prop(elem, "filename", text="") # strip.elements[0] could be a fallback - - col.prop(strip.colorspace_settings, "name", text="Color Space") - - col.prop(strip, "alpha_mode", text="Alpha") - sub = col.column(align=True) - sub.operator("sequencer.change_path", text="Change Data/Files", icon='FILEBROWSER').filter_image = True - else: # elif strip_type == 'MOVIE': - elem = strip.elements[0] - - col = layout.column() - col.prop(strip, "filepath", text="") - col.prop(strip.colorspace_settings, "name", text="Color Space") - col.prop(strip, "stream_index") - col.prop(strip, "use_deinterlace") - - if scene.render.use_multiview: - layout.prop(strip, "use_multiview") - - col = layout.column() - col.active = strip.use_multiview - - col.row().prop(strip, "views_format", expand=True) - - box = col.box() - box.active = strip.views_format == 'STEREO_3D' - box.template_image_stereo_3d(strip.stereo_3d_format) - - # Resolution. - col = layout.box() - col = col.column(align=True) - split = col.split(factor=0.5, align=False) - split.alignment = 'RIGHT' - split.label(text="Resolution") - size = (elem.orig_width, elem.orig_height) if elem else (0, 0) - if size[0] and size[1]: - split.alignment = 'LEFT' - split.label(text="{:d}x{:d}".format(*size), translate=False) - else: - split.label(text="None") - # FPS - if elem.orig_fps: - split = col.split(factor=0.5, align=False) - split.alignment = 'RIGHT' - split.label(text="FPS") - split.alignment = 'LEFT' - split.label(text="{:.2f}".format(elem.orig_fps), translate=False) - - -class SEQUENCER_PT_movie_clip(SequencerButtonsPanel, Panel): - bl_label = "Movie Clip" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type == 'MOVIECLIP' - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - strip = context.active_strip - - layout.active = not strip.mute - layout.template_ID(strip, "clip") - - if strip.type == 'MOVIECLIP': - col = layout.column(heading="Use") - col.prop(strip, "stabilize2d", text="2D Stabilized Clip") - col.prop(strip, "undistort", text="Undistorted Clip") - - clip = strip.clip - if clip: - sta = clip.frame_start - end = clip.frame_start + clip.frame_duration - layout.label( - text=rpt_("Original frame range: {:d}-{:d} ({:d})").format(sta, end, end - sta + 1), - translate=False, - ) - - -class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): - bl_label = "Scene" - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return (strip.type == 'SCENE') - - def draw(self, context): - strip = context.active_strip - scene = strip.scene - - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - layout.active = not strip.mute - - layout.template_ID(strip, "scene", text="Scene", new="scene.new_sequencer") - layout.prop(strip, "scene_input", text="Input") - - if strip.scene_input == 'CAMERA': - layout.template_ID(strip, "scene_camera", text="Camera") - - if strip.scene_input == 'CAMERA': - layout = layout.column(heading="Show") - layout.prop(strip, "use_annotations", text="Annotations") - if scene: - # Warning, this is not a good convention to follow. - # Expose here because setting the alpha from the "Render" menu is very inconvenient. - layout.prop(scene.render, "film_transparent") - - -class SEQUENCER_PT_scene_sound(SequencerButtonsPanel, Panel): - bl_label = "Sound" - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return (strip.type == 'SCENE') - - def draw(self, context): - strip = context.active_strip - - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - layout.active = not strip.mute - - col = layout.column() - - col.use_property_decorate = True - split = col.split(factor=0.4) - split.alignment = 'RIGHT' - split.label(text="Strip Volume", text_ctxt=i18n_contexts.id_sound) - split.prop(strip, "volume", text="") - col.use_property_decorate = False - - -class SEQUENCER_PT_mask(SequencerButtonsPanel, Panel): - bl_label = "Mask" - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return (strip.type == 'MASK') - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - strip = context.active_strip - - layout.active = not strip.mute - - layout.template_ID(strip, "mask") - - mask = strip.mask - - if mask: - sta = mask.frame_start - end = mask.frame_end - layout.label( - text=rpt_("Original frame range: {:d}-{:d} ({:d})").format(sta, end, end - sta + 1), - translate=False, - ) - - -class SEQUENCER_PT_time(SequencerButtonsPanel, Panel): - bl_label = "Time" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type - - def draw_header_preset(self, context): - layout = self.layout - layout.alignment = 'RIGHT' - strip = context.active_strip - - layout.prop(strip, "lock", text="", icon_only=True, emboss=False) - - def draw(self, context): - from bpy.utils import smpte_from_frame - - layout = self.layout - layout.use_property_split = False - layout.use_property_decorate = False - - scene = context.sequencer_scene - frame_current = scene.frame_current - strip = context.active_strip - - is_effect = isinstance(strip, bpy.types.EffectStrip) - - # Get once. - frame_start = strip.frame_start - frame_final_start = strip.frame_final_start - frame_final_end = strip.frame_final_end - frame_final_duration = strip.frame_final_duration - frame_offset_start = strip.frame_offset_start - frame_offset_end = strip.frame_offset_end - - length_list = ( - str(round(frame_start, 0)), - str(round(frame_final_end, 0)), - str(round(frame_final_duration, 0)), - str(round(frame_offset_start, 0)), - str(round(frame_offset_end, 0)), - ) - - if not is_effect: - length_list = length_list + ( - str(round(strip.animation_offset_start, 0)), - str(round(strip.animation_offset_end, 0)), - ) - - max_length = max(len(x) for x in length_list) - max_factor = (1.9 - max_length) / 30 - factor = 0.45 - - layout.enabled = not strip.lock - layout.active = not strip.mute - - sub = layout.row(align=True) - split = sub.split(factor=factor + max_factor) - split.alignment = 'RIGHT' - split.label(text="") - split.prop(strip, "show_retiming_keys") - - sub = layout.row(align=True) - split = sub.split(factor=factor + max_factor) - split.alignment = 'RIGHT' - split.label(text="Channel") - split.prop(strip, "channel", text="") - - sub = layout.column(align=True) - split = sub.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="Start") - split.prop(strip, "frame_start", text=smpte_from_frame(frame_start)) - - split = sub.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="Duration") - split.prop(strip, "frame_final_duration", text=smpte_from_frame(frame_final_duration)) - - # Use label, editing this value from the UI allows negative values, - # users can adjust duration. - split = sub.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="End") - split = split.split(factor=factor + 0.3 + max_factor, align=True) - split.label(text="{:>14s}".format(smpte_from_frame(frame_final_end)), translate=False) - split.alignment = 'RIGHT' - split.label(text=str(frame_final_end) + " ") - - if not is_effect: - - layout.alignment = 'RIGHT' - sub = layout.column(align=True) - - split = sub.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="Strip Offset Start") - split.prop(strip, "frame_offset_start", text=smpte_from_frame(frame_offset_start)) - - split = sub.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="End") - split.prop(strip, "frame_offset_end", text=smpte_from_frame(frame_offset_end)) - - layout.alignment = 'RIGHT' - sub = layout.column(align=True) - - split = sub.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="Hold Offset Start") - split.prop(strip, "animation_offset_start", text=smpte_from_frame(strip.animation_offset_start)) - - split = sub.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="End") - split.prop(strip, "animation_offset_end", text=smpte_from_frame(strip.animation_offset_end)) - - if strip.type == 'SOUND': - sub2 = layout.column(align=True) - split = sub2.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="Sound Offset", text_ctxt=i18n_contexts.id_sound) - split.prop(strip, "sound_offset", text="") - - col = layout.column(align=True) - col = col.box() - col.active = ( - (frame_current >= frame_final_start) and - (frame_current <= frame_final_start + frame_final_duration) - ) - - split = col.split(factor=factor + max_factor, align=True) - split.alignment = 'RIGHT' - split.label(text="Current Frame") - split = split.split(factor=factor + 0.3 + max_factor, align=True) - frame_display = frame_current - frame_final_start - split.label(text="{:>14s}".format(smpte_from_frame(frame_display)), translate=False) - split.alignment = 'RIGHT' - split.label(text=str(frame_display) + " ") - - if strip.type == 'SCENE': - scene = strip.scene - - if scene: - sta = scene.frame_start - end = scene.frame_end - split = col.split(factor=factor + max_factor) - split.alignment = 'RIGHT' - split.label(text="Original Frame Range") - split.alignment = 'LEFT' - split.label(text="{:d}-{:d} ({:d})".format(sta, end, end - sta + 1), translate=False) - - -class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): - bl_label = "Sound" - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type == 'SOUND' - - def draw(self, context): - layout = self.layout - - st = context.space_data - overlay_settings = st.timeline_overlay - strip = context.active_strip - sound = strip.sound - - layout.active = not strip.mute - - if sound is not None: - layout.use_property_split = True - col = layout.column() - - split = col.split(factor=0.4) - split.alignment = 'RIGHT' - split.label(text="Volume", text_ctxt=i18n_contexts.id_sound) - split.prop(strip, "volume", text="") - - layout.use_property_split = False - col = layout.column() - - split = col.split(factor=0.4) - split.label(text="") - split.prop(sound, "use_mono") - - layout.use_property_split = True - col = layout.column() - - 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) - - split = col.split(factor=0.4) - split.alignment = 'RIGHT' - split.label(text="Pan", text_ctxt=i18n_contexts.id_sound) - split.prop(strip, "pan", text="") - split.enabled = pan_enabled - - if audio_channels not in {'MONO', 'STEREO'}: - split = col.split(factor=0.4) - split.alignment = 'RIGHT' - split.label(text="Pan Angle") - split.enabled = pan_enabled - subsplit = split.row() - subsplit.alignment = 'CENTER' - subsplit.label(text=pan_text) - subsplit.label(text=" ") # Compensate for no decorate. - subsplit.enabled = pan_enabled - - layout.use_property_split = False - col = layout.column() - - split = col.split(factor=0.4) - split.label(text="") - split.prop(strip, "pitch_correction") - - if overlay_settings.waveform_display_type == 'DEFAULT_WAVEFORMS': - split = col.split(factor=0.4) - split.label(text="") - split.prop(strip, "show_waveform") - - -class SEQUENCER_PT_adjust_comp(SequencerButtonsPanel, Panel): - bl_label = "Compositing" - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type != 'SOUND' - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - strip = context.active_strip - - layout.active = not strip.mute - - col = layout.column() - col.prop(strip, "blend_type", text="Blend") - col.prop(strip, "blend_alpha", text="Opacity", slider=True) - - -class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): - bl_label = "Transform" - bl_category = "Strip" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type != 'SOUND' - - def draw(self, context): - strip = context.active_strip - layout = self.layout - layout.use_property_split = True - layout.active = not strip.mute - - col = layout.column(align=True) - col.prop(strip.transform, "filter", text="Filter") - - col = layout.column(align=True) - col.prop(strip.transform, "offset_x", text="Position X") - col.prop(strip.transform, "offset_y", text="Y") - - col = layout.column(align=True) - col.prop(strip.transform, "scale_x", text="Scale X") - col.prop(strip.transform, "scale_y", text="Y") - - col = layout.column(align=True) - col.prop(strip.transform, "rotation", text="Rotation") - - col = layout.column(align=True) - col.prop(strip.transform, "origin") - - col = layout.column(heading="Mirror", align=True, heading_ctxt=i18n_contexts.id_image) - col.prop(strip, "use_flip_x", text="X", toggle=True) - col.prop(strip, "use_flip_y", text="Y", toggle=True) - - -class SEQUENCER_PT_adjust_video(SequencerButtonsPanel, Panel): - bl_label = "Video" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type in { - 'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK', - 'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER', - 'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', - 'WIPE', 'GLOW', 'COLOR', 'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX', - } - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - - col = layout.column() - - strip = context.active_strip - - layout.active = not strip.mute - - col.prop(strip, "strobe") - col.prop(strip, "use_reverse_frames") - - -class SEQUENCER_PT_adjust_color(SequencerButtonsPanel, Panel): - bl_label = "Color" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - - @classmethod - def poll(cls, context): - if not cls.has_sequencer(context): - return False - - strip = context.active_strip - if not strip: - return False - - return strip.type in { - 'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK', - 'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER', - 'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', - 'WIPE', 'GLOW', 'COLOR', 'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX', - } - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - strip = context.active_strip - - layout.active = not strip.mute - - col = layout.column() - col.prop(strip, "color_saturation", text="Saturation") - col.prop(strip, "color_multiply", text="Multiply") - col.prop(strip, "multiply_alpha") - col.prop(strip, "use_float", text="Convert to Float") - - class SEQUENCER_PT_cache_settings(SequencerButtonsPanel, Panel): bl_label = "Cache Settings" bl_category = "Cache" @@ -2993,29 +1976,6 @@ class SEQUENCER_PT_view_safe_areas_center_cut(SequencerButtonsPanel_Output, Pane col.prop(safe_data, "action_center", slider=True) -class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel): - bl_label = "" - bl_options = {'HIDE_HEADER'} - bl_category = "Modifiers" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - strip = context.active_strip - if strip.type == 'SOUND': - sound = strip.sound - else: - sound = None - - if sound is None: - layout.prop(strip, "use_linear_modifiers", text="Linear Modifiers") - - layout.operator("wm.call_menu", text="Add Modifier", icon='ADD').name = "SEQUENCER_MT_modifier_add" - - layout.template_strip_modifiers() - - class SEQUENCER_PT_annotation(AnnotationDataPanel, SequencerButtonsPanel_Output, Panel): bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' @@ -3184,10 +2144,7 @@ classes = ( SEQUENCER_MT_preview_view_pie, SEQUENCER_MT_modifier_add, - SEQUENCER_PT_color_tag_picker, - SEQUENCER_PT_active_tool, - SEQUENCER_PT_strip, SEQUENCER_PT_gizmo_display, SEQUENCER_PT_overlay, @@ -3196,36 +2153,12 @@ classes = ( SEQUENCER_PT_sequencer_overlay_strips, SEQUENCER_PT_sequencer_overlay_waveforms, - SEQUENCER_PT_effect, - SEQUENCER_PT_scene, - SEQUENCER_PT_scene_sound, - SEQUENCER_PT_mask, - SEQUENCER_PT_effect_text_style, - SEQUENCER_PT_effect_text_outline, - SEQUENCER_PT_effect_text_shadow, - SEQUENCER_PT_effect_text_box, - SEQUENCER_PT_effect_text_layout, - SEQUENCER_PT_movie_clip, - - SEQUENCER_PT_adjust_comp, - SEQUENCER_PT_adjust_transform, - SEQUENCER_PT_adjust_crop, - SEQUENCER_PT_adjust_video, - SEQUENCER_PT_adjust_color, - SEQUENCER_PT_adjust_sound, - - SEQUENCER_PT_time, - SEQUENCER_PT_source, - - SEQUENCER_PT_modifiers, SEQUENCER_PT_cache_settings, SEQUENCER_PT_cache_view_settings, SEQUENCER_PT_proxy_settings, SEQUENCER_PT_strip_proxy, - SEQUENCER_PT_custom_props, - SEQUENCER_PT_view, SEQUENCER_PT_view_cursor, SEQUENCER_PT_frame_overlay, diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 858813c5b3d..2846e31d583 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -347,6 +347,31 @@ void BLO_update_defaults_workspace(WorkSpace *workspace, const char *app_templat } } } + /* For Video Editing template. */ + if (STRPREFIX(workspace->id.name + 2, "Video Editing")) { + LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { + bScreen *screen = layout->screen; + if (screen) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + if (((SpaceSeq *)sl)->view == SEQ_VIEW_PREVIEW) { + continue; + } + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + ARegion *sidebar = BKE_region_find_in_listbase_by_type(regionbase, RGN_TYPE_UI); + sidebar->flag |= RGN_FLAG_HIDDEN; + } + if (sl->spacetype == SPACE_PROPERTIES) { + SpaceProperties *properties = reinterpret_cast(sl); + properties->mainb = properties->mainbo = properties->mainbuser = BCONTEXT_STRIP; + } + } + } + } + } + } } static void blo_update_defaults_paint(Paint *paint) diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index 9b12ab2d716..9e1cbecdd91 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -33,6 +33,7 @@ set(LIB PRIVATE bf::intern::guardedalloc PRIVATE bf::nodes PRIVATE bf::windowmanager + PRIVATE bf::sequencer ) diff --git a/source/blender/editors/space_buttons/buttons_context.cc b/source/blender/editors/space_buttons/buttons_context.cc index cae3996768d..3c9eb8e9943 100644 --- a/source/blender/editors/space_buttons/buttons_context.cc +++ b/source/blender/editors/space_buttons/buttons_context.cc @@ -24,6 +24,7 @@ #include "DNA_material_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" +#include "DNA_sequence_types.h" #include "DNA_windowmanager_types.h" #include "DNA_world_types.h" @@ -38,6 +39,9 @@ #include "BKE_particle.h" #include "BKE_screen.hh" +#include "SEQ_modifier.hh" +#include "SEQ_select.hh" + #include "RNA_access.hh" #include "RNA_prototypes.hh" @@ -523,6 +527,46 @@ static bool buttons_context_path_texture(const bContext *C, return true; } +static bool buttons_context_path_strip(ButsContextPath *path) +{ + PointerRNA *ptr = &path->ptr[path->len - 1]; + /* If we already have a (pinned) strip, we're done. */ + if (RNA_struct_is_a(ptr->type, &RNA_Strip)) { + return true; + } + + if (buttons_context_path_scene(path)) { + Scene *scene = static_cast(path->ptr[path->len - 1].data); + Strip *active_strip = blender::seq::select_active_get(scene); + if (active_strip == nullptr) { + return false; + } + + path->ptr[path->len] = RNA_pointer_create_discrete(&scene->id, &RNA_Strip, active_strip); + path->len++; + return true; + } + + return false; +} + +static bool buttons_context_path_strip_modifier(Scene *sequencer_scene, ButsContextPath *path) +{ + if (sequencer_scene && buttons_context_path_strip(path)) { + Strip *active_strip = static_cast(path->ptr[path->len - 1].data); + + StripModifierData *smd = blender::seq::modifier_get_active(active_strip); + if (smd) { + path->ptr[path->len] = RNA_pointer_create_discrete( + &sequencer_scene->id, &RNA_StripModifier, smd); + path->len++; + } + return true; + } + + return false; +} + #ifdef WITH_FREESTYLE static bool buttons_context_linestyle_pinnable(const bContext *C, ViewLayer *view_layer) { @@ -554,6 +598,8 @@ static bool buttons_context_path( * Otherwise there is a loop reading the context that we are setting. */ wmWindow *window = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(window); + WorkSpace *workspace = WM_window_get_active_workspace(window); + Scene *sequencer_scene = workspace->sequencer_scene; ViewLayer *view_layer = WM_window_get_active_view_layer(window); *path = {}; @@ -568,7 +614,13 @@ static bool buttons_context_path( } /* No pinned root, use scene as initial root. */ else if (mainb != BCONTEXT_TOOL) { - path->ptr[0] = RNA_id_pointer_create(&scene->id); + if (ELEM(mainb, BCONTEXT_STRIP, BCONTEXT_STRIP_MODIFIER)) { + path->ptr[0] = RNA_id_pointer_create(&sequencer_scene->id); + } + else { + path->ptr[0] = RNA_id_pointer_create(&scene->id); + } + path->len++; if (!ELEM(mainb, @@ -576,7 +628,9 @@ static bool buttons_context_path( BCONTEXT_RENDER, BCONTEXT_OUTPUT, BCONTEXT_VIEW_LAYER, - BCONTEXT_WORLD)) + BCONTEXT_WORLD, + BCONTEXT_STRIP, + BCONTEXT_STRIP_MODIFIER)) { path->ptr[path->len] = RNA_pointer_create_discrete(nullptr, &RNA_ViewLayer, view_layer); path->len++; @@ -645,6 +699,12 @@ static bool buttons_context_path( case BCONTEXT_BONE_CONSTRAINT: found = buttons_context_path_pose_bone(path); break; + case BCONTEXT_STRIP: + found = buttons_context_path_strip(path); + break; + case BCONTEXT_STRIP_MODIFIER: + found = buttons_context_path_strip_modifier(sequencer_scene, path); + break; default: found = false; break; @@ -851,6 +911,8 @@ const char *buttons_context_dir[] = { "curves", "pointcloud", "volume", + "strip", + "strip_modifier", nullptr, }; @@ -1173,6 +1235,14 @@ int /*eContextResult*/ buttons_context(const bContext *C, set_pointer_type(path, result, &RNA_GreasePencil); return CTX_RESULT_OK; } + if (CTX_data_equals(member, "strip")) { + set_pointer_type(path, result, &RNA_Strip); + return CTX_RESULT_OK; + } + if (CTX_data_equals(member, "strip_modifier")) { + set_pointer_type(path, result, &RNA_StripModifier); + return CTX_RESULT_OK; + } return CTX_RESULT_MEMBER_NOT_FOUND; } @@ -1206,7 +1276,9 @@ static void buttons_panel_context_draw(const bContext *C, Panel *panel) BCONTEXT_OUTPUT, BCONTEXT_SCENE, BCONTEXT_VIEW_LAYER, - BCONTEXT_WORLD) && + BCONTEXT_WORLD, + BCONTEXT_STRIP, + BCONTEXT_STRIP_MODIFIER) && ptr->type == &RNA_Scene) { continue; diff --git a/source/blender/editors/space_buttons/space_buttons.cc b/source/blender/editors/space_buttons/space_buttons.cc index 6d05f6dd343..00697572e83 100644 --- a/source/blender/editors/space_buttons/space_buttons.cc +++ b/source/blender/editors/space_buttons/space_buttons.cc @@ -11,6 +11,7 @@ #include "MEM_guardedalloc.h" #include "DNA_scene_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_view2d_types.h" @@ -43,6 +44,8 @@ #include "RNA_access.hh" #include "RNA_prototypes.hh" +#include "SEQ_modifier.hh" + #include "UI_interface.hh" #include "UI_interface_c.hh" #include "UI_interface_layout.hh" @@ -177,24 +180,16 @@ void ED_buttons_visible_tabs_menu(bContext *C, uiLayout *layout, void * /*arg*/) /* These can be reordered freely. */ constexpr std::array filter_items = { - "show_properties_tool", - "show_properties_render", - "show_properties_output", - "show_properties_view_layer", - "show_properties_scene", - "show_properties_world", - "show_properties_collection", - "show_properties_object", - "show_properties_modifiers", - "show_properties_effects", - "show_properties_particles", - "show_properties_physics", - "show_properties_constraints", - "show_properties_data", - "show_properties_bone", - "show_properties_bone_constraints", - "show_properties_material", - "show_properties_texture", + "show_properties_tool", "show_properties_render", + "show_properties_output", "show_properties_view_layer", + "show_properties_scene", "show_properties_world", + "show_properties_collection", "show_properties_object", + "show_properties_modifiers", "show_properties_effects", + "show_properties_particles", "show_properties_physics", + "show_properties_constraints", "show_properties_data", + "show_properties_bone", "show_properties_bone_constraints", + "show_properties_material", "show_properties_texture", + "show_properties_strip", "show_properties_strip_modifier", }; for (blender::StringRefNull item : filter_items) { @@ -258,6 +253,11 @@ blender::Vector ED_buttons_tabs_list(const SpacePropertie add_tab(BCONTEXT_TEXTURE); + add_spacer(); + + add_tab(BCONTEXT_STRIP); + add_tab(BCONTEXT_STRIP_MODIFIER); + return tabs; } @@ -300,6 +300,10 @@ static const char *buttons_main_region_context_string(const short mainb) return "bone_constraint"; case BCONTEXT_TOOL: return "tool"; + case BCONTEXT_STRIP: + return "strip"; + case BCONTEXT_STRIP_MODIFIER: + return "strip_modifier"; } /* All the cases should be handled. */ @@ -766,6 +770,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) break; case ND_RENDER_RESULT: break; + case ND_SEQUENCER: + ED_area_tag_redraw(area); + break; case ND_MODE: case ND_LAYER: default: @@ -1129,6 +1136,14 @@ void ED_spacetype_buttons() fxti->panel_register(art); } } + /* Register the panel types from strip modifiers. The actual panels are built per strip modifier + * rather than per modifier type. */ + for (int i = 0; i < NUM_STRIP_MODIFIER_TYPES; i++) { + const blender::seq::StripModifierTypeInfo *mti = blender::seq::modifier_type_info_get(i); + if (mti != nullptr && mti->panel_register != nullptr) { + mti->panel_register(art); + } + } /* regions: header */ art = MEM_callocN("spacetype buttons region"); diff --git a/source/blender/editors/space_sequencer/space_sequencer.cc b/source/blender/editors/space_sequencer/space_sequencer.cc index b8843440dc3..698329671a2 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.cc +++ b/source/blender/editors/space_sequencer/space_sequencer.cc @@ -1036,7 +1036,6 @@ static void sequencer_buttons_region_init(wmWindowManager *wm, ARegion *region) wm->runtime->defaultconf, "Video Sequence Editor", SPACE_SEQ, RGN_TYPE_WINDOW); WM_event_add_keymap_handler_v2d_mask(®ion->runtime->handlers, keymap); - UI_panel_category_active_set_default(region, "Strip"); ED_region_panels_init(wm, region); } @@ -1213,15 +1212,6 @@ void ED_spacetype_sequencer() art->draw = sequencer_buttons_region_draw; BLI_addhead(&st->regiontypes, art); - /* Register the panel types from strip modifiers. The actual panels are built per strip modifier - * rather than per modifier type. */ - for (int i = 0; i < NUM_STRIP_MODIFIER_TYPES; i++) { - const seq::StripModifierTypeInfo *mti = seq::modifier_type_info_get(i); - if (mti != nullptr && mti->panel_register != nullptr) { - mti->panel_register(art); - } - } - sequencer_buttons_register(art); /* Toolbar. */ art = MEM_callocN("spacetype sequencer tools region"); diff --git a/source/blender/makesdna/DNA_space_enums.h b/source/blender/makesdna/DNA_space_enums.h index 276f81e827c..3b53b1fb9ce 100644 --- a/source/blender/makesdna/DNA_space_enums.h +++ b/source/blender/makesdna/DNA_space_enums.h @@ -108,6 +108,8 @@ typedef enum eSpaceButtons_Context { BCONTEXT_SHADERFX = 15, BCONTEXT_OUTPUT = 16, BCONTEXT_COLLECTION = 17, + BCONTEXT_STRIP = 18, + BCONTEXT_STRIP_MODIFIER = 19, /* Keep last. */ BCONTEXT_TOT, diff --git a/source/blender/makesrna/intern/rna_sequencer.cc b/source/blender/makesrna/intern/rna_sequencer.cc index 312b2b74a77..bf9681165d6 100644 --- a/source/blender/makesrna/intern/rna_sequencer.cc +++ b/source/blender/makesrna/intern/rna_sequencer.cc @@ -2290,6 +2290,7 @@ static void rna_def_strip(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Strip", "Sequence strip in the sequence editor"); RNA_def_struct_refine_func(srna, "rna_Strip_refine"); RNA_def_struct_path_func(srna, "rna_Strip_path"); + RNA_def_struct_ui_icon(srna, ICON_SEQ_SEQUENCER); RNA_def_struct_idprops_func(srna, "rna_Strip_idprops"); RNA_def_struct_system_idprops_func(srna, "rna_Strip_system_idprops"); diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index dde0f2ef719..73cfb8530f7 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -535,6 +535,12 @@ const EnumPropertyItem buttons_context_items[] = { {BCONTEXT_PARTICLE, "PARTICLES", ICON_PARTICLES, "Particles", "Particle Properties"}, {BCONTEXT_PHYSICS, "PHYSICS", ICON_PHYSICS, "Physics", "Physics Properties"}, {BCONTEXT_SHADERFX, "SHADERFX", ICON_SHADERFX, "Effects", "Visual Effects Properties"}, + {BCONTEXT_STRIP, "STRIP", ICON_SEQ_SEQUENCER, "Strip", "Strip Properties"}, + {BCONTEXT_STRIP_MODIFIER, + "STRIP_MODIFIER", + ICON_SEQ_STRIP_MODIFIER, + "Strip Modifiers", + "Strip Modifier Properties"}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -5816,6 +5822,8 @@ static void rna_def_space_properties_filter(StructRNA *srna) "show_properties_particles", "show_properties_physics", "show_properties_effects", + "show_properties_strip", + "show_properties_strip_modifier", }; for (const int i : blender::IndexRange(BCONTEXT_TOT)) { diff --git a/source/blender/sequencer/intern/modifiers/modifier.cc b/source/blender/sequencer/intern/modifiers/modifier.cc index 70993e96035..085c0aaa8ff 100644 --- a/source/blender/sequencer/intern/modifiers/modifier.cc +++ b/source/blender/sequencer/intern/modifiers/modifier.cc @@ -183,12 +183,6 @@ bool modifier_ui_poll(const bContext *C, PanelType * /*pt*/) if (!sequencer_scene) { return false; } - if (const SpaceSeq *sseq = CTX_wm_space_seq(C)) { - /* Only show modifiers in the sequencer view types, not the preview. */ - if (sseq->view == SEQ_VIEW_PREVIEW) { - return false; - } - } Strip *active_strip = seq::select_active_get(sequencer_scene); return active_strip != nullptr; } @@ -232,9 +226,9 @@ PanelType *modifier_panel_register(ARegionType *region_type, modifier_type_panel_id(type, panel_type->idname); STRNCPY_UTF8(panel_type->label, ""); - STRNCPY_UTF8(panel_type->category, "Modifiers"); STRNCPY_UTF8(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); STRNCPY_UTF8(panel_type->active_property, "is_active"); + STRNCPY_UTF8(panel_type->context, "strip_modifier"); panel_type->draw_header = modifier_panel_header; panel_type->draw = draw;