File: gui.py

package info (click to toggle)
blender 4.3.2%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 309,564 kB
  • sloc: cpp: 2,385,210; python: 330,236; ansic: 280,972; xml: 2,446; sh: 972; javascript: 317; makefile: 170
file content (207 lines) | stat: -rw-r--r-- 6,471 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# SPDX-FileCopyrightText: 2021-2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later

"""
Pose Library - GUI definition.
"""

import bpy
from bpy.types import (
    AssetRepresentation,
    Context,
    Menu,
    Panel,
    UILayout,
    UIList,
)
from bl_ui_utils.layout import operator_context


class PoseLibraryPanel:
    @classmethod
    def pose_library_panel_poll(cls, context: Context) -> bool:
        return context.mode == 'POSE'

    @classmethod
    def poll(cls, context: Context) -> bool:
        return cls.pose_library_panel_poll(context)


class VIEW3D_AST_pose_library(bpy.types.AssetShelf):
    bl_space_type = "VIEW_3D"
    # We have own keymap items to add custom drag behavior (pose blending), disable the default
    # asset dragging.
    bl_options = {'NO_ASSET_DRAG'}

    @classmethod
    def poll(cls, context: Context) -> bool:
        return PoseLibraryPanel.poll(context)

    @classmethod
    def asset_poll(cls, asset: AssetRepresentation) -> bool:
        return asset.id_type == 'ACTION'

    @classmethod
    def draw_context_menu(cls, _context: Context, _asset: AssetRepresentation, layout: UILayout):
        layout.operator("poselib.apply_pose_asset", text="Apply Pose").flipped = False
        layout.operator("poselib.apply_pose_asset", text="Apply Pose Flipped").flipped = True

        with operator_context(layout, 'INVOKE_DEFAULT'):
            layout.operator("poselib.blend_pose_asset", text="Blend Pose").flipped = False
            layout.operator("poselib.blend_pose_asset", text="Blend Pose Flipped").flipped = True

        layout.separator()
        props = layout.operator("poselib.pose_asset_select_bones", text="Select Pose Bones")
        props.select = True
        props = layout.operator("poselib.pose_asset_select_bones", text="Deselect Pose Bones")
        props.select = False

        layout.separator()
        layout.operator("asset.open_containing_blend_file")


def pose_library_asset_browser_context_menu(self: UIList, context: Context) -> None:
    def is_pose_library_asset_browser() -> bool:
        asset_library_ref = getattr(context, "asset_library_reference", None)
        if not asset_library_ref:
            return False
        asset = getattr(context, "asset", None)
        if not asset:
            return False
        return bool(asset.id_type == 'ACTION')

    if not is_pose_library_asset_browser():
        return

    layout = self.layout

    layout.separator()

    layout.operator("poselib.apply_pose_asset", text="Apply Pose").flipped = False
    layout.operator("poselib.apply_pose_asset", text="Apply Pose Flipped").flipped = True

    with operator_context(layout, 'INVOKE_DEFAULT'):
        layout.operator("poselib.blend_pose_asset", text="Blend Pose").flipped = False
        layout.operator("poselib.blend_pose_asset", text="Blend Pose Flipped").flipped = True

    layout.separator()
    props = layout.operator("poselib.pose_asset_select_bones", text="Select Pose Bones")
    props.select = True
    props = layout.operator("poselib.pose_asset_select_bones", text="Deselect Pose Bones")
    props.select = False

    layout.separator()
    layout.operator("asset.assign_action")

    layout.separator()


class DOPESHEET_PT_asset_panel(PoseLibraryPanel, Panel):
    bl_space_type = "DOPESHEET_EDITOR"
    bl_region_type = "UI"
    bl_label = "Create Pose Asset"
    bl_category = "Action"

    def draw(self, context: Context) -> None:
        layout = self.layout
        col = layout.column(align=True)
        row = col.row(align=True)
        row.operator("poselib.create_pose_asset").activate_new_action = True
        if bpy.types.POSELIB_OT_restore_previous_action.poll(context):
            row.operator("poselib.restore_previous_action", text="", icon='LOOP_BACK')
        col.operator("poselib.copy_as_asset", icon="COPYDOWN")

        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.
_msgbus_owner = object()


def _on_asset_library_changed() -> None:
    """Update areas when a different asset library is selected."""
    refresh_area_types = {'DOPESHEET_EDITOR', 'VIEW_3D'}
    for win in bpy.context.window_manager.windows:
        for area in win.screen.areas:
            if area.type not in refresh_area_types:
                continue

            area.tag_redraw()


def register_message_bus() -> None:
    bpy.msgbus.subscribe_rna(
        key=(bpy.types.FileAssetSelectParams, "asset_library_reference"),
        owner=_msgbus_owner,
        args=(),
        notify=_on_asset_library_changed,
        options={'PERSISTENT'},
    )


def unregister_message_bus() -> None:
    bpy.msgbus.clear_by_owner(_msgbus_owner)


@bpy.app.handlers.persistent
def _on_blendfile_load_pre(none, other_none) -> None:
    # The parameters are required, but both are None.
    unregister_message_bus()


@bpy.app.handlers.persistent
def _on_blendfile_load_post(none, other_none) -> None:
    # The parameters are required, but both are None.
    register_message_bus()


classes = (
    DOPESHEET_PT_asset_panel,
    ASSETBROWSER_MT_asset,
    VIEW3D_AST_pose_library,
)

_register, _unregister = bpy.utils.register_classes_factory(classes)


def register() -> None:
    _register()

    bpy.types.ASSETBROWSER_MT_context_menu.prepend(pose_library_asset_browser_context_menu)
    bpy.types.ASSETBROWSER_MT_editor_menus.append(pose_library_list_item_asset_menu)

    register_message_bus()
    bpy.app.handlers.load_pre.append(_on_blendfile_load_pre)
    bpy.app.handlers.load_post.append(_on_blendfile_load_post)


def unregister() -> None:
    _unregister()

    unregister_message_bus()

    bpy.types.ASSETBROWSER_MT_context_menu.remove(pose_library_asset_browser_context_menu)
    bpy.types.ASSETBROWSER_MT_editor_menus.remove(pose_library_list_item_asset_menu)