WIP: Rewrite asset browser as separate editor #107576

Draft
Julian Eisel wants to merge 95 commits from asset-browser-frontend-split into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
78 changed files with 2924 additions and 352 deletions

View File

@ -1084,6 +1084,35 @@ const bTheme U_theme_default = {
.edited_object = RGBA(0x00806266),
.row_alternate = RGBA(0xffffff04),
},
.space_assets = {
.back = RGBA(0x28282800),
.title = RGBA(0xffffffff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
.header = RGBA(0x303030b3),
.header_text = RGBA(0xeeeeeeff),
.header_text_hi = RGBA(0xffffffff),
.tab_active = RGBA(0x303030ff),
.tab_inactive = RGBA(0x1d1d1dff),
.tab_back = RGBA(0x181818ff),
.tab_outline = RGBA(0x3d3d3dff),
.button = RGBA(0x30303000),
.button_title = RGBA(0xffffffff),
.button_text = RGBA(0xccccccff),
.button_text_hi = RGBA(0xffffffff),
.navigation_bar = RGBA(0x303030ff),
.execution_buts = RGBA(0x303030ff),
.panelcolors = {
.header = RGBA(0x3d3d3dff),
.back = RGBA(0x3d3d3dff),
.sub_back = RGBA(0x0000001f),
},
.hilite = RGBA(0x4772b3ff),
.vertex_size = 3,
.outline_width = 1,
.facedot_size = 4,
.row_alternate = RGBA(0xffffff04),
},
.tarm = {
{
.solid = RGBA(0x9a0000ff),

View File

@ -44,7 +44,7 @@ class AssetBrowserPanel:
return cls.asset_browser_panel_poll(context)
class AssetMetaDataPanel:
class AssetMetaDataPanelOLD:
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
@ -52,3 +52,14 @@ class AssetMetaDataPanel:
def poll(cls, context):
active_file = context.active_file
return SpaceAssetInfo.is_asset_browser_poll(context) and active_file and active_file.asset_data
class AssetMetaDataPanel:
bl_space_type = 'ASSET_BROWSER'
bl_region_type = 'UI'
bl_category = 'Metadata'
@classmethod
def poll(cls, context):
active_asset = context.asset_handle
return active_asset

View File

@ -1388,6 +1388,42 @@
</space_list>
</ThemeSpreadsheet>
</spreadsheet>
<asset_browser>
<ThemeAssetBrowser
row_alternate="#ffffff0f"
>
<space>
<ThemeSpaceGeneric
back="#999999"
title="#000000"
text="#000000"
text_hi="#ffffff"
header="#adadadff"
header_text="#000000"
header_text_hi="#ffffff"
button="#999999e6"
button_title="#1a1a1a"
button_text="#000000"
button_text_hi="#000000"
navigation_bar="#00000000"
execution_buts="#999999e6"
tab_active="#6697e6"
tab_inactive="#cccccc"
tab_back="#999999ff"
tab_outline="#999999"
>
<panelcolors>
<ThemePanelColors
header="#42424200"
back="#00000028"
sub_back="#00000024"
>
</ThemePanelColors>
</panelcolors>
</ThemeSpaceGeneric>
</space>
</ThemeAssetBrowser>
</asset_browser>
<bone_color_sets>
<ThemeBoneColorSet
normal="#9a0000"

View File

@ -986,7 +986,7 @@ def km_view2d_buttons_list(_params):
return keymap
def km_user_interface(_params):
def km_user_interface(params):
items = []
keymap = (
"User Interface",
@ -1022,6 +1022,13 @@ def km_user_interface(_params):
("ui.list_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
# UI views (polls check if there's a UI view under the cursor).
("ui.view_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
*_template_items_select_actions(params, "ui.view_select_all"),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("deselect_all", not params.legacy)]}),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
{"properties": [("extend", True), ("toggle", True)]}),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("extend", True), ("fill", True)]}),
])
return keymap
@ -2455,6 +2462,32 @@ def km_file_browser_buttons(_params):
return keymap
# ------------------------------------------------------------------------------
# Editor (Asset Browser)
def km_asset_browser(params):
items = []
keymap = (
"Asset Browser",
{"space_type": 'ASSET_BROWSER', "region_type": 'WINDOW'},
{"items": items},
)
items.extend([
("wm.context_toggle", {"type": 'T', "value": 'PRESS'},
{"properties": [("data_path", 'space_data.show_region_nav_bar')]}),
*_template_space_region_type_toggle(
params,
sidebar_key={"type": 'N', "value": 'PRESS'},
),
# UI keymap already adds "select all" items for the hovered view. We add our own here that
# don't rely on the hovered view, so they can be displayed with shortcuts in menus.
*_template_items_select_actions(params, "assetbrowser.select_all"),
])
return keymap
# ------------------------------------------------------------------------------
# Editor (Dope Sheet)
@ -8371,6 +8404,7 @@ def generate_keymaps(params=None):
km_node_editor(params),
km_spreadsheet_generic(params),
km_info(params),
km_asset_browser(params),
km_file_browser(params),
km_file_browser_main(params),
km_file_browser_buttons(params),

View File

@ -4424,6 +4424,7 @@ def generate_keymaps_impl(params=None):
km_clip_editor(params),
km_clip_graph_editor(params),
km_clip_dopesheet_editor(params),
# TODO asset browser
# Animation.
km_frames(params),

View File

@ -68,6 +68,7 @@ _modules = [
"space_toolsystem_common",
"space_toolsystem_toolbar",
"space_assets",
"space_clip",
"space_console",
"space_dopesheet",

View File

@ -0,0 +1,228 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# <pep8 compliant>
import bpy
from bpy.types import Header, Menu, Panel, UIList
from bpy_extras import (
asset_utils,
)
class ASSETBROWSER_HT_header(Header):
bl_space_type = 'ASSET_BROWSER'
def draw(self, context):
layout = self.layout
space = context.space_data
layout.template_header()
ASSETBROWSER_MT_editor_menus.draw_collapsible(context, layout)
layout.separator_spacer()
layout.operator(
"screen.region_toggle",
text="",
icon='PREFERENCES',
depress=is_option_region_visible(context, space)
).region_type = 'UI'
def is_option_region_visible(context, space):
for region in context.area.regions:
if region.type == 'UI' and region.width <= 1:
return False
return True
class ASSETBROWSER_MT_editor_menus(Menu):
bl_idname = "ASSETBROWSER_MT_editor_menus"
bl_label = ""
def draw(self, _context):
layout = self.layout
layout.menu("ASSETBROWSER_MT_view")
layout.menu("ASSETBROWSER_MT_select")
layout.menu("ASSETBROWSER_MT_edit")
class ASSETBROWSER_MT_view(Menu):
bl_label = "View"
def draw(self, context):
layout = self.layout
st = context.space_data
layout.prop(st, "show_region_nav_bar", text="Navigation")
layout.separator()
layout.menu("INFO_MT_area")
class ASSETBROWSER_MT_select(Menu):
bl_label = "Select"
def draw(self, _context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("assetbrowser.select_all", text="All").action = 'SELECT'
layout.operator("assetbrowser.select_all", text="None").action = 'DESELECT'
layout.operator("assetbrowser.select_all", text="Inverse").action = 'INVERT'
# TODO box select
class ASSETBROWSER_MT_edit(Menu):
bl_label = "Edit"
def draw(self, _context):
layout = self.layout
layout.operator("asset.catalog_undo", text="Undo")
layout.operator("asset.catalog_redo", text="Redo")
# Doesn't use the AssetMetaDataPanel mixin because when there is no active asset
# this shows a info label instead.
class ASSETBROWSER_PT_metadata(Panel):
bl_space_type = 'ASSET_BROWSER'
bl_region_type = 'UI'
bl_label = "Asset Metadata"
bl_options = {'HIDE_HEADER'}
bl_category = 'Metadata'
@staticmethod
def metadata_prop(layout, asset_data, propname):
"""
Only display properties that are either set or can be modified (i.e. the
asset is in the current file). Empty, non-editable fields are not really useful.
"""
if getattr(asset_data, propname) or not asset_data.is_property_readonly(propname):
layout.prop(asset_data, propname)
def draw(self, context):
layout = self.layout
wm = context.window_manager
asset_handle = context.asset_handle
if asset_handle is None:
layout.label(text="No active asset", icon='INFO')
return
asset_file = asset_handle.file_data
prefs = context.preferences
show_asset_debug_info = prefs.view.show_developer_ui and prefs.experimental.show_asset_debug_info
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
if asset_handle.local_id:
# If the active file is an ID, use its name directly so renaming is possible from right here.
layout.prop(asset_handle.local_id, "name")
if show_asset_debug_info:
col = layout.column(align=True)
col.label(text="Asset Catalog:")
col.prop(asset_handle.local_id.asset_data, "catalog_id", text="UUID")
col.prop(asset_handle.local_id.asset_data, "catalog_simple_name", text="Simple Name")
else:
layout.prop(asset_file, "name")
if show_asset_debug_info:
col = layout.column(align=True)
col.enabled = False
col.label(text="Asset Catalog:")
col.prop(asset_file.asset_data, "catalog_id", text="UUID")
col.prop(asset_file.asset_data, "catalog_simple_name", text="Simple Name")
row = layout.row(align=True)
row.prop(wm, "asset_path_dummy", text="Source")
row.operator("asset.open_containing_blend_file", text="", icon='TOOL_SETTINGS')
asset_data = asset_file.asset_data
self.metadata_prop(layout, asset_data, "description")
self.metadata_prop(layout, asset_data, "license")
self.metadata_prop(layout, asset_data, "copyright")
self.metadata_prop(layout, asset_data, "author")
class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
bl_label = "Preview"
def draw(self, context):
layout = self.layout
asset_handle = context.asset_handle
row = layout.row()
box = row.box()
box.template_icon(icon_value=asset_handle.preview_icon_id, scale=5.0)
col = row.column(align=True)
col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="")
col.separator()
col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="")
col.menu("ASSETBROWSER_MT_metadata_preview_menu", icon='DOWNARROW_HLT', text="")
class ASSETBROWSER_MT_metadata_preview_menu(Menu):
bl_label = "Preview"
def draw(self, context):
layout = self.layout
layout.operator("ed.lib_id_generate_preview_from_object", text="Render Active Object")
class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
bl_label = "Tags"
def draw(self, context):
layout = self.layout
asset = context.asset_handle
asset_data = asset.file_data.asset_data
row = layout.row()
row.template_list("ASSETBROWSEROLD_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 = (
ASSETBROWSER_HT_header,
ASSETBROWSER_MT_editor_menus,
ASSETBROWSER_MT_view,
ASSETBROWSER_MT_select,
ASSETBROWSER_MT_edit,
ASSETBROWSER_PT_metadata,
ASSETBROWSER_PT_metadata_preview,
ASSETBROWSER_MT_metadata_preview_menu,
ASSETBROWSER_PT_metadata_tags,
ASSETBROWSER_UL_metadata_tags,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@ -66,7 +66,7 @@ class FILEBROWSER_HT_header(Header):
layout.template_header()
if SpaceAssetInfo.is_asset_browser(space_data):
ASSETBROWSER_MT_editor_menus.draw_collapsible(context, layout)
ASSETBROWSEROLD_MT_editor_menus.draw_collapsible(context, layout)
layout.separator()
self.draw_asset_browser_buttons(context)
else:
@ -641,19 +641,19 @@ class AssetBrowserMenu:
return SpaceAssetInfo.is_asset_browser_poll(context)
class ASSETBROWSER_MT_editor_menus(AssetBrowserMenu, Menu):
bl_idname = "ASSETBROWSER_MT_editor_menus"
class ASSETBROWSEROLD_MT_editor_menus(AssetBrowserMenu, Menu):
bl_idname = "ASSETBROWSEROLD_MT_editor_menus"
bl_label = ""
def draw(self, _context):
layout = self.layout
layout.menu("ASSETBROWSER_MT_view")
layout.menu("ASSETBROWSER_MT_select")
layout.menu("ASSETBROWSEROLD_MT_view")
layout.menu("ASSETBROWSEROLD_MT_select")
layout.menu("ASSETBROWSER_MT_catalog")
class ASSETBROWSER_MT_view(AssetBrowserMenu, Menu):
class ASSETBROWSEROLD_MT_view(AssetBrowserMenu, Menu):
bl_label = "View"
def draw(self, context):
@ -674,7 +674,7 @@ class ASSETBROWSER_MT_view(AssetBrowserMenu, Menu):
layout.menu("INFO_MT_area")
class ASSETBROWSER_MT_select(AssetBrowserMenu, Menu):
class ASSETBROWSEROLD_MT_select(AssetBrowserMenu, Menu):
bl_label = "Select"
def draw(self, _context):
@ -703,7 +703,7 @@ class ASSETBROWSER_MT_catalog(AssetBrowserMenu, Menu):
layout.operator("asset.catalog_new").parent_path = ""
class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
class ASSETBROWSEROLD_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
bl_region_type = 'TOOL_PROPS'
bl_label = "Asset Metadata"
bl_options = {'HIDE_HEADER'}
@ -763,7 +763,7 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
self.metadata_prop(layout, metadata, "author")
class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
class ASSETBROWSEROLD_PT_metadata_preview(asset_utils.AssetMetaDataPanelOLD, Panel):
bl_label = "Preview"
def draw(self, context):
@ -778,10 +778,10 @@ class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="")
col.separator()
col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="")
col.menu("ASSETBROWSER_MT_metadata_preview_menu", icon='DOWNARROW_HLT', text="")
col.menu("ASSETBROWSEROLD_MT_metadata_preview_menu", icon='DOWNARROW_HLT', text="")
class ASSETBROWSER_MT_metadata_preview_menu(bpy.types.Menu):
class ASSETBROWSEROLD_MT_metadata_preview_menu(bpy.types.Menu):
bl_label = "Preview"
def draw(self, _context):
@ -789,7 +789,7 @@ class ASSETBROWSER_MT_metadata_preview_menu(bpy.types.Menu):
layout.operator("ed.lib_id_generate_preview_from_object", text="Render Active Object")
class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
class ASSETBROWSEROLD_PT_metadata_tags(asset_utils.AssetMetaDataPanelOLD, Panel):
bl_label = "Tags"
def draw(self, context):
@ -797,7 +797,7 @@ class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
asset_data = asset_utils.SpaceAssetInfo.get_active_asset(context)
row = layout.row()
row.template_list("ASSETBROWSER_UL_metadata_tags", "asset_tags", asset_data, "tags",
row.template_list("ASSETBROWSEROLD_UL_metadata_tags", "asset_tags", asset_data, "tags",
asset_data, "active_tag", rows=4)
col = row.column(align=True)
@ -805,7 +805,7 @@ class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
col.operator("asset.tag_remove", icon='REMOVE', text="")
class ASSETBROWSER_UL_metadata_tags(UIList):
class ASSETBROWSEROLD_UL_metadata_tags(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
tag = item
@ -863,15 +863,15 @@ classes = (
FILEBROWSER_MT_view_pie,
ASSETBROWSER_PT_display,
ASSETBROWSER_PT_filter,
ASSETBROWSER_MT_editor_menus,
ASSETBROWSER_MT_view,
ASSETBROWSER_MT_select,
ASSETBROWSEROLD_MT_editor_menus,
ASSETBROWSEROLD_MT_view,
ASSETBROWSEROLD_MT_select,
ASSETBROWSER_MT_catalog,
ASSETBROWSER_MT_metadata_preview_menu,
ASSETBROWSER_PT_metadata,
ASSETBROWSER_PT_metadata_preview,
ASSETBROWSER_PT_metadata_tags,
ASSETBROWSER_UL_metadata_tags,
ASSETBROWSEROLD_MT_metadata_preview_menu,
ASSETBROWSEROLD_PT_metadata,
ASSETBROWSEROLD_PT_metadata_preview,
ASSETBROWSEROLD_PT_metadata_tags,
ASSETBROWSEROLD_UL_metadata_tags,
ASSETBROWSER_MT_context_menu,
)

View File

@ -1226,6 +1226,7 @@ class ThemeGenericClassGenerator:
def generate_panel_classes_for_wcols():
wcols = [
("Box", "wcol_box"),
("Data-View Item", "wcol_view_item"),
("List Item", "wcol_list_item"),
("Menu", "wcol_menu"),
("Menu Background", "wcol_menu_back"),

View File

@ -209,6 +209,7 @@ struct SpaceUserPref *CTX_wm_space_userpref(const bContext *C);
struct SpaceClip *CTX_wm_space_clip(const bContext *C);
struct SpaceTopBar *CTX_wm_space_topbar(const bContext *C);
struct SpaceSpreadsheet *CTX_wm_space_spreadsheet(const bContext *C);
struct SpaceAssets *CTX_wm_space_assets(const bContext *C);
void CTX_wm_manager_set(bContext *C, struct wmWindowManager *wm);
void CTX_wm_window_set(bContext *C, struct wmWindow *win);
@ -413,6 +414,8 @@ bool CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list);
bool CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list);
const struct AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C);
struct AssetHandle *CTX_wm_asset_handle_ptr(const bContext *C);
#ifdef __cplusplus
class blender::asset_system::AssetRepresentation *CTX_wm_asset(const bContext *C);
#endif

View File

@ -938,6 +938,15 @@ SpaceSpreadsheet *CTX_wm_space_spreadsheet(const bContext *C)
return nullptr;
}
struct SpaceAssets *CTX_wm_space_assets(const bContext *C)
{
ScrArea *area = CTX_wm_area(C);
if (area && area->spacetype == SPACE_ASSETS) {
return static_cast<SpaceAssets *>(area->spacedata.first);
}
return nullptr;
}
void CTX_wm_manager_set(bContext *C, wmWindowManager *wm)
{
C->wm.manager = wm;
@ -1512,6 +1521,17 @@ static AssetHandle ctx_wm_asset_handle(const bContext *C, bool *r_is_valid)
return AssetHandle{nullptr};
}
/**
* \note Only works in the new Asset Browser and the asset view template (not in the old File
* Browser based Asset Browser).
* TODO Replace #CTX_wm_asset_handle() with this.
*/
AssetHandle *CTX_wm_asset_handle_ptr(const bContext *C)
{
return static_cast<AssetHandle *>(
CTX_data_pointer_get_type(C, "asset_handle", &RNA_AssetHandle).data);
}
blender::asset_system::AssetRepresentation *CTX_wm_asset(const bContext *C)
{
if (auto *asset = static_cast<blender::asset_system::AssetRepresentation *>(

View File

@ -405,6 +405,9 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size)
ImBuf *thumb = IMB_thumb_manage(prv_deferred.filepath.c_str(), THB_LARGE, prv_deferred.source);
if (!thumb) {
/* Thumbnail loading doesn't differentiate between sizes, so if loading for one size fails
* it would fail for the other too. No point in tagging the sizes separately. */
prv->tag |= PRV_TAG_LOADING_FAILED;
return;
}

View File

@ -1070,6 +1070,10 @@ static void write_area(BlendWriter *writer, ScrArea *area)
if (space_type && space_type->blend_write) {
space_type->blend_write(writer, sl);
}
else if (sl->spacetype == SPACE_ASSETS) {
BLO_write_struct(writer, SpaceAssets, sl);
// SpaceAssets *space_assets = (SpaceAssets *)sl;
}
}
}

