# SPDX-License-Identifier: GPL-2.0-or-later # panels get subclassed (not registered directly) # menus are referenced `as is` from bpy.types import Menu, UIList from bpy.app.translations import contexts as i18n_contexts # Use by both image & clip context menus. def draw_mask_context_menu(layout, _context): layout.operator_menu_enum("mask.handle_type_set", "type") layout.operator("mask.switch_direction") layout.operator("mask.cyclic_toggle") layout.separator() layout.operator("mask.copy_splines", icon='COPYDOWN') layout.operator("mask.paste_splines", icon='PASTEDOWN') layout.separator() layout.operator("mask.shape_key_rekey", text="Re-Key Shape Points") layout.operator("mask.feather_weight_clear") layout.operator("mask.shape_key_feather_reset", text="Reset Feather Animation") layout.separator() layout.operator("mask.parent_set") layout.operator("mask.parent_clear") layout.separator() layout.operator("mask.delete") class MASK_UL_layers(UIList): def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): # assert(isinstance(item, bpy.types.MaskLayer) mask = item if self.layout_type in {'DEFAULT', 'COMPACT'}: layout.prop(mask, "name", text="", emboss=False, icon_value=icon) row = layout.row(align=True) row.prop(mask, "hide", text="", emboss=False) row.prop(mask, "hide_select", text="", emboss=False) row.prop(mask, "hide_render", text="", emboss=False) elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) class MASK_PT_mask: # subclasses must define... # ~ bl_space_type = 'CLIP_EDITOR' # ~ bl_region_type = 'UI' bl_label = "Mask Settings" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): space_data = context.space_data return space_data.mask and space_data.mode == 'MASK' def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False sc = context.space_data mask = sc.mask col = layout.column(align=True) col.prop(mask, "frame_start") col.prop(mask, "frame_end") class MASK_PT_layers: # subclasses must define... # ~ bl_space_type = 'CLIP_EDITOR' # ~ bl_region_type = 'UI' bl_label = "Mask Layers" @classmethod def poll(cls, context): space_data = context.space_data return space_data.mask and space_data.mode == 'MASK' def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False sc = context.space_data mask = sc.mask active_layer = mask.layers.active rows = 4 if active_layer else 1 row = layout.row() row.template_list("MASK_UL_layers", "", mask, "layers", mask, "active_layer_index", rows=rows) sub = row.column(align=True) sub.operator("mask.layer_new", icon='ADD', text="") sub.operator("mask.layer_remove", icon='REMOVE', text="") if active_layer: sub.separator() sub.operator("mask.layer_move", icon='TRIA_UP', text="").direction = 'UP' sub.operator("mask.layer_move", icon='TRIA_DOWN', text="").direction = 'DOWN' # blending row = layout.row(align=True) row.prop(active_layer, "alpha") row.prop(active_layer, "invert", text="", icon='IMAGE_ALPHA') layout.prop(active_layer, "blend") layout.prop(active_layer, "falloff") col = layout.column() col.prop(active_layer, "use_fill_overlap", text="Overlap") col.prop(active_layer, "use_fill_holes", text="Holes") class MASK_PT_spline: # subclasses must define... # ~ bl_space_type = 'CLIP_EDITOR' # ~ bl_region_type = 'UI' bl_label = "Active Spline" @classmethod def poll(cls, context): sc = context.space_data mask = sc.mask if mask and sc.mode == 'MASK': return mask.layers.active and mask.layers.active.splines.active return False def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False sc = context.space_data mask = sc.mask spline = mask.layers.active.splines.active col = layout.column() col.prop(spline, "offset_mode") col.prop(spline, "weight_interpolation", text="Interpolation") col.prop(spline, "use_cyclic") col.prop(spline, "use_fill") col.prop(spline, "use_self_intersection_check") class MASK_PT_point: # subclasses must define... # ~ bl_space_type = 'CLIP_EDITOR' # ~ bl_region_type = 'UI' bl_label = "Active Point" @classmethod def poll(cls, context): sc = context.space_data mask = sc.mask if mask and sc.mode == 'MASK': mask_layer_active = mask.layers.active return (mask_layer_active and mask_layer_active.splines.active_point) return False def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False sc = context.space_data mask = sc.mask point = mask.layers.active.splines.active_point parent = point.parent col = layout.column() # Currently only parenting the movie-clip is allowed, # so do not over-complicate things for now by using single template_ID #col.template_any_ID(parent, "id", "id_type", text="") col.label(text="Parent:") col.prop(parent, "id", text="") if parent.id_type == 'MOVIECLIP' and parent.id: clip = parent.id tracking = clip.tracking row = col.row() row.prop(parent, "type", expand=True) col.prop_search(parent, "parent", tracking, "objects", icon='OBJECT_DATA', text="Object") tracks_list = "tracks" if parent.type == 'POINT_TRACK' else "plane_tracks" if parent.parent in tracking.objects: ob = tracking.objects[parent.parent] col.prop_search(parent, "sub_parent", ob, tracks_list, icon='ANIM_DATA', text="Track") else: col.prop_search(parent, "sub_parent", tracking, tracks_list, icon='ANIM_DATA', text="Track") class MASK_PT_display: # subclasses must define... # ~ bl_space_type = 'CLIP_EDITOR' # ~ bl_region_type = 'HEADER' bl_label = "Mask Display" @classmethod def poll(cls, context): space_data = context.space_data return space_data.mask and space_data.mode == 'MASK' def draw(self, context): layout = self.layout space_data = context.space_data row = layout.row(align=True) row.prop(space_data, "show_mask_spline", text="Spline") sub = row.row() sub.active = space_data.show_mask_spline sub.prop(space_data, "mask_display_type", text="") row = layout.row(align=True) row.prop(space_data, "show_mask_overlay", text="Overlay") sub = row.row() sub.active = space_data.show_mask_overlay sub.prop(space_data, "mask_overlay_mode", text="") row = layout.row() row.active = (space_data.mask_overlay_mode in ['COMBINED'] and space_data.show_mask_overlay) row.prop(space_data, "blend_factor", text="Blending Factor") class MASK_PT_transforms: # subclasses must define... # ~ bl_space_type = 'CLIP_EDITOR' # ~ bl_region_type = 'TOOLS' bl_label = "Transforms" bl_category = "Mask" @classmethod def poll(cls, context): space_data = context.space_data return space_data.mask and space_data.mode == 'MASK' def draw(self, _context): layout = self.layout col = layout.column(align=True) col.label(text="Transform:") col.operator("transform.translate") col.operator("transform.rotate") col.operator("transform.resize", text="Scale") col.operator("transform.transform", text="Scale Feather").mode = 'MASK_SHRINKFATTEN' class MASK_PT_tools: bl_label = "Mask Tools" bl_category = "Mask" @classmethod def poll(cls, context): space_data = context.space_data return space_data.mask and space_data.mode == 'MASK' def draw(self, _context): layout = self.layout col = layout.column(align=True) col.label(text="Spline:") col.operator("mask.delete") col.operator("mask.cyclic_toggle") col.operator("mask.switch_direction") col.operator("mask.handle_type_set").type = 'VECTOR' col.operator("mask.feather_weight_clear") col = layout.column(align=True) col.label(text="Parenting:") row = col.row(align=True) row.operator("mask.parent_set", text="Parent") row.operator("mask.parent_clear", text="Clear") col = layout.column(align=True) col.label(text="Animation:") row = col.row(align=True) row.operator("mask.shape_key_insert", text="Insert Key") row.operator("mask.shape_key_clear", text="Clear Key") col.operator("mask.shape_key_feather_reset", text="Reset Feather Animation") col.operator("mask.shape_key_rekey", text="Re-Key Shape Points") class MASK_MT_mask(Menu): bl_label = "Mask" def draw(self, _context): layout = self.layout layout.menu("MASK_MT_transform") layout.operator("mask.feather_weight_clear") layout.separator() layout.operator("mask.cyclic_toggle") layout.operator("mask.handle_type_set") layout.operator("mask.normals_make_consistent") layout.operator("mask.switch_direction") layout.separator() layout.operator("mask.copy_splines") layout.operator("mask.paste_splines") layout.separator() layout.operator("mask.parent_clear") layout.operator("mask.parent_set") layout.separator() layout.menu("MASK_MT_animation") layout.separator() layout.menu("MASK_MT_visibility") layout.operator("mask.delete") class MASK_MT_add(Menu): bl_idname = "MASK_MT_add" bl_label = "Add" bl_translation_context = i18n_contexts.operator_default def draw(self, _context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("mask.primitive_circle_add", text="Circle", icon='MESH_CIRCLE') layout.operator("mask.primitive_square_add", text="Square", icon='MESH_PLANE') class MASK_MT_visibility(Menu): bl_label = "Show/Hide" def draw(self, _context): layout = self.layout layout.operator("mask.hide_view_clear") layout.operator("mask.hide_view_set", text="Hide Selected").unselected = False layout.operator("mask.hide_view_set", text="Hide Unselected").unselected = True class MASK_MT_transform(Menu): bl_label = "Transform" def draw(self, _context): layout = self.layout layout.operator("transform.translate") layout.operator("transform.rotate") layout.operator("transform.resize") layout.separator() layout.operator("transform.tosphere") layout.operator("transform.shear") layout.operator("transform.push_pull") layout.separator() layout.operator("transform.transform", text="Scale Feather").mode = 'MASK_SHRINKFATTEN' class MASK_MT_animation(Menu): bl_label = "Animation" def draw(self, _context): layout = self.layout layout.operator("mask.shape_key_clear") layout.operator("mask.shape_key_insert") layout.operator("mask.shape_key_feather_reset") layout.operator("mask.shape_key_rekey") class MASK_MT_select(Menu): bl_label = "Select" def draw(self, _context): layout = self.layout layout.operator("mask.select_all", text="All").action = 'SELECT' layout.operator("mask.select_all", text="None").action = 'DESELECT' layout.operator("mask.select_all", text="Invert").action = 'INVERT' layout.separator() layout.operator("mask.select_box") layout.operator("mask.select_circle") layout.operator_menu_enum("mask.select_lasso", "mode") layout.separator() layout.operator("mask.select_more") layout.operator("mask.select_less") layout.separator() layout.operator("mask.select_linked", text="Select Linked") classes = ( MASK_UL_layers, MASK_MT_mask, MASK_MT_add, MASK_MT_visibility, MASK_MT_transform, MASK_MT_animation, MASK_MT_select, ) if __name__ == "__main__": # only for live edit. from bpy.utils import register_class for cls in classes: register_class(cls)