Asset Browser: Initial Asset Catalog UI

The Asset Browser now displays a tree with asset catalogs in the left
sidebar.
This replaces the asset categories. It uses the new UI tree-view API
(https://wiki.blender.org/wiki/Source/Interface/Views#Tree-View).
Buttons are displayed for adding and removing of catalogs. Parent items
can be collapsed, but the collapsed/uncollapsed state is not stored in
files yet.
Note that edits to catalogs (e.g. new or removed catalogs) are only
written to the asset library's catalog definition files when saving a
.blend.

In the "Current File" asset library, we try to show asset catalogs from
a parent asset library, or if that fails, from the directory the file is
stored in. See adaf4f56e1.

There are plenty of TODOs and smaller glitches to be fixed still. Plus a
UI polishing pass should be done.

Important missing UI features:
* Dragging assets into catalogs (WIP, close to being ready).
* Renaming catalogs
* Proper handling of catalogs in the "Current File" asset library
  (currently not working well).

The "Current File" asset library is especially limited still. Since this
is the only place where you can assign assets to a catalog, this makes
the catalogs very cumbersome in general. To assign an asset to a
catalog, one has to manually copy the Catalog ID (a random hash like
number) to the asset metadata through a temporary UI in the Asset
Browser Sidebar. These limitations should be addressed over the next few
days, they are high priority.

Differential Revision: https://developer.blender.org/D12670
This commit is contained in:
Julian Eisel 2021-09-29 17:01:13 +02:00
parent df9120b365
commit 9d9f205dc4
Notes: blender-bot 2023-02-14 00:44:02 +01:00
Referenced by issue #93690, not showing sections of asset browser
Referenced by issue #92668, I do not see the setting experimental
Referenced by issue #91958, No Asset Category visible
Referenced by issue #91940, Asset Browser editor continually redraws when the catalog tree is visible
23 changed files with 638 additions and 107 deletions

View File

@ -52,19 +52,12 @@ class AssetBrowserPanel:
bl_space_type = 'FILE_BROWSER'
@classmethod
def poll(cls, context):
def asset_browser_panel_poll(cls, context):
return SpaceAssetInfo.is_asset_browser_poll(context)
class AssetBrowserSpecificCategoryPanel(AssetBrowserPanel):
asset_categories = set() # Set of strings like 'ANIMATIONS', see `asset_category_items` in rna_space.c
@classmethod
def poll(cls, context):
return (
SpaceAssetInfo.is_asset_browser_poll(context)
and context.space_data.params.asset_category in cls.asset_categories
)
return cls.asset_browser_panel_poll(context)
class AssetMetaDataPanel:

View File

@ -648,30 +648,6 @@ class ASSETBROWSER_MT_select(AssetBrowserMenu, Menu):
layout.operator("file.select_box")
class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel):
bl_label = "Asset Navigation"
bl_region_type = 'TOOLS'
bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
return (
asset_utils.AssetBrowserPanel.poll(context) and
context.preferences.experimental.use_extended_asset_browser
)
def draw(self, context):
layout = self.layout
space_file = context.space_data
col = layout.column()
col.scale_x = 1.3
col.scale_y = 1.3
col.prop(space_file.params, "asset_category", expand=True)
class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
bl_region_type = 'TOOL_PROPS'
bl_label = "Asset Metadata"
@ -821,7 +797,6 @@ classes = (
ASSETBROWSER_MT_editor_menus,
ASSETBROWSER_MT_view,
ASSETBROWSER_MT_select,
ASSETBROWSER_PT_navigation_bar,
ASSETBROWSER_PT_metadata,
ASSETBROWSER_PT_metadata_preview,
ASSETBROWSER_PT_metadata_details,

View File

@ -48,3 +48,7 @@ struct AssetLibrary {
};
} // namespace blender::bke
blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service(
const ::AssetLibrary *library);
blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library);

View File

