Support displaying project asset libraries in asset browser

The hint in the asset browser for when the selected library path doesn't
exists is updated to mention the project settings too, and there is a
button to open the project settings in the asset library section.
This commit is contained in:
Julian Eisel 2022-10-13 21:31:20 +02:00
parent 97f667058c
commit 6177df9dce
11 changed files with 216 additions and 89 deletions

View File

@ -34,6 +34,8 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value);
const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
bool include_local_library);
struct CustomAssetLibraryDefinition *ED_asset_library_find_custom_library_from_reference(
const AssetLibraryReference *library_ref);
#ifdef __cplusplus
}
#endif

View File

@ -4,8 +4,15 @@
* \ingroup edasset
*/
#include "BKE_asset_library_custom.h"
#include "BKE_blender_project.h"
#include "BKE_context.h"
#include "DNA_userdef_types.h"
#include "BLI_hash.hh"
#include "ED_asset_library.h"
#include "asset_library_reference.hh"
namespace blender::ed::asset {
@ -18,14 +25,15 @@ AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryRef
bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b)
{
return (a.type == b.type) &&
((a.type == ASSET_LIBRARY_CUSTOM) ? (a.custom_library_index == b.custom_library_index) :
true);
(ELEM(a.type, ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES, ASSET_LIBRARY_CUSTOM_FROM_PROJECT) ?
(a.custom_library_index == b.custom_library_index) :
true);
}
uint64_t AssetLibraryReferenceWrapper::hash() const
{
uint64_t hash1 = DefaultHash<decltype(type)>{}(type);
if (type != ASSET_LIBRARY_CUSTOM) {
if (!ELEM(type, ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES, ASSET_LIBRARY_CUSTOM_FROM_PROJECT)) {
return hash1;
}
@ -34,3 +42,24 @@ uint64_t AssetLibraryReferenceWrapper::hash() const
}
} // namespace blender::ed::asset
CustomAssetLibraryDefinition *ED_asset_library_find_custom_library_from_reference(
const AssetLibraryReference *library_ref)
{
switch (library_ref->type) {
case ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES:
return BKE_asset_library_custom_find_from_index(&U.asset_libraries,
library_ref->custom_library_index);
case ASSET_LIBRARY_CUSTOM_FROM_PROJECT: {
BlenderProject *project = CTX_wm_project();
if (project) {
return BKE_asset_library_custom_find_from_index(
BKE_project_custom_asset_libraries_get(project), library_ref->custom_library_index);
}
}
case ASSET_LIBRARY_LOCAL:
return nullptr;
}
return nullptr;
}

View File

