diff options
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/modules/bpy_extras/__init__.py | 1 | ||||
-rw-r--r-- | release/scripts/modules/bpy_extras/asset_utils.py | 63 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/__init__.py | 1 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/assets.py | 74 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_filebrowser.py | 196 |
5 files changed, 313 insertions, 22 deletions
diff --git a/release/scripts/modules/bpy_extras/__init__.py b/release/scripts/modules/bpy_extras/__init__.py index 1caef074d43..cb990b014a1 100644 --- a/release/scripts/modules/bpy_extras/__init__.py +++ b/release/scripts/modules/bpy_extras/__init__.py @@ -24,6 +24,7 @@ Utility modules associated with the bpy module. __all__ = ( "anim_utils", + "asset_utils", "object_utils", "io_utils", "image_utils", diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py new file mode 100644 index 00000000000..db982e119d4 --- /dev/null +++ b/release/scripts/modules/bpy_extras/asset_utils.py @@ -0,0 +1,63 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +""" +Helpers for asset management tasks. +""" + +import bpy +from bpy.types import ( + Context, +) + +__all__ = ( + "SpaceAssetInfo", +) + +class SpaceAssetInfo: + @classmethod + def is_asset_browser(cls, space_data: bpy.types.Space): + return space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS' + + @classmethod + def is_asset_browser_poll(cls, context: Context): + return cls.is_asset_browser(context.space_data) + + @classmethod + def get_active_asset(cls, context: Context): + if hasattr(context, "active_file"): + active_file = context.active_file + return active_file.asset_data if active_file else None + +class AssetBrowserPanel: + bl_space_type = 'FILE_BROWSER' + + @classmethod + def poll(cls, context): + return SpaceAssetInfo.is_asset_browser_poll(context) + +class AssetMetaDataPanel: + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + + @classmethod + def poll(cls, context): + active_file = context.active_file + return SpaceAssetInfo.is_asset_browser_poll(context) and active_file and active_file.asset_data diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index 71b2de41d9e..e91d3b3ce60 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -27,6 +27,7 @@ if "bpy" in locals(): _modules = [ "add_mesh_torus", "anim", + "assets", "clip", "console", "constraint", diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py new file mode 100644 index 00000000000..c317df78aa5 --- /dev/null +++ b/release/scripts/startup/bl_operators/assets.py @@ -0,0 +1,74 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy + +from bpy_extras.asset_utils import ( + SpaceAssetInfo, +) + +class ASSET_OT_tag_add(bpy.types.Operator): + """Add a new keyword tag to the active asset""" + + bl_idname = "asset.tag_add" + bl_label = "Add Asset Tag" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + return SpaceAssetInfo.is_asset_browser_poll(context) and SpaceAssetInfo.get_active_asset(context) + + def execute(self, context): + active_asset = SpaceAssetInfo.get_active_asset(context) + active_asset.tags.new("Unnamed Tag") + + return {'FINISHED'} + + +class ASSET_OT_tag_remove(bpy.types.Operator): + """Remove an existing keyword tag from the active asset""" + + bl_idname = "asset.tag_remove" + bl_label = "Remove Asset Tag" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + if not SpaceAssetInfo.is_asset_browser_poll(context): + return False + + active_asset = SpaceAssetInfo.get_active_asset(context) + if not active_asset: + return False + + return active_asset.active_tag in range(len(active_asset.tags)) + + def execute(self, context): + active_asset = SpaceAssetInfo.get_active_asset(context) + tag = active_asset.tags[active_asset.active_tag] + + active_asset.tags.remove(tag) + active_asset.active_tag -= 1 + + return {'FINISHED'} + + +classes = ( + ASSET_OT_tag_add, + ASSET_OT_tag_remove, +) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index a9bb2e79762..527a5bc623e 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -17,27 +17,79 @@ # ##### END GPL LICENSE BLOCK ##### # <pep8 compliant> + +import bpy + from bpy.types import Header, Panel, Menu, UIList +from bpy_extras import ( + asset_utils, +) + class FILEBROWSER_HT_header(Header): bl_space_type = 'FILE_BROWSER' + def draw_asset_browser_buttons(self, context): + layout = self.layout + + space_data = context.space_data + params = space_data.params + + row = layout.row(align=True) + row.prop(params, "asset_library", text="") + # External libraries don't auto-refresh, add refresh button. + if params.asset_library != 'LOCAL': + row.operator("file.refresh", text="", icon="FILE_REFRESH") + + layout.separator_spacer() + + # Uses prop_with_popover() as popover() only adds the triangle icon in headers. + layout.prop_with_popover( + params, + "display_type", + panel="FILEBROWSER_PT_display", + text="", + icon_only=True, + ) + layout.prop_with_popover( + params, + "display_type", + panel="FILEBROWSER_PT_filter", + text="", + icon='FILTER', + icon_only=True, + ) + + layout.prop(params, "filter_search", text="", icon='VIEWZOOM') + + layout.operator( + "screen.region_toggle", + text="", + icon='PREFERENCES', + depress=is_option_region_visible(context, space_data) + ).region_type = 'TOOL_PROPS' + def draw(self, context): + from bpy_extras.asset_utils import SpaceAssetInfo + layout = self.layout - st = context.space_data + space_data = context.space_data - if st.active_operator is None: + if space_data.active_operator is None: layout.template_header() FILEBROWSER_MT_editor_menus.draw_collapsible(context, layout) - # can be None when save/reload with a file selector open - - layout.separator_spacer() + if SpaceAssetInfo.is_asset_browser(space_data): + layout.separator() + self.draw_asset_browser_buttons(context) + else: + layout.separator_spacer() - layout.template_running_jobs() + if not context.screen.show_statusbar: + layout.template_running_jobs() class FILEBROWSER_PT_display(Panel): @@ -144,6 +196,9 @@ class FILEBROWSER_PT_filter(Panel): row.label(icon='BLANK1') # Indentation sub = row.column(align=True) + + sub.prop(params, "use_filter_asset_only") + filter_id = params.filter_id for identifier in dir(filter_id): if identifier.startswith("category_"): @@ -160,6 +215,11 @@ def panel_poll_is_upper_region(region): return region.alignment in {'LEFT', 'RIGHT'} +def panel_poll_is_asset_browsing(context): + from bpy_extras.asset_utils import SpaceAssetInfo + return SpaceAssetInfo.is_asset_browser_poll(context) + + class FILEBROWSER_UL_dir(UIList): def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): direntry = item @@ -187,7 +247,7 @@ class FILEBROWSER_PT_bookmarks_volumes(Panel): @classmethod def poll(cls, context): - return panel_poll_is_upper_region(context.region) + return panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) def draw(self, context): layout = self.layout @@ -207,7 +267,7 @@ class FILEBROWSER_PT_bookmarks_system(Panel): @classmethod def poll(cls, context): - return not context.preferences.filepaths.hide_system_bookmarks and panel_poll_is_upper_region(context.region) + return not context.preferences.filepaths.hide_system_bookmarks and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) def draw(self, context): layout = self.layout @@ -241,7 +301,7 @@ class FILEBROWSER_PT_bookmarks_favorites(Panel): @classmethod def poll(cls, context): - return panel_poll_is_upper_region(context.region) + return panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) def draw(self, context): layout = self.layout @@ -278,7 +338,7 @@ class FILEBROWSER_PT_bookmarks_recents(Panel): @classmethod def poll(cls, context): - return not context.preferences.filepaths.hide_recent_locations and panel_poll_is_upper_region(context.region) + return not context.preferences.filepaths.hide_recent_locations and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) def draw(self, context): layout = self.layout @@ -302,7 +362,7 @@ class FILEBROWSER_PT_advanced_filter(Panel): @classmethod def poll(cls, context): # only useful in append/link (library) context currently... - return context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region) + return context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) def draw(self, context): layout = self.layout @@ -314,12 +374,26 @@ class FILEBROWSER_PT_advanced_filter(Panel): if params.use_filter_blendid: layout.separator() col = layout.column(align=True) + + col.prop(params, "use_filter_asset_only") + filter_id = params.filter_id for identifier in dir(filter_id): if identifier.startswith("filter_"): col.prop(filter_id, identifier, toggle=True) +def is_option_region_visible(context, space): + if not space.active_operator: + return False + + for region in context.area.regions: + if region.type == 'TOOL_PROPS' and region.width <= 1: + return False + + return True + + class FILEBROWSER_PT_directory_path(Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'UI' @@ -334,16 +408,6 @@ class FILEBROWSER_PT_directory_path(Panel): return True - def is_option_region_visible(self, context, space): - if not space.active_operator: - return False - - for region in context.area.regions: - if region.type == 'TOOL_PROPS' and region.width <= 1: - return False - - return True - def draw(self, context): layout = self.layout space = context.space_data @@ -388,7 +452,7 @@ class FILEBROWSER_PT_directory_path(Panel): "screen.region_toggle", text="", icon='PREFERENCES', - depress=self.is_option_region_visible(context, space) + depress=is_option_region_visible(context, space) ).region_type = 'TOOL_PROPS' @@ -482,6 +546,89 @@ class FILEBROWSER_MT_context_menu(Menu): layout.prop_menu_enum(params, "sort_method") +class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel): + bl_label = "Asset Navigation" + bl_region_type = 'TOOLS' + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + + space_file = context.space_data + + col = layout.column() + + col.scale_x = 1.3 + col.scale_y = 1.3 + col.prop(space_file.params, "asset_category", expand=True) + + +class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): + bl_region_type = 'TOOL_PROPS' + bl_label = "Asset Metadata" + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + active_file = context.active_file + active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) + + layout.use_property_split = True + + if not active_file or not active_asset: + layout.label(text="No asset selected.") + return + + box = layout.box() + box.template_icon(icon_value=active_file.preview_icon_id, scale=5.0) + if bpy.ops.ed.lib_id_load_custom_preview.poll(): + box.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="Load Custom") + layout.prop(active_file, "name") + + +class ASSETBROWSER_PT_metadata_details(asset_utils.AssetBrowserPanel, Panel): + bl_region_type = 'TOOL_PROPS' + bl_label = "Details" + bl_parent_id = "ASSETBROWSER_PT_metadata" + + def draw(self, context): + layout = self.layout + active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) + + layout.use_property_split = True + + if active_asset: + layout.prop(active_asset, "description") + + +class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel): + bl_label = "Tags" + + def draw(self, context): + layout = self.layout + asset_data = asset_utils.SpaceAssetInfo.get_active_asset(context) + + row = layout.row() + row.template_list("ASSETBROWSER_UL_metadata_tags", "asset_tags", asset_data, "tags", + asset_data, "active_tag", rows=4) + + col = row.column(align=True) + col.operator("asset.tag_add", icon='ADD', text="") + col.operator("asset.tag_remove", icon='REMOVE', text="") + + +class ASSETBROWSER_UL_metadata_tags(UIList): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + tag = item + + row = layout.row(align=True) + # Non-editable entries would show grayed-out, which is bad in this specific case, so switch to mere label. + if tag.is_property_readonly("name"): + row.label(text=tag.name, icon_value=icon) + else: + row.prop(tag, "name", text="", emboss=False, icon_value=icon) + + classes = ( FILEBROWSER_HT_header, FILEBROWSER_PT_display, @@ -498,6 +645,11 @@ classes = ( FILEBROWSER_MT_view, FILEBROWSER_MT_select, FILEBROWSER_MT_context_menu, + ASSETBROWSER_PT_navigation_bar, + ASSETBROWSER_PT_metadata, + ASSETBROWSER_PT_metadata_details, + ASSETBROWSER_PT_metadata_tags, + ASSETBROWSER_UL_metadata_tags, ) if __name__ == "__main__": # only for live edit. |