@ -68,6 +68,29 @@ bool BKE_asset_library_find_suitable_root_path_from_main(const Main *bmain, char
return BKE_asset_library_find_suitable_root_path_from_path(bmain->name, r_library_path);
}
blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service(
const ::AssetLibrary *library_c)
{
if (library_c == nullptr) {
return nullptr;
}
const blender::bke::AssetLibrary &library = reinterpret_cast<const blender::bke::AssetLibrary &>(
*library_c);
return library.catalog_service.get();
}
blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library)
{
blender::bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(
library);
if (catalog_service == nullptr) {
return nullptr;
}
return catalog_service->get_catalog_tree();
}
namespace blender::bke {
void AssetLibrary::load(StringRefNull library_root_directory)

View File

@ -31,6 +31,7 @@ set(INC_SYS
)
set(SRC
intern/asset_catalog.cc
intern/asset_filter.cc
intern/asset_handle.cc
intern/asset_library_reference.cc
@ -40,6 +41,7 @@ set(SRC
intern/asset_ops.cc
intern/asset_temp_id_consumer.cc
ED_asset_catalog.hh
ED_asset_filter.h
ED_asset_handle.h
ED_asset_library.h

View File

@ -0,0 +1,35 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup edasset
*/
#pragma once
#include "BKE_asset_catalog.hh"
#include "BLI_string_ref.hh"
struct AssetLibrary;
namespace blender::bke {
class AssetCatalog;
} // namespace blender::bke
blender::bke::AssetCatalog *ED_asset_catalog_add(AssetLibrary *library,
blender::StringRefNull name,
blender::StringRef parent_path = nullptr);
void ED_asset_catalog_remove(AssetLibrary *library, const blender::bke::CatalogID &catalog_id);

View File

@ -0,0 +1,87 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup edasset
*/
#include "BKE_asset_catalog.hh"
#include "BKE_asset_library.hh"
#include "BLI_string_utils.h"
#include "ED_asset_catalog.hh"
using namespace blender;
using namespace blender::bke;
struct CatalogUniqueNameFnData {
const AssetCatalogService &catalog_service;
StringRef parent_path;
};
static std::string to_full_path(StringRef parent_path, StringRef name)
{
return parent_path.is_empty() ?
std::string(name) :
std::string(parent_path) + AssetCatalogService::PATH_SEPARATOR + name;
}
static bool catalog_name_exists_fn(void *arg, const char *name)
{
CatalogUniqueNameFnData &fn_data = *static_cast<CatalogUniqueNameFnData *>(arg);
std::string fullpath = to_full_path(fn_data.parent_path, name);
return fn_data.catalog_service.find_catalog_by_path(fullpath);
}
static std::string catalog_name_ensure_unique(AssetCatalogService &catalog_service,
StringRefNull name,
StringRef parent_path)
{
CatalogUniqueNameFnData fn_data = {catalog_service, parent_path};
char unique_name[NAME_MAX] = "";
BLI_uniquename_cb(
catalog_name_exists_fn, &fn_data, name.c_str(), '.', unique_name, sizeof(unique_name));
return unique_name;
}
AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library,
StringRefNull name,
StringRef parent_path)
{
bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library);
if (!catalog_service) {
return nullptr;
}
std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path);
std::string fullpath = to_full_path(parent_path, unique_name);
return catalog_service->create_catalog(fullpath);
}
void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_id)
{
bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library);
if (!catalog_service) {
BLI_assert_unreachable();
return;
}
catalog_service->delete_catalog(catalog_id);
}

View File