View File

@ -125,6 +125,9 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
/* Keep this block, even when empty. */
FROM_DEFAULT_V4_UCHAR(space_sequencer.transition);
FROM_DEFAULT_V4_UCHAR(tui.wcol_list_item.inner_sel);
/* TODO version bump. */
btheme->space_assets = btheme->space_file;
}
#undef FROM_DEFAULT_V4_UCHAR

View File

@ -30,6 +30,7 @@ if(WITH_BLENDER)
add_subdirectory(sound)
add_subdirectory(space_action)
add_subdirectory(space_api)
add_subdirectory(space_assets)
add_subdirectory(space_buttons)
add_subdirectory(space_clip)
add_subdirectory(space_console)

View File

@ -40,6 +40,7 @@ set(SRC
intern/asset_shelf_settings.cc
intern/asset_temp_id_consumer.cc
intern/asset_type.cc
intern/asset_view_catalog_filter.cc
ED_asset_catalog.h
ED_asset_catalog.hh
@ -54,6 +55,7 @@ set(SRC
ED_asset_shelf.h
ED_asset_temp_id_consumer.h
ED_asset_type.h
ED_asset_view_catalog_filter.h
intern/asset_library_reference.hh
intern/asset_shelf.hh
)

View File

@ -35,6 +35,8 @@ extern "C" {
struct AssetHandle;
AssetRepresentationHandle *ED_asset_handle_get_representation(const struct AssetHandle *asset);
const char *ED_asset_handle_get_name(const AssetHandle *asset_handle);
const char *ED_asset_handle_get_identifier(const struct AssetHandle *asset);
ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset);
int ED_asset_handle_get_preview_icon_id(const struct AssetHandle *asset);
int ED_asset_handle_get_preview_or_type_icon_id(const struct AssetHandle *asset);

View File

@ -20,6 +20,7 @@ class AssetRepresentation;
extern "C" {
#endif
struct AssetCatalogFilterSettings;
struct AssetLibraryReference;
struct ID;
struct bContext;
@ -35,11 +36,12 @@ struct wmRegionListenerParams;
*/
void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference,
const struct bContext *C);
void ED_assetlist_catalog_filter_set(const struct AssetLibraryReference *,
const struct AssetCatalogFilterSettings *catalog_filter);
bool ED_assetlist_is_loaded(const struct AssetLibraryReference *library_reference);
void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference,
const struct bContext *C);
void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C);
bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference);
/**
* Tag all asset lists in the storage that show main data as needing an update (re-fetch).
*
@ -59,21 +61,27 @@ void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new);
*/
void ED_assetlist_storage_exit(void);
AssetHandle ED_assetlist_asset_handle_get_by_index(const AssetLibraryReference *library_reference,
int asset_index);
AssetHandle *ED_assetlist_asset_handle_get_by_index(const AssetLibraryReference *library_reference,
int asset_index);
#ifdef __cplusplus
blender::asset_system::AssetRepresentation *ED_assetlist_asset_get_by_index(
const AssetLibraryReference &library_reference, int asset_index);
#endif
PreviewImage *ED_assetlist_asset_preview_request(AssetHandle *asset_handle);
int ED_assetlist_asset_preview_icon_id_request(AssetHandle *asset_handle);
int ED_assetlist_asset_preview_or_type_icon_id_request(AssetHandle *asset_handle);
bool ED_assetlist_asset_image_is_loading(const AssetLibraryReference *library_reference,
const AssetHandle *asset_handle);
struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle);
struct AssetLibrary *ED_assetlist_library_get(
const struct AssetLibraryReference *library_reference);
/**
* \return True if the region needs a UI redraw.
*/
bool ED_assetlist_listen(const struct wmNotifier *notifier);
bool ED_assetlist_listen(const AssetLibraryReference *library_reference,
const struct wmNotifier *notifier);
/**
* \return The number of assets stored in the asset list for \a library_reference, or -1 if there
* is no list fetched for it.

View File

@ -8,6 +8,7 @@
#pragma once
#include <optional>
#include <string>
#include "BLI_function_ref.hh"
@ -32,10 +33,14 @@ blender::asset_system::AssetLibrary *ED_assetlist_library_get_once_available(
const AssetLibraryReference &library_reference);
/* Can return false to stop iterating. */
using AssetListHandleIterFn = blender::FunctionRef<bool(AssetHandle)>;
using AssetListHandleIterFn = blender::FunctionRef<bool(AssetHandle &)>;
using AssetListIterFn = blender::FunctionRef<bool(blender::asset_system::AssetRepresentation &)>;
/**
* Iterate the currently loaded assets for the referenced asset library, calling \a fn for each
* asset. This may be executed while the asset list is loading asynchronously. Assets will then be
* included as they get done loading.
*
* \warning Never keep the asset handle passed to \a fn outside of \a fn's scope. While iterating,
* the file data wrapped by the asset handle can be freed, since the file cache has a maximum size.
*/

View File

@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edasset
*/
#pragma once
#include "DNA_space_types.h"
struct AssetLibrary;
struct AssetMetaData;
struct bUUID;
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AssetViewCatalogFilterSettingsHandle AssetViewCatalogFilterSettingsHandle;
AssetViewCatalogFilterSettingsHandle *asset_view_create_catalog_filter_settings(void);
void asset_view_delete_catalog_filter_settings(
AssetViewCatalogFilterSettingsHandle **filter_settings_handle);
bool asset_view_set_catalog_filter_settings(
AssetViewCatalogFilterSettingsHandle *filter_settings_handle,
AssetCatalogFilterMode catalog_visibility,
bUUID catalog_id);
void asset_view_ensure_updated_catalog_filter_data(
AssetViewCatalogFilterSettingsHandle *filter_settings_handle,
const AssetLibrary *asset_library);
bool asset_view_is_asset_visible_in_catalog_filter_settings(
const AssetViewCatalogFilterSettingsHandle *filter_settings_handle,
const AssetMetaData *asset_data);
#ifdef __cplusplus
}
#endif

View File

@ -110,7 +110,6 @@ AssetItemTree build_filtered_all_catalog_tree(
Vector<asset_system::AssetRepresentation *> unassigned_assets;
ED_assetlist_storage_fetch(&library_ref, &C);
ED_assetlist_ensure_previews_job(&library_ref, &C);
asset_system::AssetLibrary *library = ED_assetlist_library_get_once_available(library_ref);
if (!library) {
return {};

View File

@ -25,9 +25,19 @@
#include "ED_asset_handle.h"
blender::asset_system::AssetRepresentation *ED_asset_handle_get_representation(
const AssetHandle *asset)
const AssetHandle *asset_handle)
{
return asset->file_data->asset;
return asset_handle->file_data->asset;
}
const char *ED_asset_handle_get_identifier(const AssetHandle *asset_handle)
{
return asset_handle->file_data->asset->get_identifier().library_relative_identifier().c_str();
}
const char *ED_asset_handle_get_name(const AssetHandle *asset_handle)
{
return asset_handle->file_data->asset->get_name().c_str();
}
ID_Type ED_asset_handle_get_id_type(const AssetHandle *asset_handle)

View File

@ -16,8 +16,10 @@
#include <string>
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
#include "BKE_context.h"
#include "BKE_preview_image.hh"
#include "BKE_screen.h"
#include "BLI_map.hh"
@ -26,6 +28,7 @@
#include "DNA_space_types.h"
#include "BKE_icons.h"
#include "BKE_preferences.h"
#include "WM_api.hh"
@ -34,12 +37,16 @@
#include "../space_file/file_indexer.hh"
#include "../space_file/filelist.hh"
#include "ED_asset_handle.h"
#include "ED_asset_indexer.h"
#include "ED_asset_list.h"
#include "ED_asset_list.hh"
#include "ED_screen.hh"
#include "asset_library_reference.hh"
#include "UI_interface_icons.hh"
#include "UI_resources.hh"
namespace blender::ed::asset {
/* -------------------------------------------------------------------- */
@ -79,32 +86,13 @@ class FileListWrapper {
}
};
class PreviewTimer {
/* Non-owning! The Window-Manager registers and owns this. */
wmTimer *timer_ = nullptr;
public:
void ensureRunning(const bContext *C)
{
if (!timer_) {
timer_ = WM_event_timer_add_notifier(
CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_LIST_PREVIEW, 0.01);
}
}
void stop(const bContext *C)
{
if (timer_) {
WM_event_timer_remove_notifier(CTX_wm_manager(C), CTX_wm_window(C), timer_);
timer_ = nullptr;
}
}
};
class AssetList : NonCopyable {
FileListWrapper filelist_;
/** Storage for asset handles, items are lazy-created on request.
* Asset handles are stored as a pointer here, to ensure a consistent memory address (address
* inside the map changes as the map changes). */
mutable Map<uint32_t, std::unique_ptr<AssetHandle>> asset_handle_map_;
AssetLibraryReference library_ref_;
PreviewTimer previews_timer_;
public:
AssetList() = delete;
@ -112,19 +100,20 @@ class AssetList : NonCopyable {
AssetList(AssetList &&other) = default;
~AssetList() = default;
static bool listen(const wmNotifier &notifier);
bool listen(const wmNotifier &notifier);
void setup();
void fetch(const bContext &C);
void ensurePreviewsJob(const bContext *C);
void setCatalogFilterSettings(const AssetCatalogFilterSettings &settings);
void clear(bContext *C);
AssetHandle asset_get_by_index(int index) const;
AssetHandle *asset_get_by_index(int index) const;
bool needsRefetch() const;
bool isLoaded() const;
bool isAssetPreviewLoading(const AssetHandle &asset) const;
asset_system::AssetLibrary *asset_library() const;
AssetHandle &asset_handle_from_file(const FileDirEntry &) const;
void iterate(AssetListHandleIterFn fn) const;
void iterate(AssetListIterFn fn) const;
int size() const;
@ -175,6 +164,7 @@ void AssetList::fetch(const bContext &C)
if (filelist_needs_force_reset(files)) {
filelist_readjob_stop(files, CTX_wm_manager(&C));
filelist_clear_from_reset_tag(files);
asset_handle_map_.clear();
}
if (filelist_needs_reading(files)) {
@ -186,6 +176,12 @@ void AssetList::fetch(const bContext &C)
filelist_filter(files);
}
void AssetList::setCatalogFilterSettings(const AssetCatalogFilterSettings &settings)
{
filelist_set_asset_catalog_filter_options(
filelist_, (AssetCatalogFilterMode)settings.filter_mode, &settings.active_catalog_id);
}
bool AssetList::needsRefetch() const
{
return filelist_needs_force_reset(filelist_) || filelist_needs_reading(filelist_);
@ -206,6 +202,15 @@ asset_system::AssetLibrary *AssetList::asset_library() const
return reinterpret_cast<asset_system::AssetLibrary *>(filelist_asset_library(filelist_));
}
AssetHandle &AssetList::asset_handle_from_file(const FileDirEntry &file) const
{
AssetHandle &asset = *asset_handle_map_.lookup_or_add(
file.uid, std::make_unique<AssetHandle>(AssetHandle{&file}));
/* The file is recreated while loading, update the pointer here. */
asset.file_data = &file;
return asset;
}
void AssetList::iterate(AssetListHandleIterFn fn) const
{
FileList *files = filelist_;
@ -217,7 +222,7 @@ void AssetList::iterate(AssetListHandleIterFn fn) const
continue;
}
AssetHandle asset_handle = {file};
AssetHandle &asset_handle = asset_handle_from_file(*file);
if (!fn(asset_handle)) {
/* If the callback returns false, we stop iterating. */
break;
@ -235,31 +240,6 @@ void AssetList::iterate(AssetListIterFn fn) const
});
}
void AssetList::ensurePreviewsJob(const bContext *C)
{
FileList *files = filelist_;
int numfiles = filelist_files_ensure(files);
filelist_cache_previews_set(files, true);
/* TODO fetch all previews for now. */
/* Add one extra entry to ensure nothing is lost because of integer division. */
filelist_file_cache_slidingwindow_set(files, numfiles / 2 + 1);
filelist_file_cache_block(files, 0);
filelist_cache_previews_update(files);
{
const bool previews_running = filelist_cache_previews_running(files) &&
!filelist_cache_previews_done(files);
if (previews_running) {
previews_timer_.ensureRunning(C);
}
else {
/* Preview is not running, no need to keep generating update events! */
previews_timer_.stop(C);
}
}
}
void AssetList::clear(bContext *C)
{
/* Based on #ED_fileselect_clear() */
@ -268,13 +248,18 @@ void AssetList::clear(bContext *C)
filelist_readjob_stop(files, CTX_wm_manager(C));
filelist_freelib(files);
filelist_clear(files);
asset_handle_map_.clear();
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr);
}
AssetHandle AssetList::asset_get_by_index(int index) const
AssetHandle *AssetList::asset_get_by_index(int index) const
{
return {filelist_file(filelist_, index)};
FileDirEntry *file = filelist_file(filelist_, index);
if (!file) {
return nullptr;
}
return &asset_handle_from_file(*file);
}
/**
@ -290,7 +275,11 @@ bool AssetList::listen(const wmNotifier &notifier)
break;
}
case NC_ASSET:
if (ELEM(notifier.data, ND_ASSET_LIST, ND_ASSET_LIST_READING, ND_ASSET_LIST_PREVIEW)) {
if (ELEM(notifier.data, ND_ASSET_LIST)) {
filelist_tag_needs_filtering(filelist_);
return true;
}
if (ELEM(notifier.data, ND_ASSET_LIST_READING)) {
return true;
}
if (ELEM(notifier.action, NA_ADDED, NA_REMOVED, NA_EDITED)) {
@ -476,13 +465,12 @@ bool ED_assetlist_is_loaded(const AssetLibraryReference *library_reference)
return list->isLoaded();
}
void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference,
const bContext *C)
void ED_assetlist_catalog_filter_set(const struct AssetLibraryReference *library_reference,
const struct AssetCatalogFilterSettings *settings)
{
AssetList *list = AssetListStorage::lookup_list(*library_reference);
if (list) {
list->ensurePreviewsJob(C);
list->setCatalogFilterSettings(*settings);
}
}
@ -525,8 +513,8 @@ asset_system::AssetLibrary *ED_assetlist_library_get_once_available(
return list->asset_library();
}
AssetHandle ED_assetlist_asset_handle_get_by_index(const AssetLibraryReference *library_reference,
int asset_index)
AssetHandle *ED_assetlist_asset_handle_get_by_index(const AssetLibraryReference *library_reference,
int asset_index)
{
const AssetList *list = AssetListStorage::lookup_list(*library_reference);
return list->asset_get_by_index(asset_index);
@ -535,9 +523,56 @@ AssetHandle ED_assetlist_asset_handle_get_by_index(const AssetLibraryReference *
asset_system::AssetRepresentation *ED_assetlist_asset_get_by_index(
const AssetLibraryReference &library_reference, int asset_index)
{
AssetHandle asset_handle = ED_assetlist_asset_handle_get_by_index(&library_reference,
asset_index);
return reinterpret_cast<asset_system::AssetRepresentation *>(asset_handle.file_data->asset);
AssetHandle *asset_handle = ED_assetlist_asset_handle_get_by_index(&library_reference,
asset_index);
return reinterpret_cast<asset_system::AssetRepresentation *>(asset_handle->file_data->asset);
}
PreviewImage *ED_assetlist_asset_preview_request(AssetHandle *asset_handle)
{
if (asset_handle->preview) {
return asset_handle->preview;
}
asset_system::AssetRepresentation *asset = ED_asset_handle_get_representation(asset_handle);
if (ID *local_id = asset->local_id()) {
asset_handle->preview = BKE_previewimg_id_get(local_id);
}
else {
const char *asset_identifier = asset->get_identifier().library_relative_identifier().c_str();
const int source = filelist_preview_source_get(asset_handle->file_data->typeflag);
const std::string asset_path = asset->get_identifier().full_path();
asset_handle->preview = BKE_previewimg_cached_thumbnail_read(
asset_identifier, asset_path.c_str(), source, false);
}
return asset_handle->preview;
}
static int preview_icon_id_ensure(AssetHandle *asset_handle, PreviewImage *preview)
{
asset_system::AssetRepresentation *asset = ED_asset_handle_get_representation(asset_handle);
return BKE_icon_preview_ensure(asset->local_id(), preview);
}
int ED_assetlist_asset_preview_icon_id_request(AssetHandle *asset_handle)
{
PreviewImage *preview = ED_assetlist_asset_preview_request(asset_handle);
return preview_icon_id_ensure(asset_handle, preview);
}
int ED_assetlist_asset_preview_or_type_icon_id_request(AssetHandle *asset_handle)
{
PreviewImage *preview = ED_assetlist_asset_preview_request(asset_handle);
/* Preview is invalid or still loading. Return an icon ID based on the type. */
if (preview->tag & (PRV_TAG_LOADING_FAILED | PRV_TAG_DEFFERED_RENDERING)) {
ID_Type id_type = ED_asset_handle_get_id_type(asset_handle);
return UI_icon_from_idcode(id_type);
}
return preview_icon_id_ensure(asset_handle, preview);
}
bool ED_assetlist_asset_image_is_loading(const AssetLibraryReference *library_reference,
@ -557,9 +592,23 @@ ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle)
return filelist_geticon_image_ex(asset_handle->file_data);
}
bool ED_assetlist_listen(const wmNotifier *notifier)
AssetLibrary *ED_assetlist_library_get(const AssetLibraryReference *library_reference)
{
return AssetList::listen(*notifier);
AssetList *list = AssetListStorage::lookup_list(*library_reference);
if (list) {
return reinterpret_cast<AssetLibrary *>(list->asset_library());
}
return nullptr;
}
bool ED_assetlist_listen(const AssetLibraryReference *library_reference,
const wmNotifier *notifier)
{
AssetList *list = AssetListStorage::lookup_list(*library_reference);
if (list) {
return list->listen(*notifier);
}
return false;
}
int ED_assetlist_size(const AssetLibraryReference *library_reference)

