blender/scripts/startup/bl_ui/properties_strip.py
Guillermo Venegas bad8871dc5 UI: Add UILayout::property_split_factor static value
This adds the `UILayout::property_split_factor` property which contains
the split factor used in the layout system, and avoids to use the this
as raw value all over the place.

Pull Request: https://projects.blender.org/blender/blender/pulls/163393
2026-09-09 00:27:44 +02:00

1144 lines
37 KiB
Python

# 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
from bl_ui.utils import PresetPanel
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_effect_text_style_presets(PresetPanel, Panel):
bl_label = "Text Style Presets"
preset_subdir = "sequencer/text_style"
preset_operator = "script.execute_preset"
preset_add_operator = "sequencer.text_strip_style_preset_add"
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', 'COMPOSITOR',
}:
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_STRIP'
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")
def draw_compositor_effect_node_group_errors(layout, node_tree, strip_input_num):
if not node_tree or not node_tree.interface:
return
float_input_sockets_num = 0
color_input_sockets_num = 0
output_sockets = []
for socket in node_tree.interface.items_tree:
if socket.item_type == 'SOCKET':
if socket.in_out == 'INPUT' and socket.socket_type == 'NodeSocketColor':
color_input_sockets_num += 1
elif socket.in_out == 'INPUT' and socket.socket_type == 'NodeSocketFloat':
float_input_sockets_num += 1
elif socket.in_out == 'OUTPUT':
output_sockets.append(socket)
if color_input_sockets_num < strip_input_num:
layout.label(
text=rpt_("Node group must have at least {:d} Color input(s).").format(strip_input_num),
icon='STATUS_ERROR',
translate=False,
)
if float_input_sockets_num == 0:
layout.label(
text=rpt_("Node group does not have an input of type Float. Fade is unused."),
icon='STATUS_ERROR',
translate=False,
)
if len(output_sockets) < 1:
layout.label(
text=rpt_("Node group must have an output."),
icon='STATUS_ERROR',
translate=False,
)
elif output_sockets[0].socket_type != 'NodeSocketColor':
layout.label(
text=rpt_("The first node group output must have the Color type."),
icon='STATUS_ERROR',
translate=False,
)
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',
'COMPOSITOR',
'MULTIPLY',
'WIPE',
'GLOW',
'COLOR',
'SPEED',
'MULTICAM',
'GAUSSIAN_BLUR',
'TEXT',
'COLORMIX',
}
def draw_header_preset(self, context):
strip = context.active_strip
if strip.type == 'TEXT':
STRIP_PT_effect_text_style_presets.draw_panel_header(self.layout)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
strip = context.active_strip
layout.active = not strip.mute
strip_type = strip.type
if strip_type == 'COMPOSITOR':
layout.template_ID(strip, "node_group", new="node.new_compositor_sequencer_node_group")
draw_compositor_effect_node_group_errors(layout, strip.node_group, strip.input_count)
if strip.input_count > 0:
col = layout.column()
row = col.row()
row.prop(strip, "input_1")
if strip.input_count > 1:
is_transition = strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'COMPOSITOR'}
if not is_transition:
row.operator("sequencer.swap_inputs", text="", icon='SORT_ASC')
row = col.row()
row.prop(strip, "input_2")
if not is_transition:
row.operator("sequencer.swap_inputs", text="", icon='SORT_DESC')
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='STATUS_INFO')
elif strip_type == 'TEXT':
layout = self.layout
col = layout.column()
col.textbox_with_state(strip, "text", textbox_state=strip.textbox_state)
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', 'COMPOSITOR'}:
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)
if strip_type == 'COMPOSITOR':
layout.template_compositor_strip_inputs(strip)
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")
row = col.row()
if strip.use_absolute_line_spacing:
row.prop(strip, "abs_space_line", text="Line Spacing")
else:
row.prop(strip, "space_line", text="Line Spacing")
row.prop(strip, "use_absolute_line_spacing", text="", icon='FIXED_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_with_menu(
strip.colorspace_settings,
"name",
text="Color Space",
menu="UI_MT_color_space_select")
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_with_menu(
strip.colorspace_settings,
"name",
text="Color Space",
menu="UI_MT_color_space_select")
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")
if scene:
row = layout.row()
row.enabled = (strip.scene_input == 'CAMERA')
row.template_search(strip, "view_layer", scene, "view_layers", text="View Layer")
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=col.property_split_factor)
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.
content_start = strip.content_start
content_duration = strip.content_duration
content_end = strip.content_end
left_handle = strip.left_handle
duration = strip.duration
right_handle = strip.right_handle
length_list = (
str(round(left_handle, 0)),
str(round(duration, 0)),
str(round(right_handle, 0)),
)
if not is_effect:
length_list = length_list + (
str(round(strip.content_trim_start, 0)),
str(round(strip.content_trim_end, 0)),
str(round(content_start, 0)),
str(round(content_duration, 0)),
str(round(content_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="Channel")
split.prop(strip, "channel", text="")
layout.alignment = 'RIGHT'
sub = layout.column(align=True)
split = sub.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="Left Handle")
split.prop(strip, "left_handle", text=smpte_from_frame(left_handle))
split = sub.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="Strip Duration")
split.prop(strip, "duration", text=smpte_from_frame(duration))
split = sub.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="Right Handle")
split.prop(strip, "right_handle", text=smpte_from_frame(right_handle))
if not is_effect:
sub = layout.column(align=True)
split = sub.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="Content Start")
split.prop(strip, "content_start", text=smpte_from_frame(content_start))
split = sub.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="Duration")
split.prop(strip, "content_duration", text=smpte_from_frame(content_duration))
split = sub.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="End")
split.prop(strip, "content_end", text=smpte_from_frame(content_end))
layout.alignment = 'RIGHT'
sub = layout.column(align=True)
split = sub.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="Content Trim Start")
split.prop(strip, "content_trim_start", text=smpte_from_frame(strip.content_trim_start))
split = sub.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="End")
split.prop(strip, "content_trim_end", text=smpte_from_frame(strip.content_trim_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 >= left_handle) and
(frame_current <= left_handle + duration)
)
split = col.split(factor=factor + max_factor, align=True)
split.alignment = 'RIGHT'
split.label(text="Playhead Offset")
split = split.split(factor=factor + 0.3 + max_factor, align=True)
frame_display = frame_current - left_handle
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="Scene Frame Range")
split.alignment = 'LEFT'
split.label(text="{:d}-{:d} ({:d})".format(sta, end, end - sta + 1), translate=False)
if not is_effect:
sub = layout.row(align=True)
split = sub.split(factor=factor + max_factor)
split.alignment = 'RIGHT'
split.label(text="")
split.prop(strip, "show_retiming_keys")
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 in {'SOUND', 'META', }
def draw(self, context):
layout = self.layout
strip = context.active_strip
sound = getattr(strip, "sound", None)
layout.active = not strip.mute
if sound is not None or strip.type == 'META':
layout.use_property_split = True
col = layout.column()
split = col.split(factor=col.property_split_factor)
split.alignment = 'RIGHT'
split.label(text="Volume", text_ctxt=i18n_contexts.id_sound)
split.prop(strip, "volume", text="")
if sound is not None:
layout.use_property_split = False
col = layout.column()
split = col.split(factor=col.property_split_factor)
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=col.property_split_factor)
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=col.property_split_factor)
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=col.property_split_factor)
split.label(text="")
split.prop(strip, "pitch_correction")
split = col.split(factor=col.property_split_factor)
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")
if strip.type == 'COLOR':
col = layout.column(align=True)
col.prop(strip, "width", text="Width")
col.prop(strip, "height", text="Height")
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',
}
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', 'COMPOSITOR',
'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_style_presets,
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)