@ -18,13 +18,18 @@
* \ingroup edasset
*/
#include "BKE_asset_catalog.hh"
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_report.h"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "ED_asset.h"
#include "ED_asset_catalog.hh"
/* XXX needs access to the file list, should all be done via the asset system in future. */
#include "ED_fileselect.h"
#include "RNA_access.h"
#include "RNA_define.h"
@ -32,6 +37,8 @@
#include "WM_api.h"
#include "WM_types.h"
using namespace blender;
/* -------------------------------------------------------------------- */
using PointerRNAVec = blender::Vector<PointerRNA>;
@ -372,10 +379,91 @@ static void ASSET_OT_list_refresh(struct wmOperatorType *ot)
/* -------------------------------------------------------------------- */
static bool asset_catalog_operator_poll(bContext *C)
{
const SpaceFile *sfile = CTX_wm_space_file(C);
return asset_operation_poll(C) && sfile && ED_fileselect_active_asset_library_get(sfile);
}
static int asset_catalog_new_exec(bContext *C, wmOperator *op)
{
SpaceFile *sfile = CTX_wm_space_file(C);
struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr);
ED_asset_catalog_add(asset_library, "Catalog", parent_path);
MEM_freeN(parent_path);
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
return OPERATOR_FINISHED;
}
static void ASSET_OT_catalog_new(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "New Asset Catalog";
ot->description = "Create a new catalog to put assets in";
ot->idname = "ASSET_OT_catalog_new";
/* api callbacks */
ot->exec = asset_catalog_new_exec;
ot->poll = asset_catalog_operator_poll;
RNA_def_string(ot->srna,
"parent_path",
nullptr,
0,
"Parent Path",
"Optional path defining the location to put the new catalog under");
}
static int asset_catalog_delete_exec(bContext *C, wmOperator *op)
{
SpaceFile *sfile = CTX_wm_space_file(C);
struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
char *catalog_id_str = RNA_string_get_alloc(op->ptr, "catalog_id", nullptr, 0, nullptr);
bke::CatalogID catalog_id;
if (!BLI_uuid_parse_string(&catalog_id, catalog_id_str)) {
return OPERATOR_CANCELLED;
}
ED_asset_catalog_remove(asset_library, catalog_id);
MEM_freeN(catalog_id_str);
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
return OPERATOR_FINISHED;
}
static void ASSET_OT_catalog_delete(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Asset Catalog";
ot->description =
"Remove an asset catalog from the asset library (contained assets will not be affected and "
"show up as unassigned)";
ot->idname = "ASSET_OT_catalog_delete";
/* api callbacks */
ot->exec = asset_catalog_delete_exec;
ot->invoke = WM_operator_confirm;
ot->poll = asset_catalog_operator_poll;
RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete");
}
/* -------------------------------------------------------------------- */
void ED_operatortypes_asset(void)
{
WM_operatortype_append(ASSET_OT_mark);
WM_operatortype_append(ASSET_OT_clear);
WM_operatortype_append(ASSET_OT_catalog_new);
WM_operatortype_append(ASSET_OT_catalog_delete);
WM_operatortype_append(ASSET_OT_list_refresh);
}

View File

@ -142,6 +142,7 @@ void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile);
bool ED_fileselect_is_file_browser(const struct SpaceFile *sfile);
bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile);
struct AssetLibrary *ED_fileselect_active_asset_library_get(const struct SpaceFile *sfile);
struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile);
/* Activate the file that corresponds to the given ID.

View File

@ -2761,7 +2761,8 @@ void UI_interface_tag_script_reload(void);
/* Support click-drag motion which presses the button and closes a popover (like a menu). */
#define USE_UI_POPOVER_ONCE
bool UI_tree_view_item_is_active(uiTreeViewItemHandle *item_);
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item);
bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b);
#ifdef __cplusplus
}

View File

@ -190,6 +190,12 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
* last redraw to this item. If sub-classes introduce more advanced state they should override
* this and make it update their state accordingly. */
virtual void update_from_old(const AbstractTreeViewItem &old);
/** Compare this item to \a other to check if they represent the same data. This is critical for
* being able to recognize an item from a previous redraw, to be able to keep its state (e.g.
* open/closed, active, etc.). Items are only matched if their parents also match.
* By default this just matches the items names/labels (if their parents match!). If that isn't
* good enough for a sub-class, that can override it. */
virtual bool matches(const AbstractTreeViewItem &other) const;
const AbstractTreeView &get_tree_view() const;
int count_parents() const;