View File

@ -71,7 +71,6 @@ static const asset_system::AssetRepresentation *get_local_asset_from_relative_id
AssetLibraryReference library_ref{};
library_ref.type = ASSET_LIBRARY_LOCAL;
ED_assetlist_storage_fetch(&library_ref, &C);
ED_assetlist_ensure_previews_job(&library_ref, &C);
const asset_system::AssetRepresentation *matching_asset = nullptr;
ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) {
@ -104,7 +103,6 @@ static const asset_system::AssetRepresentation *find_asset_from_weak_ref(
const AssetLibraryReference library_ref = asset_system::all_library_reference();
ED_assetlist_storage_fetch(&library_ref, &C);
ED_assetlist_ensure_previews_job(&library_ref, &C);
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(
asset_system::all_library_reference());
if (!all_library) {

View File

@ -439,13 +439,24 @@ static void ASSET_OT_library_refresh(wmOperatorType *ot)
/* -------------------------------------------------------------------- */
static AssetLibrary *get_asset_library(const bContext *C)
{
if (const SpaceFile *sfile = CTX_wm_space_file(C)) {
return ED_fileselect_active_asset_library_get(sfile);
}
if (const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C)) {
return ED_assetlist_library_get(library_ref);
}
return nullptr;
}
static bool asset_catalog_operator_poll(bContext *C)
{
const SpaceFile *sfile = CTX_wm_space_file(C);
if (!sfile) {
return false;
}
const AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
const AssetLibrary *asset_library = get_asset_library(C);
if (!asset_library) {
return false;
}
@ -458,16 +469,20 @@ static bool asset_catalog_operator_poll(bContext *C)
static int asset_catalog_new_exec(bContext *C, wmOperator *op)
{
SpaceFile *sfile = CTX_wm_space_file(C);
AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
AssetLibrary *asset_library = get_asset_library(C);
char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr);
blender::asset_system::AssetCatalog *new_catalog = ED_asset_catalog_add(
asset_library, DATA_("Catalog"), parent_path);
if (sfile) {
if (SpaceFile *sfile = CTX_wm_space_file(C)) {
ED_fileselect_activate_asset_catalog(sfile, new_catalog->catalog_id);
}
else if (SpaceAssets *sassets = CTX_wm_space_assets(C)) {
/* TODO how can we select the catalog here in a nice way, without being space dependent? Idea:
* use an operator macro instead? */
UNUSED_VARS(sassets);
}
MEM_freeN(parent_path);
@ -498,8 +513,7 @@ static void ASSET_OT_catalog_new(wmOperatorType *ot)
static int asset_catalog_delete_exec(bContext *C, wmOperator *op)
{
SpaceFile *sfile = CTX_wm_space_file(C);
AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
AssetLibrary *asset_library = get_asset_library(C);
char *catalog_id_str = RNA_string_get_alloc(op->ptr, "catalog_id", nullptr, 0, nullptr);
asset_system::CatalogID catalog_id;
if (!BLI_uuid_parse_string(&catalog_id, catalog_id_str)) {
@ -534,13 +548,16 @@ static void ASSET_OT_catalog_delete(wmOperatorType *ot)
static asset_system::AssetCatalogService *get_catalog_service(bContext *C)
{
const SpaceFile *sfile = CTX_wm_space_file(C);
if (!sfile) {
return nullptr;
if (const SpaceFile *sfile = CTX_wm_space_file(C)) {
AssetLibrary *asset_lib = ED_fileselect_active_asset_library_get(sfile);
return AS_asset_library_get_catalog_service(asset_lib);
}
if (const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C)) {
AssetLibrary *asset_lib = ED_assetlist_library_get(library_ref);
return AS_asset_library_get_catalog_service(asset_lib);
}
AssetLibrary *asset_lib = ED_fileselect_active_asset_library_get(sfile);
return AS_asset_library_get_catalog_service(asset_lib);
return nullptr;
}
static int asset_catalog_undo_exec(bContext *C, wmOperator * /*op*/)
@ -551,7 +568,7 @@ static int asset_catalog_undo_exec(bContext *C, wmOperator * /*op*/)
}
catalog_service->undo();
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
WM_event_add_notifier(C, NC_ASSET | ND_ASSET_CATALOGS, nullptr);
return OPERATOR_FINISHED;
}
@ -581,7 +598,7 @@ static int asset_catalog_redo_exec(bContext *C, wmOperator * /*op*/)
}
catalog_service->redo();
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
WM_event_add_notifier(C, NC_ASSET | ND_ASSET_CATALOGS, nullptr);
return OPERATOR_FINISHED;
}
@ -658,8 +675,7 @@ static bool asset_catalogs_save_poll(bContext *C)
static int asset_catalogs_save_exec(bContext *C, wmOperator * /*op*/)
{
const SpaceFile *sfile = CTX_wm_space_file(C);
::AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
::AssetLibrary *asset_library = get_asset_library(C);
ED_asset_catalogs_save_from_main_path(asset_library, CTX_data_main(C));
@ -694,11 +710,12 @@ static bool has_external_files(Main *bmain, ReportList *reports);
static bool asset_bundle_install_poll(bContext *C)
{
/* This operator only works when the asset browser is set to Current File. */
const SpaceFile *sfile = CTX_wm_space_file(C);
if (sfile == nullptr) {
const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C);
if (asset_library_ref == nullptr) {
return false;
}
if (!ED_fileselect_is_local_asset_library(sfile)) {
if (asset_library_ref->type == ASSET_LIBRARY_LOCAL) {
return false;
}

View File

@ -41,6 +41,7 @@ using namespace blender;
using namespace blender::ed::asset;
static int asset_shelf_default_tile_height();
static const AssetLibraryReference &asset_shelf_library_ref();
namespace blender::ed::asset::shelf {
@ -250,7 +251,8 @@ static void asset_shelf_region_listen(const wmRegionListenerParams *params)
void ED_asset_shelf_region_listen(const wmRegionListenerParams *params)
{
if (ED_assetlist_listen(params->notifier)) {
const AssetLibraryReference &library_ref = asset_shelf_library_ref();
if (ED_assetlist_listen(&library_ref, params->notifier)) {
ED_region_tag_redraw_no_rebuild(params->region);
}
/* If the asset list didn't catch the notifier, let the region itself listen. */

View File

@ -248,7 +248,6 @@ void build_asset_view(uiLayout &layout,
ARegion &region)
{
ED_assetlist_storage_fetch(&library_ref, &C);
ED_assetlist_ensure_previews_job(&library_ref, &C);
const asset_system::AssetLibrary *library = ED_assetlist_library_get_once_available(library_ref);
if (!library) {

View File

@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edasset
*/
#include <memory>
#include "AS_asset_catalog.hh"
#include "AS_asset_library.hh"
#include "DNA_space_types.h"
#include "ED_asset_view_catalog_filter.h"
namespace asset_system = blender::asset_system;
struct AssetViewCatalogFilter {
AssetCatalogFilterSettings filter_settings;
std::unique_ptr<asset_system::AssetCatalogFilter> catalog_filter;
};
AssetViewCatalogFilterSettingsHandle *asset_view_create_catalog_filter_settings()
{
AssetViewCatalogFilter *filter_settings = MEM_new<AssetViewCatalogFilter>(__func__);
return reinterpret_cast<AssetViewCatalogFilterSettingsHandle *>(filter_settings);
}
void asset_view_delete_catalog_filter_settings(
AssetViewCatalogFilterSettingsHandle **filter_settings_handle)
{
AssetViewCatalogFilter **filter_settings = reinterpret_cast<AssetViewCatalogFilter **>(
filter_settings_handle);
MEM_delete(*filter_settings);
*filter_settings = nullptr;
}
bool asset_view_set_catalog_filter_settings(
AssetViewCatalogFilterSettingsHandle *filter_settings_handle,
AssetCatalogFilterMode catalog_visibility,
::bUUID catalog_id)
{
AssetViewCatalogFilter *filter_settings = reinterpret_cast<AssetViewCatalogFilter *>(
filter_settings_handle);
bool needs_update = false;
if (filter_settings->filter_settings.filter_mode != catalog_visibility) {
filter_settings->filter_settings.filter_mode = catalog_visibility;
needs_update = true;
}
if (filter_settings->filter_settings.filter_mode == ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG &&
!BLI_uuid_equal(filter_settings->filter_settings.active_catalog_id, catalog_id))
{
filter_settings->filter_settings.active_catalog_id = catalog_id;
needs_update = true;
}
return needs_update;
}
void asset_view_ensure_updated_catalog_filter_data(
AssetViewCatalogFilterSettingsHandle *filter_settings_handle,
const ::AssetLibrary *asset_library)
{
AssetViewCatalogFilter *filter_settings = reinterpret_cast<AssetViewCatalogFilter *>(
filter_settings_handle);
const asset_system::AssetCatalogService *catalog_service = AS_asset_library_get_catalog_service(
asset_library);
if (filter_settings->filter_settings.filter_mode != ASSET_CATALOG_SHOW_ALL_ASSETS) {
filter_settings->catalog_filter = std::make_unique<asset_system::AssetCatalogFilter>(
catalog_service->create_catalog_filter(
filter_settings->filter_settings.active_catalog_id));
}
}
bool asset_view_is_asset_visible_in_catalog_filter_settings(
const AssetViewCatalogFilterSettingsHandle *filter_settings_handle,
const AssetMetaData *asset_data)
{
const AssetViewCatalogFilter *filter_settings = reinterpret_cast<const AssetViewCatalogFilter *>(
filter_settings_handle);
switch (filter_settings->filter_settings.filter_mode) {
case ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG:
return !filter_settings->catalog_filter->is_known(asset_data->catalog_id);
case ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG:
return filter_settings->catalog_filter->contains(asset_data->catalog_id);
case ASSET_CATALOG_SHOW_ALL_ASSETS:
/* All asset files should be visible. */
return true;
}
BLI_assert_unreachable();
return false;
}

View File

@ -506,6 +506,7 @@ bool ED_operator_file_active(bContext *C);
*/
bool ED_operator_file_browsing_active(bContext *C);
bool ED_operator_asset_browsing_active(bContext *C);
bool ED_operator_asset_browser_active(bContext *C);
bool ED_operator_spreadsheet_active(bContext *C);
bool ED_operator_action_active(bContext *C);
bool ED_operator_buttons_active(bContext *C);

View File

@ -14,12 +14,12 @@ struct KDTree_1d;
struct PointerRNA;
struct wmOperatorType;
enum {
typedef enum SelectAction {
SEL_TOGGLE = 0,
SEL_SELECT = 1,
SEL_DESELECT = 2,
SEL_INVERT = 3,
};
} SelectAction;
enum WalkSelectDirection {
UI_SELECT_WALK_UP,

View File

@ -23,6 +23,7 @@ void ED_spacemacros_init();
* Calls for registering default spaces, only called once, from #ED_spacetypes_init
* \{ */
void ED_spacetype_assets();
void ED_spacetype_outliner();
void ED_spacetype_view3d();
void ED_spacetype_ipo();

View File

@ -88,6 +88,10 @@ class AbstractView {
virtual void draw_overlays(const ARegion &region) const;
/**
* Iterate over all elements in the view executing \a iter_fn for each. Typically views would
* want to implement their own `foreach_item()` function with a more specific type.
*/
virtual void foreach_view_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const = 0;
/**
@ -149,9 +153,13 @@ class AbstractViewItem {
AbstractView *view_ = nullptr;
/** See #view_item_button() */
uiButViewItem *view_item_but_ = nullptr;
/* Behavior toggles. */
bool is_activatable_ = true;
bool is_interactive_ = true;
bool is_selectable_ = false;
/* State properties. */
bool is_active_ = false;
bool is_selected_ = false;
bool is_renaming_ = false;
/** Cache filtered state here to avoid having to re-query. */
@ -237,8 +245,10 @@ class AbstractViewItem {
*
* Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise the
* actual item state is unknown, possibly calling state-change update functions incorrectly.
*
* \return True if the item was activated successfully.
*/
void activate(bContext &C);
bool activate(bContext &C);
void deactivate();
/**
* Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we
@ -246,6 +256,25 @@ class AbstractViewItem {
*/
bool is_active() const;
void enable_selectable();
bool is_selectable() const;
/**
* Mark this item as selected if it supports selection (#AbstractViewItem::supports_selection()
* returns true).
* \return True if the selection state changed (redraw needed).
*/
bool select();
/**
* Mark this item as not selected.
* \return True if the selection state changed (redraw needed).
*/
bool deselect();
/**
* Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we
* can't be sure about the item state.
*/
bool is_selected() const;
bool is_renaming() const;
void begin_renaming();
void end_renaming();

View File

@ -202,7 +202,7 @@ class PreviewGridItem : public AbstractGridViewItem {
std::string label{};
int preview_icon_id = ICON_NONE;
PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id);
PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id = ICON_NONE);
void build_grid_tile(uiLayout &layout) const override;

View File

@ -194,6 +194,18 @@ std::unique_ptr<DropTargetInterface> view_item_drop_target(uiViewItemHandle *ite
std::unique_ptr<DropTargetInterface> region_views_find_drop_target_at(const ARegion *region,
const int xy[2]);
/**
* \note #SEL_TOGGLE is ignored here as it has no meaning for individual items (should be refined
* to #SEL_SELECT or #SEL_DESELECT based on the selection state of other items).
* \return True if any selection state changed (redraw necessary).
*/
bool view_item_select_from_action(uiViewItemHandle *item_handle,
const int /*SelectAction*/ action);
/**
* \return True if any selection state changed (redraw necessary).
*/
bool view_select_all_from_action(uiViewHandle *view_handle, int /*SelectAction*/ action);
} // namespace blender::ui
enum eUIListFilterResult {

View File

@ -3275,7 +3275,11 @@ void UI_interface_tag_script_reload();
bool UI_view_begin_filtering(const bContext *C, const uiViewHandle *view_handle);
bool UI_view_item_is_interactive(const uiViewItemHandle *item_handle);
/** \return True if the active state changed (requiring redraw). */
bool UI_view_item_activate(bContext *C, uiViewItemHandle *item_handle);
bool UI_view_item_is_active(const uiViewItemHandle *item_handle);
bool UI_view_item_is_selectable(const uiViewItemHandle *item_handle);
bool UI_view_item_is_selected(const uiViewItemHandle *item_handle);
bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle);
/**
* Can \a item_handle be renamed right now? Note that this isn't just a mere wrapper around
@ -3297,11 +3301,14 @@ bool UI_view_item_supports_drag(const uiViewItemHandle *item_);
*/
bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_);
uiViewHandle *UI_region_view_find_from_idname(const struct ARegion *region, const char *idname);
/**
* \param xy: Coordinate to find a view item at, in window space.
* \param pad: Extra padding added to the bounding box of the view.
*/
uiViewHandle *UI_region_view_find_at(const ARegion *region, const int xy[2], int pad);
uiViewHandle *UI_region_view_find_at(const ARegion *region,
const int xy[2],
int pad CPP_ARG_DEFAULT(0));
/**
* \param xy: Coordinate to find a view item at, in window space.
*/

View File

@ -2282,10 +2282,21 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
break;
case UI_BTYPE_VIEW_ITEM: {
const uiButViewItem *view_item_but = (const uiButViewItem *)but;
const uiViewItemHandle *view_item = view_item_but->view_item;
is_push = -1;
if (view_item_but->view_item) {
is_push = UI_view_item_is_active(view_item_but->view_item);
if (!view_item) {
break;
}
/* Highlights for active items tend to look as if they were selected, so don't highlight
* them when the item is also selectable. For not selectable items, the active state is
* important to display though. */
if (UI_view_item_is_selectable(view_item)) {
is_push = UI_view_item_is_selected(view_item);
}
else {
is_push = UI_view_item_is_active(view_item);
}
break;
}

View File

@ -4581,8 +4581,8 @@ static void ui_litem_layout_grid_flow(uiLayout *litem)
BLI_assert(gflow->tot_columns > 0);
BLI_assert(gflow->tot_rows > 0);
const int space_x = style->columnspace;
const int space_y = style->buttonspacey;
const int space_x = litem->align ? 0 : style->columnspace;
const int space_y = litem->align ? 0 : style->buttonspacey;
blender::Array<int, 64> widths(gflow->tot_columns);
blender::Array<int, 64> heights(gflow->tot_rows);

View File

@ -58,6 +58,7 @@
#include "ED_object.hh"
#include "ED_paint.hh"
#include "ED_select_utils.hh"
/* for Copy As Driver */
#include "ED_keyframing.hh"
@ -2453,6 +2454,127 @@ static void UI_OT_view_start_filter(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name UI View Item Select Operator
* \{ */
static int ui_view_item_select_exec(bContext *C, wmOperator *op)
{
ARegion *region = CTX_wm_region(C);
const bool extend = RNA_boolean_get(op->ptr, "extend");
const bool deselect = RNA_boolean_get(op->ptr, "deselect");
const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
const bool toggle = RNA_boolean_get(op->ptr, "toggle");
// const bool fill = RNA_boolean_get(op->ptr, "fill");
int xy[2];
xy[0] = RNA_int_get(op->ptr, "mouse_x");
xy[1] = RNA_int_get(op->ptr, "mouse_y");
ui_region_to_window(region, &xy[0], &xy[1]);
uiViewHandle *view = UI_region_view_find_at(region, xy);
uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, xy);
bool changed = false;
bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
if (extend || toggle) {
wait_to_deselect_others = false;
}
if (hovered_item) {
const bool is_selected = UI_view_item_is_selected(hovered_item);
if (wait_to_deselect_others && is_selected) {
return OPERATOR_RUNNING_MODAL;
}
else if (!extend) {
changed |= view_select_all_from_action(view, SEL_DESELECT);
}
SelectAction action = deselect ? SEL_DESELECT : SEL_SELECT;
if (toggle) {
action = SEL_INVERT;
}
changed |= view_item_select_from_action(hovered_item, action);
/* The last selected item should be activated. */
if (UI_view_item_is_selected(hovered_item) ||
/* Items may not support selection but a click should still activate it. */
(action == SEL_SELECT))
{
changed |= UI_view_item_activate(C, hovered_item);
}
}
/* Click on empty space, deselect all if requested. */
else if (deselect_all) {
changed |= view_select_all_from_action(view, SEL_DESELECT);
}
if (changed) {
ED_region_tag_redraw(region);
}
return OPERATOR_FINISHED;
}
static void UI_OT_view_item_select(wmOperatorType *ot)
{
ot->name = "View Item Select";
ot->idname = "UI_OT_view_item_select";
ot->description = "Select or deselect the item under the cursor";
ot->poll = ui_view_focused_poll;
ot->exec = ui_view_item_select_exec;
ot->invoke = WM_generic_select_invoke;
ot->modal = WM_generic_select_modal;
PropertyRNA *prop;
WM_operator_properties_generic_select(ot);
WM_operator_properties_mouse_select(ot);
prop = RNA_def_boolean(
ot->srna, "fill", false, "Fill", "Select everything beginning with the last selection");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name UI View Select All Operator
* \{ */
static int ui_view_select_all_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
uiViewHandle *view = UI_region_view_find_at(region, event->xy);
const int action = RNA_enum_get(op->ptr, "action");
if (view_select_all_from_action(view, action)) {
ED_region_tag_redraw(region);
}
return OPERATOR_FINISHED;
}
static void UI_OT_view_select_all(wmOperatorType *ot)
{
ot->name = "View Select All";
ot->idname = "UI_OT_view_select_all";
ot->description = "Select or deselect all items in the view";
ot->invoke = ui_view_select_all_invoke;
ot->poll = ui_view_focused_poll;
WM_operator_properties_select_all(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name UI View Drop Operator
* \{ */
@ -2646,6 +2768,8 @@ void ED_operatortypes_ui()
WM_operatortype_append(UI_OT_list_start_filter);
WM_operatortype_append(UI_OT_view_start_filter);
WM_operatortype_append(UI_OT_view_item_select);
WM_operatortype_append(UI_OT_view_select_all);
WM_operatortype_append(UI_OT_view_drop);
WM_operatortype_append(UI_OT_view_item_rename);

View File

@ -54,12 +54,12 @@ static void asset_view_item_but_drag_set(uiBut *but, AssetHandle *asset_handle)
return;
}
BIFIconID preview_icon_id = ED_assetlist_asset_preview_icon_id_request(asset_handle);
const eAssetImportMethod import_method = asset->get_import_method().value_or(
ASSET_IMPORT_APPEND_REUSE);
ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle);
UI_but_drag_set_asset(
but, asset, import_method, ED_asset_handle_get_preview_icon_id(asset_handle), imbuf, 1.0f);
UI_but_drag_set_asset(but, asset, import_method, preview_icon_id, imbuf, 1.0f);
}
static void asset_view_draw_item(uiList *ui_list,
@ -75,12 +75,12 @@ static void asset_view_draw_item(uiList *ui_list,
{
AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata;
AssetHandle asset_handle = ED_assetlist_asset_handle_get_by_index(&list_data->asset_library_ref,
index);
AssetHandle *asset_handle = ED_assetlist_asset_handle_get_by_index(&list_data->asset_library_ref,
index);
PointerRNA file_ptr = RNA_pointer_create(&list_data->screen->id,
&RNA_FileSelectEntry,
const_cast<FileDirEntry *>(asset_handle.file_data));
const_cast<FileDirEntry *>(asset_handle->file_data));
uiLayoutSetContextPointer(layout, "active_file", &file_ptr);
uiBlock *block = uiLayoutGetBlock(layout);
@ -91,8 +91,8 @@ static void asset_view_draw_item(uiList *ui_list,
block,
UI_BTYPE_PREVIEW_TILE,
0,
ED_asset_handle_get_preview_icon_id(&asset_handle),
show_names ? ED_asset_handle_get_representation(&asset_handle)->get_name().c_str() : "",
ED_assetlist_asset_preview_icon_id_request(asset_handle),
show_names ? ED_asset_handle_get_representation(asset_handle)->get_name().c_str() : "",
0,
0,
size_x,
@ -104,12 +104,12 @@ static void asset_view_draw_item(uiList *ui_list,
0,
"");
ui_def_but_icon(but,
ED_asset_handle_get_preview_icon_id(&asset_handle),
ED_assetlist_asset_preview_icon_id_request(asset_handle),
/* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */
UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
but->emboss = UI_EMBOSS_NONE;
if (!ui_list->dyn_data->custom_drag_optype) {
asset_view_item_but_drag_set(but, &asset_handle);
asset_view_item_but_drag_set(but, asset_handle);
}
}
@ -146,8 +146,9 @@ static void asset_view_filter_items(uiList *ui_list,
});
}
static void asset_view_listener(uiList * /*ui_list*/, wmRegionListenerParams *params)
static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params)
{
const AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata;
const wmNotifier *notifier = params->notifier;
switch (notifier->category) {
@ -159,7 +160,7 @@ static void asset_view_listener(uiList * /*ui_list*/, wmRegionListenerParams *pa
}
}
if (ED_assetlist_listen(params->notifier)) {
if (ED_assetlist_listen(&list_data->asset_library_ref, params->notifier)) {
ED_region_tag_redraw(params->region);
}
}
@ -255,7 +256,6 @@ void uiTemplateAssetView(uiLayout *layout,
}
ED_assetlist_storage_fetch(&asset_library_ref, C);
ED_assetlist_ensure_previews_job(&asset_library_ref, C);
const int tot_items = ED_assetlist_size(&asset_library_ref);
populate_asset_collection(asset_library_ref, *assets_dataptr, assets_propname);

View File

@ -652,6 +652,8 @@ static MenuSearch_Data *menu_items_from_ui_create(bContext *C,
SPACE_MENU_NOP(SPACE_STATUSBAR);
SPACE_MENU_NOP(SPACE_TOPBAR);
SPACE_MENU_NOP(SPACE_SPREADSHEET);
/* TODO */
SPACE_MENU_NOP(SPACE_ASSETS);
}
}
for (int i = 0; i < idname_array_len; i++) {

View File

@ -5595,8 +5595,9 @@ void ui_draw_preview_item_stateless(const uiFontStyle *fstyle,
bool draw_as_icon)
{
rcti trect = *rect;
const float text_size = UI_UNIT_Y;
float font_dims[2] = {0.0f, 0.0f};
const bool has_text = name && name[0];
const float padding = PREVIEW_PAD;
float alpha = 1.0f;
@ -5611,8 +5612,12 @@ void ui_draw_preview_item_stateless(const uiFontStyle *fstyle,
}
if (has_text) {
UI_fontstyle_set(fstyle);
BLF_width_and_height(
fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]);
/* draw icon in rect above the space reserved for the label */
rect->ymin += text_size;
rect->ymin += round_fl_to_int(font_dims[1] + 2 * padding);
}
GPU_blend(GPU_BLEND_ALPHA);
if (draw_as_icon) {
@ -5628,10 +5633,8 @@ void ui_draw_preview_item_stateless(const uiFontStyle *fstyle,
}
/* text rect */
trect.ymax = trect.ymin + text_size;
trect.ymin += PREVIEW_PAD;
trect.xmin += PREVIEW_PAD;
trect.xmax -= PREVIEW_PAD;
BLI_rcti_pad(&trect, -padding * 2, -padding * 2);
trect.ymax = round_fl_to_int(trect.ymin + font_dims[1]);
{
char drawstr[UI_MAX_DRAW_STR];

View File

@ -141,6 +141,9 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
case SPACE_SPREADSHEET:
ts = &btheme->space_spreadsheet;
break;
case SPACE_ASSETS:
ts = &btheme->space_assets;
break;
default:
ts = &btheme->space_view3d;
break;

View File

@ -6,6 +6,10 @@
* \ingroup edinterface
*/
#include <string>
#include "ED_select_utils.hh"
#include "interface_intern.hh"
#include "UI_abstract_view.hh"
@ -159,14 +163,80 @@ std::optional<rcti> AbstractView::get_bounds() const
/** \} */
} // namespace blender::ui
/* ---------------------------------------------------------------------- */
/** \name Selection
* \{ */
/**
* If \a action is #SEL_TOGGLE, check the selection if the actual action to apply should be
* #SEL_DESELECT or #SEL_SELECT and return that.
*/
static SelectAction select_all_refine_action_type(const AbstractView &view, SelectAction action)
{
if (action != SEL_TOGGLE) {
return action;
}
bool any_selected = false;
view.foreach_view_item([&any_selected](AbstractViewItem &item) {
if (item.is_selected()) {
any_selected = true;
}
});
return any_selected ? SEL_DESELECT : SEL_SELECT;
}
static bool view_item_select_from_action(AbstractViewItem &item, const SelectAction action)
{
switch (action) {
case SEL_SELECT:
return item.select();
case SEL_DESELECT:
return item.deselect();
case SEL_INVERT:
if (item.is_selected()) {
item.deselect();
}
else {
item.select();
}
return true;
case SEL_TOGGLE:
BLI_assert_msg(
false,
"TOGGLE action should have been refined to be either SELECT or DESELECT at this point");
break;
}
return false;
}
bool view_item_select_from_action(uiViewItemHandle *item_handle, const int /*SelectAction*/ action)
{
AbstractViewItem &item = reinterpret_cast<AbstractViewItem &>(*item_handle);
return view_item_select_from_action(item, SelectAction(action));
}
bool view_select_all_from_action(uiViewHandle *view_handle, const int /*SelectAction*/ action)
{
AbstractView &view = reinterpret_cast<AbstractView &>(*view_handle);
const SelectAction refined_action = select_all_refine_action_type(view, SelectAction(action));
bool changed = false;
view.foreach_view_item([&changed, refined_action](AbstractViewItem &item) {
changed |= view_item_select_from_action(item, refined_action);
});
return changed;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name General API functions
* \{ */
namespace blender::ui {
std::unique_ptr<DropTargetInterface> view_drop_target(uiViewHandle *view_handle)
{
AbstractView &view = reinterpret_cast<AbstractView &>(*view_handle);

View File

@ -29,6 +29,7 @@ namespace blender::ui {
void AbstractViewItem::update_from_old(const AbstractViewItem &old)
{
is_active_ = old.is_active_;
is_selected_ = old.is_selected_;
is_renaming_ = old.is_renaming_;
}
@ -67,11 +68,13 @@ bool AbstractViewItem::set_state_active()
return true;
}
void AbstractViewItem::activate(bContext &C)
bool AbstractViewItem::activate(bContext &C)
{
if (set_state_active()) {
on_activate(C);
return true;
}
return false;
}
void AbstractViewItem::deactivate()
@ -317,6 +320,44 @@ bool AbstractViewItem::is_active() const
return is_active_;
}
void AbstractViewItem::enable_selectable()
{
is_selectable_ = true;
}
bool AbstractViewItem::is_selectable() const
{
return is_selectable_;
}
bool AbstractViewItem::select()
{
if (!is_selectable_) {
return false;
}
if (is_selected_) {
return false;
}
is_selected_ = true;
return true;
}
bool AbstractViewItem::deselect()
{
if (!is_selected_) {
return false;
}
is_selected_ = false;
return true;
}
bool AbstractViewItem::is_selected() const
{
BLI_assert_msg(get_view().is_reconstructed(),
"State can't be queried until reconstruction is completed");
return is_selected_;
}
/** \} */
/* ---------------------------------------------------------------------- */
@ -400,12 +441,30 @@ bool UI_view_item_is_interactive(const uiViewItemHandle *item_handle)
return item.is_interactive();
}
bool UI_view_item_activate(bContext *C, uiViewItemHandle *item_handle)
{
AbstractViewItem &item = reinterpret_cast<AbstractViewItem &>(*item_handle);
return item.activate(*C);
}
bool UI_view_item_is_active(const uiViewItemHandle *item_handle)
{
const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
return item.is_active();
}
bool UI_view_item_is_selectable(const uiViewItemHandle *item_handle)
{
const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
return item.is_selectable();
}
bool UI_view_item_is_selected(const uiViewItemHandle *item_handle)
{
const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
return item.is_selected();
}
bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle)
{
const AbstractViewItem &a = reinterpret_cast<const AbstractViewItem &>(*a_handle);

View File

@ -129,15 +129,6 @@ bool AbstractGridViewItem::matches(const AbstractViewItem &other) const
return identifier_ == other_grid_item.identifier_;
}
void AbstractGridViewItem::grid_tile_click_fn(bContext *C, void *but_arg1, void * /*arg2*/)
{
uiButViewItem *view_item_but = (uiButViewItem *)but_arg1;
AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>(
*view_item_but->view_item);
grid_item.activate(*C);
}
void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
{
const GridViewStyle &style = get_view().get_style();
@ -157,7 +148,6 @@ void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
"");
view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this);
UI_but_func_set(view_item_but_, grid_tile_click_fn, view_item_but_, nullptr);
}
AbstractGridView &AbstractGridViewItem::get_view() const

View File

@ -142,6 +142,19 @@ void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *l
}
}
uiViewHandle *UI_region_view_find_from_idname(const ARegion *region, const char *idname)
{
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
if (view_link->idname == idname) {
return reinterpret_cast<uiViewHandle *>(view_link->view.get());
}
}
}
return nullptr;
}
void ui_block_views_draw_overlays(const ARegion *region, const uiBlock *block)
{
LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {

View File

@ -51,7 +51,8 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item(
return added_item;
}
void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const
void TreeViewItemContainer::foreach_item_recursive(
FunctionRef<void(AbstractTreeViewItem &)> iter_fn, IterOptions options) const
{
for (const auto &child : children_) {
bool skip = false;
@ -282,12 +283,11 @@ std::optional<DropLocation> TreeViewItemDropTarget::choose_drop_location(
/* ---------------------------------------------------------------------- */
void AbstractTreeViewItem::tree_row_click_fn(bContext *C, void *but_arg1, void * /*arg2*/)
void AbstractTreeViewItem::tree_row_click_fn(bContext * /*C*/, void *but_arg1, void * /*arg2*/)
{
uiButViewItem *item_but = (uiButViewItem *)but_arg1;
AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(*item_but->view_item);
tree_item.activate(*C);
/* Not only activate the item, also show its children. Maybe this should be optional, or
* controlled by the specific tree-view. */
tree_item.set_collapsed(false);

View File

@ -1833,6 +1833,9 @@ void PreviewLoadJob::run_fn(void *customdata, bool *stop, bool *do_update, float
preview->rect[request->icon_size]);
IMB_freeImBuf(thumb);
}
else {
preview->tag |= PRV_TAG_LOADING_FAILED;
}
*do_update = true;
}

View File

@ -317,6 +317,11 @@ bool ED_operator_asset_browsing_active(bContext *C)
return false;
}
bool ED_operator_asset_browser_active(bContext *C)
{
return ed_spacetype_test(C, SPACE_ASSETS);
}
bool ED_operator_spreadsheet_active(bContext *C)
{
return ed_spacetype_test(C, SPACE_SPREADSHEET);

View File

@ -25,6 +25,7 @@ set(LIB
PRIVATE bf::dna
bf_editor_geometry
bf_editor_space_action
bf_editor_space_assets
bf_editor_space_buttons
bf_editor_space_clip
bf_editor_space_console

View File

@ -65,6 +65,7 @@ void ED_spacetypes_init()
U.widget_unit = 20;
/* Create space types. */
ED_spacetype_assets();
ED_spacetype_outliner();
ED_spacetype_view3d();
ED_spacetype_ipo();

View File

@ -0,0 +1,42 @@
# SPDX-FileCopyrightText: 2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
set(INC
../include
../../asset_system
../../blenfont
../../blenkernel
../../blentranslation
../../gpu
../../makesrna
../../windowmanager
../../../../extern/fmtlib/include
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
set(SRC
asset_browser_draw.cc
asset_browser_ops.cc
asset_browser_panels.cc
asset_catalog_tree_view.cc
asset_view.cc
space_assets.cc
asset_browser_intern.hh
asset_view.hh
)
set(LIB
bf_blenkernel
PRIVATE bf::blenlib
PRIVATE bf::dna
PRIVATE bf::intern::guardedalloc
extern_fmtlib
)
blender_add_lib(bf_editor_space_assets "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# RNA_prototypes.h
add_dependencies(bf_editor_space_assets bf_rna)

View File

@ -0,0 +1,77 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spassets
*/
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "UI_view2d.hh"
#include "asset_browser_intern.hh"
#include "asset_view.hh"
namespace blender::ed::asset_browser {
} // namespace blender::ed::asset_browser
using namespace blender::ed::asset_browser;
void asset_browser_main_region_draw(const bContext *C, ARegion *region)
{
SpaceAssets *asset_space = CTX_wm_space_assets(C);
bScreen *screen = CTX_wm_screen(C);
View2D *v2d = &region->v2d;
UI_ThemeClearColor(TH_BACK);
UI_view2d_view_ortho(v2d);
const uiStyle *style = UI_style_get_dpi();
const float padding = style->panelouter;
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
uiLayout *layout = UI_block_layout(
block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
padding,
-padding,
/* 3x (instead of 2x) padding to add extra space for the scrollbar on the right. */
region->winx - 3 * padding,
1,
0,
style);
PointerRNA asset_space_ptr = RNA_pointer_create(
&screen->id, &RNA_SpaceAssetBrowser, asset_space);
PropertyRNA *active_asset_idx_prop = RNA_struct_find_property(&asset_space_ptr,
"active_asset_idx");
asset_view_create_in_layout(*C,
asset_space->asset_library_ref,
asset_space->catalog_filter,
asset_space_ptr,
active_asset_idx_prop,
*v2d,
*layout);
/* Update main region View2d dimensions. */
int layout_width, layout_height;
UI_block_layout_resolve(block, &layout_width, &layout_height);
UI_view2d_totRect_set(v2d, layout_width, layout_height);
UI_block_end(C, block);
UI_block_draw(C, block);
/* reset view matrix */
UI_view2d_view_restore(C);
UI_view2d_scrollers_draw(v2d, nullptr);
}

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spassets
*/
#pragma once
void asset_browser_operatortypes();
struct ARegion;
struct ARegionType;
struct AssetLibrary;
struct bContext;
struct PointerRNA;
struct PropertyRNA;
struct uiLayout;
struct wmMsgBus;
void asset_browser_main_region_draw(const bContext *C, ARegion *region);
void asset_browser_navigation_region_panels_register(ARegionType *art);
void asset_view_create_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
PointerRNA *catalog_filter_owner_ptr,
PropertyRNA *catalog_filter_prop,
wmMsgBus *msg_bus);

View File

@ -0,0 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spassets
*/
#include "BKE_context.h"
#include "ED_screen.hh"
#include "RNA_access.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "UI_interface.hh"
#include "asset_browser_intern.hh"
using namespace blender;
/* -------------------------------------------------------------------- */
/** \name Select All Operator
*
* Asset Browser specific version of #UI_OT_view_select_all() that looks up the asset view, so that
* it doesn't require the view to be under the cursor. So this operator can be displayed in menus
* too.
*
* \{ */
static int select_all_exec(bContext *C, wmOperator *op)
{
ARegion *region = CTX_wm_region(C);
uiViewHandle *view = UI_region_view_find_from_idname(region, "asset grid view");
const int action = RNA_enum_get(op->ptr, "action");
if (ui::view_select_all_from_action(view, action)) {
ED_region_tag_redraw(region);
}
return OPERATOR_FINISHED;
}
static void ASSETBROWSER_OT_select_all(wmOperatorType *ot)
{
ot->name = "Select All";
ot->idname = "ASSETBROWSER_OT_select_all";
ot->description = "Select or deselect all displayed assets";
ot->exec = select_all_exec;
ot->poll = ED_operator_asset_browser_active;
WM_operator_properties_select_all(ot);
}
/** \} */
void asset_browser_operatortypes()
{
WM_operatortype_append(ASSETBROWSER_OT_select_all);
}

View File

@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spassets
*/
#include <cstring>
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "BKE_screen.h"
#include "BLI_listbase.h"
#include "BLT_translation.h"
#include "ED_asset.hh"
#include "MEM_guardedalloc.h"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "WM_api.hh"
#include "asset_browser_intern.hh"
static void assets_panel_asset_catalog_buttons_draw(const bContext *C, Panel *panel)
{
bScreen *screen = CTX_wm_screen(C);
SpaceAssets *assets_space = CTX_wm_space_assets(C);
uiLayout *col = uiLayoutColumn(panel->layout, false);
uiLayout *row = uiLayoutRow(col, true);
PointerRNA assets_space_ptr = RNA_pointer_create(
&screen->id, &RNA_SpaceAssetBrowser, assets_space);
uiItemR(row, &assets_space_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE);
if (assets_space->asset_library_ref.type == ASSET_LIBRARY_LOCAL) {
bContext *mutable_ctx = CTX_copy(C);
if (WM_operator_name_poll(mutable_ctx, "asset.bundle_install")) {
uiItemS(col);
uiItemMenuEnumO(col,
mutable_ctx,
"asset.bundle_install",
"asset_library_reference",
"Copy Bundle to Asset Library...",
ICON_IMPORT);
}
CTX_free(mutable_ctx);
}
else {
uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh");
}
uiItemS(col);
AssetLibrary *library = ED_assetlist_library_get(&assets_space->asset_library_ref);
PropertyRNA *catalog_filter_prop = RNA_struct_find_property(&assets_space_ptr, "catalog_filter");
asset_view_create_catalog_tree_view_in_layout(
library, col, &assets_space_ptr, catalog_filter_prop, CTX_wm_message_bus(C));
}
void asset_browser_navigation_region_panels_register(ARegionType *art)
{
PanelType *pt;
pt = MEM_cnew<PanelType>("asset browser catalog buttons");
strcpy(pt->idname, "FILE_PT_asset_catalog_buttons");
strcpy(pt->label, N_("Asset Catalogs"));
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
pt->flag = PANEL_TYPE_NO_HEADER;
pt->draw = assets_panel_asset_catalog_buttons_draw;
BLI_addtail(&art->paneltypes, pt);
}

View File

@ -0,0 +1,752 @@
/* SPDX-FileCopyrightText: 2021 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spassets
*/
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.hh"
#include "DNA_space_types.h"
#include "BKE_asset.h"
#include "BLI_string_ref.hh"
#include "BLT_translation.h"
#include "ED_asset.hh"
#include "ED_undo.hh"
#include "RNA_access.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "UI_tree_view.hh"
#include "RNA_prototypes.h"
#include "WM_api.hh"
#include "WM_message.hh"
#include "WM_types.hh"
#include "asset_browser_intern.hh"
#include <fmt/format.h>
using namespace blender;
using namespace blender::asset_system;
namespace blender::ed::asset_browser {
class AssetCatalogTreeViewAllItem;
class AssetCatalogTreeView : public ui::AbstractTreeView {
/** The asset library this catalog tree comes from. May be null when drawing the catalog tree
* before the library was read. */
::AssetLibrary *asset_library_;
/** The asset catalog tree this tree-view represents. */
AssetCatalogTree *catalog_tree_;
PointerRNA catalog_filter_owner_;
PropertyRNA &catalog_filter_prop_;
/** Used to notify the parts of the UI that display the filtered assets. */
wmMsgBus *msg_bus_;
friend class AssetCatalogTreeViewItem;
friend class AssetCatalogDropTarget;
friend class AssetCatalogTreeViewAllItem;
public:
AssetCatalogTreeView(::AssetLibrary *library,
const PointerRNA &catalog_filter_owner,
PropertyRNA &catalog_filter_prop,
wmMsgBus *msg_bus);
void build_tree() override;
bool listen(const wmNotifier &notifier) const override;
void activate_catalog_by_id(asset_system::CatalogID catalog_id);
private:
ui::BasicTreeViewItem &build_catalog_items_recursive(ui::TreeViewOrItem &view_parent_item,
AssetCatalogTreeItem &catalog);
AssetCatalogTreeViewAllItem &add_all_item();
void add_unassigned_item();
bool is_active_catalog(CatalogID catalog_id) const;
AssetCatalogFilterSettings &catalog_filter_settings() const;
void notify_catalog_filter_change();
void notify_catalog_tree_change();
void notify_catalog_assignments_change();
};
/* ---------------------------------------------------------------------- */
class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
/** The catalog tree item this tree view item represents. */
AssetCatalogTreeItem &catalog_item_;
public:
AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item);
void on_activate(bContext &C) override;
void build_row(uiLayout &row) override;
void build_context_menu(bContext &C, uiLayout &column) const override;
bool supports_renaming() const override;
bool rename(const bContext &C, StringRefNull new_name) override;
/** Add drag support for catalog items. */
std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
/** Add dropping support for catalog items. */
std::unique_ptr<ui::TreeViewItemDropTarget> create_drop_target() override;
};
class AssetCatalogDragController : public ui::AbstractViewItemDragController {
AssetCatalogTreeItem &catalog_item_;
public:
explicit AssetCatalogDragController(AssetCatalogTreeView &tree_view,
AssetCatalogTreeItem &catalog_item);
eWM_DragDataType get_drag_type() const override;
void *create_drag_data() const override;
void on_drag_start() override;
};
class AssetCatalogDropTarget : public ui::TreeViewItemDropTarget {
AssetCatalogTreeItem &catalog_item_;
public:
AssetCatalogDropTarget(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
std::string drop_tooltip(const ui::DragInfo &drag) const override;
bool on_drop(bContext *C, const ui::DragInfo &drag) const override;
::AssetLibrary &get_asset_library() const;
static AssetCatalog *get_drag_catalog(const wmDrag &drag, const ::AssetLibrary &asset_library);
static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint);
static bool can_modify_catalogs(const ::AssetLibrary &asset_library,
const char **r_disabled_hint);
static bool drop_assets_into_catalog(bContext *C,
AssetCatalogTreeView &tree_view,
const ui::DragInfo &drag,
CatalogID catalog_id,
StringRefNull simple_name = "");
/**
* \param drop_catalog_id: Can be unset to drop into the root level of the tree.
*/
static bool drop_asset_catalog_into_catalog(
const ui::DragInfo &drag,
AssetCatalogTreeView &tree_view,
const std::optional<CatalogID> drop_catalog_id = std::nullopt);
private:
std::string drop_tooltip_asset_list(const ui::DragInfo &drag) const;
std::string drop_tooltip_asset_catalog(const ui::DragInfo &drag) const;
};
/** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level
* catalog. */
class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
using BasicTreeViewItem::BasicTreeViewItem;
void build_row(uiLayout &row) override;
struct DropTarget : public ui::TreeViewItemDropTarget {
DropTarget(AssetCatalogTreeView &tree_view);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
std::string drop_tooltip(const ui::DragInfo &drag) const override;
bool on_drop(bContext *C, const ui::DragInfo &drag) const override;
};
std::unique_ptr<ui::TreeViewItemDropTarget> create_drop_target() override;
};
class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
using BasicTreeViewItem::BasicTreeViewItem;
struct DropTarget : public ui::TreeViewItemDropTarget {
DropTarget(AssetCatalogTreeView &tree_view);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
std::string drop_tooltip(const ui::DragInfo &drag) const override;
bool on_drop(bContext *C, const ui::DragInfo &drag) const override;
};
std::unique_ptr<ui::TreeViewItemDropTarget> create_drop_target() override;
};
/* ---------------------------------------------------------------------- */
AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library,
const PointerRNA &catalog_filter_owner,
PropertyRNA &catalog_filter_prop,
wmMsgBus *msg_bus)
: asset_library_(library),
catalog_tree_(AS_asset_library_get_catalog_tree(library)),
catalog_filter_owner_(catalog_filter_owner),
catalog_filter_prop_(catalog_filter_prop),
msg_bus_(msg_bus)
{
}
void AssetCatalogTreeView::build_tree()
{
AssetCatalogTreeViewAllItem &all_item = add_all_item();
all_item.set_collapsed(false);
if (catalog_tree_) {
/* Pass the "All" item on as parent of the actual catalog items. */
catalog_tree_->foreach_root_item([this, &all_item](AssetCatalogTreeItem &item) {
build_catalog_items_recursive(all_item, item);
});
}
add_unassigned_item();
}
ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive(
ui::TreeViewOrItem &view_parent_item, AssetCatalogTreeItem &catalog)
{
ui::BasicTreeViewItem &view_item = view_parent_item.add_tree_item<AssetCatalogTreeViewItem>(
&catalog);
view_item.set_is_active_fn(
[this, &catalog]() { return is_active_catalog(catalog.get_catalog_id()); });
catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) {
build_catalog_items_recursive(view_item, child);
});
return view_item;
}
AssetCatalogTreeViewAllItem &AssetCatalogTreeView::add_all_item()
{
AssetCatalogTreeViewAllItem &item = add_tree_item<AssetCatalogTreeViewAllItem>(IFACE_("All"));
item.set_on_activate_fn([this](bContext & /*C*/, ui::BasicTreeViewItem & /*item*/) {
catalog_filter_settings().filter_mode = ASSET_CATALOG_SHOW_ALL_ASSETS;
notify_catalog_filter_change();
});
item.set_is_active_fn(
[this]() { return catalog_filter_settings().filter_mode == ASSET_CATALOG_SHOW_ALL_ASSETS; });
return item;
}
void AssetCatalogTreeView::add_unassigned_item()
{
AssetCatalogTreeViewUnassignedItem &item = add_tree_item<AssetCatalogTreeViewUnassignedItem>(
IFACE_("Unassigned"), ICON_FILE_HIDDEN);
item.set_on_activate_fn([this](bContext & /*C*/, ui::BasicTreeViewItem & /*item*/) {
catalog_filter_settings().filter_mode = ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG;
notify_catalog_filter_change();
});
item.set_is_active_fn([this]() {
return catalog_filter_settings().filter_mode == ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG;
});
}
void AssetCatalogTreeView::activate_catalog_by_id(CatalogID catalog_id)
{
AssetCatalogFilterSettings &catalog_filter = catalog_filter_settings();
catalog_filter.filter_mode = ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG;
catalog_filter.active_catalog_id = catalog_id;
notify_catalog_filter_change();
}
bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const
{
const AssetCatalogFilterSettings &catalog_filter = catalog_filter_settings();
return (catalog_filter.filter_mode == ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG) &&
(catalog_filter.active_catalog_id == catalog_id);
}
AssetCatalogFilterSettings &AssetCatalogTreeView::catalog_filter_settings() const
{
/* Copy so we can pass a non-const pointer to this to RNA functions. */
PointerRNA catalog_filter_owner = catalog_filter_owner_;
PointerRNA catalog_filter_ptr = RNA_property_pointer_get(&catalog_filter_owner,
&catalog_filter_prop_);
BLI_assert(catalog_filter_ptr.type == &RNA_AssetCatalogFilterSettings);
return *reinterpret_cast<AssetCatalogFilterSettings *>(catalog_filter_ptr.data);
}
bool AssetCatalogTreeView::listen(const wmNotifier &notifier) const
{
switch (notifier.category) {
case NC_ASSET:
if (ELEM(notifier.data, ND_ASSET_CATALOGS, ND_ASSET_LIST_READING)) {
return true;
}
}
return false;
}
void AssetCatalogTreeView::notify_catalog_filter_change()
{
WM_msg_publish_rna(msg_bus_, &catalog_filter_owner_, &catalog_filter_prop_);
}
void AssetCatalogTreeView::notify_catalog_tree_change()
{
WM_main_add_notifier(NC_ASSET | ND_ASSET_CATALOGS, nullptr);
}
void AssetCatalogTreeView::notify_catalog_assignments_change()
{
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr);
}
/* ---------------------------------------------------------------------- */
AssetCatalogTreeViewItem::AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item)
: BasicTreeViewItem(catalog_item->get_name()), catalog_item_(*catalog_item)
{
}
void AssetCatalogTreeViewItem::on_activate(bContext & /*C*/)
{
AssetCatalogTreeView &tree_view = static_cast<AssetCatalogTreeView &>(get_tree_view());
tree_view.activate_catalog_by_id(catalog_item_.get_catalog_id());
}
void AssetCatalogTreeViewItem::build_row(uiLayout &row)
{
const std::string label_override = catalog_item_.has_unsaved_changes() ? (label_ + "*") : label_;
add_label(row, label_override);
if (!is_hovered()) {
return;
}
uiButViewItem *item_but = view_item_button();
PointerRNA *props;
props = UI_but_extra_operator_icon_add(
(uiBut *)item_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str());
}
void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) const
{
PointerRNA props;
uiItemFullO(&column,
"ASSET_OT_catalog_new",
"New Catalog",
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&props);
RNA_string_set(&props, "parent_path", catalog_item_.catalog_path().c_str());
char catalog_id_str_buffer[UUID_STRING_SIZE] = "";
BLI_uuid_format(catalog_id_str_buffer, catalog_item_.get_catalog_id());
uiItemFullO(&column,
"ASSET_OT_catalog_delete",
"Delete Catalog",
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&props);
RNA_string_set(&props, "catalog_id", catalog_id_str_buffer);
uiItemO(&column, "Rename", ICON_NONE, "UI_OT_view_item_rename");
/* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done
* in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on
* catalog to the menu draw callback (via context probably). */
MenuType *mt = WM_menutype_find("ASSETBROWSER_MT_catalog_context_menu", true);
if (!mt) {
return;
}
UI_menutype_draw(&C, mt, &column);
}
bool AssetCatalogTreeViewItem::supports_renaming() const
{
const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
get_tree_view());
return !ED_asset_catalogs_read_only(*tree_view.asset_library_);
}
bool AssetCatalogTreeViewItem::rename(const bContext &C, StringRefNull new_name)
{
/* Important to keep state. */
BasicTreeViewItem::rename(C, new_name);
const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
get_tree_view());
ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name);
return true;
}
std::unique_ptr<ui::TreeViewItemDropTarget> AssetCatalogTreeViewItem::create_drop_target()
{
return std::make_unique<AssetCatalogDropTarget>(
static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_);
}
std::unique_ptr<ui::AbstractViewItemDragController> AssetCatalogTreeViewItem::
create_drag_controller() const
{
return std::make_unique<AssetCatalogDragController>(
static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_);
}
/* ---------------------------------------------------------------------- */
AssetCatalogDropTarget::AssetCatalogDropTarget(AssetCatalogTreeView &tree_view,
AssetCatalogTreeItem &catalog_item)
: ui::TreeViewItemDropTarget(tree_view), catalog_item_(catalog_item)
{
}
bool AssetCatalogDropTarget::can_drop(const wmDrag &drag, const char **r_disabled_hint) const
{
if (drag.type == WM_DRAG_ASSET_CATALOG) {
const ::AssetLibrary &library = get_asset_library();
if (!can_modify_catalogs(library, r_disabled_hint)) {
return false;
}
const AssetCatalog *drag_catalog = get_drag_catalog(drag, library);
/* NOTE: Technically it's not an issue to allow this (the catalog will just receive a new
* path and the catalog system will generate missing parents from the path). But it does
* appear broken to users, so disabling entirely. */
if (catalog_item_.catalog_path().is_contained_in(drag_catalog->path)) {
*r_disabled_hint = TIP_("Catalog cannot be dropped into itself");
return false;
}
if (catalog_item_.catalog_path() == drag_catalog->path.parent()) {
*r_disabled_hint = TIP_("Catalog is already placed inside this catalog");
return false;
}
return true;
}
if (drag.type == WM_DRAG_ASSET_LIST && has_droppable_asset(drag, r_disabled_hint)) {
return true;
}
return false;
}
std::string AssetCatalogDropTarget::drop_tooltip(const ui::DragInfo &drag) const
{
if (drag.drag_data.type == WM_DRAG_ASSET_CATALOG) {
return drop_tooltip_asset_catalog(drag);
}
return drop_tooltip_asset_list(drag);
}
std::string AssetCatalogDropTarget::drop_tooltip_asset_catalog(const ui::DragInfo &drag) const
{
BLI_assert(drag.drag_data.type == WM_DRAG_ASSET_CATALOG);
const AssetCatalog *src_catalog = get_drag_catalog(drag.drag_data, get_asset_library());
return fmt::format(TIP_("Move catalog {} into {}"),
std::string_view(src_catalog->path.name()),
std::string_view(catalog_item_.get_name()));
}
std::string AssetCatalogDropTarget::drop_tooltip_asset_list(const ui::DragInfo &drag) const
{
BLI_assert(drag.drag_data.type == WM_DRAG_ASSET_LIST);
const ListBase *asset_drags = WM_drag_asset_list_get(&drag.drag_data);
const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags);
/* Don't try to be smart by dynamically adding the 's' for the plural. Just makes translation
* harder, so use full literals. */
std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") :
TIP_("Move asset to catalog");
basic_tip += ": " + catalog_item_.get_name();
/* Display the full catalog path, but only if it's not exactly the same as the already shown name
* (i.e. not a root level catalog with no parent). */
if (catalog_item_.get_name() != catalog_item_.catalog_path().str()) {
basic_tip += " (" + catalog_item_.catalog_path().str() + ")";
}
return basic_tip;
}
bool AssetCatalogDropTarget::on_drop(bContext *C, const ui::DragInfo &drag) const
{
if (drag.drag_data.type == WM_DRAG_ASSET_CATALOG) {
return drop_asset_catalog_into_catalog(
drag, get_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id());
}
return drop_assets_into_catalog(C,
get_view<AssetCatalogTreeView>(),
drag,
catalog_item_.get_catalog_id(),
catalog_item_.get_simple_name());
}
bool AssetCatalogDropTarget::drop_asset_catalog_into_catalog(
const ui::DragInfo &drag,
AssetCatalogTreeView &tree_view,
const std::optional<CatalogID> drop_catalog_id)
{
BLI_assert(drag.drag_data.type == WM_DRAG_ASSET_CATALOG);
wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag.drag_data);
ED_asset_catalog_move(tree_view.asset_library_, catalog_drag->drag_catalog_id, drop_catalog_id);
tree_view.activate_catalog_by_id(catalog_drag->drag_catalog_id);
tree_view.notify_catalog_tree_change();
return true;
}
bool AssetCatalogDropTarget::drop_assets_into_catalog(bContext *C,
AssetCatalogTreeView &tree_view,
const ui::DragInfo &drag,
CatalogID catalog_id,
StringRefNull simple_name)
{
BLI_assert(drag.drag_data.type == WM_DRAG_ASSET_LIST);
const ListBase *asset_drags = WM_drag_asset_list_get(&drag.drag_data);
if (!asset_drags) {
return false;
}
bool did_update = false;
LISTBASE_FOREACH (wmDragAssetListItem *, asset_item, asset_drags) {
if (asset_item->is_external) {
/* Only internal assets can be modified! */
continue;
}
did_update = true;
BKE_asset_metadata_catalog_id_set(
asset_item->asset_data.local_id->asset_data, catalog_id, simple_name.c_str());
/* TODO */
/* Trigger re-run of filtering to update visible assets. */
// filelist_tag_needs_filtering(tree_view.space_file_.files);
// file_select_deselect_all(&tree_view.space_file_, FILE_SEL_SELECTED |
// FILE_SEL_HIGHLIGHTED);
}
if (did_update) {
tree_view.notify_catalog_assignments_change();
ED_undo_push(C, "Assign Asset Catalog");
}
return true;
}
AssetCatalog *AssetCatalogDropTarget::get_drag_catalog(const wmDrag &drag,
const ::AssetLibrary &asset_library)
{
if (drag.type != WM_DRAG_ASSET_CATALOG) {
return nullptr;
}
const asset_system::AssetCatalogService *catalog_service = AS_asset_library_get_catalog_service(
&asset_library);
const wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag);
return catalog_service->find_catalog(catalog_drag->drag_catalog_id);
}
bool AssetCatalogDropTarget::has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint)
{
const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
*r_disabled_hint = nullptr;
/* There needs to be at least one asset from the current file. */
LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) {
if (!asset_item->is_external) {
return true;
}
}
*r_disabled_hint = TIP_("Only assets from this current file can be moved between catalogs");
return false;
}
bool AssetCatalogDropTarget::can_modify_catalogs(const ::AssetLibrary &library,
const char **r_disabled_hint)
{
if (ED_asset_catalogs_read_only(library)) {
*r_disabled_hint = TIP_("Catalogs cannot be edited in this asset library");
return false;
}
return true;
}
::AssetLibrary &AssetCatalogDropTarget::get_asset_library() const
{
return *get_view<AssetCatalogTreeView>().asset_library_;
}
/* ---------------------------------------------------------------------- */
AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeView &tree_view,
AssetCatalogTreeItem &catalog_item)
: ui::AbstractViewItemDragController(tree_view), catalog_item_(catalog_item)
{
}
eWM_DragDataType AssetCatalogDragController::get_drag_type() const
{
return WM_DRAG_ASSET_CATALOG;
}
void *AssetCatalogDragController::create_drag_data() const
{
wmDragAssetCatalog *drag_catalog = (wmDragAssetCatalog *)MEM_callocN(sizeof(*drag_catalog),
__func__);
drag_catalog->drag_catalog_id = catalog_item_.get_catalog_id();
return drag_catalog;
}
void AssetCatalogDragController::on_drag_start()
{
AssetCatalogTreeView &tree_view_ = get_view<AssetCatalogTreeView>();
tree_view_.activate_catalog_by_id(catalog_item_.get_catalog_id());
}
/* ---------------------------------------------------------------------- */
void AssetCatalogTreeViewAllItem::build_row(uiLayout &row)
{
ui::BasicTreeViewItem::build_row(row);
PointerRNA *props;
UI_but_extra_operator_icon_add(
(uiBut *)view_item_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK);
props = UI_but_extra_operator_icon_add(
(uiBut *)view_item_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
/* No parent path to use the root level. */
RNA_string_set(props, "parent_path", nullptr);
}
std::unique_ptr<ui::TreeViewItemDropTarget> AssetCatalogTreeViewAllItem::create_drop_target()
{
return std::make_unique<AssetCatalogTreeViewAllItem::DropTarget>(
static_cast<AssetCatalogTreeView &>(get_tree_view()));
}
AssetCatalogTreeViewAllItem::DropTarget::DropTarget(AssetCatalogTreeView &tree_view)
: ui::TreeViewItemDropTarget(tree_view)
{
}
bool AssetCatalogTreeViewAllItem::DropTarget::can_drop(const wmDrag &drag,
const char **r_disabled_hint) const
{
if (drag.type != WM_DRAG_ASSET_CATALOG) {
return false;
}
::AssetLibrary &library = *get_view<AssetCatalogTreeView>().asset_library_;
if (!AssetCatalogDropTarget::can_modify_catalogs(library, r_disabled_hint)) {
return false;
}
const AssetCatalog *drag_catalog = AssetCatalogDropTarget::get_drag_catalog(drag, library);
if (drag_catalog->path.parent() == "") {
*r_disabled_hint = TIP_("Catalog is already placed at the highest level");
return false;
}
return true;
}
std::string AssetCatalogTreeViewAllItem::DropTarget::drop_tooltip(const ui::DragInfo &drag) const
{
BLI_assert(drag.drag_data.type == WM_DRAG_ASSET_CATALOG);
const AssetCatalog *drag_catalog = AssetCatalogDropTarget::get_drag_catalog(
drag.drag_data, *get_view<AssetCatalogTreeView>().asset_library_);
return fmt::format(TIP_("Move catalog {} to the top level of the tree"),
std::string_view(drag_catalog->path.name()));
}
bool AssetCatalogTreeViewAllItem::DropTarget::on_drop(bContext * /*C*/,
const ui::DragInfo &drag) const
{
BLI_assert(drag.drag_data.type == WM_DRAG_ASSET_CATALOG);
return AssetCatalogDropTarget::drop_asset_catalog_into_catalog(
drag,
get_view<AssetCatalogTreeView>(),
/* No value to drop into the root level. */
std::nullopt);
}
/* ---------------------------------------------------------------------- */
std::unique_ptr<ui::TreeViewItemDropTarget> AssetCatalogTreeViewUnassignedItem::
create_drop_target()
{
return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropTarget>(
static_cast<AssetCatalogTreeView &>(get_tree_view()));
}
AssetCatalogTreeViewUnassignedItem::DropTarget::DropTarget(AssetCatalogTreeView &tree_view)
: ui::TreeViewItemDropTarget(tree_view)
{
}
bool AssetCatalogTreeViewUnassignedItem::DropTarget::can_drop(const wmDrag &drag,
const char **r_disabled_hint) const
{
if (drag.type != WM_DRAG_ASSET_LIST) {
return false;
}
return AssetCatalogDropTarget::has_droppable_asset(drag, r_disabled_hint);
}
std::string AssetCatalogTreeViewUnassignedItem::DropTarget::drop_tooltip(
const ui::DragInfo &drag) const
{
const ListBase *asset_drags = WM_drag_asset_list_get(&drag.drag_data);
const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags);
return is_multiple_assets ? TIP_("Move assets out of any catalog") :
TIP_("Move asset out of any catalog");
}
bool AssetCatalogTreeViewUnassignedItem::DropTarget::on_drop(bContext *C,
const ui::DragInfo &drag) const
{
/* Assign to nil catalog ID. */
return AssetCatalogDropTarget::drop_assets_into_catalog(
C, get_view<AssetCatalogTreeView>(), drag, CatalogID{});
}
} // namespace blender::ed::asset_browser
/* ---------------------------------------------------------------------- */
void asset_view_create_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
PointerRNA *catalog_filter_owner_ptr,
PropertyRNA *catalog_filter_prop,
wmMsgBus *msg_bus)
{
uiBlock *block = uiLayoutGetBlock(layout);
UI_block_layout_set_current(block, layout);
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset catalog tree view",
std::make_unique<ed::asset_browser::AssetCatalogTreeView>(
asset_library, *catalog_filter_owner_ptr, *catalog_filter_prop, msg_bus));
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout);
}

