Store active item, asset metadata sidebar

Also:
- Fix double-free bug when closing Blender
- Fix issues with identifying assets with the same name.
- Add functions to store asset handle as pointer in the asset list storage,
  asset browser exposes it via context.
This commit is contained in:
Julian Eisel 2022-02-15 21:36:21 +01:00
parent 8f48dd8f72
commit ef58467594
23 changed files with 492 additions and 61 deletions

View File

@ -2288,6 +2288,9 @@ def km_asset_browser(params):
items.extend([
("wm.context_toggle", {"type": 'T', "value": 'PRESS'},
{"properties": [("data_path", 'space_data.show_region_nav_bar')]}),
*_template_space_region_type_toggle(
sidebar_key={"type": 'N', "value": 'PRESS'},
),
])
return keymap

View File

@ -4102,6 +4102,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

@ -17,7 +17,8 @@
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
from bpy.types import Header, Menu
import bpy
from bpy.types import Header, Menu, Panel, UIList
class ASSETBROWSER_HT_header(Header):
@ -31,6 +32,23 @@ class ASSETBROWSER_HT_header(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"
@ -75,12 +93,132 @@ class ASSETBROWSER_MT_edit(Menu):
layout.operator("asset.catalog_redo", text="Redo")
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'
def draw(self, context):
layout = self.layout
wm = context.window_manager
asset_handle = context.asset_handle
asset_file = asset_handle.file_data
if asset_handle is None:
layout.label(text="No active asset", icon='INFO')
return
asset_library_ref = context.asset_library_ref
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file, asset_library_ref)
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')
layout.prop(asset_file.asset_data, "description")
layout.prop(asset_file.asset_data, "author")
class ASSETBROWSER_PT_metadata_preview(Panel):
bl_space_type = 'ASSET_BROWSER'
bl_region_type = 'UI'
bl_label = "Preview"
bl_category = 'Metadata'
def draw(self, context):
layout = self.layout
asset_handle = context.asset_handle
asset_file = asset_handle.file_data
row = layout.row()
box = row.box()
box.template_icon(icon_value=asset_file.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(Panel):
bl_space_type = 'ASSET_BROWSER'
bl_region_type = 'UI'
bl_label = "Tags"
bl_category = 'Metadata'
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.

View File

@ -680,7 +680,7 @@ class ASSETBROWSEROLD_MT_edit(AssetBrowserMenu, Menu):
layout.operator("asset.catalog_redo", text="Redo")
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'}
@ -730,7 +730,7 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
layout.prop(asset_file_handle.asset_data, "author")
class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
class ASSETBROWSEROLD_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
bl_label = "Preview"
def draw(self, context):
@ -745,10 +745,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):
@ -756,7 +756,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.AssetMetaDataPanel, Panel):
bl_label = "Tags"
def draw(self, context):
@ -764,7 +764,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)
@ -772,7 +772,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
@ -833,11 +833,11 @@ classes = (
ASSETBROWSEROLD_MT_view,
ASSETBROWSEROLD_MT_select,
ASSETBROWSEROLD_MT_edit,
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

@ -360,6 +360,7 @@ int 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(const bContext *C, bool *r_is_valid);
struct AssetHandle *CTX_wm_asset_handle_ptr(const bContext *C);
bool CTX_wm_interface_locked(const bContext *C);

View File

@ -1473,6 +1473,16 @@ AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid)
return (AssetHandle){0};
}
/**
* \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 CTX_data_pointer_get_type(C, "asset_handle", &RNA_AssetHandle).data;
}
Depsgraph *CTX_data_depsgraph_pointer(const bContext *C)
{
Main *bmain = CTX_data_main(C);

View File

@ -23,6 +23,7 @@ struct AssetLibraryReference;
struct bContext;
const char *ED_asset_handle_get_name(const struct AssetHandle *asset);
const char *ED_asset_handle_get_identifier(const struct AssetHandle *asset);
struct AssetMetaData *ED_asset_handle_get_metadata(const struct AssetHandle *asset);
struct ID *ED_asset_handle_get_local_id(const struct AssetHandle *asset);
ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset);

View File

@ -6,6 +6,7 @@
#pragma once
#include <optional>
#include <string>
#include "BLI_function_ref.hh"
@ -19,8 +20,11 @@ std::string ED_assetlist_asset_filepath_get(const bContext *C,
const AssetLibraryReference &library_reference,
const AssetHandle &asset_handle);
AssetHandle *ED_assetlist_asset_get_from_index(const AssetLibraryReference *library_reference,
int index);
/* Can return false to stop iterating. */
using AssetListIterFn = blender::FunctionRef<bool(AssetHandle)>;
using AssetListIterFn = blender::FunctionRef<bool(AssetHandle &)>;
/**
* 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

View File

@ -18,6 +18,11 @@ const char *ED_asset_handle_get_name(const AssetHandle *asset)
return asset->file_data->name;
}
const char *ED_asset_handle_get_identifier(const AssetHandle *asset)
{
return asset->file_data->relpath;
}
AssetMetaData *ED_asset_handle_get_metadata(const AssetHandle *asset)
{
return asset->file_data->asset_data;

View File

@ -99,6 +99,8 @@ class PreviewTimer {
class AssetList : NonCopyable {
FileListWrapper filelist_;
/** Storage for asset handles, items are lazy-created on request. */
mutable Map<const FileDirEntry *, AssetHandle> asset_handle_map_;
AssetLibraryReference library_ref_;
PreviewTimer previews_timer_;
@ -115,6 +117,7 @@ class AssetList : NonCopyable {
void clear(bContext *C);
bool needsRefetch() const;
AssetHandle &asset_handle_from_file(const FileDirEntry &) const;
void iterate(AssetListIterFn fn) const;
bool listen(const wmNotifier &notifier) const;
int size() const;
@ -179,6 +182,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)) {
@ -201,6 +205,11 @@ bool AssetList::needsRefetch() const
return filelist_needs_force_reset(filelist_) || filelist_needs_reading(filelist_);
}
AssetHandle &AssetList::asset_handle_from_file(const FileDirEntry &file) const
{
return asset_handle_map_.lookup_or_add(&file, AssetHandle{&file});
}
void AssetList::iterate(AssetListIterFn fn) const
{
FileList *files = filelist_;
@ -212,7 +221,7 @@ void AssetList::iterate(AssetListIterFn 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;
@ -252,6 +261,7 @@ 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);
}
@ -349,7 +359,6 @@ class AssetListStorage {
const AssetLibraryReference &library_reference, eFileSelectType filesel_type);
static AssetListMap &global_storage();
static bool &global_storage_is_destructed();
};
void AssetListStorage::fetch_library(const AssetLibraryReference &library_reference,
@ -369,8 +378,7 @@ void AssetListStorage::fetch_library(const AssetLibraryReference &library_refere
void AssetListStorage::destruct()
{
global_storage().~AssetListMap();
global_storage_is_destructed() = true;
global_storage().clear();
}
AssetList *AssetListStorage::lookup_list(const AssetLibraryReference &library_ref)
@ -423,19 +431,9 @@ std::tuple<AssetList &, AssetListStorage::is_new_t> AssetListStorage::ensure_lis
AssetListStorage::AssetListMap &AssetListStorage::global_storage()
{
static AssetListMap global_storage_;
if (global_storage_is_destructed()) {
global_storage_ = AssetListMap();
global_storage_is_destructed() = false;
}
return global_storage_;
}
bool &AssetListStorage::global_storage_is_destructed()
{
static bool is_destructed = false;
return is_destructed;
}
/** \} */
} // namespace blender::ed::asset
@ -541,6 +539,29 @@ ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle)
return filelist_geticon_image_ex(asset_handle->file_data);
}
AssetHandle *ED_assetlist_asset_get_from_index(const AssetLibraryReference *library_reference,
const int index)
{
AssetList *list = AssetListStorage::lookup_list(*library_reference);
if (!list) {
return nullptr;
}
AssetHandle *asset = nullptr;
int i = 0;
list->iterate([&](AssetHandle &iter_asset) {
if (i == index) {
asset = &iter_asset;
return false;
}
i++;
return true;
});
return asset;
}
const char *ED_assetlist_library_path(const AssetLibraryReference *library_reference)
{
AssetList *list = AssetListStorage::lookup_list(*library_reference);

View File

@ -83,6 +83,11 @@ class AbstractGridViewItem {
/** Called when the item's state changes from inactive to active. */
virtual void on_activate();
/**
* If the result is not empty, it controls whether the item should be active or not,
* usually depending on the data that the view represents.
*/
virtual std::optional<bool> should_be_active() const;
/**
* Copy persistent state (e.g. active, selection, etc.) from a matching item of
@ -101,6 +106,8 @@ class AbstractGridViewItem {
void deactivate();
private:
/** See #AbstractTreeView::change_state_delayed() */
void change_state_delayed();
static void grid_tile_click_fn(bContext *, void *but_arg1, void *);
void add_grid_tile_button(uiBlock &block);
};
@ -172,6 +179,12 @@ class AbstractGridView {
void update_from_old(uiBlock &new_block);
AbstractGridViewItem *find_matching_item(const AbstractGridViewItem &lookup_item,
const AbstractGridView &view) const;
/**
* Items may want to do additional work when state changes. But these state changes can only be
* reliably detected after the view has completed reconstruction (see #is_reconstructed()). So
* the actual state changes are done in a delayed manner through this function.
*/
void change_state_delayed();
/**
* Add an already constructed item, moving ownership to the grid-view.
@ -212,12 +225,37 @@ class GridViewBuilder {
* unit size).
*/
class PreviewGridItem : public AbstractGridViewItem {
public:
using IsActiveFn = std::function<bool()>;
using ActivateFn = std::function<void(PreviewGridItem &new_active)>;
protected:
/** See #set_on_activate_fn() */
ActivateFn activate_fn_;
/** See #set_is_active_fn() */
IsActiveFn is_active_fn_;
public:
int preview_icon_id = ICON_NONE;
PreviewGridItem(StringRef label, int preview_icon_id);
void build_grid_tile(uiLayout &layout) const override;
/**
* Set a custom callback to execute when activating this view item. This way users don't have to
* sub-class #PreviewGridItem, just to implement custom activation behavior (a common thing to
* do).
*/
void set_on_activate_fn(ActivateFn fn);
/**
* Set a custom callback to check if this item should be active.
*/
void set_is_active_fn(IsActiveFn fn);
private:
std::optional<bool> should_be_active() const override;
void on_activate() override;
};
/** \} */

View File

@ -73,6 +73,14 @@ AbstractGridViewItem *AbstractGridView::find_matching_item(const AbstractGridVie
return nullptr;
}
void AbstractGridView::change_state_delayed()
{
BLI_assert_msg(
is_reconstructed(),
"These state changes are supposed to be delayed until reconstruction is completed");
foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); });
}
void AbstractGridView::update_from_old(uiBlock &new_block)
{
uiGridViewHandle *old_view_handle = ui_block_grid_view_find_matching_in_old_block(
@ -169,6 +177,19 @@ void AbstractGridViewItem::on_activate()
/* Do nothing by default. */
}
std::optional<bool> AbstractGridViewItem::should_be_active() const
{
return std::nullopt;
}
void AbstractGridViewItem::change_state_delayed()
{
const std::optional<bool> should_be_active = this->should_be_active();
if (should_be_active.has_value() && *should_be_active) {
activate();
}
}
void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old)
{
is_active_ = old.is_active_;
@ -334,7 +355,7 @@ void BuildOnlyVisibleButtonsHelper::add_spacer_button(uiBlock &block, const int
class GridViewLayoutBuilder {
uiBlock &block_;
friend GridViewBuilder;
friend class GridViewBuilder;
public:
GridViewLayoutBuilder(uiBlock &block);
@ -420,14 +441,10 @@ void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D
{
grid_view.build_items();
grid_view.update_from_old(block_);
grid_view.change_state_delayed();
GridViewLayoutBuilder builder(block_);
builder.build_from_view(grid_view, v2d);
// grid_view.update_from_old(block_);
// grid_view.change_state_delayed();
// TreeViewLayoutBuilder builder(block_);
// builder.build_from_tree(tree_view);
}
/* ---------------------------------------------------------------------- */
@ -463,6 +480,31 @@ void PreviewGridItem::build_grid_tile(uiLayout &layout) const
UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
}
void PreviewGridItem::set_on_activate_fn(ActivateFn fn)
{
activate_fn_ = fn;
}
void PreviewGridItem::set_is_active_fn(IsActiveFn fn)
{
is_active_fn_ = fn;
}
void PreviewGridItem::on_activate()
{
if (activate_fn_) {
activate_fn_(*this);
}
}
std::optional<bool> PreviewGridItem::should_be_active() const
{
if (is_active_fn_) {
return is_active_fn_();
}
return std::nullopt;
}
} // namespace blender::ui
using namespace blender::ui;

View File

@ -163,7 +163,7 @@ static void asset_view_template_refresh_asset_collection(
RNA_property_collection_clear(&assets_dataptr, assets_prop);
ED_assetlist_iterate(asset_library_ref, [&](AssetHandle asset) {
ED_assetlist_iterate(asset_library_ref, [&](AssetHandle &asset) {
if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) {
/* Don't do anything else, but return true to continue iterating. */
return true;

View File

@ -23,6 +23,8 @@
#include "BKE_context.h"
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "UI_view2d.h"
@ -38,7 +40,8 @@ using namespace blender::ed::asset_browser;
void asset_browser_main_region_draw(const bContext *C, ARegion *region)
{
const SpaceAssets *asset_space = CTX_wm_space_assets(C);
SpaceAssets *asset_space = CTX_wm_space_assets(C);
bScreen *screen = CTX_wm_screen(C);
View2D *v2d = &region->v2d;
UI_ThemeClearColor(TH_BACK);
@ -60,8 +63,18 @@ void asset_browser_main_region_draw(const bContext *C, ARegion *region)
0,
style);
asset_view_create_in_layout(
*C, asset_space->asset_library_ref, asset_space->catalog_filter, *v2d, *layout);
PointerRNA asset_space_ptr;
RNA_pointer_create(&screen->id, &RNA_SpaceAssetBrowser, asset_space, &asset_space_ptr);
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;

View File

@ -51,7 +51,7 @@ static void assets_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pa
uiLayout *row = uiLayoutRow(col, true);
PointerRNA assets_space_ptr;
RNA_pointer_create(&screen->id, &RNA_SpaceAssets, assets_space, &assets_space_ptr);
RNA_pointer_create(&screen->id, &RNA_SpaceAssetBrowser, assets_space, &assets_space_ptr);
uiItemR(row, &assets_space_ptr, "asset_library_ref", 0, "", ICON_NONE);
if (assets_space->asset_library_ref.type == ASSET_LIBRARY_LOCAL) {

View File

@ -23,27 +23,46 @@
#include "DNA_asset_types.h"
#include "RNA_access.h"
#include "RNA_types.h"
#include "ED_asset.h"
#include "UI_interface.h"
#include "UI_interface.hh"
#include "asset_view.hh"
#include "WM_message.h"
namespace ui = blender::ui;
#include "asset_view.hh"
namespace blender::ed::asset_browser {
AssetGridView::AssetGridView(const AssetLibraryReference &asset_library_ref)
: asset_library_ref_(asset_library_ref)
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()
{
ED_assetlist_iterate(asset_library_ref_, [this](AssetHandle asset) {
add_item<ui::PreviewGridItem>(ED_asset_handle_get_name(&asset),
ED_asset_handle_get_preview_icon_id(&asset));
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](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;
});
}
@ -53,9 +72,34 @@ 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_name(&asset),
ED_asset_handle_get_preview_icon_id(&asset)),
asset_(asset),
asset_identifier(ED_asset_handle_get_identifier(&asset))
{
}
bool AssetGridViewItem::matches(const ui::AbstractGridViewItem &other) const
{
const AssetGridViewItem &other_item = dynamic_cast<const AssetGridViewItem &>(other);
return StringRef(asset_identifier) == StringRef(other_item.asset_identifier);
}
AssetHandle &AssetGridViewItem::get_asset()
{
return asset_;
}
/* ---------------------------------------------------------------------- */
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)
{
@ -67,7 +111,12 @@ void asset_view_create_in_layout(const bContext &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));
*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);

View File

@ -26,24 +26,49 @@
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 &);
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 {
AssetHandle &asset_;
std::string asset_identifier;
public:
AssetGridViewItem(AssetHandle &);
bool matches(const AbstractGridViewItem &other) const override;
AssetHandle &get_asset();
};
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);

View File

@ -28,6 +28,7 @@
#include "BLI_listbase.h"
#include "ED_asset.h"
#include "ED_screen.h"
#include "ED_space_api.h"
@ -64,7 +65,7 @@ static SpaceLink *asset_browser_create(const ScrArea *UNUSED(area), const Scene
}
{
/* navigation region */
/* Navigation region */
ARegion *region = MEM_cnew<ARegion>("asset browser navigation region");
BLI_addtail(&assets_space->regionbase, region);
@ -72,6 +73,16 @@ static SpaceLink *asset_browser_create(const ScrArea *UNUSED(area), const Scene
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");
@ -112,6 +123,7 @@ static void asset_browser_keymap(wmKeyConfig *keyconf)
}
const char *asset_browser_context_dir[] = {
"asset_handle",
"asset_library_ref",
NULL,
};
@ -134,6 +146,17 @@ static int /*eContextResult*/ asset_browser_context(const bContext *C,
return CTX_RESULT_OK;
}
if (CTX_data_equals(member, "asset_handle")) {
AssetHandle *asset = ED_assetlist_asset_get_from_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;
}
@ -171,7 +194,7 @@ static void asset_browser_main_region_message_subscribe(
WM_msg_subscribe_rna_prop(mbus,
&screen->id,
assets_space,
SpaceAssets,
SpaceAssetBrowser,
catalog_filter,
&msg_sub_value_region_tag_redraw);
}
@ -213,6 +236,30 @@ static void asset_browser_navigation_region_listener(
{
}
/* ---------------------------------------------------------------------- */
/* 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 *UNUSED(listener_params))
{
}
/* ---------------------------------------------------------------------- */
/* Asset Browser Space-Type */
@ -262,7 +309,16 @@ void ED_spacetype_assets(void)
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

@ -812,8 +812,8 @@ typedef struct FileAssetSelectParams {
AssetLibraryReference asset_library_ref;
short asset_catalog_visibility; /* AssetCatalogFilterMode */
char _pad[6];
/** If #asset_catalog_visibility is #ASSET_CATALOG_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 */
@ -2042,6 +2042,9 @@ typedef struct SpaceAssets {
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;
/** \} */

View File

@ -593,7 +593,7 @@ extern StructRNA RNA_SolidifyModifier;
extern StructRNA RNA_Sound;
extern StructRNA RNA_SoundSequence;
extern StructRNA RNA_Space;
extern StructRNA RNA_SpaceAssets;
extern StructRNA RNA_SpaceAssetBrowser;
extern StructRNA RNA_SpaceClipEditor;
extern StructRNA RNA_SpaceConsole;
extern StructRNA RNA_SpaceDopeSheetEditor;

View File

@ -59,13 +59,17 @@ static int rna_AssetTag_editable(PointerRNA *ptr, const char **r_info)
{
AssetTag *asset_tag = 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 :
0;
}

View File

@ -138,6 +138,14 @@ static PointerRNA rna_Context_asset_file_handle_get(PointerRNA *ptr)
return newptr;
}
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), &newptr);
return newptr;
}
static PointerRNA rna_Context_main_get(PointerRNA *ptr)
{
bContext *C = (bContext *)ptr->data;
@ -296,6 +304,11 @@ void RNA_def_context(BlenderRNA *brna)
"The file of an active asset. Avoid using this, it will be replaced by "
"a proper AssetHandle design");
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", NULL, NULL, NULL);
/* Data */
prop = RNA_def_property(srna, "blend_data", PROP_POINTER, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);

View File

@ -611,7 +611,7 @@ static StructRNA *rna_Space_refine(struct PointerRNA *ptr)
case SPACE_SPREADSHEET:
return &RNA_SpaceSpreadsheet;
case SPACE_ASSETS:
return &RNA_SpaceAssets;
return &RNA_SpaceAssetBrowser;
/* Currently no type info. */
case SPACE_SCRIPT:
@ -3265,13 +3265,13 @@ static int rna_FileAssetSelectParams_catalog_id_length(PointerRNA *UNUSED(ptr))
return UUID_STRING_LEN - 1;
}
static int rna_SpaceAssets_asset_library_get(PointerRNA *ptr)
static int RNA_SpaceAssetBrowser_asset_library_get(PointerRNA *ptr)
{
SpaceAssets *asset_space = ptr->data;
return ED_asset_library_reference_to_enum_value(&asset_space->asset_library_ref);
}
static void rna_SpaceAssets_asset_library_set(PointerRNA *ptr, int value)
static void RNA_SpaceAssetBrowser_asset_library_set(PointerRNA *ptr, int value)
{
SpaceAssets *asset_space = ptr->data;
asset_space->asset_library_ref = ED_asset_library_reference_from_enum_value(value);
@ -8057,13 +8057,14 @@ static void rna_def_space_assets(BlenderRNA *brna)
rna_def_asset_catalog_filter_settings(brna);
srna = RNA_def_struct(brna, "SpaceAssets", "Space");
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));
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_SpaceAssets_asset_library_get", "rna_SpaceAssets_asset_library_set");
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);
@ -8075,6 +8076,9 @@ static void rna_def_space_assets(BlenderRNA *brna)
"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)