View File

@ -743,6 +743,15 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
return false;
}
if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) {
uiButTreeRow *but_treerow = (uiButTreeRow *)but;
uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut;
if (!but_treerow->tree_item || !oldbut_treerow->tree_item ||
!UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) {
return false;
}
}
return true;
}

View File

@ -123,7 +123,7 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child(
const AbstractTreeViewItem &lookup_item, const TreeViewItemContainer &items)
{
for (const auto &iter_item : items.children_) {
if (lookup_item.label_ == iter_item->label_) {
if (lookup_item.matches(*iter_item)) {
/* We have a matching item! */
return iter_item.get();
}
@ -145,6 +145,11 @@ void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old)
is_active_ = old.is_active_;
}
bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const
{
return label_ == other.label_;
}
const AbstractTreeView &AbstractTreeViewItem::get_tree_view() const
{
return static_cast<AbstractTreeView &>(*root_);
@ -309,8 +314,16 @@ uiBut *BasicTreeViewItem::button()
using namespace blender::ui;
bool UI_tree_view_item_is_active(uiTreeViewItemHandle *item_)
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle)
{
AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_);
const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
return item.is_active();
}
bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle,
const uiTreeViewItemHandle *b_handle)
{
const AbstractTreeViewItem &a = reinterpret_cast<const AbstractTreeViewItem &>(*a_handle);
const AbstractTreeViewItem &b = reinterpret_cast<const AbstractTreeViewItem &>(*b_handle);
return a.matches(b);
}

View File