View File

@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spassets
*/
#include "BKE_context.h"
#include "DNA_asset_types.h"
#include "RNA_access.hh"
#include "RNA_types.hh"
#include "ED_asset.hh"
#include "UI_interface.hh"
#include "WM_message.hh"
#include "asset_view.hh"
namespace blender::ed::asset_browser {
AssetGridView::AssetGridView(const AssetLibraryReference &asset_library_ref,
const PointerRNA &active_asset_idx_owner_ptr,
PropertyRNA *active_asset_idx_prop,
wmMsgBus *msg_bus)
: asset_library_ref_(asset_library_ref),
active_asset_idx_owner_(active_asset_idx_owner_ptr),
active_asset_idx_prop_(*active_asset_idx_prop),
msg_bus_(*msg_bus)
{
}
void AssetGridView::build_items()
{
int idx = 0;
ED_assetlist_iterate(asset_library_ref_, [this, &idx](AssetHandle &asset) {
AssetGridViewItem &item = add_item<AssetGridViewItem>(asset);
item.set_is_active_fn([this, idx]() -> bool {
return idx == RNA_property_int_get(&active_asset_idx_owner_, &active_asset_idx_prop_);
});
item.set_on_activate_fn([this, idx](bContext & /*C*/, ui::PreviewGridItem & /*item*/) {
RNA_property_int_set(&active_asset_idx_owner_, &active_asset_idx_prop_, idx);
WM_msg_publish_rna(&msg_bus_, &active_asset_idx_owner_, &active_asset_idx_prop_);
});
idx++;
return true;
});
}
bool AssetGridView::listen(const wmNotifier &notifier) const
{
return ED_assetlist_listen(&asset_library_ref_, &notifier);
}
/* ---------------------------------------------------------------------- */
AssetGridViewItem::AssetGridViewItem(AssetHandle &asset)
: ui::PreviewGridItem(ED_asset_handle_get_identifier(&asset),
ED_asset_handle_get_name(&asset)),
/* Get a copy so the identifier is always available (the file data wrapped by the handle may
* be freed). */
asset_identifier_(identifier_)
{
preview_icon_id = ED_assetlist_asset_preview_or_type_icon_id_request(&asset);
/* Update reference so we don't point into the possibly freed file data. */
identifier_ = asset_identifier_;
enable_selectable();
}
/* ---------------------------------------------------------------------- */
void asset_view_create_in_layout(const bContext &C,
const AssetLibraryReference &asset_library_ref,
const AssetCatalogFilterSettings &catalog_filter_settings,
const PointerRNA &active_asset_idx_owner_ptr,
PropertyRNA *active_asset_idx_prop,
const View2D &v2d,
uiLayout &layout)
{
uiBlock *block = uiLayoutGetBlock(&layout);
UI_block_layout_set_current(block, &layout);
ED_assetlist_storage_fetch(&asset_library_ref, &C);
ED_assetlist_catalog_filter_set(&asset_library_ref, &catalog_filter_settings);
ui::AbstractGridView *grid_view = UI_block_add_view(
*block,
"asset grid view",
std::make_unique<AssetGridView>(asset_library_ref,
active_asset_idx_owner_ptr,
active_asset_idx_prop,
CTX_wm_message_bus(&C)));
ui::GridViewBuilder builder(*block);
builder.build_grid_view(*grid_view, v2d, layout);
}
} // namespace blender::ed::asset_browser