@ -12,6 +12,8 @@
#include "BLI_listbase.h"
#include "BKE_asset_library_custom.h"
#include "BKE_blender_project.h"
#include "BKE_context.h"
#include "DNA_userdef_types.h"
@ -23,17 +25,15 @@
int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library)
{
/* Simple case: Predefined repository, just set the value. */
if (library->type < ASSET_LIBRARY_CUSTOM) {
/* Simple case: Predefined library, just set the value. */
if (library->type < ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES) {
return library->type;
}
/* Note that the path isn't checked for validity here. If an invalid library path is used, the
* Asset Browser can give a nice hint on what's wrong. */
const CustomAssetLibraryDefinition *user_library = BKE_asset_library_custom_find_from_index(
&U.asset_libraries, library->custom_library_index);
if (user_library) {
return ASSET_LIBRARY_CUSTOM + library->custom_library_index;
if (ED_asset_library_find_custom_library_from_reference(library)) {
return library->type + library->custom_library_index;
}
return ASSET_LIBRARY_LOCAL;
@ -44,32 +44,63 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value)
AssetLibraryReference library;
/* Simple case: Predefined repository, just set the value. */
if (value < ASSET_LIBRARY_CUSTOM) {
if (value < ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES) {
library.type = value;
library.custom_library_index = -1;
BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL));
return library;
}
const CustomAssetLibraryDefinition *user_library = BKE_asset_library_custom_find_from_index(
&U.asset_libraries, value - ASSET_LIBRARY_CUSTOM);
const eAssetLibraryType type = (value < ASSET_LIBRARY_CUSTOM_FROM_PROJECT) ?
ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES :
ASSET_LIBRARY_CUSTOM_FROM_PROJECT;
/* Note that there is no check if the path exists here. If an invalid library path is used, the
* Asset Browser can give a nice hint on what's wrong. */
if (!user_library) {
library.type = ASSET_LIBRARY_LOCAL;
library.custom_library_index = -1;
}
else {
const bool is_valid = (user_library->name[0] && user_library->path[0]);
if (is_valid) {
library.custom_library_index = value - ASSET_LIBRARY_CUSTOM;
library.type = ASSET_LIBRARY_CUSTOM;
const CustomAssetLibraryDefinition *custom_library = nullptr;
library.type = type;
library.custom_library_index = value - type;
{
custom_library = ED_asset_library_find_custom_library_from_reference(&library);
/* Note that there is no check if the path exists here. If an invalid library path is used, the
* Asset Browser can give a nice hint on what's wrong. */
const bool is_valid = custom_library && (custom_library->name[0] && custom_library->path[0]);
if (!is_valid) {
library.custom_library_index = -1;
}
}
return library;
}
static void add_custom_asset_library_enum_items(
const ListBase * /*CustomAssetLibraryDefinition*/ libraries,
const eAssetLibraryType library_type,
EnumPropertyItem **items,
int *totitem)
{
int i;
LISTBASE_FOREACH_INDEX (CustomAssetLibraryDefinition *, custom_library, libraries, i) {
/* Note that the path itself isn't checked for validity here. If an invalid library path is
* used, the Asset Browser can give a nice hint on what's wrong. */
const bool is_valid = (custom_library->name[0] && custom_library->path[0]);
if (!is_valid) {
continue;
}
AssetLibraryReference library_reference;
library_reference.type = library_type;
library_reference.custom_library_index = i;
const int enum_value = ED_asset_library_reference_to_enum_value(&library_reference);
/* Use library path as description, it's a nice hint for users. */
EnumPropertyItem tmp = {
enum_value, custom_library->name, ICON_NONE, custom_library->name, custom_library->path};
RNA_enum_item_add(items, totitem, &tmp);
}
}
const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
const bool include_local_library)
{
@ -92,29 +123,21 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
RNA_enum_items_add(&item, &totitem, predefined_items);
}
/* Add separator if needed. */
if (!BLI_listbase_is_empty(&U.asset_libraries)) {
BlenderProject *project = CTX_wm_project();
if (project && !BLI_listbase_is_empty(BKE_project_custom_asset_libraries_get(project))) {
RNA_enum_item_add_separator(&item, &totitem);
add_custom_asset_library_enum_items(BKE_project_custom_asset_libraries_get(project),
ASSET_LIBRARY_CUSTOM_FROM_PROJECT,
&item,
&totitem);
}
int i;
LISTBASE_FOREACH_INDEX (CustomAssetLibraryDefinition *, user_library, &U.asset_libraries, i) {
/* Note that the path itself isn't checked for validity here. If an invalid library path is
* used, the Asset Browser can give a nice hint on what's wrong. */
const bool is_valid = (user_library->name[0] && user_library->path[0]);
if (!is_valid) {
continue;
}
if (!BLI_listbase_is_empty(&U.asset_libraries)) {
RNA_enum_item_add_separator(&item, &totitem);
AssetLibraryReference library_reference;
library_reference.type = ASSET_LIBRARY_CUSTOM;
library_reference.custom_library_index = i;
const int enum_value = ED_asset_library_reference_to_enum_value(&library_reference);
/* Use library path as description, it's a nice hint for users. */
EnumPropertyItem tmp = {
enum_value, user_library->name, ICON_NONE, user_library->name, user_library->path};
RNA_enum_item_add(&item, &totitem, &tmp);
add_custom_asset_library_enum_items(
&U.asset_libraries, ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES, &item, &totitem);
}
RNA_enum_item_end(&item, &totitem);

View File

@ -13,6 +13,7 @@
#include <string>
#include "BKE_asset_library_custom.h"
#include "BKE_blender_project.h"
#include "BKE_context.h"
#include "BLI_map.hh"
@ -31,6 +32,7 @@
#include "ED_asset_handle.h"
#include "ED_asset_indexer.h"
#include "ED_asset_library.h"
#include "ED_asset_list.h"
#include "ED_asset_list.hh"
#include "asset_library_reference.hh"
@ -130,15 +132,8 @@ void AssetList::setup()
{
FileList *files = filelist_;
CustomAssetLibraryDefinition *user_library = nullptr;
/* Ensure valid repository, or fall-back to local one. */
if (library_ref_.type == ASSET_LIBRARY_CUSTOM) {
BLI_assert(library_ref_.custom_library_index >= 0);
user_library = BKE_asset_library_custom_find_from_index(&U.asset_libraries,
library_ref_.custom_library_index);
}
CustomAssetLibraryDefinition *custom_library =
ED_asset_library_find_custom_library_from_reference(&library_ref_);
/* Relevant bits from file_refresh(). */
/* TODO pass options properly. */
@ -160,8 +155,18 @@ void AssetList::setup()
filelist_setindexer(files, use_asset_indexer ? &file_indexer_asset : &file_indexer_noop);
char path[FILE_MAXDIR] = "";
if (user_library) {
BLI_strncpy(path, user_library->path, sizeof(path));
if (custom_library) {
/* Project asset libraries typically use relative paths (relative to project root directory).
*/
if ((library_ref_.type == ASSET_LIBRARY_CUSTOM_FROM_PROJECT) &&
BLI_path_is_rel(custom_library->path)) {
BlenderProject *project = CTX_wm_project();
const char *project_root_path = BKE_project_root_path_get(project);
BLI_path_join(path, sizeof(path), project_root_path, custom_library->path, NULL);
}
else {
BLI_strncpy(path, custom_library->path, sizeof(path));
}
filelist_setdir(files, path);
}
else {
@ -376,7 +381,8 @@ std::optional<eFileSelectType> AssetListStorage::asset_library_reference_to_file
const AssetLibraryReference &library_reference)
{
switch (library_reference.type) {
case ASSET_LIBRARY_CUSTOM:
case ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES:
case ASSET_LIBRARY_CUSTOM_FROM_PROJECT:
return FILE_ASSET_LIBRARY;
case ASSET_LIBRARY_LOCAL:
return FILE_MAIN_ASSET;

View File

@ -5138,11 +5138,25 @@ bool ED_project_settings_window_show(bContext *C, ReportList *reports)
static int project_settings_show_exec(bContext *C, wmOperator *op)
{
if (ED_project_settings_window_show(C, op->reports)) {
return OPERATOR_FINISHED;
if (!ED_project_settings_window_show(C, op->reports)) {
return OPERATOR_CANCELLED;
}
return OPERATOR_CANCELLED;
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "section");
SpaceProjectSettings *space_project = CTX_wm_space_project_settings(C);
if (space_project && prop && RNA_property_is_set(op->ptr, prop)) {
/* Set active section via RNA, so it can fail properly. */
bScreen *screen = CTX_wm_screen(C);
PointerRNA space_ptr;
RNA_pointer_create(&screen->id, &RNA_SpaceProjectSettings, space_project, &space_ptr);
PropertyRNA *active_section_prop = RNA_struct_find_property(&space_ptr, "active_section");
RNA_property_enum_set(&space_ptr, active_section_prop, RNA_property_enum_get(op->ptr, prop));
RNA_property_update(C, &space_ptr, active_section_prop);
}
return OPERATOR_FINISHED;
}
static void SCREEN_OT_project_settings_show(struct wmOperatorType *ot)
@ -5155,6 +5169,15 @@ static void SCREEN_OT_project_settings_show(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = project_settings_show_exec;
ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */
PropertyRNA *prop;
prop = RNA_def_enum(ot->srna,
"section",
rna_enum_project_settings_section_items,
0,
"",
"Section to activate in the project settings");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
/** \} */

View File

@ -1118,13 +1118,17 @@ static void file_draw_invalid_library_hint(const bContext *C,
{
UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO);
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
const char *suggestion = TIP_(
"Asset Libraries are local directories that can contain .blend files with assets inside.\n"
"Manage Asset Libraries from the File Paths section in Preferences");
"Manage Asset Libraries from the File Paths section in the Preferences or in the Project "
"Settings.");
file_draw_string_multiline(
sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, &sy);
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
const short but_offset_y = line_height + UI_UNIT_Y * 1.2f;
const short but_width = UI_UNIT_X * 8;
uiBut *but = uiDefIconTextButO(block,
UI_BTYPE_BUT,
"SCREEN_OT_userpref_show",
@ -1132,13 +1136,26 @@ static void file_draw_invalid_library_hint(const bContext *C,
ICON_PREFERENCES,
NULL,
sx + UI_UNIT_X,
sy - line_height - UI_UNIT_Y * 1.2f,
UI_UNIT_X * 8,
sy - but_offset_y,
but_width,
UI_UNIT_Y,
NULL);
PointerRNA *but_opptr = UI_but_operator_ptr_get(but);
RNA_enum_set(but_opptr, "section", USER_SECTION_FILE_PATHS);
but = uiDefButO(block,
UI_BTYPE_BUT,
"SCREEN_OT_project_settings_show",
WM_OP_INVOKE_DEFAULT,
NULL,
sx + UI_UNIT_X + but_width + UI_UNIT_X,
sy - but_offset_y,
but_width,
UI_UNIT_Y,
NULL);
but_opptr = UI_but_operator_ptr_get(but);
RNA_enum_set(but_opptr, "section", PROJECT_SETTINGS_SECTION_ASSET_LIBRARIES);
UI_block_end(C, block);
UI_block_draw(C, block);
}

View File

@ -56,6 +56,7 @@
#include "DNA_asset_types.h"
#include "DNA_space_types.h"
#include "ED_asset_library.h"
#include "ED_datafiles.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
@ -1038,12 +1039,18 @@ static bool filelist_compare_asset_libraries(const AssetLibraryReference *librar
if (library_a->type != library_b->type) {
return false;
}
if (library_a->type == ASSET_LIBRARY_CUSTOM) {
const bool is_custom_library = ELEM(
library_a->type, ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES, ASSET_LIBRARY_CUSTOM_FROM_PROJECT);
if (is_custom_library) {
if (library_a->custom_library_index != library_b->custom_library_index) {
return false;
}
/* Don't only check the index, also check that it's valid. */
CustomAssetLibraryDefinition *library_ptr_a = BKE_asset_library_custom_find_from_index(
&U.asset_libraries, library_a->custom_library_index);
return (library_ptr_a != nullptr) &&
(library_a->custom_library_index == library_b->custom_library_index);
if (!ED_asset_library_find_custom_library_from_reference(library_a)) {
return false;
}
}
return true;

View File

@ -40,12 +40,14 @@
#include "BKE_appdir.h"
#include "BKE_asset_library_custom.h"
#include "BKE_blender_project.h"
#include "BKE_context.h"
#include "BKE_idtype.h"
#include "BKE_main.h"
#include "BLF_api.h"
#include "ED_asset_library.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
@ -409,27 +411,42 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
{
AssetLibraryReference *library = &asset_params->asset_library_ref;
FileSelectParams *base_params = &asset_params->base_params;
CustomAssetLibraryDefinition *user_library = NULL;
CustomAssetLibraryDefinition *custom_library =
ED_asset_library_find_custom_library_from_reference(library);
/* Ensure valid repository, or fall-back to local one. */
if (library->type == ASSET_LIBRARY_CUSTOM) {
BLI_assert(library->custom_library_index >= 0);
user_library = BKE_asset_library_custom_find_from_index(&U.asset_libraries,
library->custom_library_index);
if (!user_library) {
library->type = ASSET_LIBRARY_LOCAL;
}
/* Ensure valid asset library, or fall-back to local one. */
if (!custom_library) {
library->type = ASSET_LIBRARY_LOCAL;
}
switch (library->type) {
case ASSET_LIBRARY_LOCAL:
base_params->dir[0] = '\0';
break;
case ASSET_LIBRARY_CUSTOM:
BLI_assert(user_library);
BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
case ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES:
BLI_assert(custom_library);
BLI_strncpy(base_params->dir, custom_library->path, sizeof(base_params->dir));
break;
/* Project asset libraries typically use relative paths (relative to project root directory).
*/
case ASSET_LIBRARY_CUSTOM_FROM_PROJECT: {
BlenderProject *project = CTX_wm_project();
BLI_assert(custom_library);
BLI_assert(project);
if (BLI_path_is_rel(custom_library->path)) {
const char *project_root_path = BKE_project_root_path_get(project);
BLI_path_join(base_params->dir,
sizeof(base_params->dir),
project_root_path,
custom_library->path,
NULL);
}
else {
BLI_strncpy(base_params->dir, custom_library->path, sizeof(base_params->dir));
}
break;
}
}
base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET :
FILE_ASSET_LIBRARY;

View File

@ -127,7 +127,7 @@ static void gather_search_items_for_all_assets(const bContext &C,
const CustomAssetLibraryDefinition *, asset_library, &U.asset_libraries, i) {
AssetLibraryReference library_ref{};
library_ref.custom_library_index = i;
library_ref.type = ASSET_LIBRARY_CUSTOM;
library_ref.type = ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES;
/* Skip local assets to avoid duplicates when the asset is part of the local file library. */
gather_search_items_for_asset_library(C, node_tree, library_ref, r_added_assets, search_items);
}

View File

@ -247,7 +247,7 @@ static void gather_search_link_ops_for_all_assets(const bContext &C,
const CustomAssetLibraryDefinition *, asset_library, &U.asset_libraries, i) {
AssetLibraryReference library_ref{};
library_ref.custom_library_index = i;
library_ref.type = ASSET_LIBRARY_CUSTOM;
library_ref.type = ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES;
/* Skip local assets to avoid duplicates when the asset is part of the local file library. */
gather_search_link_ops_for_asset_library(
C, node_tree, socket, library_ref, true, search_link_ops);

View File

@ -92,23 +92,26 @@ typedef enum eAssetLibraryType {
/** Display assets from custom asset libraries, as defined in the preferences
* (#CustomAssetLibraryDefinition). In RNA, we add the index of the custom library to this to
* identify it by index. So keep this last! */
ASSET_LIBRARY_CUSTOM = 100,
* identify it by index. */
ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES = 100,
/** Same as #ASSET_LIBRARY_CUSTOM_FROM_PREFERENCES, but the library is defined for a specific
project only. */
ASSET_LIBRARY_CUSTOM_FROM_PROJECT = 500,
} eAssetLibraryType;
/**
* Information to identify an asset library. May be either one of the predefined types (current
* 'Main', builtin library, project library), or a custom type as defined in the Preferences.
* Information to identify an asset library. May be either one of the predefined types ("Current
* File" builtin library), or a custom type as defined in the Preferences/Project.
*
* If the type is set to #ASSET_LIBRARY_CUSTOM, `custom_library_index` must be set to identify the
* custom library. Otherwise it is not used.
*/
typedef struct AssetLibraryReference {
/* If set to #ASSET_LIBRARY_CUSTOM_XXX, `custom_library_index` must be set to identify
* the custom library. Otherwise it is not used. */
short type; /* eAssetLibraryType */
char _pad1[2];
/**
* If showing a custom asset library (#ASSET_LIBRARY_CUSTOM), this is the index of the
* #CustomAssetLibraryDefinition within #UserDef.asset_libraries.
* If showing a custom asset library (#ASSET_LIBRARY_CUSTOM_XXX), this is the index of the
* #CustomAssetLibraryDefinition within its owner (preferences or project settings).
* Should be ignored otherwise (but better set to -1 then, for sanity and debugging).
*/
int custom_library_index;