@ -34,6 +34,7 @@ set(INC
)
set(SRC
asset_catalog_tree_view.cc
file_draw.c
file_ops.c
file_panels.c

View File

@ -0,0 +1,230 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2007 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup spfile
*/
#include "ED_fileselect.h"
#include "DNA_space_types.h"
#include "BKE_asset_catalog.hh"
#include "BKE_asset_library.hh"
#include "BLI_string_ref.hh"
#include "BLT_translation.h"
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_interface.hh"
#include "UI_resources.h"
#include "UI_tree_view.hh"
#include "WM_api.h"
#include "WM_types.h"
#include "file_intern.h"
using namespace blender;
using namespace blender::bke;
namespace blender::ed::asset_browser {
class AssetCatalogTreeView : public ui::AbstractTreeView {
/** The asset catalog tree this tree-view represents. */
bke::AssetCatalogTree *catalog_tree_;
FileAssetSelectParams *params_;
friend class AssetCatalogTreeViewItem;
public:
AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params);
void build_tree() override;
private:
ui::BasicTreeViewItem &build_catalog_items_recursive(ui::TreeViewItemContainer &view_parent_item,
AssetCatalogTreeItem &catalog);
void add_all_item();
void add_unassigned_item();
bool is_active_catalog(CatalogID catalog_id) const;
};
/* ---------------------------------------------------------------------- */
class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
/** The catalog tree item this tree view item represents. */
AssetCatalogTreeItem &catalog_item_;
public:
AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item)
: BasicTreeViewItem(catalog_item->get_name()), catalog_item_(*catalog_item)
{
}
void on_activate() override
{
const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
get_tree_view());
tree_view.params_->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG;
tree_view.params_->catalog_id = catalog_item_.get_catalog_id();
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
}
void build_row(uiLayout &row) override
{
ui::BasicTreeViewItem::build_row(row);
if (!is_active()) {
return;
}
PointerRNA *props;
const CatalogID catalog_id = catalog_item_.get_catalog_id();
props = UI_but_extra_operator_icon_add(
button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str());
/* Tree items without a catalog ID represent components of catalog paths that are not
* associated with an actual catalog. They exist merely by the presence of a child catalog, and
* thus cannot be deleted themselves. */
if (!BLI_uuid_is_nil(catalog_id)) {
char catalog_id_str_buffer[UUID_STRING_LEN] = "";
BLI_uuid_format(catalog_id_str_buffer, catalog_id);
props = UI_but_extra_operator_icon_add(
button(), "ASSET_OT_catalog_delete", WM_OP_INVOKE_DEFAULT, ICON_X);
RNA_string_set(props, "catalog_id", catalog_id_str_buffer);
}
}
};
/** 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
{
ui::BasicTreeViewItem::build_row(row);
if (!is_active()) {
return;
}
PointerRNA *props;
props = UI_but_extra_operator_icon_add(
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);
}
};
AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, FileAssetSelectParams *params)
: catalog_tree_(BKE_asset_library_get_catalog_tree(library)), params_(params)
{
}
void AssetCatalogTreeView::build_tree()
{
add_all_item();
if (catalog_tree_) {
catalog_tree_->foreach_root_item([this](AssetCatalogTreeItem &item) {
ui::BasicTreeViewItem &child_view_item = build_catalog_items_recursive(*this, item);
/* Open root-level items by default. */
child_view_item.set_collapsed(false);
});
}
add_unassigned_item();
}
ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive(
ui::TreeViewItemContainer &view_parent_item, AssetCatalogTreeItem &catalog)
{
ui::BasicTreeViewItem &view_item = view_parent_item.add_tree_item<AssetCatalogTreeViewItem>(
&catalog);
if (is_active_catalog(catalog.get_catalog_id())) {
view_item.set_active();
}
catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) {
build_catalog_items_recursive(view_item, child);
});
return view_item;
}
void AssetCatalogTreeView::add_all_item()
{
FileAssetSelectParams *params = params_;
ui::AbstractTreeViewItem &item = add_tree_item<AssetCatalogTreeViewAllItem>(
IFACE_("All"), ICON_HOME, [params](ui::BasicTreeViewItem & /*item*/) {
params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS;
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
});
if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS) {
item.set_active();
}
}
void AssetCatalogTreeView::add_unassigned_item()
{
FileAssetSelectParams *params = params_;
ui::AbstractTreeViewItem &item = add_tree_item<ui::BasicTreeViewItem>(
IFACE_("Unassigned"), ICON_FILE_HIDDEN, [params](ui::BasicTreeViewItem & /*item*/) {
params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG;
WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
});
if (params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG) {
item.set_active();
}
}
bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const
{
return (params_->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG) &&
(params_->catalog_id == catalog_id);
}
} // namespace blender::ed::asset_browser
/* ---------------------------------------------------------------------- */
void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
FileAssetSelectParams *params)
{
uiBlock *block = uiLayoutGetBlock(layout);
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset catalog tree view",
std::make_unique<ed::asset_browser::AssetCatalogTreeView>(asset_library, params));
ui::TreeViewBuilder builder(*block);
builder.build_tree_view(*tree_view);
}

View File

@ -31,8 +31,11 @@ extern "C" {
struct ARegion;
struct ARegionType;
struct AssetLibrary;
struct FileSelectParams;
struct FileAssetSelectParams;
struct SpaceFile;
struct uiLayout;
struct View2D;
/* file_draw.c */
@ -151,12 +154,19 @@ void file_on_reload_callback_register(struct SpaceFile *sfile,
/* file_panels.c */
void file_tool_props_region_panels_register(struct ARegionType *art);
void file_execute_region_panels_register(struct ARegionType *art);
void file_tools_region_panels_register(struct ARegionType *art);
/* file_utils.c */
void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds);
void file_path_to_ui_path(const char *path, char *r_pathi, int max_size);
/* asset_catalog_tree_view.cc */
void file_create_asset_catalog_tree_view_in_layout(struct AssetLibrary *asset_library,
struct uiLayout *layout,
struct FileAssetSelectParams *params);
#ifdef __cplusplus
}
#endif

View File