View File

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spassets
*/
#pragma once
#include "DNA_asset_types.h"
#include "UI_grid_view.hh"
struct AssetCatalogFilterSettings;
struct bContext;
struct PointerRNA;
struct PropertyRNA;
struct uiLayout;
struct View2D;
struct wmMsgBus;
namespace blender::ed::asset_browser {
class AssetGridView : public blender::ui::AbstractGridView {
AssetLibraryReference asset_library_ref_;
/** Reference to bind the active asset of the editor to the view. */
PointerRNA active_asset_idx_owner_;
PropertyRNA &active_asset_idx_prop_;
wmMsgBus &msg_bus_;
public:
AssetGridView(const AssetLibraryReference &,
const PointerRNA &active_asset_idx_owner_ptr,
PropertyRNA *active_asset_idx_prop,
wmMsgBus *msg_bus);
void build_items() override;
bool listen(const wmNotifier &) const override;
};
class AssetGridViewItem : public ui::PreviewGridItem {
/* Can't store this here, since the wrapped FileDirEntry will be freed while progressively
* loading items. */
// AssetHandle &asset_;
std::string asset_identifier_;
public:
AssetGridViewItem(AssetHandle &);
};
void asset_view_create_in_layout(const bContext &C,
const AssetLibraryReference &asset_library_ref,
const AssetCatalogFilterSettings &catalog_filter_settings,
const PointerRNA &active_asset_idx_owner_ptr,
PropertyRNA *active_asset_idx_prop,
const View2D &v2d,
uiLayout &layout);
} // namespace blender::ed::asset_browser

