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;