@ -47,6 +47,7 @@
#include "WM_types.h"
#include "file_intern.h"
#include "filelist.h"
#include "fsmenu.h"
#include <string.h>
@ -57,6 +58,12 @@ static bool file_panel_operator_poll(const bContext *C, PanelType *UNUSED(pt))
return (sfile && sfile->op);
}
static bool file_panel_asset_browsing_poll(const bContext *C, PanelType *UNUSED(pt))
{
SpaceFile *sfile = CTX_wm_space_file(C);
return sfile && sfile->files && ED_fileselect_is_asset_browser(sfile);
}
static void file_panel_operator_header(const bContext *C, Panel *panel)
{
SpaceFile *sfile = CTX_wm_space_file(C);
@ -222,3 +229,28 @@ void file_execute_region_panels_register(ARegionType *art)
pt->draw = file_panel_execution_buttons_draw;
BLI_addtail(&art->paneltypes, pt);
}
static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *panel)
{
SpaceFile *sfile = CTX_wm_space_file(C);
/* May be null if the library wasn't loaded yet. */
struct AssetLibrary *asset_library = filelist_asset_library(sfile->files);
FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile);
BLI_assert(params != NULL);
file_create_asset_catalog_tree_view_in_layout(asset_library, panel->layout, params);
}
void file_tools_region_panels_register(ARegionType *art)
{
PanelType *pt;
pt = MEM_callocN(sizeof(PanelType), "spacetype file asset 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->poll = file_panel_asset_browsing_poll;
pt->draw = file_panel_asset_catalog_buttons_draw;
BLI_addtail(&art->paneltypes, pt);
}

View File

@ -50,6 +50,7 @@
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BLI_uuid.h"
#ifdef WIN32
# include "BLI_winstuff.h"
@ -369,6 +370,9 @@ typedef struct FileListFilter {
char filter_glob[FILE_MAXFILE];
char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
short flags;
eFileSel_Params_AssetCatalogVisibility asset_catalog_visibility;
bUUID asset_catalog_id;
} FileListFilter;
/* FileListFilter.flags */
@ -806,6 +810,22 @@ static bool is_filtered_hidden(const char *filename,
return true;
}
/* TODO Make catalog activation work properly with the "Current File" asset library. Currently
* this will only work for external asset data. */
if (file->imported_asset_data) {
switch (filter->asset_catalog_visibility) {
case FILE_SHOW_ASSETS_WITHOUT_CATALOG:
return !BLI_uuid_is_nil(file->imported_asset_data->catalog_id);
case FILE_SHOW_ASSETS_FROM_CATALOG:
/* TODO show all assets that are in child catalogs of the selected catalog. */
return BLI_uuid_is_nil(filter->asset_catalog_id) ||
!BLI_uuid_equal(filter->asset_catalog_id, file->imported_asset_data->catalog_id);
case FILE_SHOW_ASSETS_ALL_CATALOGS:
/* All asset files should be visible. */
break;
}
}
return false;
}
@ -1037,6 +1057,33 @@ void filelist_setfilter_options(FileList *filelist,
}
}
/**
* \param catalog_id: The catalog that should be filtered by if \a catalog_visibility is
* #FILE_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)
{
bool update = false;
if (filelist->filter_data.asset_catalog_visibility != catalog_visibility) {
filelist->filter_data.asset_catalog_visibility = catalog_visibility;
update = true;
}
if (filelist->filter_data.asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG &&
catalog_id && !BLI_uuid_equal(filelist->filter_data.asset_catalog_id, *catalog_id)) {
filelist->filter_data.asset_catalog_id = *catalog_id;
update = true;
}
if (update) {
filelist_filter_clear(filelist);
}
}
/**
* Checks two libraries for equality.
* \return True if the libraries match.
@ -1809,6 +1856,11 @@ void filelist_free(struct FileList *filelist)
filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
}
AssetLibrary *filelist_asset_library(FileList *filelist)
{
return filelist->asset_library;
}
void filelist_freelib(struct FileList *filelist)
{
if (filelist->libfiledata) {

View File

@ -31,6 +31,7 @@ struct AssetLibraryReference;
struct BlendHandle;
struct FileList;
struct FileSelection;
struct bUUID;
struct wmWindowManager;
struct FileDirEntry;
@ -71,6 +72,10 @@ void filelist_setfilter_options(struct FileList *filelist,
const bool filter_assets_only,
const char *filter_glob,
const char *filter_search);
void filelist_set_asset_catalog_filter_options(
struct FileList *filelist,
eFileSel_Params_AssetCatalogVisibility catalog_visibility,
const struct bUUID *catalog_id);
void filelist_filter(struct FileList *filelist);
void filelist_setlibrary(struct FileList *filelist,
const struct AssetLibraryReference *asset_library_ref);
@ -144,6 +149,8 @@ void filelist_entry_parent_select_set(struct FileList *filelist,
void filelist_setrecursion(struct FileList *filelist, const int recursion_level);
struct AssetLibrary *filelist_asset_library(struct FileList *filelist);
struct BlendHandle *filelist_lib(struct FileList *filelist);
bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group);
void filelist_freelib(struct FileList *filelist);

View File

@ -126,13 +126,10 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
FileSelectParams *base_params = &asset_params->base_params;
base_params->file[0] = '\0';
base_params->filter_glob[0] = '\0';
/* TODO: this way of using filters to form categories is notably slower than specifying a
* "group" to read. That's because all types are read and filtering is applied afterwards. Would
* be nice if we could lazy-read individual groups. */
base_params->flag |= U_default.file_space_data.flag | FILE_ASSETS_ONLY | FILE_FILTER;
base_params->flag &= ~FILE_DIRSEL_ONLY;
base_params->filter |= FILE_TYPE_BLENDERLIB;
base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR;
base_params->filter_id = FILTER_ID_ALL;
base_params->display = FILE_IMGDISPLAY;
base_params->sort = FILE_SORT_ALPHA;
/* Asset libraries include all sub-directories, so enable maximal recursion. */
@ -462,6 +459,15 @@ bool ED_fileselect_is_asset_browser(const SpaceFile *sfile)
return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
}
struct AssetLibrary *ED_fileselect_active_asset_library_get(const SpaceFile *sfile)
{
if (!ED_fileselect_is_asset_browser(sfile) || !sfile->files) {
return NULL;
}
return filelist_asset_library(sfile->files);
}
struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile)
{
if (!ED_fileselect_is_asset_browser(sfile)) {

View File

@ -335,9 +335,9 @@ static void file_refresh(const bContext *C, ScrArea *area)
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
if (!U.experimental.use_extended_asset_browser && ED_fileselect_is_asset_browser(sfile)) {
if (ED_fileselect_is_asset_browser(sfile)) {
/* Only poses supported as non-experimental right now. */
params->filter_id = FILTER_ID_AC;
params->filter_id = U.experimental.use_extended_asset_browser ? FILTER_ID_ALL : FILTER_ID_AC;
}
filelist_settype(sfile->files, params->type);
@ -355,6 +355,10 @@ static void file_refresh(const bContext *C, ScrArea *area)
(params->flag & FILE_ASSETS_ONLY) != 0,
params->filter_glob,
params->filter_search);
if (asset_params) {
filelist_set_asset_catalog_filter_options(
sfile->files, asset_params->asset_catalog_visibility, &asset_params->catalog_id);
}
/* Update the active indices of bookmarks & co. */
sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir);
@ -1050,6 +1054,7 @@ void ED_spacetype_file(void)
art->init = file_tools_region_init;
art->draw = file_tools_region_draw;
BLI_addhead(&st->regiontypes, art);
file_tools_region_panels_register(art);
/* regions: tool properties */
art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region");

View File

@ -806,9 +806,14 @@ typedef struct FileAssetSelectParams {
FileSelectParams base_params;
AssetLibraryReference asset_library_ref;
short asset_catalog_visibility; /* eFileSel_Params_AssetCatalogVisibility */
char _pad[6];
/** If #asset_catalog_visibility is #FILE_SHOW_ASSETS_FROM_CATALOG, this sets the ID of the
* catalog to show. */
bUUID catalog_id;
short import_type; /* eFileAssetImportType */
char _pad[6];
char _pad2[6];
} FileAssetSelectParams;
typedef enum eFileAssetImportType {
@ -1009,8 +1014,16 @@ typedef enum eFileSel_Params_Flag {
FILE_HIDE_TOOL_PROPS = (1 << 12),
FILE_CHECK_EXISTING = (1 << 13),
FILE_ASSETS_ONLY = (1 << 14),
/** Enables filtering by asset catalog. */
FILE_FILTER_ASSET_CATALOG = (1 << 15),
} eFileSel_Params_Flag;
typedef enum eFileSel_Params_AssetCatalogVisibility {
FILE_SHOW_ASSETS_ALL_CATALOGS,
FILE_SHOW_ASSETS_FROM_CATALOG,
FILE_SHOW_ASSETS_WITHOUT_CATALOG,
} eFileSel_Params_AssetCatalogVisibility;
/* sfile->params->rename_flag */
/* NOTE: short flag. Defined as bitflags, but currently only used as exclusive status markers... */
typedef enum eFileSel_Params_RenameFlag {

View File

@ -2614,18 +2614,6 @@ static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int val
params->asset_library_ref = ED_asset_library_reference_from_enum_value(value);
}
static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value)
{
FileSelectParams *params = ptr->data;
params->filter_id = value;
}
static uint64_t rna_FileAssetSelectParams_asset_category_get(PointerRNA *ptr)
{
FileSelectParams *params = ptr->data;
return params->filter_id;
}
static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr)
{
const FileDirEntry *entry = ptr->data;
@ -6595,47 +6583,6 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
/* XXX copied from rna_enum_id_type_filter_items. */
static const EnumPropertyItem asset_category_items[] = {
{FILTER_ID_SCE, "SCENES", ICON_SCENE_DATA, "Scenes", "Show scenes"},
{FILTER_ID_AC, "ANIMATIONS", ICON_ANIM_DATA, "Animations", "Show animation data"},
{FILTER_ID_OB | FILTER_ID_GR,
"OBJECTS_AND_COLLECTIONS",
ICON_GROUP,
"Objects & Collections",
"Show objects and collections"},
{FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME
/* XXX avoid warning */
// | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO
,
"GEOMETRY",
ICON_MESH_DATA,
"Geometry",
"Show meshes, curves, lattice, armatures and metaballs data"},
{FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
"SHADING",
ICON_MATERIAL_DATA,
"Shading",
"Show materials, nodetrees, textures and Freestyle's linestyles"},
{FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO,
"IMAGES_AND_SOUNDS",
ICON_IMAGE_DATA,
"Images & Sounds",
"Show images, movie clips, sounds and masks"},
{FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO,
"ENVIRONMENTS",
ICON_WORLD_DATA,
"Environment",
"Show worlds, lights, cameras and speakers"},
{FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT |
FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS,
"MISC",
ICON_GREASEPENCIL,
"Miscellaneous",
"Show other data types"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem asset_import_type_items[] = {
{FILE_ASSET_IMPORT_LINK, "LINK", 0, "Link", "Import the assets as linked data-block"},
{FILE_ASSET_IMPORT_APPEND,
@ -6664,15 +6611,6 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Asset Library", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "asset_category", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, asset_category_items);
RNA_def_property_enum_funcs(prop,
"rna_FileAssetSelectParams_asset_category_get",
"rna_FileAssetSelectParams_asset_category_set",
NULL);
RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, asset_import_type_items);
RNA_def_property_ui_text(prop, "Import Type", "Determine how the asset will be imported");