View File

@ -0,0 +1,312 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spassets
*/
#include <cstring>
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "BKE_asset.h"
#include "BKE_screen.h"
#include "BLI_listbase.h"
#include "ED_asset.hh"
#include "ED_screen.hh"
#include "ED_space_api.hh"
#include "MEM_guardedalloc.h"
#include "RNA_access.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "UI_view2d.hh"
#include "WM_api.hh"
#include "WM_message.hh"
#include "asset_browser_intern.hh"
#include "asset_view.hh"
/* ---------------------------------------------------------------------- */
/* Asset Browser Space */
static SpaceLink *asset_browser_create(const ScrArea * /*area*/, const Scene * /*scene*/)
{
SpaceAssets *assets_space = MEM_cnew<SpaceAssets>("asset browser space");
assets_space->spacetype = SPACE_ASSETS;
BKE_asset_library_reference_init_default(&assets_space->asset_library_ref);
{
/* Header. */
ARegion *region = MEM_cnew<ARegion>("asset browser header");
BLI_addtail(&assets_space->regionbase, region);
region->regiontype = RGN_TYPE_HEADER;
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
}
{
/* Navigation region */
ARegion *region = MEM_cnew<ARegion>("asset browser navigation region");
BLI_addtail(&assets_space->regionbase, region);
region->regiontype = RGN_TYPE_NAV_BAR;
region->alignment = RGN_ALIGN_LEFT;
}
{
/* Sidebar region */
ARegion *region = MEM_cnew<ARegion>("asset browser sidebar region");
BLI_addtail(&assets_space->regionbase, region);
region->regiontype = RGN_TYPE_UI;
region->alignment = RGN_ALIGN_RIGHT;
region->flag = RGN_FLAG_HIDDEN;
}
{
/* Main region. */
ARegion *region = MEM_cnew<ARegion>("asset browser main region");
BLI_addtail(&assets_space->regionbase, region);
region->regiontype = RGN_TYPE_WINDOW;
region->v2d.scroll = V2D_SCROLL_RIGHT;
region->v2d.align = (V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_POS_Y);
region->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT);
region->v2d.keeptot = V2D_KEEPTOT_STRICT;
region->v2d.minzoom = region->v2d.maxzoom = 1.0f;
}
return (SpaceLink *)assets_space;
}
static void asset_browser_free(SpaceLink * /*sl*/) {}
static void asset_browser_init(wmWindowManager * /*wm*/, ScrArea * /*area*/) {}
static SpaceLink *asset_browser_duplicate(SpaceLink *sl)
{
const SpaceAssets *asset_browser_old = (SpaceAssets *)sl;
SpaceAssets *asset_browser_new = reinterpret_cast<SpaceAssets *>(
MEM_dupallocN(asset_browser_old));
return (SpaceLink *)asset_browser_new;
}
static void asset_browser_keymap(wmKeyConfig *keyconf)
{
/* keys for all regions */
WM_keymap_ensure(keyconf, "Asset Browser", SPACE_ASSETS, 0);
}
const char *asset_browser_context_dir[] = {
"asset_handle",
"asset_library_reference",
NULL,
};
static int /*eContextResult*/ asset_browser_context(const bContext *C,
const char *member,
bContextDataResult *result)
{
if (CTX_data_dir(member)) {
CTX_data_dir_set(result, asset_browser_context_dir);
return CTX_RESULT_OK;
}
bScreen *screen = CTX_wm_screen(C);
SpaceAssets *assets_space = CTX_wm_space_assets(C);
if (CTX_data_equals(member, "asset_library_reference")) {
CTX_data_pointer_set(
result, &screen->id, &RNA_AssetLibraryReference, &assets_space->asset_library_ref);
return CTX_RESULT_OK;
}
if (CTX_data_equals(member, "asset_handle")) {
AssetHandle *asset = ED_assetlist_asset_handle_get_by_index(&assets_space->asset_library_ref,
assets_space->active_asset_idx);
if (!asset) {
return CTX_RESULT_NO_DATA;
}
CTX_data_pointer_set(result, &screen->id, &RNA_AssetHandle, asset);
return CTX_RESULT_OK;
}
return CTX_RESULT_MEMBER_NOT_FOUND;
}
/* ---------------------------------------------------------------------- */
/* Main Region */
static void asset_browser_main_region_init(wmWindowManager *wm, ARegion *region)
{
UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy);
{
wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Asset Browser", SPACE_ASSETS, 0);
WM_event_add_keymap_handler(&region->handlers, keymap);
}
}
static void asset_browser_main_region_listener(const wmRegionListenerParams *params)
{
const wmNotifier *notifier = params->notifier;
ARegion *region = params->region;
switch (notifier->category) {
case NC_ASSET:
if (ELEM(notifier->data, ND_SPACE_ASSET_PARAMS)) {
ED_region_tag_redraw(region);
}
break;
}
}
static void asset_browser_main_region_message_subscribe(
const wmRegionMessageSubscribeParams *params)
{
struct wmMsgBus *mbus = params->message_bus;
bScreen *screen = params->screen;
SpaceAssets *assets_space = reinterpret_cast<SpaceAssets *>(params->area->spacedata.first);
wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
msg_sub_value_region_tag_redraw.owner = params->region;
msg_sub_value_region_tag_redraw.user_data = params->region;
msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
WM_msg_subscribe_rna_prop(mbus,
&screen->id,
assets_space,
SpaceAssetBrowser,
catalog_filter,
&msg_sub_value_region_tag_redraw);
}
/* ---------------------------------------------------------------------- */
/* Header Region */
static void asset_browser_header_init(wmWindowManager * /*wm*/, ARegion *region)
{
ED_region_header_init(region);
}
static void asset_browser_header_listener(const wmRegionListenerParams * /*params*/) {}
/* ---------------------------------------------------------------------- */
/* Navigation Region */
static void asset_browser_navigation_region_init(wmWindowManager *wm, ARegion *region)
{
region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE;
ED_region_panels_init(wm, region);
{
wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Asset Browser", SPACE_ASSETS, 0);
WM_event_add_keymap_handler(&region->handlers, keymap);
}
}
static void asset_browser_navigation_region_draw(const bContext *C, ARegion *region)
{
ED_region_panels(C, region);
}
static void asset_browser_navigation_region_listener(
const wmRegionListenerParams * /*listener_params*/)
{
}
/* ---------------------------------------------------------------------- */
/* Sidebar Region */
static void asset_browser_sidebar_region_init(wmWindowManager *wm, ARegion *region)
{
region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE;
ED_region_panels_init(wm, region);
{
wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Asset Browser", SPACE_ASSETS, 0);
WM_event_add_keymap_handler(&region->handlers, keymap);
}
}
static void asset_browser_sidebar_region_draw(const bContext *C, ARegion *region)
{
ED_region_panels(C, region);
}
static void asset_browser_sidebar_region_listener(
const wmRegionListenerParams * /*listener_params*/)
{
}
/* ---------------------------------------------------------------------- */
/* Asset Browser Space-Type */
void ED_spacetype_assets(void)
{
SpaceType *st = MEM_cnew<SpaceType>("spacetype asset browser");
ARegionType *art;
st->spaceid = SPACE_ASSETS;
strncpy(st->name, "Asset Browser", BKE_ST_MAXNAME);
st->create = asset_browser_create;
st->free = asset_browser_free;
st->init = asset_browser_init;
st->duplicate = asset_browser_duplicate;
st->operatortypes = asset_browser_operatortypes;
st->keymap = asset_browser_keymap;
st->context = asset_browser_context;
/* Main region. */
art = MEM_cnew<ARegionType>("spacetype asset browser main region");
art->regionid = RGN_TYPE_WINDOW;
art->init = asset_browser_main_region_init;
art->draw = asset_browser_main_region_draw;
art->listener = asset_browser_main_region_listener;
art->message_subscribe = asset_browser_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
BLI_addhead(&st->regiontypes, art);
/* Header region. */
art = MEM_cnew<ARegionType>("spacetype asset browser header region");
art->regionid = RGN_TYPE_HEADER;
art->prefsizey = HEADERY;
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
art->listener = asset_browser_header_listener;
art->init = asset_browser_header_init;
art->layout = ED_region_header_layout;
art->draw = ED_region_header_draw;
BLI_addhead(&st->regiontypes, art);
/* Navigation region */
art = MEM_cnew<ARegionType>("spacetype asset browser navigation region");
art->regionid = RGN_TYPE_NAV_BAR;
art->prefsizex = 240;
art->init = asset_browser_navigation_region_init;
art->draw = asset_browser_navigation_region_draw;
art->listener = asset_browser_navigation_region_listener;
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_NAVBAR;
asset_browser_navigation_region_panels_register(art);
BLI_addhead(&st->regiontypes, art);
/* Sidebar region */
art = MEM_cnew<ARegionType>("spacetype asset browser sidebar region");
art->regionid = RGN_TYPE_UI;
art->prefsizex = 240;
art->init = asset_browser_sidebar_region_init;
art->draw = asset_browser_sidebar_region_draw;
art->listener = asset_browser_sidebar_region_listener;
art->keymapflag = ED_KEYMAP_UI;
BLI_addhead(&st->regiontypes, art);
BKE_spacetype_register(st);
}

