Cleanup: use 'tool_settings' name everywhere in UI scripts
[blender.git] / release / scripts / startup / bl_ui / space_sequencer.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20 import bpy
21 from bpy.types import Header, Menu, Panel
22 from rna_prop_ui import PropertyPanel
23 from .properties_grease_pencil_common import (
24     GreasePencilDataPanel,
25     GreasePencilPaletteColorPanel,
26     GreasePencilToolsPanel,
27 )
28 from bpy.app.translations import pgettext_iface as iface_
29
30
31 def act_strip(context):
32     try:
33         return context.scene.sequence_editor.active_strip
34     except AttributeError:
35         return None
36
37
38 def draw_color_balance(layout, color_balance):
39     box = layout.box()
40     split = box.split(percentage=0.35)
41     col = split.column(align=True)
42     col.label(text="Lift:")
43     col.separator()
44     col.separator()
45     col.prop(color_balance, "lift", text="")
46     col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT')
47     split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True)
48
49     box = layout.box()
50     split = box.split(percentage=0.35)
51     col = split.column(align=True)
52     col.label(text="Gamma:")
53     col.separator()
54     col.separator()
55     col.prop(color_balance, "gamma", text="")
56     col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT')
57     split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
58
59     box = layout.box()
60     split = box.split(percentage=0.35)
61     col = split.column(align=True)
62     col.label(text="Gain:")
63     col.separator()
64     col.separator()
65     col.prop(color_balance, "gain", text="")
66     col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT')
67     split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
68
69
70 class SEQUENCER_HT_header(Header):
71     bl_space_type = 'SEQUENCE_EDITOR'
72
73     def draw(self, context):
74         layout = self.layout
75
76         st = context.space_data
77         scene = context.scene
78
79         row = layout.row(align=True)
80         row.template_header()
81
82         SEQUENCER_MT_editor_menus.draw_collapsible(context, layout)
83
84         row = layout.row(align=True)
85         row.prop(scene, "use_preview_range", text="", toggle=True)
86         row.prop(scene, "lock_frame_selection_to_range", text="", toggle=True)
87
88         layout.prop(st, "view_type", expand=True, text="")
89
90         if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
91             layout.prop(st, "display_mode", expand=True, text="")
92
93         if st.view_type == 'SEQUENCER':
94             row = layout.row(align=True)
95             row.operator("sequencer.copy", text="", icon='COPYDOWN')
96             row.operator("sequencer.paste", text="", icon='PASTEDOWN')
97
98             layout.separator()
99             layout.operator("sequencer.refresh_all")
100             layout.prop(st, "show_backdrop")
101         else:
102             if st.view_type == 'SEQUENCER_PREVIEW':
103                 layout.separator()
104                 layout.operator("sequencer.refresh_all")
105
106             layout.prop(st, "preview_channels", expand=True, text="")
107             layout.prop(st, "display_channel", text="Channel")
108
109             ed = scene.sequence_editor
110             if ed:
111                 row = layout.row(align=True)
112                 row.prop(ed, "show_overlay", text="", icon='GHOST_ENABLED')
113                 if ed.show_overlay:
114                     row.prop(ed, "overlay_frame", text="")
115                     row.prop(ed, "use_overlay_lock", text="", icon='LOCKED')
116
117                     row = layout.row()
118                     row.prop(st, "overlay_type", text="")
119
120         if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
121             gpd = context.gpencil_data
122             tool_settings = context.tool_settings
123
124             # Proportional editing
125             if gpd and gpd.use_stroke_edit_mode:
126                 row = layout.row(align=True)
127                 row.prop(tool_settings, "proportional_edit", icon_only=True)
128                 if tool_settings.proportional_edit != 'DISABLED':
129                     row.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
130
131         row = layout.row(align=True)
132         row.operator("render.opengl", text="", icon='RENDER_STILL').sequencer = True
133         props = row.operator("render.opengl", text="", icon='RENDER_ANIMATION')
134         props.animation = True
135         props.sequencer = True
136
137         layout.template_running_jobs()
138
139
140 class SEQUENCER_MT_editor_menus(Menu):
141     bl_idname = "SEQUENCER_MT_editor_menus"
142     bl_label = ""
143
144     def draw(self, context):
145         self.draw_menus(self.layout, context)
146
147     @staticmethod
148     def draw_menus(layout, context):
149         st = context.space_data
150
151         layout.menu("SEQUENCER_MT_view")
152
153         if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
154             layout.menu("SEQUENCER_MT_select")
155             layout.menu("SEQUENCER_MT_marker")
156             layout.menu("SEQUENCER_MT_add")
157             layout.menu("SEQUENCER_MT_frame")
158             layout.menu("SEQUENCER_MT_strip")
159
160
161 class SEQUENCER_MT_view_toggle(Menu):
162     bl_label = "View Type"
163
164     def draw(self, context):
165         layout = self.layout
166
167         layout.operator("sequencer.view_toggle").type = 'SEQUENCER'
168         layout.operator("sequencer.view_toggle").type = 'PREVIEW'
169         layout.operator("sequencer.view_toggle").type = 'SEQUENCER_PREVIEW'
170
171
172 class SEQUENCER_MT_view(Menu):
173     bl_label = "View"
174
175     def draw(self, context):
176         layout = self.layout
177
178         st = context.space_data
179         is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}
180         is_sequencer_view = st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}
181
182         if st.view_type == 'PREVIEW':
183             # Specifying the REGION_PREVIEW context is needed in preview-only
184             # mode, else the lookup for the shortcut will fail in
185             # wm_keymap_item_find_props() (see #32595).
186             layout.operator_context = 'INVOKE_REGION_PREVIEW'
187         layout.operator("sequencer.properties", icon='MENU_PANEL')
188         layout.operator_context = 'INVOKE_DEFAULT'
189
190         layout.separator()
191
192         if is_sequencer_view:
193             layout.operator_context = 'INVOKE_REGION_WIN'
194             layout.operator("sequencer.view_all", text="View all Sequences")
195             layout.operator("sequencer.view_selected")
196             layout.operator("sequencer.view_frame")
197             layout.operator_context = 'INVOKE_DEFAULT'
198         if is_preview:
199             layout.operator_context = 'INVOKE_REGION_PREVIEW'
200             layout.operator("sequencer.view_all_preview", text="Fit Preview in window")
201
202             layout.separator()
203
204             ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1))
205
206             for a, b in ratios:
207                 layout.operator(
208                     "sequencer.view_zoom_ratio",
209                     text=iface_("Zoom %d:%d") % (a, b),
210                     translate=False,
211                 ).ratio = a / b
212
213             layout.separator()
214
215             layout.operator_context = 'INVOKE_DEFAULT'
216
217             # # XXX, invokes in the header view
218             # layout.operator("sequencer.view_ghost_border", text="Overlay Border")
219
220         if is_sequencer_view:
221             layout.prop(st, "show_seconds")
222             layout.prop(st, "show_frame_indicator")
223             layout.prop(st, "show_strip_offset")
224
225             layout.prop_menu_enum(st, "waveform_draw_type")
226
227         if is_preview:
228             if st.display_mode == 'IMAGE':
229                 layout.prop(st, "show_safe_areas")
230                 layout.prop(st, "show_metadata")
231             elif st.display_mode == 'WAVEFORM':
232                 layout.prop(st, "show_separate_color")
233
234         layout.separator()
235
236         if is_sequencer_view:
237             layout.prop(st, "use_marker_sync")
238             layout.separator()
239
240         layout.operator("screen.area_dupli")
241         layout.operator("screen.screen_full_area")
242         layout.operator("screen.screen_full_area", text="Toggle Fullscreen Area").use_hide_panels = True
243
244
245 class SEQUENCER_MT_select(Menu):
246     bl_label = "Select"
247
248     def draw(self, context):
249         layout = self.layout
250
251         layout.operator("sequencer.select_active_side", text="Strips to the Left").side = 'LEFT'
252         layout.operator("sequencer.select_active_side", text="Strips to the Right").side = 'RIGHT'
253         props = layout.operator("sequencer.select", text="All Strips to the Left")
254         props.left_right = 'LEFT'
255         props.linked_time = True
256         props = layout.operator("sequencer.select", text="All Strips to the Right")
257         props.left_right = 'RIGHT'
258         props.linked_time = True
259
260         layout.separator()
261         layout.operator("sequencer.select_handles", text="Surrounding Handles").side = 'BOTH'
262         layout.operator("sequencer.select_handles", text="Left Handle").side = 'LEFT'
263         layout.operator("sequencer.select_handles", text="Right Handle").side = 'RIGHT'
264         layout.separator()
265         layout.operator_menu_enum("sequencer.select_grouped", "type", text="Grouped")
266         layout.operator("sequencer.select_linked")
267         layout.operator("sequencer.select_less")
268         layout.operator("sequencer.select_more")
269         layout.operator("sequencer.select_all").action = 'TOGGLE'
270         layout.operator("sequencer.select_all", text="Inverse").action = 'INVERT'
271
272
273 class SEQUENCER_MT_marker(Menu):
274     bl_label = "Marker"
275
276     def draw(self, context):
277         layout = self.layout
278
279         from .space_time import marker_menu_generic
280         marker_menu_generic(layout)
281
282
283 class SEQUENCER_MT_frame(Menu):
284     bl_label = "Frame"
285
286     def draw(self, context):
287         layout = self.layout
288
289         layout.operator("anim.previewrange_clear")
290         layout.operator("anim.previewrange_set")
291
292         layout.separator()
293
294         props = layout.operator("sequencer.strip_jump", text="Jump to Previous Strip")
295         props.next = False
296         props.center = False
297         props = layout.operator("sequencer.strip_jump", text="Jump to Next Strip")
298         props.next = True
299         props.center = False
300
301         layout.separator()
302
303         props = layout.operator("sequencer.strip_jump", text="Jump to Previous Strip (Center)")
304         props.next = False
305         props.center = True
306         props = layout.operator("sequencer.strip_jump", text="Jump to Next Strip (Center)")
307         props.next = True
308         props.center = True
309
310
311 class SEQUENCER_MT_add(Menu):
312     bl_label = "Add"
313
314     def draw(self, context):
315         layout = self.layout
316
317         layout.operator_context = 'INVOKE_REGION_WIN'
318
319         if len(bpy.data.scenes) > 10:
320             layout.operator_context = 'INVOKE_DEFAULT'
321             layout.operator("sequencer.scene_strip_add", text="Scene...")
322         else:
323             layout.operator_menu_enum("sequencer.scene_strip_add", "scene", text="Scene")
324
325         if len(bpy.data.movieclips) > 10:
326             layout.operator_context = 'INVOKE_DEFAULT'
327             layout.operator("sequencer.movieclip_strip_add", text="Clips...")
328         else:
329             layout.operator_menu_enum("sequencer.movieclip_strip_add", "clip", text="Clip")
330
331         if len(bpy.data.masks) > 10:
332             layout.operator_context = 'INVOKE_DEFAULT'
333             layout.operator("sequencer.mask_strip_add", text="Masks...")
334         else:
335             layout.operator_menu_enum("sequencer.mask_strip_add", "mask", text="Mask")
336
337         layout.operator("sequencer.movie_strip_add", text="Movie")
338         layout.operator("sequencer.image_strip_add", text="Image")
339         layout.operator("sequencer.sound_strip_add", text="Sound")
340
341         layout.menu("SEQUENCER_MT_add_generate")
342         layout.menu("SEQUENCER_MT_add_effect")
343
344
345 class SEQUENCER_MT_add_generate(Menu):
346     bl_label = "Generate"
347
348     def draw(self, context):
349         layout = self.layout
350
351         layout.operator_context = 'INVOKE_REGION_WIN'
352         layout.operator("sequencer.effect_strip_add", text="Color").type = 'COLOR'
353         layout.operator("sequencer.effect_strip_add", text="Text").type = 'TEXT'
354
355
356 class SEQUENCER_MT_add_effect(Menu):
357     bl_label = "Effect Strip"
358
359     def draw(self, context):
360         layout = self.layout
361
362         layout.operator_context = 'INVOKE_REGION_WIN'
363
364         layout.operator("sequencer.effect_strip_add", text="Add").type = 'ADD'
365         layout.operator("sequencer.effect_strip_add", text="Subtract").type = 'SUBTRACT'
366         layout.operator("sequencer.effect_strip_add", text="Alpha Over").type = 'ALPHA_OVER'
367         layout.operator("sequencer.effect_strip_add", text="Alpha Under").type = 'ALPHA_UNDER'
368         layout.operator("sequencer.effect_strip_add", text="Cross").type = 'CROSS'
369         layout.operator("sequencer.effect_strip_add", text="Gamma Cross").type = 'GAMMA_CROSS'
370         layout.operator("sequencer.effect_strip_add", text="Gaussian Blur").type = 'GAUSSIAN_BLUR'
371         layout.operator("sequencer.effect_strip_add", text="Multiply").type = 'MULTIPLY'
372         layout.operator("sequencer.effect_strip_add", text="Over Drop").type = 'OVER_DROP'
373         layout.operator("sequencer.effect_strip_add", text="Wipe").type = 'WIPE'
374         layout.operator("sequencer.effect_strip_add", text="Glow").type = 'GLOW'
375         layout.operator("sequencer.effect_strip_add", text="Color Mix").type = 'COLORMIX'
376         layout.operator("sequencer.effect_strip_add", text="Transform").type = 'TRANSFORM'
377         layout.operator("sequencer.effect_strip_add", text="Speed Control").type = 'SPEED'
378         layout.operator("sequencer.effect_strip_add", text="Multicam Selector").type = 'MULTICAM'
379         layout.operator("sequencer.effect_strip_add", text="Adjustment Layer").type = 'ADJUSTMENT'
380
381
382 class SEQUENCER_MT_strip_transform(Menu):
383     bl_label = "Transform"
384
385     def draw(self, context):
386         layout = self.layout
387
388         layout.operator("transform.transform", text="Grab/Move").mode = 'TRANSLATION'
389         layout.operator("transform.transform", text="Grab/Extend from Frame").mode = 'TIME_EXTEND'
390         layout.operator("sequencer.slip", text="Slip Strip Contents")
391
392         layout.separator()
393         layout.operator_menu_enum("sequencer.swap", "side")
394
395         layout.separator()
396         layout.operator("sequencer.gap_remove").all = False
397         layout.operator("sequencer.gap_insert")
398
399
400 class SEQUENCER_MT_strip_input(Menu):
401     bl_label = "Inputs"
402
403     def draw(self, context):
404         layout = self.layout
405         strip = act_strip(context)
406
407         layout.operator("sequencer.reload", text="Reload Strips")
408         layout.operator("sequencer.reload", text="Reload Strips and Adjust Length").adjust_length = True
409         prop = layout.operator("sequencer.change_path", text="Change Path/Files")
410         layout.operator("sequencer.swap_data", text="Swap Data")
411
412         if strip:
413             stype = strip.type
414
415             if stype == 'IMAGE':
416                 prop.filter_image = True
417             elif stype == 'MOVIE':
418                 prop.filter_movie = True
419             elif stype == 'SOUND':
420                 prop.filter_sound = True
421
422
423 class SEQUENCER_MT_strip_lock_mute(Menu):
424     bl_label = "Lock/Mute"
425
426     def draw(self, context):
427         layout = self.layout
428
429         layout.operator("sequencer.lock")
430         layout.operator("sequencer.unlock")
431         layout.operator("sequencer.mute").unselected = False
432         layout.operator("sequencer.unmute").unselected = False
433         layout.operator("sequencer.mute", text="Mute Deselected Strips").unselected = True
434
435
436 class SEQUENCER_MT_strip(Menu):
437     bl_label = "Strip"
438
439     def draw(self, context):
440         layout = self.layout
441
442         layout.operator_context = 'INVOKE_REGION_WIN'
443
444         layout.operator("ed.undo")
445         layout.operator("ed.redo")
446         layout.operator("ed.undo_history")
447
448         layout.separator()
449         layout.menu("SEQUENCER_MT_strip_transform")
450         layout.operator("sequencer.snap")
451         layout.operator("sequencer.offset_clear")
452
453         layout.separator()
454         layout.operator("sequencer.duplicate_move")
455         layout.operator("sequencer.delete", text="Delete...")
456
457         layout.separator()
458         layout.operator("sequencer.cut", text="Cut (Hard) at frame").type = 'HARD'
459         layout.operator("sequencer.cut", text="Cut (Soft) at frame").type = 'SOFT'
460
461         layout.separator()
462         layout.operator("sequencer.deinterlace_selected_movies")
463         layout.operator("sequencer.rebuild_proxy")
464
465         strip = act_strip(context)
466
467         if strip:
468             stype = strip.type
469
470             if stype in {
471                     'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
472                     'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW',
473                     'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT',
474                     'GAUSSIAN_BLUR', 'TEXT',
475             }:
476                 layout.separator()
477                 layout.operator_menu_enum("sequencer.change_effect_input", "swap")
478                 layout.operator_menu_enum("sequencer.change_effect_type", "type")
479                 layout.operator("sequencer.reassign_inputs")
480                 layout.operator("sequencer.swap_inputs")
481             elif stype in {'IMAGE', 'MOVIE'}:
482                 layout.separator()
483                 layout.operator("sequencer.rendersize")
484                 layout.operator("sequencer.images_separate")
485             elif stype == 'SOUND':
486                 layout.separator()
487                 layout.operator("sequencer.crossfade_sounds")
488             elif stype == 'META':
489                 layout.separator()
490                 layout.operator("sequencer.meta_separate")
491
492         layout.separator()
493         layout.operator("sequencer.meta_make")
494
495         layout.separator()
496         layout.menu("SEQUENCER_MT_strip_input")
497
498         layout.separator()
499         layout.menu("SEQUENCER_MT_strip_lock_mute")
500
501
502 class SequencerButtonsPanel:
503     bl_space_type = 'SEQUENCE_EDITOR'
504     bl_region_type = 'UI'
505
506     @staticmethod
507     def has_sequencer(context):
508         return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
509
510     @classmethod
511     def poll(cls, context):
512         return cls.has_sequencer(context) and (act_strip(context) is not None)
513
514
515 class SequencerButtonsPanel_Output:
516     bl_space_type = 'SEQUENCE_EDITOR'
517     bl_region_type = 'UI'
518
519     @staticmethod
520     def has_preview(context):
521         st = context.space_data
522         return (st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}) or st.show_backdrop
523
524     @classmethod
525     def poll(cls, context):
526         return cls.has_preview(context)
527
528
529 class SEQUENCER_PT_edit(SequencerButtonsPanel, Panel):
530     bl_label = "Edit Strip"
531     bl_category = "Strip"
532
533     def draw(self, context):
534         layout = self.layout
535
536         scene = context.scene
537         frame_current = scene.frame_current
538         strip = act_strip(context)
539
540         split = layout.split(percentage=0.25)
541         split.label(text="Name:")
542         split.prop(strip, "name", text="")
543
544         split = layout.split(percentage=0.25)
545         split.label(text="Type:")
546         split.prop(strip, "type", text="")
547
548         if strip.type != 'SOUND':
549             split = layout.split(percentage=0.25)
550             split.label(text="Blend:")
551             split.prop(strip, "blend_type", text="")
552
553             row = layout.row(align=True)
554             sub = row.row(align=True)
555             sub.active = (not strip.mute)
556             sub.prop(strip, "blend_alpha", text="Opacity", slider=True)
557             row.prop(strip, "mute", toggle=True, icon_only=True)
558
559         else:
560             row = layout.row()
561             row.prop(strip, "mute", toggle=True, icon_only=True, icon='MUTE_IPO_OFF')
562
563         col = layout.column(align=True)
564         row = col.row(align=True)
565
566         row_sub = row.row(align=True)
567         row_sub.enabled = not strip.lock
568         row_sub.prop(strip, "channel")
569         row.prop(strip, "lock", toggle=True, icon_only=True)
570
571         sub = col.column(align=True)
572         sub.enabled = not strip.lock
573         sub.prop(strip, "frame_start")
574         sub.prop(strip, "frame_final_duration")
575
576         col = layout.column(align=True)
577         row = col.row(align=True)
578         row.label(text=iface_("Final Length: %s") % bpy.utils.smpte_from_frame(strip.frame_final_duration),
579                   translate=False)
580         row = col.row(align=True)
581         row.active = (frame_current >= strip.frame_start and frame_current <= strip.frame_start + strip.frame_duration)
582         row.label(text=iface_("Playhead: %d") % (frame_current - strip.frame_start), translate=False)
583
584         col.label(text=iface_("Frame Offset %d:%d") % (strip.frame_offset_start, strip.frame_offset_end),
585                   translate=False)
586         col.label(text=iface_("Frame Still %d:%d") % (strip.frame_still_start, strip.frame_still_end), translate=False)
587
588         elem = False
589
590         if strip.type == 'IMAGE':
591             elem = strip.strip_elem_from_frame(frame_current)
592         elif strip.type == 'MOVIE':
593             elem = strip.elements[0]
594
595         if elem and elem.orig_width > 0 and elem.orig_height > 0:
596             col.label(text=iface_("Original Dimension: %dx%d") % (elem.orig_width, elem.orig_height), translate=False)
597         else:
598             col.label(text="Original Dimension: None")
599
600
601 class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
602     bl_label = "Effect Strip"
603     bl_category = "Strip"
604
605     @classmethod
606     def poll(cls, context):
607         if not cls.has_sequencer(context):
608             return False
609
610         strip = act_strip(context)
611         if not strip:
612             return False
613
614         return strip.type in {
615             'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
616             'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
617             'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED',
618             'MULTICAM', 'GAUSSIAN_BLUR', 'TEXT', 'COLORMIX'
619         }
620
621     def draw(self, context):
622         layout = self.layout
623
624         strip = act_strip(context)
625
626         if strip.input_count > 0:
627             col = layout.column()
628             col.enabled = False
629             col.prop(strip, "input_1")
630             if strip.input_count > 1:
631                 col.prop(strip, "input_2")
632
633         if strip.type == 'COLOR':
634             layout.prop(strip, "color")
635
636         elif strip.type == 'WIPE':
637             col = layout.column()
638             col.prop(strip, "transition_type")
639             col.label(text="Direction:")
640             col.row().prop(strip, "direction", expand=True)
641
642             col = layout.column()
643             col.prop(strip, "blur_width", slider=True)
644             if strip.transition_type in {'SINGLE', 'DOUBLE'}:
645                 col.prop(strip, "angle")
646
647         elif strip.type == 'GLOW':
648             flow = layout.column_flow()
649             flow.prop(strip, "threshold", slider=True)
650             flow.prop(strip, "clamp", slider=True)
651             flow.prop(strip, "boost_factor")
652             flow.prop(strip, "blur_radius")
653
654             row = layout.row()
655             row.prop(strip, "quality", slider=True)
656             row.prop(strip, "use_only_boost")
657
658         elif strip.type == 'SPEED':
659             layout.prop(strip, "use_default_fade", "Stretch to input strip length")
660             if not strip.use_default_fade:
661                 layout.prop(strip, "use_as_speed")
662                 if strip.use_as_speed:
663                     layout.prop(strip, "speed_factor")
664                 else:
665                     layout.prop(strip, "speed_factor", text="Frame Number")
666                     layout.prop(strip, "scale_to_length")
667
668         elif strip.type == 'TRANSFORM':
669             layout = self.layout
670             col = layout.column()
671
672             col.prop(strip, "interpolation")
673             col.prop(strip, "translation_unit")
674             col = layout.column(align=True)
675             col.label(text="Position:")
676             row = col.row(align=True)
677             row.prop(strip, "translate_start_x", text="X")
678             row.prop(strip, "translate_start_y", text="Y")
679
680             layout.separator()
681
682             col = layout.column(align=True)
683             col.prop(strip, "use_uniform_scale")
684             if strip.use_uniform_scale:
685                 col = layout.column(align=True)
686                 col.prop(strip, "scale_start_x", text="Scale")
687             else:
688                 col = layout.column(align=True)
689                 col.label(text="Scale:")
690                 row = col.row(align=True)
691                 row.prop(strip, "scale_start_x", text="X")
692                 row.prop(strip, "scale_start_y", text="Y")
693
694             layout.separator()
695
696             col = layout.column(align=True)
697             col.label(text="Rotation:")
698             col.prop(strip, "rotation_start", text="Rotation")
699
700         elif strip.type == 'MULTICAM':
701             col = layout.column(align=True)
702             strip_channel = strip.channel
703
704             col.prop(strip, "multicam_source", text="Source Channel")
705
706             # The multicam strip needs at least 2 strips to be useful
707             if strip_channel > 2:
708                 BT_ROW = 4
709
710                 col.label("Cut To:")
711                 row = col.row()
712
713                 for i in range(1, strip_channel):
714                     if (i % BT_ROW) == 1:
715                         row = col.row(align=True)
716
717                     # Workaround - .enabled has to have a separate UI block to work
718                     if i == strip.multicam_source:
719                         sub = row.row(align=True)
720                         sub.enabled = False
721                         sub.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i
722                     else:
723                         sub_1 = row.row(align=True)
724                         sub_1.enabled = True
725                         sub_1.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i
726
727                 if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW:
728                     for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)):
729                         row.label("")
730             else:
731                 col.separator()
732                 col.label(text="Two or more channels are needed below this strip", icon='INFO')
733
734         elif strip.type == 'TEXT':
735             col = layout.column()
736             col.prop(strip, "text")
737             col.prop(strip, "font_size")
738
739             row = col.row()
740             row.prop(strip, "color")
741             row = col.row()
742             row.prop(strip, "use_shadow")
743             rowsub = row.row()
744             rowsub.active = strip.use_shadow
745             rowsub.prop(strip, "shadow_color", text="")
746
747             col.prop(strip, "align_x")
748             col.prop(strip, "align_y")
749             col.label("Location")
750             row = col.row(align=True)
751             row.prop(strip, "location", text="")
752             col.prop(strip, "wrap_width")
753             layout.operator("sequencer.export_subtitles")
754
755         col = layout.column(align=True)
756         if strip.type == 'SPEED':
757             col.prop(strip, "multiply_speed")
758         elif strip.type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}:
759             col.prop(strip, "use_default_fade", "Default fade")
760             if not strip.use_default_fade:
761                 col.prop(strip, "effect_fader", text="Effect Fader")
762         elif strip.type == 'GAUSSIAN_BLUR':
763             row = col.row(align=True)
764             row.prop(strip, "size_x")
765             row.prop(strip, "size_y")
766         elif strip.type == 'COLORMIX':
767             split = layout.split(percentage=0.35)
768             split.label(text="Blend Mode:")
769             split.prop(strip, "blend_effect", text="")
770             row = layout.row(align=True)
771             row.prop(strip, "factor", slider=True)
772
773
774 class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
775     bl_label = "Strip Input"
776     bl_category = "Strip"
777
778     @classmethod
779     def poll(cls, context):
780         if not cls.has_sequencer(context):
781             return False
782
783         strip = act_strip(context)
784         if not strip:
785             return False
786
787         return strip.type in {
788             'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'META',
789             'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
790             'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
791             'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
792             'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX'
793         }
794
795     def draw(self, context):
796         layout = self.layout
797         scene = context.scene
798
799         strip = act_strip(context)
800
801         seq_type = strip.type
802
803         # draw a filename if we have one
804         if seq_type == 'IMAGE':
805             split = layout.split(percentage=0.2)
806             split.label(text="Path:")
807             split.prop(strip, "directory", text="")
808
809             # Current element for the filename
810
811             elem = strip.strip_elem_from_frame(scene.frame_current)
812             if elem:
813                 split = layout.split(percentage=0.2)
814                 split.label(text="File:")
815                 split.prop(elem, "filename", text="")  # strip.elements[0] could be a fallback
816
817             split = layout.split(percentage=0.4)
818             split.label(text="Color Space:")
819             split.prop(strip.colorspace_settings, "name", text="")
820
821             split = layout.split(percentage=0.4)
822             split.label(text="Alpha:")
823             split.prop(strip, "alpha_mode", text="")
824
825             layout.operator("sequencer.change_path", icon='FILESEL').filter_image = True
826
827         elif seq_type == 'MOVIE':
828             split = layout.split(percentage=0.2)
829             split.label(text="Path:")
830             split.prop(strip, "filepath", text="")
831
832             split = layout.split(percentage=0.4)
833             split.label(text="Color Space:")
834             split.prop(strip.colorspace_settings, "name", text="")
835
836             layout.prop(strip, "mpeg_preseek")
837             layout.prop(strip, "stream_index")
838
839         layout.prop(strip, "use_translation", text="Image Offset")
840         if strip.use_translation:
841             row = layout.row(align=True)
842             row.prop(strip.transform, "offset_x", text="X")
843             row.prop(strip.transform, "offset_y", text="Y")
844
845         layout.prop(strip, "use_crop", text="Image Crop")
846         if strip.use_crop:
847             col = layout.column(align=True)
848             col.prop(strip.crop, "max_y")
849             row = col.row(align=True)
850             row.prop(strip.crop, "min_x")
851             row.prop(strip.crop, "max_x")
852             col.prop(strip.crop, "min_y")
853
854         if not isinstance(strip, bpy.types.EffectSequence):
855             layout.label(text="Trim Duration (hard):")
856             row = layout.row(align=True)
857             row.prop(strip, "animation_offset_start", text="Start")
858             row.prop(strip, "animation_offset_end", text="End")
859
860         layout.label(text="Trim Duration (soft):")
861         row = layout.row(align=True)
862         row.prop(strip, "frame_offset_start", text="Start")
863         row.prop(strip, "frame_offset_end", text="End")
864
865         if scene.render.use_multiview and seq_type in {'IMAGE', 'MOVIE'}:
866             layout.prop(strip, "use_multiview")
867
868             col = layout.column()
869             col.active = strip.use_multiview
870
871             col.label(text="Views Format:")
872             col.row().prop(strip, "views_format", expand=True)
873
874             box = col.box()
875             box.active = strip.views_format == 'STEREO_3D'
876             box.template_image_stereo_3d(strip.stereo_3d_format)
877
878
879 class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel):
880     bl_label = "Sound"
881     bl_category = "Strip"
882
883     @classmethod
884     def poll(cls, context):
885         if not cls.has_sequencer(context):
886             return False
887
888         strip = act_strip(context)
889         if not strip:
890             return False
891
892         return (strip.type == 'SOUND')
893
894     def draw(self, context):
895         layout = self.layout
896
897         st = context.space_data
898         strip = act_strip(context)
899         sound = strip.sound
900
901         layout.template_ID(strip, "sound", open="sound.open")
902         if sound is not None:
903             layout.prop(sound, "filepath", text="")
904
905             row = layout.row()
906             if sound.packed_file:
907                 row.operator("sound.unpack", icon='PACKAGE', text="Unpack")
908             else:
909                 row.operator("sound.pack", icon='UGLYPACKAGE', text="Pack")
910
911             row.prop(sound, "use_memory_cache")
912
913             layout.prop(sound, "use_mono")
914
915         if st.waveform_draw_type == 'DEFAULT_WAVEFORMS':
916             layout.prop(strip, "show_waveform")
917
918         col = layout.column(align=True)
919         col.prop(strip, "volume")
920         col.prop(strip, "pitch")
921         col.prop(strip, "pan")
922
923         col = layout.column(align=True)
924         col.label(text="Trim Duration (hard):")
925         row = layout.row(align=True)
926         row.prop(strip, "animation_offset_start", text="Start")
927         row.prop(strip, "animation_offset_end", text="End")
928
929         col = layout.column(align=True)
930         col.label(text="Trim Duration (soft):")
931         row = layout.row(align=True)
932         row.prop(strip, "frame_offset_start", text="Start")
933         row.prop(strip, "frame_offset_end", text="End")
934
935
936 class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
937     bl_label = "Scene"
938     bl_category = "Strip"
939
940     @classmethod
941     def poll(cls, context):
942         if not cls.has_sequencer(context):
943             return False
944
945         strip = act_strip(context)
946         if not strip:
947             return False
948
949         return (strip.type == 'SCENE')
950
951     def draw(self, context):
952         layout = self.layout
953
954         strip = act_strip(context)
955
956         layout.template_ID(strip, "scene")
957
958         scene = strip.scene
959         layout.prop(strip, "use_sequence")
960
961         if not strip.use_sequence:
962             layout.label(text="Camera Override")
963             layout.template_ID(strip, "scene_camera")
964
965             layout.prop(strip, "use_grease_pencil", text="Show Grease Pencil")
966
967         if scene:
968             layout.prop(scene, "audio_volume", text="Audio Volume")
969
970         if not strip.use_sequence:
971             if scene:
972                 # Warning, this is not a good convention to follow.
973                 # Expose here because setting the alpha from the 'Render' menu is very inconvenient.
974                 layout.label("Preview")
975                 layout.prop(scene.render, "alpha_mode")
976
977         if scene:
978             sta = scene.frame_start
979             end = scene.frame_end
980             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
981
982
983 class SEQUENCER_PT_mask(SequencerButtonsPanel, Panel):
984     bl_label = "Mask"
985     bl_category = "Strip"
986
987     @classmethod
988     def poll(cls, context):
989         if not cls.has_sequencer(context):
990             return False
991
992         strip = act_strip(context)
993         if not strip:
994             return False
995
996         return (strip.type == 'MASK')
997
998     def draw(self, context):
999         layout = self.layout
1000
1001         strip = act_strip(context)
1002
1003         layout.template_ID(strip, "mask")
1004
1005         mask = strip.mask
1006
1007         if mask:
1008             sta = mask.frame_start
1009             end = mask.frame_end
1010             layout.label(text=iface_("Original frame range: %d-%d (%d)") % (sta, end, end - sta + 1), translate=False)
1011
1012
1013 class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel):
1014     bl_label = "Filter"
1015     bl_category = "Strip"
1016
1017     @classmethod
1018     def poll(cls, context):
1019         if not cls.has_sequencer(context):
1020             return False
1021
1022         strip = act_strip(context)
1023         if not strip:
1024             return False
1025
1026         return strip.type in {
1027             'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK',
1028             'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER',
1029             'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY',
1030             'OVER_DROP', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
1031             'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX'
1032         }
1033
1034     def draw(self, context):
1035         layout = self.layout
1036
1037         strip = act_strip(context)
1038
1039         col = layout.column()
1040         col.label(text="Video:")
1041         col.prop(strip, "strobe")
1042
1043         if strip.type == 'MOVIECLIP':
1044             col = layout.column()
1045             col.label(text="Tracker:")
1046             col.prop(strip, "stabilize2d")
1047
1048             col = layout.column()
1049             col.label(text="Distortion:")
1050             col.prop(strip, "undistort")
1051
1052         split = layout.split(percentage=0.6)
1053         col = split.column()
1054         col.prop(strip, "use_reverse_frames", text="Reverse")
1055         col.prop(strip, "use_deinterlace")
1056
1057         col = split.column()
1058         col.prop(strip, "use_flip_x", text="X Flip")
1059         col.prop(strip, "use_flip_y", text="Y Flip")
1060
1061         layout.label("Color:")
1062         col = layout.column(align=True)
1063         col.prop(strip, "color_saturation", text="Saturation")
1064         col.prop(strip, "color_multiply", text="Multiply")
1065         layout.prop(strip, "use_float", text="Convert to Float")
1066
1067
1068 class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel):
1069     bl_label = "Proxy/Timecode"
1070     bl_category = "Strip"
1071
1072     @classmethod
1073     def poll(cls, context):
1074         if not cls.has_sequencer(context):
1075             return False
1076
1077         strip = act_strip(context)
1078         if not strip:
1079             return False
1080
1081         return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'META', 'MULTICAM'}
1082
1083     def draw_header(self, context):
1084         strip = act_strip(context)
1085
1086         self.layout.prop(strip, "use_proxy", text="")
1087
1088     def draw(self, context):
1089         layout = self.layout
1090
1091         ed = context.scene.sequence_editor
1092
1093         strip = act_strip(context)
1094
1095         if strip.proxy:
1096             proxy = strip.proxy
1097
1098             flow = layout.column_flow()
1099             flow.prop(ed, "proxy_storage", text="Storage")
1100             if ed.proxy_storage == 'PROJECT':
1101                 flow.prop(ed, "proxy_dir", text="Directory")
1102             else:
1103                 flow.prop(proxy, "use_proxy_custom_directory")
1104                 flow.prop(proxy, "use_proxy_custom_file")
1105
1106                 if proxy.use_proxy_custom_directory and not proxy.use_proxy_custom_file:
1107                     flow.prop(proxy, "directory")
1108                 if proxy.use_proxy_custom_file:
1109                     flow.prop(proxy, "filepath")
1110
1111             row = layout.row(align=True)
1112             row.prop(strip.proxy, "build_25", toggle=True)
1113             row.prop(strip.proxy, "build_50", toggle=True)
1114             row.prop(strip.proxy, "build_75", toggle=True)
1115             row.prop(strip.proxy, "build_100", toggle=True)
1116
1117             layout.prop(proxy, "use_overwrite")
1118
1119             col = layout.column()
1120             col.prop(proxy, "quality", text="Build JPEG Quality")
1121
1122             if strip.type == 'MOVIE':
1123                 col = layout.column()
1124                 col.label(text="Use timecode index:")
1125
1126                 col.prop(proxy, "timecode")
1127
1128         col = layout.column()
1129         col.operator("sequencer.enable_proxies")
1130         col.operator("sequencer.rebuild_proxy")
1131
1132
1133 class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel):
1134     bl_label = "Scene Preview/Render"
1135     bl_space_type = 'SEQUENCE_EDITOR'
1136     bl_region_type = 'UI'
1137
1138     def draw(self, context):
1139         layout = self.layout
1140
1141         render = context.scene.render
1142
1143         col = layout.column()
1144         col.prop(render, "sequencer_gl_preview", text="")
1145
1146         row = col.row()
1147         row.active = render.sequencer_gl_preview == 'SOLID'
1148         row.prop(render, "use_sequencer_gl_textured_solid")
1149
1150         col.prop(render, "use_sequencer_gl_dof")
1151
1152
1153 class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel):
1154     bl_label = "View Settings"
1155
1156     def draw(self, context):
1157         layout = self.layout
1158
1159         st = context.space_data
1160
1161         col = layout.column()
1162         if st.display_mode == 'IMAGE':
1163             col.prop(st, "draw_overexposed")
1164             col.separator()
1165
1166         elif st.display_mode == 'WAVEFORM':
1167             col.prop(st, "show_separate_color")
1168
1169         col = layout.column()
1170         col.separator()
1171         col.prop(st, "proxy_render_size")
1172
1173
1174 class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel):
1175     bl_label = "Safe Areas"
1176     bl_options = {'DEFAULT_CLOSED'}
1177
1178     @classmethod
1179     def poll(cls, context):
1180         st = context.space_data
1181         is_preview = st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}
1182         return is_preview and (st.display_mode == 'IMAGE')
1183
1184     def draw_header(self, context):
1185         st = context.space_data
1186
1187         self.layout.prop(st, "show_safe_areas", text="")
1188
1189     def draw(self, context):
1190         from .properties_data_camera import draw_display_safe_settings
1191
1192         layout = self.layout
1193         st = context.space_data
1194         safe_data = context.scene.safe_areas
1195
1196         draw_display_safe_settings(layout, safe_data, st)
1197
1198
1199 class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
1200     bl_label = "Modifiers"
1201     bl_category = "Modifiers"
1202
1203     def draw(self, context):
1204         layout = self.layout
1205
1206         strip = act_strip(context)
1207         ed = context.scene.sequence_editor
1208
1209         layout.prop(strip, "use_linear_modifiers")
1210
1211         layout.operator_menu_enum("sequencer.strip_modifier_add", "type")
1212         layout.operator("sequencer.strip_modifier_copy")
1213
1214         for mod in strip.modifiers:
1215             box = layout.box()
1216
1217             row = box.row()
1218             row.prop(mod, "show_expanded", text="", emboss=False)
1219             row.prop(mod, "name", text="")
1220
1221             row.prop(mod, "mute", text="")
1222
1223             sub = row.row(align=True)
1224             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_UP')
1225             props.name = mod.name
1226             props.direction = 'UP'
1227             props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_DOWN')
1228             props.name = mod.name
1229             props.direction = 'DOWN'
1230
1231             row.operator("sequencer.strip_modifier_remove", text="", icon='X', emboss=False).name = mod.name
1232
1233             if mod.show_expanded:
1234                 row = box.row()
1235                 row.prop(mod, "input_mask_type", expand=True)
1236
1237                 if mod.input_mask_type == 'STRIP':
1238                     sequences_object = ed
1239                     if ed.meta_stack:
1240                         sequences_object = ed.meta_stack[-1]
1241                     box.prop_search(mod, "input_mask_strip", sequences_object, "sequences", text="Mask")
1242                 else:
1243                     box.prop(mod, "input_mask_id")
1244                     row = box.row()
1245                     row.prop(mod, "mask_time", expand=True)
1246
1247                 if mod.type == 'COLOR_BALANCE':
1248                     box.prop(mod, "color_multiply")
1249                     draw_color_balance(box, mod.color_balance)
1250                 elif mod.type == 'CURVES':
1251                     box.template_curve_mapping(mod, "curve_mapping", type='COLOR')
1252                 elif mod.type == 'HUE_CORRECT':
1253                     box.template_curve_mapping(mod, "curve_mapping", type='HUE')
1254                 elif mod.type == 'BRIGHT_CONTRAST':
1255                     col = box.column()
1256                     col.prop(mod, "bright")
1257                     col.prop(mod, "contrast")
1258                 elif mod.type == 'WHITE_BALANCE':
1259                     col = box.column()
1260                     col.prop(mod, "white_value")
1261                 elif mod.type == 'TONEMAP':
1262                     col = box.column()
1263                     col.prop(mod, "tonemap_type")
1264                     if mod.tonemap_type == 'RD_PHOTORECEPTOR':
1265                         col.prop(mod, "intensity")
1266                         col.prop(mod, "contrast")
1267                         col.prop(mod, "adaptation")
1268                         col.prop(mod, "correction")
1269                     elif mod.tonemap_type == 'RH_SIMPLE':
1270                         col.prop(mod, "key")
1271                         col.prop(mod, "offset")
1272                         col.prop(mod, "gamma")
1273
1274
1275 class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Output, Panel):
1276     bl_space_type = 'SEQUENCE_EDITOR'
1277     bl_region_type = 'UI'
1278
1279     # NOTE: this is just a wrapper around the generic GP Panel
1280     # But, it should only show up when there are images in the preview region
1281
1282
1283 class SEQUENCER_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, SequencerButtonsPanel_Output, Panel):
1284     bl_space_type = 'SEQUENCE_EDITOR'
1285     bl_region_type = 'UI'
1286
1287     # NOTE: this is just a wrapper around the generic GP Panel
1288     # But, it should only show up when there are images in the preview region
1289
1290
1291 class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel):
1292     bl_space_type = 'SEQUENCE_EDITOR'
1293     bl_region_type = 'UI'
1294
1295     # NOTE: this is just a wrapper around the generic GP tools panel
1296     # It contains access to some essential tools usually found only in
1297     # toolbar, which doesn't exist here...
1298
1299
1300 class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel):
1301     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
1302     _context_path = "scene.sequence_editor.active_strip"
1303     _property_type = (bpy.types.Sequence,)
1304     bl_category = "Strip"
1305
1306
1307 classes = (
1308     SEQUENCER_HT_header,
1309     SEQUENCER_MT_editor_menus,
1310     SEQUENCER_MT_view,
1311     SEQUENCER_MT_view_toggle,
1312     SEQUENCER_MT_select,
1313     SEQUENCER_MT_marker,
1314     SEQUENCER_MT_frame,
1315     SEQUENCER_MT_add,
1316     SEQUENCER_MT_add_generate,
1317     SEQUENCER_MT_add_effect,
1318     SEQUENCER_MT_strip,
1319     SEQUENCER_MT_strip_transform,
1320     SEQUENCER_MT_strip_input,
1321     SEQUENCER_MT_strip_lock_mute,
1322     SEQUENCER_PT_edit,
1323     SEQUENCER_PT_effect,
1324     SEQUENCER_PT_input,
1325     SEQUENCER_PT_sound,
1326     SEQUENCER_PT_scene,
1327     SEQUENCER_PT_mask,
1328     SEQUENCER_PT_filter,
1329     SEQUENCER_PT_proxy,
1330     SEQUENCER_PT_preview,
1331     SEQUENCER_PT_view,
1332     SEQUENCER_PT_view_safe_areas,
1333     SEQUENCER_PT_modifiers,
1334     SEQUENCER_PT_grease_pencil,
1335     SEQUENCER_PT_grease_pencil_palettecolor,
1336     SEQUENCER_PT_grease_pencil_tools,
1337     SEQUENCER_PT_custom_props,
1338 )
1339
1340 if __name__ == "__main__":  # only for live edit.
1341     from bpy.utils import register_class
1342     for cls in classes:
1343         register_class(cls)