Changeset View
Changeset View
Standalone View
Standalone View
pose_library/gui.py
| # SPDX-License-Identifier: GPL-2.0-or-later | # SPDX-License-Identifier: GPL-2.0-or-later | ||||
| """ | """ | ||||
| Pose Library - GUI definition. | Pose Library - GUI definition. | ||||
| """ | """ | ||||
| import bpy | import bpy | ||||
| from bpy.types import ( | from bpy.types import ( | ||||
| AssetHandle, | AssetHandle, | ||||
| Context, | Context, | ||||
| Menu, | |||||
| Panel, | Panel, | ||||
| UIList, | UIList, | ||||
| WindowManager, | WindowManager, | ||||
| WorkSpace, | WorkSpace, | ||||
| ) | ) | ||||
| from bpy_extras import asset_utils | from bpy_extras import asset_utils | ||||
| Show All 15 Lines | class VIEW3D_PT_pose_library(PoseLibraryPanel, Panel): | ||||
| bl_space_type = "VIEW_3D" | bl_space_type = "VIEW_3D" | ||||
| bl_region_type = "UI" | bl_region_type = "UI" | ||||
| bl_category = "Animation" | bl_category = "Animation" | ||||
| bl_label = "Pose Library" | bl_label = "Pose Library" | ||||
| def draw(self, context: Context) -> None: | def draw(self, context: Context) -> None: | ||||
| layout = self.layout | layout = self.layout | ||||
| row = layout.row(align=True) | |||||
| row.operator("poselib.create_pose_asset").activate_new_action = False | |||||
| if bpy.types.POSELIB_OT_restore_previous_action.poll(context): | |||||
| row.operator("poselib.restore_previous_action", text="", icon='LOOP_BACK') | |||||
| row.operator("poselib.copy_as_asset", icon="COPYDOWN", text="") | |||||
| wm = context.window_manager | |||||
| layout.prop(wm, "poselib_flipped") | |||||
| if hasattr(layout, "template_asset_view"): | if hasattr(layout, "template_asset_view"): | ||||
| workspace = context.workspace | workspace = context.workspace | ||||
| wm = context.window_manager | |||||
| activate_op_props, drag_op_props = layout.template_asset_view( | activate_op_props, drag_op_props = layout.template_asset_view( | ||||
| "pose_assets", | "pose_assets", | ||||
| workspace, | workspace, | ||||
| "asset_library_ref", | "asset_library_ref", | ||||
| wm, | wm, | ||||
| "pose_assets", | "pose_assets", | ||||
| workspace, | workspace, | ||||
| "active_pose_asset_index", | "active_pose_asset_index", | ||||
| Show All 28 Lines | def pose_library_list_item_context_menu(self: UIList, context: Context) -> None: | ||||
| if not is_pose_asset_view() and not is_pose_library_asset_browser(): | if not is_pose_asset_view() and not is_pose_library_asset_browser(): | ||||
| return | return | ||||
| layout = self.layout | layout = self.layout | ||||
| wm = context.window_manager | wm = context.window_manager | ||||
| layout.separator() | layout.separator() | ||||
| layout.operator("poselib.apply_pose_asset", text="Apply Pose") | layout.operator("poselib.apply_pose_asset", text="Apply Pose").flipped = False | ||||
| layout.operator("poselib.apply_pose_asset", text="Apply Pose Flipped").flipped = True | |||||
| old_op_ctx = layout.operator_context | old_op_ctx = layout.operator_context | ||||
| layout.operator_context = 'INVOKE_DEFAULT' | layout.operator_context = 'INVOKE_DEFAULT' | ||||
| props = layout.operator("poselib.blend_pose_asset", text="Blend Pose") | props = layout.operator("poselib.blend_pose_asset", text="Blend Pose") | ||||
| props.flipped = wm.poselib_flipped | props.flipped = wm.poselib_flipped | ||||
| layout.operator_context = old_op_ctx | layout.operator_context = old_op_ctx | ||||
| layout.separator() | |||||
| props = layout.operator("poselib.pose_asset_select_bones", text="Select Pose Bones") | props = layout.operator("poselib.pose_asset_select_bones", text="Select Pose Bones") | ||||
| props.flipped = wm.poselib_flipped | props.flipped = wm.poselib_flipped | ||||
| props.select = True | props.select = True | ||||
| props = layout.operator("poselib.pose_asset_select_bones", text="Deselect Pose Bones") | props = layout.operator("poselib.pose_asset_select_bones", text="Deselect Pose Bones") | ||||
| props.flipped = wm.poselib_flipped | props.flipped = wm.poselib_flipped | ||||
| props.select = False | props.select = False | ||||
| if not is_pose_asset_view(): | |||||
| layout.separator() | |||||
| layout.operator("asset.assign_action") | |||||
| layout.separator() | layout.separator() | ||||
| if is_pose_asset_view(): | if is_pose_asset_view(): | ||||
| layout.operator("asset.open_containing_blend_file") | layout.operator("asset.open_containing_blend_file") | ||||
| class ASSETBROWSER_PT_pose_library_usage(PoseLibraryPanel, asset_utils.AssetBrowserPanel, Panel): | |||||
| bl_region_type = "TOOLS" | |||||
| bl_label = "Pose Library" | |||||
| asset_categories = {'ANIMATIONS'} | |||||
| @classmethod | |||||
| def poll(cls, context: Context) -> bool: | |||||
| return ( | |||||
| cls.pose_library_panel_poll(context) | |||||
| and cls.asset_browser_panel_poll(context) | |||||
| ) | |||||
| def draw(self, context: Context) -> None: | |||||
| layout = self.layout | |||||
| wm = context.window_manager | |||||
| col = layout.column(align=True) | |||||
| col.prop(wm, "poselib_flipped") | |||||
| props = col.operator("poselib.apply_pose_asset", text="Apply") | |||||
| props.flipped = wm.poselib_flipped | |||||
| props = col.operator("poselib.blend_pose_asset", text="Interactive Blend") | |||||
| props.flipped = wm.poselib_flipped | |||||
| row = col.row(align=True) | |||||
| props = row.operator("poselib.pose_asset_select_bones", text="Select", icon="BONE_DATA") | |||||
| props.flipped = wm.poselib_flipped | |||||
| props.select = True | |||||
| props = row.operator("poselib.pose_asset_select_bones", text="Deselect") | |||||
| props.flipped = wm.poselib_flipped | |||||
| props.select = False | props.select = False | ||||
| class ASSETBROWSER_PT_pose_library_editing(PoseLibraryPanel, asset_utils.AssetBrowserPanel, Panel): | |||||
| bl_region_type = "TOOL_PROPS" | |||||
| bl_label = "Pose Library" | |||||
| asset_categories = {'ANIMATIONS'} | |||||
| @classmethod | |||||
| def poll(cls, context: Context) -> bool: | |||||
| return ( | |||||
| cls.pose_library_panel_poll(context) | |||||
| and cls.asset_browser_panel_poll(context) | |||||
| ) | |||||
| def draw(self, context: Context) -> None: | |||||
| layout = self.layout | |||||
| col = layout.column(align=True) | |||||
| col.enabled = bpy.types.ASSET_OT_assign_action.poll(context) | |||||
| col.label(text="Activate & Edit") | |||||
| col.operator("asset.assign_action") | |||||
| # Creation | |||||
| col = layout.column(align=True) | |||||
| col.enabled = bpy.types.POSELIB_OT_paste_asset.poll(context) | |||||
| col.label(text="Create Pose Asset") | |||||
| col.operator("poselib.paste_asset", icon="PASTEDOWN") | |||||
| class DOPESHEET_PT_asset_panel(PoseLibraryPanel, Panel): | class DOPESHEET_PT_asset_panel(PoseLibraryPanel, Panel): | ||||
| bl_space_type = "DOPESHEET_EDITOR" | bl_space_type = "DOPESHEET_EDITOR" | ||||
| bl_region_type = "UI" | bl_region_type = "UI" | ||||
| bl_label = "Create Pose Asset" | bl_label = "Create Pose Asset" | ||||
| bl_category = "Pose Library" | bl_category = "Pose Library" | ||||
| def draw(self, context: Context) -> None: | def draw(self, context: Context) -> None: | ||||
| layout = self.layout | layout = self.layout | ||||
| col = layout.column(align=True) | col = layout.column(align=True) | ||||
| row = col.row(align=True) | row = col.row(align=True) | ||||
| row.operator("poselib.create_pose_asset").activate_new_action = True | row.operator("poselib.create_pose_asset").activate_new_action = True | ||||
| if bpy.types.POSELIB_OT_restore_previous_action.poll(context): | if bpy.types.POSELIB_OT_restore_previous_action.poll(context): | ||||
| row.operator("poselib.restore_previous_action", text="", icon='LOOP_BACK') | row.operator("poselib.restore_previous_action", text="", icon='LOOP_BACK') | ||||
| col.operator("poselib.copy_as_asset", icon="COPYDOWN") | col.operator("poselib.copy_as_asset", icon="COPYDOWN") | ||||
| layout.operator("poselib.convert_old_poselib") | layout.operator("poselib.convert_old_poselib") | ||||
| def pose_library_list_item_asset_menu(self: UIList, context: Context) -> None: | |||||
| layout = self.layout | |||||
| layout.menu("ASSETBROWSER_MT_asset") | |||||
| class ASSETBROWSER_MT_asset(Menu): | |||||
| bl_label = "Asset" | |||||
| @classmethod | |||||
| def poll(cls, context): | |||||
| from bpy_extras.asset_utils import SpaceAssetInfo | |||||
| return SpaceAssetInfo.is_asset_browser_poll(context) | |||||
| def draw(self, context: Context) -> None: | |||||
| layout = self.layout | |||||
| layout.operator("poselib.paste_asset", icon='PASTEDOWN') | |||||
| layout.separator() | |||||
| layout.operator("poselib.create_pose_asset").activate_new_action = False | |||||
| ### Messagebus subscription to monitor asset library changes. | ### Messagebus subscription to monitor asset library changes. | ||||
| _msgbus_owner = object() | _msgbus_owner = object() | ||||
| def _on_asset_library_changed() -> None: | def _on_asset_library_changed() -> None: | ||||
| """Update areas when a different asset library is selected.""" | """Update areas when a different asset library is selected.""" | ||||
| refresh_area_types = {'DOPESHEET_EDITOR', 'VIEW_3D'} | refresh_area_types = {'DOPESHEET_EDITOR', 'VIEW_3D'} | ||||
| for win in bpy.context.window_manager.windows: | for win in bpy.context.window_manager.windows: | ||||
| for area in win.screen.areas: | for area in win.screen.areas: | ||||
| Show All 21 Lines | |||||
| @bpy.app.handlers.persistent | @bpy.app.handlers.persistent | ||||
| def _on_blendfile_load_post(none, other_none) -> None: | def _on_blendfile_load_post(none, other_none) -> None: | ||||
| # The parameters are required, but both are None. | # The parameters are required, but both are None. | ||||
| register_message_bus() | register_message_bus() | ||||
| classes = ( | classes = ( | ||||
| ASSETBROWSER_PT_pose_library_editing, | |||||
| ASSETBROWSER_PT_pose_library_usage, | |||||
| DOPESHEET_PT_asset_panel, | DOPESHEET_PT_asset_panel, | ||||
| VIEW3D_PT_pose_library, | VIEW3D_PT_pose_library, | ||||
| ASSETBROWSER_MT_asset, | |||||
| ) | ) | ||||
| _register, _unregister = bpy.utils.register_classes_factory(classes) | _register, _unregister = bpy.utils.register_classes_factory(classes) | ||||
| def register() -> None: | def register() -> None: | ||||
| _register() | _register() | ||||
| WorkSpace.active_pose_asset_index = bpy.props.IntProperty( | WorkSpace.active_pose_asset_index = bpy.props.IntProperty( | ||||
| name="Active Pose Asset", | name="Active Pose Asset", | ||||
| # TODO explain which list the index belongs to, or how it can be used to get the pose. | # TODO explain which list the index belongs to, or how it can be used to get the pose. | ||||
| description="Per workspace index of the active pose asset" | description="Per workspace index of the active pose asset" | ||||
| ) | ) | ||||
| # Register for window-manager. This is a global property that shouldn't be | # Register for window-manager. This is a global property that shouldn't be | ||||
| # written to files. | # written to files. | ||||
| WindowManager.pose_assets = bpy.props.CollectionProperty(type=AssetHandle) | WindowManager.pose_assets = bpy.props.CollectionProperty(type=AssetHandle) | ||||
| bpy.types.UI_MT_list_item_context_menu.prepend(pose_library_list_item_context_menu) | bpy.types.UI_MT_list_item_context_menu.prepend(pose_library_list_item_context_menu) | ||||
| bpy.types.ASSETBROWSER_MT_context_menu.prepend(pose_library_list_item_context_menu) | bpy.types.ASSETBROWSER_MT_context_menu.prepend(pose_library_list_item_context_menu) | ||||
| bpy.types.ASSETBROWSER_MT_editor_menus.append(pose_library_list_item_asset_menu) | |||||
| register_message_bus() | register_message_bus() | ||||
| bpy.app.handlers.load_pre.append(_on_blendfile_load_pre) | bpy.app.handlers.load_pre.append(_on_blendfile_load_pre) | ||||
| bpy.app.handlers.load_post.append(_on_blendfile_load_post) | bpy.app.handlers.load_post.append(_on_blendfile_load_post) | ||||
| def unregister() -> None: | def unregister() -> None: | ||||
| _unregister() | _unregister() | ||||
| unregister_message_bus() | unregister_message_bus() | ||||
| del WorkSpace.active_pose_asset_index | del WorkSpace.active_pose_asset_index | ||||
| del WindowManager.pose_assets | del WindowManager.pose_assets | ||||
| bpy.types.UI_MT_list_item_context_menu.remove(pose_library_list_item_context_menu) | bpy.types.UI_MT_list_item_context_menu.remove(pose_library_list_item_context_menu) | ||||
| bpy.types.ASSETBROWSER_MT_context_menu.remove(pose_library_list_item_context_menu) | bpy.types.ASSETBROWSER_MT_context_menu.remove(pose_library_list_item_context_menu) | ||||