View File

@ -39,7 +39,7 @@
using namespace blender;
using namespace blender::asset_system;
namespace blender::ed::asset_browser {
namespace blender::ed::space_file::asset_browser {
class AssetCatalogTreeViewAllItem;
@ -220,11 +220,11 @@ AssetCatalogTreeViewAllItem &AssetCatalogTreeView::add_all_item()
AssetCatalogTreeViewAllItem &item = add_tree_item<AssetCatalogTreeViewAllItem>(IFACE_("All"));
item.set_on_activate_fn([params](bContext & /*C*/, ui::BasicTreeViewItem & /*item*/) {
params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS;
params->asset_catalog_visibility = ASSET_CATALOG_SHOW_ALL_ASSETS;
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
});
item.set_is_active_fn(
[params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS; });
[params]() { return params->asset_catalog_visibility == ASSET_CATALOG_SHOW_ALL_ASSETS; });
return item;
}
@ -236,23 +236,24 @@ void AssetCatalogTreeView::add_unassigned_item()
IFACE_("Unassigned"), ICON_FILE_HIDDEN);
item.set_on_activate_fn([params](bContext & /*C*/, ui::BasicTreeViewItem & /*item*/) {
params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG;
params->asset_catalog_visibility = ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG;
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
});
item.set_is_active_fn(
[params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG; });
item.set_is_active_fn([params]() {
return params->asset_catalog_visibility == ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG;
});
}
void AssetCatalogTreeView::activate_catalog_by_id(CatalogID catalog_id)
{
params_->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG;
params_->asset_catalog_visibility = ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG;
params_->catalog_id = catalog_id;
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
}
bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const
{
return (params_->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG) &&
return (params_->asset_catalog_visibility == ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG) &&
(params_->catalog_id == catalog_id);
}
@ -676,99 +677,11 @@ bool AssetCatalogTreeViewUnassignedItem::DropTarget::on_drop(bContext *C,
C, get_view<AssetCatalogTreeView>(), drag.drag_data, CatalogID{});
}
} // namespace blender::ed::asset_browser
} // namespace blender::ed::space_file::asset_browser
/* ---------------------------------------------------------------------- */
namespace blender::ed::asset_browser {
class AssetCatalogFilterSettings {
public:
eFileSel_Params_AssetCatalogVisibility asset_catalog_visibility;
bUUID asset_catalog_id;
std::unique_ptr<AssetCatalogFilter> catalog_filter;
};
} // namespace blender::ed::asset_browser
using namespace blender::ed::asset_browser;
FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings()
{
AssetCatalogFilterSettings *filter_settings = MEM_new<AssetCatalogFilterSettings>(__func__);
return reinterpret_cast<FileAssetCatalogFilterSettingsHandle *>(filter_settings);
}
void file_delete_asset_catalog_filter_settings(
FileAssetCatalogFilterSettingsHandle **filter_settings_handle)
{
AssetCatalogFilterSettings **filter_settings = reinterpret_cast<AssetCatalogFilterSettings **>(
filter_settings_handle);
MEM_delete(*filter_settings);
*filter_settings = nullptr;
}
bool file_set_asset_catalog_filter_settings(
FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
eFileSel_Params_AssetCatalogVisibility catalog_visibility,
::bUUID catalog_id)
{
AssetCatalogFilterSettings *filter_settings = reinterpret_cast<AssetCatalogFilterSettings *>(
filter_settings_handle);
bool needs_update = false;
if (filter_settings->asset_catalog_visibility != catalog_visibility) {
filter_settings->asset_catalog_visibility = catalog_visibility;
needs_update = true;
}
if (filter_settings->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG &&
!BLI_uuid_equal(filter_settings->asset_catalog_id, catalog_id))
{
filter_settings->asset_catalog_id = catalog_id;
needs_update = true;
}
return needs_update;
}
void file_ensure_updated_catalog_filter_data(
FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
const asset_system::AssetLibrary *asset_library)
{
AssetCatalogFilterSettings *filter_settings = reinterpret_cast<AssetCatalogFilterSettings *>(
filter_settings_handle);
const AssetCatalogService *catalog_service = asset_library->catalog_service.get();
if (filter_settings->asset_catalog_visibility != FILE_SHOW_ASSETS_ALL_CATALOGS) {
filter_settings->catalog_filter = std::make_unique<AssetCatalogFilter>(
catalog_service->create_catalog_filter(filter_settings->asset_catalog_id));
}
}
bool file_is_asset_visible_in_catalog_filter_settings(
const FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
const AssetMetaData *asset_data)
{
const AssetCatalogFilterSettings *filter_settings =
reinterpret_cast<const AssetCatalogFilterSettings *>(filter_settings_handle);
switch (filter_settings->asset_catalog_visibility) {
case FILE_SHOW_ASSETS_WITHOUT_CATALOG:
return !filter_settings->catalog_filter->is_known(asset_data->catalog_id);
case FILE_SHOW_ASSETS_FROM_CATALOG:
return filter_settings->catalog_filter->contains(asset_data->catalog_id);
case FILE_SHOW_ASSETS_ALL_CATALOGS:
/* All asset files should be visible. */
return true;
}
BLI_assert_unreachable();
return false;
}
/* ---------------------------------------------------------------------- */
namespace asset_browser = blender::ed::space_file::asset_browser;
void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
@ -782,8 +695,7 @@ void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset catalog tree view",
std::make_unique<ed::asset_browser::AssetCatalogTreeView>(
asset_library, params, *space_file));
std::make_unique<asset_browser::AssetCatalogTreeView>(asset_library, params, *space_file));
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout);
}

View File

@ -223,32 +223,7 @@ void file_path_to_ui_path(const char *path, char *r_pathi, int max_size);
/* asset_catalog_tree_view.cc */
/* C-handle for #ed::asset_browser::AssetCatalogFilterSettings. */
typedef struct FileAssetCatalogFilterSettingsHandle FileAssetCatalogFilterSettingsHandle;
void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
SpaceFile *space_file,
FileAssetSelectParams *params);
namespace blender::asset_system {
class AssetLibrary;
}
FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings();
void file_delete_asset_catalog_filter_settings(
FileAssetCatalogFilterSettingsHandle **filter_settings_handle);
/**
* \return True if the file list should update its filtered results
* (e.g. because filtering parameters changed).
*/
bool file_set_asset_catalog_filter_settings(
FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
eFileSel_Params_AssetCatalogVisibility catalog_visibility,
::bUUID catalog_id);
void file_ensure_updated_catalog_filter_data(
FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
const blender::asset_system::AssetLibrary *asset_library);
bool file_is_asset_visible_in_catalog_filter_settings(
const FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
const AssetMetaData *asset_data);

View File

@ -63,6 +63,7 @@
#include "DNA_asset_types.h"
#include "DNA_space_types.h"
#include "ED_asset_view_catalog_filter.h"
#include "ED_datafiles.h"
#include "ED_fileselect.hh"
#include "ED_screen.hh"
@ -199,7 +200,7 @@ struct FileListFilter {
char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
short flags;
FileAssetCatalogFilterSettingsHandle *asset_catalog_filter;
AssetViewCatalogFilterSettingsHandle *asset_catalog_filter;
};
/** #FileListFilter.flags */
@ -814,7 +815,8 @@ static void prepare_filter_asset_library(const FileList *filelist, FileListFilte
"prepare_filter_asset_library() should only be called when the file browser is "
"in asset browser mode");
file_ensure_updated_catalog_filter_data(filter->asset_catalog_filter, filelist->asset_library);
asset_view_ensure_updated_catalog_filter_data(
filter->asset_catalog_filter, reinterpret_cast<::AssetLibrary *>(filelist->asset_library));
}
/**
@ -844,8 +846,8 @@ static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter)
const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file);
/* Not used yet for the asset view template. */
if (filter->asset_catalog_filter &&
!file_is_asset_visible_in_catalog_filter_settings(filter->asset_catalog_filter, asset_data))
if (filter->asset_catalog_filter && !asset_view_is_asset_visible_in_catalog_filter_settings(
filter->asset_catalog_filter, asset_data))
{
return false;
}
@ -1033,17 +1035,16 @@ void filelist_setindexer(FileList *filelist, const FileIndexerType *indexer)
filelist->indexer = indexer;
}
void filelist_set_asset_catalog_filter_options(
FileList *filelist,
eFileSel_Params_AssetCatalogVisibility catalog_visibility,
const ::bUUID *catalog_id)
void filelist_set_asset_catalog_filter_options(FileList *filelist,
AssetCatalogFilterMode catalog_visibility,
const ::bUUID *catalog_id)
{
if (!filelist->filter_data.asset_catalog_filter) {
/* There's no filter data yet. */
filelist->filter_data.asset_catalog_filter = file_create_asset_catalog_filter_settings();
filelist->filter_data.asset_catalog_filter = asset_view_create_catalog_filter_settings();
}
const bool needs_update = file_set_asset_catalog_filter_settings(
const bool needs_update = asset_view_set_catalog_filter_settings(
filelist->filter_data.asset_catalog_filter, catalog_visibility, *catalog_id);
if (needs_update) {
@ -1496,6 +1497,29 @@ static int filelist_intern_free_main_files(FileList *filelist)
return removed_counter;
}
int filelist_preview_source_get(int /* eFileSel_File_Types */ file_type)
{
if (file_type & FILE_TYPE_IMAGE) {
return THB_SOURCE_IMAGE;
}
else if (file_type & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) {
return THB_SOURCE_BLEND;
}
else if (file_type & FILE_TYPE_MOVIE) {
return THB_SOURCE_MOVIE;
}
else if (file_type & FILE_TYPE_FTFONT) {
return THB_SOURCE_FONT;
}
else if (file_type & FILE_TYPE_OBJECT_IO) {
return THB_SOURCE_OBJECT_IO;
}
else {
BLI_assert_unreachable();
return 0;
}
}
static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata)
{
FileListEntryCache *cache = static_cast<FileListEntryCache *>(BLI_task_pool_user_data(pool));
@ -1503,33 +1527,11 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
taskdata);
FileListEntryPreview *preview = preview_taskdata->preview;
/* XXX #THB_SOURCE_IMAGE for "historic" reasons. The case of an undefined source should be
* handled better. */
ThumbSource source = THB_SOURCE_IMAGE;
ThumbSource source = static_cast<ThumbSource>(filelist_preview_source_get(preview->flags));
// printf("%s: Start (%d)...\n", __func__, threadid);
// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
BLI_assert(preview->flags &
(FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER |
FILE_TYPE_OBJECT_IO | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB));
if (preview->flags & FILE_TYPE_IMAGE) {
source = THB_SOURCE_IMAGE;
}
else if (preview->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))
{
source = THB_SOURCE_BLEND;
}
else if (preview->flags & FILE_TYPE_MOVIE) {
source = THB_SOURCE_MOVIE;
}
else if (preview->flags & FILE_TYPE_FTFONT) {
source = THB_SOURCE_FONT;
}
else if (preview->flags & FILE_TYPE_OBJECT_IO) {
source = THB_SOURCE_OBJECT_IO;
}
IMB_thumb_path_lock(preview->filepath);
/* Always generate biggest preview size for now, it's simpler and avoids having to re-generate
@ -1845,7 +1847,7 @@ static void filelist_clear_asset_library(FileList *filelist)
{
/* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */
filelist->asset_library = nullptr;
file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter);
asset_view_delete_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter);
}
void filelist_clear_ex(FileList *filelist,

View File

@ -56,12 +56,11 @@ void filelist_setfilter_options(FileList *filelist,
void filelist_setindexer(FileList *filelist, const FileIndexerType *indexer);
/**
* \param catalog_id: The catalog that should be filtered by if \a catalog_visibility is
* #FILE_SHOW_ASSETS_FROM_CATALOG. May be NULL otherwise.
* #ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG. May be NULL otherwise.
*/
void filelist_set_asset_catalog_filter_options(
FileList *filelist,
eFileSel_Params_AssetCatalogVisibility catalog_visibility,
const bUUID *catalog_id);
void filelist_set_asset_catalog_filter_options(FileList *filelist,
AssetCatalogFilterMode catalog_visibility,
const bUUID *catalog_id);
void filelist_tag_needs_filtering(FileList *filelist);
void filelist_filter(FileList *filelist);
/**
@ -80,6 +79,7 @@ ImBuf *filelist_file_getimage(const FileDirEntry *file);
ImBuf *filelist_geticon_image_ex(const FileDirEntry *file);
ImBuf *filelist_geticon_image(FileList *filelist, int index);
int filelist_geticon(FileList *filelist, int index, bool is_main);
int /* ThumbSource */ filelist_preview_source_get(int /* eFileSel_File_Types */ file_type);
FileList *filelist_new(short type);
void filelist_settype(FileList *filelist, short type);

View File

@ -500,7 +500,7 @@ void ED_fileselect_activate_asset_catalog(const SpaceFile *sfile, const bUUID ca
}
FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile);
params->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG;
params->asset_catalog_visibility = ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG;
params->catalog_id = catalog_id;
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
}

View File

@ -252,7 +252,7 @@ static void file_refresh(const bContext *C, ScrArea *area)
if (asset_params) {
filelist_set_asset_catalog_filter_options(
sfile->files,
eFileSel_Params_AssetCatalogVisibility(asset_params->asset_catalog_visibility),
AssetCatalogFilterMode(asset_params->asset_catalog_visibility),
&asset_params->catalog_id);
}

View File

@ -237,7 +237,6 @@ static void gather_search_link_ops_for_asset_library(const bContext &C,
filter_settings.id_types = FILTER_ID_NT;
ED_assetlist_storage_fetch(&library_ref, &C);
ED_assetlist_ensure_previews_job(&library_ref, &C);
ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) {
if (!ED_asset_filter_matches_asset(&filter_settings, asset)) {
return true;

View File

@ -602,6 +602,8 @@ enum {
PRV_TAG_DEFFERED_RENDERING = (1 << 1),
/** Deferred preview should be deleted asap. */
PRV_TAG_DEFFERED_DELETE = (1 << 2),
/** Attempted to load this preview from disk (deferred loading), but it couldn't be found. */
PRV_TAG_LOADING_FAILED = (1 << 3),
};
/**

View File

@ -206,6 +206,7 @@ typedef struct AssetWeakReference {
#
typedef struct AssetHandle {
const struct FileDirEntry *file_data;
struct PreviewImage *preview;
} AssetHandle;
typedef enum eUserExtensionRepo_Flag {

View File

@ -840,10 +840,10 @@ typedef struct FileAssetSelectParams {
FileSelectParams base_params;
AssetLibraryReference asset_library_ref;
short asset_catalog_visibility; /* eFileSel_Params_AssetCatalogVisibility */
short asset_catalog_visibility; /* AssetCatalogFilterMode */
char _pad[6];
/** If #asset_catalog_visibility is #FILE_SHOW_ASSETS_FROM_CATALOG, this sets the ID of the
* catalog to show. */
/** If #asset_catalog_visibility is #ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG, this sets the ID of
* the catalog to show. */
bUUID catalog_id;
short import_type; /* eFileAssetImportType */
@ -1050,12 +1050,6 @@ typedef enum eFileSel_Params_Flag {
} eFileSel_Params_Flag;
ENUM_OPERATORS(eFileSel_Params_Flag, FILE_FILTER_ASSET_CATALOG);
typedef enum eFileSel_Params_AssetCatalogVisibility {
FILE_SHOW_ASSETS_ALL_CATALOGS,
FILE_SHOW_ASSETS_FROM_CATALOG,
FILE_SHOW_ASSETS_WITHOUT_CATALOG,
} eFileSel_Params_AssetCatalogVisibility;
/**
* #FileSelectParams.rename_flag / `sfile->params->rename_flag`.
* \note short flag. Defined as bit-flags, but currently only used as exclusive status markers.
@ -2066,6 +2060,42 @@ typedef enum eSpreadsheetColumnValueType {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Browser
* \{ */
typedef enum AssetCatalogFilterMode {
ASSET_CATALOG_SHOW_ALL_ASSETS,
ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG,
ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG,
} AssetCatalogFilterMode;
typedef struct AssetCatalogFilterSettings {
short filter_mode; /* AssetCatalogFilterMode */
char _pad[6];
/** If #visibility_mode is #ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG, this sets the ID of the
* catalog to show. */
bUUID active_catalog_id;
} AssetCatalogFilterSettings;
typedef struct SpaceAssets {
SpaceLink *next, *prev;
/** Storage of regions for inactive spaces. */
ListBase regionbase;
char spacetype;
char link_flag;
char _pad0[6];
/* End 'SpaceLink' header. */
AssetLibraryReference asset_library_ref;
AssetCatalogFilterSettings catalog_filter;
/** For now store active asset as index. In future, this could store an #AssetIdentifier. */
int active_asset_idx;
char _pad1[4];
} SpaceAssets;
/** \} */
/* -------------------------------------------------------------------- */
/** \name Space Defines (eSpace_Type)
* \{ */
@ -2105,9 +2135,10 @@ typedef enum eSpace_Type {
SPACE_CLIP = 20,
SPACE_TOPBAR = 21,
SPACE_STATUSBAR = 22,
SPACE_SPREADSHEET = 23
SPACE_SPREADSHEET = 23,
SPACE_ASSETS = 24
#define SPACE_TYPE_NUM (SPACE_SPREADSHEET + 1)
#define SPACE_TYPE_NUM (SPACE_ASSETS + 1)
} eSpace_Type;
/* use for function args */

View File

@ -515,6 +515,7 @@ typedef struct bTheme {
ThemeSpace space_topbar;
ThemeSpace space_statusbar;
ThemeSpace space_spreadsheet;
ThemeSpace space_assets;
/* 20 sets of bone colors for this theme */
ThemeWireColor tarm[20];
@ -532,7 +533,7 @@ typedef struct bTheme {
#define UI_THEMESPACE_START(btheme) \
(CHECK_TYPE_INLINE(btheme, bTheme *), &((btheme)->space_properties))
#define UI_THEMESPACE_END(btheme) \
(CHECK_TYPE_INLINE(btheme, bTheme *), (&((btheme)->space_spreadsheet) + 1))
(CHECK_TYPE_INLINE(btheme, bTheme *), (&((btheme)->space_assets) + 1))
typedef struct bAddon {
struct bAddon *next, *prev;

View File

@ -88,13 +88,17 @@ static int rna_AssetTag_editable(PointerRNA *ptr, const char **r_info)
{
AssetTag *asset_tag = static_cast<AssetTag *>(ptr->data);
ID *owner_id = ptr->owner_id;
if (owner_id && owner_id->asset_data) {
if (!owner_id) {
return 0;
}
if (owner_id->asset_data) {
BLI_assert_msg(BLI_findindex(&owner_id->asset_data->tags, asset_tag) != -1,
"The owner of the asset tag pointer is not the asset ID containing the tag");
UNUSED_VARS_NDEBUG(asset_tag);
}
return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, owner_id->asset_data, r_info) ?
return rna_AssetMetaData_editable_from_owner_id(owner_id, owner_id->asset_data, r_info) ?
PROP_EDITABLE :
PropertyFlag(0);
}
@ -425,6 +429,12 @@ static int rna_AssetRepresentation_full_library_path_length(PointerRNA *ptr)
return full_library_path.size();
}
static int rna_AssetHandle_preview_icon_id_get(PointerRNA *ptr)
{
AssetHandle *asset = static_cast<AssetHandle *>(ptr->data);
return ED_assetlist_asset_preview_or_type_icon_id_request(asset);
}
const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
@ -598,6 +608,19 @@ static void rna_def_asset_handle(BlenderRNA *brna)
prop, "rna_AssetHandle_file_data_get", "rna_AssetHandle_file_data_set", nullptr, nullptr);
RNA_def_property_ui_text(
prop, "File Entry", "TEMPORARY, DO NOT USE - File data used to refer to the asset");
prop = RNA_def_int(
srna,
"preview_icon_id",
0,
INT_MIN,
INT_MAX,
"Icon ID",
"Unique integer identifying the preview of this asset as an icon (zero means invalid)",
INT_MIN,
INT_MAX);
RNA_def_property_int_funcs(prop, "rna_AssetHandle_preview_icon_id_get", NULL, NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
static void rna_def_asset_representation(BlenderRNA *brna)

View File

@ -131,6 +131,13 @@ static PointerRNA rna_Context_asset_get(PointerRNA *ptr)
return RNA_pointer_create(nullptr, &RNA_AssetRepresentation, CTX_wm_asset(C));
}
static PointerRNA rna_Context_asset_handle_get(PointerRNA *ptr)
{
bContext *C = (bContext *)ptr->data;
PointerRNA newptr = RNA_pointer_create(NULL, &RNA_AssetHandle, CTX_wm_asset_handle_ptr(C));
return newptr;
}
static PointerRNA rna_Context_main_get(PointerRNA *ptr)
{
bContext *C = (bContext *)ptr->data;
@ -286,6 +293,11 @@ void RNA_def_context(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "AssetRepresentation");
RNA_def_property_pointer_funcs(prop, "rna_Context_asset_get", nullptr, nullptr, nullptr);
prop = RNA_def_property(srna, "asset_handle", PROP_POINTER, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "AssetHandle");
RNA_def_property_pointer_funcs(prop, "rna_Context_asset_handle_get", nullptr, nullptr, nullptr);
/* Data */
prop = RNA_def_property(srna, "blend_data", PROP_POINTER, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);

View File

@ -169,6 +169,11 @@ const EnumPropertyItem rna_enum_space_type_items[] = {
ICON_PROPERTIES,
"Properties",
"Edit properties of active object and related data-blocks"},
{SPACE_ASSETS,
"ASSET_BROWSER",
ICON_ASSET_MANAGER,
"Asset Browser",
"Browse in asset libraries"},
{SPACE_FILE, "FILE_BROWSER", ICON_FILEBROWSER, "File Browser", "Browse for files and assets"},
{SPACE_SPREADSHEET,
"SPREADSHEET",
@ -618,6 +623,8 @@ static StructRNA *rna_Space_refine(PointerRNA *ptr)
return &RNA_SpaceClipEditor;
case SPACE_SPREADSHEET:
return &RNA_SpaceSpreadsheet;
case SPACE_ASSETS:
return &RNA_SpaceAssetBrowser;
/* Currently no type info. */
case SPACE_SCRIPT:
@ -873,6 +880,20 @@ static void rna_Space_show_region_asset_shelf_update(bContext *C, PointerRNA *pt
rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_ASSET_SHELF, RGN_FLAG_HIDDEN);
}
/* Navigation Region. */
static bool rna_Space_show_region_nav_bar_get(PointerRNA *ptr)
{
return !rna_Space_bool_from_region_flag_get_by_type(ptr, RGN_TYPE_NAV_BAR, RGN_FLAG_HIDDEN);
}
static void rna_Space_show_region_nav_bar_set(PointerRNA *ptr, bool value)
{
rna_Space_bool_from_region_flag_set_by_type(ptr, RGN_TYPE_NAV_BAR, RGN_FLAG_HIDDEN, !value);
}
static void rna_Space_show_region_nav_bar_update(bContext *C, PointerRNA *ptr)
{
rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_NAV_BAR, RGN_FLAG_HIDDEN);
}
/** \} */
static bool rna_Space_view2d_sync_get(PointerRNA *ptr)
@ -3325,7 +3346,7 @@ static void rna_FileAssetSelectParams_catalog_id_set(PointerRNA *ptr, const char
if (value[0] == '\0') {
params->catalog_id = BLI_uuid_nil();
params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS;
params->asset_catalog_visibility = ASSET_CATALOG_SHOW_ALL_ASSETS;
return;
}
@ -3336,7 +3357,31 @@ static void rna_FileAssetSelectParams_catalog_id_set(PointerRNA *ptr, const char
}
params->catalog_id = new_uuid;
params->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG;
params->asset_catalog_visibility = ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG;
}
static int RNA_SpaceAssetBrowser_asset_library_get(PointerRNA *ptr)
{
SpaceAssets *asset_space = static_cast<SpaceAssets *>(ptr->data);
return ED_asset_library_reference_to_enum_value(&asset_space->asset_library_ref);
}
static void RNA_SpaceAssetBrowser_asset_library_set(PointerRNA *ptr, int value)
{
SpaceAssets *asset_space = static_cast<SpaceAssets *>(ptr->data);
asset_space->asset_library_ref = ED_asset_library_reference_from_enum_value(value);
}
static void rna_AssetCatalogFilterSettings_active_catalog_id_get(PointerRNA *ptr, char *value)
{
const AssetCatalogFilterSettings *settings = static_cast<AssetCatalogFilterSettings *>(
ptr->data);
BLI_uuid_format(value, settings->active_catalog_id);
}
static int rna_AssetCatalogFilterSettings_active_catalog_id_length(PointerRNA * /*ptr*/)
{
return UUID_STRING_SIZE - 1;
}
#else
@ -3440,6 +3485,10 @@ static void rna_def_space_generic_show_region_toggles(StructRNA *srna, int regio
region_type_mask &= ~((1 << RGN_TYPE_ASSET_SHELF) | (1 << RGN_TYPE_ASSET_SHELF_HEADER));
DEF_SHOW_REGION_PROPERTY(show_region_asset_shelf, "Asset Shelf", "");
}
if (region_type_mask & (1 << RGN_TYPE_NAV_BAR)) {
region_type_mask &= ~(1 << RGN_TYPE_NAV_BAR);
DEF_SHOW_REGION_PROPERTY(show_region_nav_bar, "Navigation Bar", "");
}
BLI_assert(region_type_mask == 0);
}
@ -8222,6 +8271,83 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
}
static void rna_def_asset_catalog_filter_settings(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem asset_catalog_filter_mode[] = {
{ASSET_CATALOG_SHOW_ALL_ASSETS,
"SHOW_ALL_ASSETS",
ICON_NONE,
"All Assets",
"Show all assets, regardless of catalogs"},
{ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG,
"SHOW_ASSETS_FROM_CATALOG",
ICON_NONE,
"From Catalog",
"Show assets assigned to a specific catalog"},
{ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG,
"SHOW_ASSETS_WITHOUT_CATALOG",
ICON_NONE,
"Assets Without Catalog",
"Show any asset that doesn't have a recognized asset catalog assigned to it"},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "AssetCatalogFilterSettings", NULL);
RNA_def_struct_ui_text(
srna,
"Asset Catalog Filter Settings",
"Options to determine how catalogs should affect which assets are visible");
prop = RNA_def_property(srna, "filter_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, asset_catalog_filter_mode);
RNA_def_property_ui_text(prop,
"Asset Catalog Filter Mode",
"Determine how filtering based on asset catalogs should be done");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
prop = RNA_def_property(srna, "active_catalog_id", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop,
"rna_AssetCatalogFilterSettings_active_catalog_id_get",
"rna_AssetCatalogFilterSettings_active_catalog_id_length",
NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Catalog UUID", "The UUID of the catalog to show assets from");
}
static void rna_def_space_assets(BlenderRNA *brna)
{
PropertyRNA *prop;
StructRNA *srna;
rna_def_asset_catalog_filter_settings(brna);
srna = RNA_def_struct(brna, "SpaceAssetBrowser", "Space");
RNA_def_struct_sdna(srna, "SpaceAssets");
RNA_def_struct_ui_text(srna, "Space Asset Browser", "Asset browser space data");
rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_NAV_BAR) | (1 << RGN_TYPE_UI));
prop = rna_def_asset_library_reference_common(
srna, "RNA_SpaceAssetBrowser_asset_library_get", "RNA_SpaceAssetBrowser_asset_library_set");
RNA_def_property_ui_text(prop, "Asset Library", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
prop = RNA_def_property(srna, "catalog_filter", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "AssetCatalogFilterSettings");
RNA_def_property_ui_text(prop,
"Asset Catalog Filter",
"Parameters to set up rules for filtering assets based on the catalogs "
"they are assigned to");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
prop = RNA_def_property(srna, "active_asset_idx", PROP_INT, PROP_NONE);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
}
void RNA_def_space(BlenderRNA *brna)
{
rna_def_space(brna);
@ -8250,6 +8376,7 @@ void RNA_def_space(BlenderRNA *brna)
rna_def_space_node(brna);
rna_def_space_clip(brna);
rna_def_space_spreadsheet(brna);
rna_def_space_assets(brna);
}
#endif

View File

@ -2710,6 +2710,26 @@ static void rna_def_userdef_theme_space_file(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
}
static void rna_def_userdef_theme_space_assets(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
/* space_file */
srna = RNA_def_struct(brna, "ThemeAssetBrowser", NULL);
RNA_def_struct_sdna(srna, "ThemeSpace");
RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
RNA_def_struct_ui_text(srna, "Theme Asset Browser", "Theme settings for the Asset Browser");
rna_def_userdef_theme_spaces_main(srna);
prop = RNA_def_property(srna, "row_alternate", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Alternate Rows", "Overlay color on every other row");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
}
static void rna_def_userdef_theme_space_outliner(BlenderRNA *brna)
{
StructRNA *srna;
@ -4134,6 +4154,7 @@ static void rna_def_userdef_themes(BlenderRNA *brna)
{1, "VIEW_3D", ICON_VIEW3D, "3D Viewport", ""},
{4, "DOPESHEET_EDITOR", ICON_ACTION, "Dope Sheet/Timeline", ""},
{16, "FILE_BROWSER", ICON_FILEBROWSER, "File/Asset Browser", ""},
{24, "ASSET_BROWSER", ICON_ASSET_MANAGER, "Asset Browser", ""},
{3, "GRAPH_EDITOR", ICON_GRAPH, "Graph Editor/Drivers", ""},
{6, "IMAGE_EDITOR", ICON_IMAGE, "Image/UV Editor", ""},
{15, "INFO", ICON_INFO, "Info", ""},
@ -4284,6 +4305,13 @@ static void rna_def_userdef_themes(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, nullptr, "space_spreadsheet");
RNA_def_property_struct_type(prop, "ThemeSpreadsheet");
RNA_def_property_ui_text(prop, "Spreadsheet", "");
prop = RNA_def_property(srna, "asset_browser", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_pointer_sdna(prop, NULL, "space_assets");
RNA_def_property_struct_type(prop, "ThemeAssetBrowser");
RNA_def_property_ui_text(prop, "Asset Browser", "");
/* end space types */
prop = RNA_def_property(srna, "bone_color_sets", PROP_COLLECTION, PROP_NONE);
@ -4536,6 +4564,7 @@ static void rna_def_userdef_dothemes(BlenderRNA *brna)
rna_def_userdef_theme_space_view3d(brna);
rna_def_userdef_theme_space_graph(brna);
rna_def_userdef_theme_space_file(brna);
rna_def_userdef_theme_space_assets(brna);
rna_def_userdef_theme_space_nla(brna);
rna_def_userdef_theme_space_action(brna);
rna_def_userdef_theme_space_image(brna);

View File

@ -499,10 +499,9 @@ struct wmNotifier {
#define ND_REGIONS_ASSET_SHELF (23 << 16)
/* NC_ASSET */
/* Denotes that the AssetList is done reading some previews. NOT that the preview generation of
* assets is done. */
/* Denote that something in the contents of an AssetList may have changed. Triggers re-filtering of
* items. */
#define ND_ASSET_LIST (1 << 16)
#define ND_ASSET_LIST_PREVIEW (2 << 16)
#define ND_ASSET_LIST_READING (3 << 16)
/* Catalog data changed, requiring a redraw of catalog UIs. Note that this doesn't denote a
* reloading of asset libraries & their catalogs should happen. That only happens on explicit user

View File

@ -648,6 +648,10 @@ static void wm_file_read_pre(bool use_data, bool /*use_userdef*/)
UI_view2d_zoom_cache_reset();
ED_preview_restart_queue_free();
/* #AssetLibraryService and the contained #AssetLibrary instances are destroyed on file loading.
* Asset lists may still reference them, so clear the asset list storage entirely for now. Later
* on, asset lists should actually live in the library, so this can be solved differently. */
ED_assetlist_storage_exit();
}
/**

View File

@ -427,6 +427,10 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
else if (STRPREFIX(opname, "FILE_OT")) {
km = WM_keymap_find_all(wm, "File Browser", sl->spacetype, RGN_TYPE_WINDOW);
}
/* Asset browser */
else if (STRPREFIX(opname, "ASSET_OT")) {
km = WM_keymap_find_all(wm, "Asset Browser", sl->spacetype, 0);
}
/* Logic Editor */
else if (STRPREFIX(opname, "LOGIC_OT")) {
km = WM_keymap_find_all(wm, "Logic Editor", sl->spacetype, RGN_TYPE_WINDOW);