Merge remote-tracking branch 'origin/blender-v3.0-release'

This commit is contained in:
Sybren A. Stüvel 2021-11-23 12:39:26 +01:00
commit f392ce50c4
12 changed files with 353 additions and 17 deletions

View file

@ -97,6 +97,15 @@ class AssetCatalogService {
* or the save was successful. */
bool write_to_disk(const CatalogFilePath &blend_file_path);
/**
* Ensure that the next call to #on_blend_save_post() will choose a new location for the CDF
* suitable for the location of the blend file (regardless of where the current catalogs come
* from), and that catalogs will be merged with already-existing ones in that location.
*
* Use this for a "Save as..." that has to write the catalogs to the new blend file location,
* instead of updating the previously read CDF. */
void prepare_to_merge_on_write();
/**
* Merge on-disk changes into the in-memory asset catalogs.
* This should be called before writing the asset catalogs to disk.
@ -238,6 +247,11 @@ class AssetCatalogService {
*/
void create_missing_catalogs();
/**
* For every catalog, mark it as "dirty".
*/
void tag_all_catalogs_as_unsaved_changes();
/* For access by subclasses, as those will not be marked as friend by #AssetCatalogCollection. */
AssetCatalogDefinitionFile *get_catalog_definition_file();
OwningAssetCatalogMap &get_catalogs();

View file

@ -98,6 +98,14 @@ bool AssetCatalogService::has_unsaved_changes() const
return catalog_collection_->has_unsaved_changes_;
}
void AssetCatalogService::tag_all_catalogs_as_unsaved_changes()
{
for (auto &catalog : catalog_collection_->catalogs_.values()) {
catalog->flags.has_unsaved_changes = true;
}
catalog_collection_->has_unsaved_changes_ = true;
}
bool AssetCatalogService::is_empty() const
{
BLI_assert(catalog_collection_);
@ -486,6 +494,24 @@ bool AssetCatalogService::write_to_disk_ex(const CatalogFilePath &blend_file_pat
return catalog_collection_->catalog_definition_file_->write_to_disk();
}
void AssetCatalogService::prepare_to_merge_on_write()
{
/* TODO(Sybren): expand to support multiple CDFs. */
if (!catalog_collection_->catalog_definition_file_) {
/* There is no CDF connected, so it's a no-op. */
return;
}
/* Remove any association with the CDF, so that a new location will be chosen
* when the blend file is saved. */
catalog_collection_->catalog_definition_file_.reset();
/* Mark all in-memory catalogs as "dirty", to force them to be kept around on
* the next "load-merge-write" cycle. */
tag_all_catalogs_as_unsaved_changes();
}
CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing(
const CatalogFilePath &blend_file_path)
{

View file

@ -24,6 +24,10 @@
* \brief external writefile function prototypes.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct BlendThumbnail;
struct Main;
struct MemFile;
@ -72,3 +76,7 @@ extern bool BLO_write_file_mem(struct Main *mainvar,
int write_flags);
/** \} */
#ifdef __cplusplus
}
#endif

View file

@ -28,7 +28,8 @@ extern "C" {
int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library);
AssetLibraryReference ED_asset_library_reference_from_enum_value(int value);
const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(void);
const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
bool include_local_library);
#ifdef __cplusplus
}

View file

@ -98,25 +98,30 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value)
*
* Since this is meant for UI display, skips non-displayable libraries, that is, libraries with an
* empty name or path.
*
* \param include_local_library whether to include the "Current File" library or not.
*/
const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf()
const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
const bool include_local_library)
{
const EnumPropertyItem predefined_items[] = {
/* For the future. */
// {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
{ASSET_LIBRARY_LOCAL,
"LOCAL",
ICON_BLENDER,
"Current File",
"Show the assets currently available in this Blender session"},
{0, nullptr, 0, nullptr, nullptr},
};
EnumPropertyItem *item = nullptr;
int totitem = 0;
/* Add predefined items. */
RNA_enum_items_add(&item, &totitem, predefined_items);
if (include_local_library) {
const EnumPropertyItem predefined_items[] = {
/* For the future. */
// {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
{ASSET_LIBRARY_LOCAL,
"LOCAL",
ICON_BLENDER,
"Current File",
"Show the assets currently available in this Blender session"},
{0, nullptr, 0, nullptr, nullptr},
};
/* Add predefined items. */
RNA_enum_items_add(&item, &totitem, predefined_items);
}
/* Add separator if needed. */
if (!BLI_listbase_is_empty(&U.asset_libraries)) {

View file

@ -19,12 +19,19 @@
*/
#include "BKE_asset_library.hh"
#include "BKE_bpath.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_preferences.h"
#include "BKE_report.h"
#include "BLI_fileops.h"
#include "BLI_fnmatch.h"
#include "BLI_path_util.h"
#include "ED_asset.h"
#include "ED_util.h"
/* XXX needs access to the file list, should all be done via the asset system in future. */
#include "ED_fileselect.h"
@ -33,6 +40,10 @@
#include "WM_api.h"
#include "DNA_space_types.h"
#include "BLO_writefile.h"
using namespace blender;
/* -------------------------------------------------------------------- */
@ -643,6 +654,240 @@ static void ASSET_OT_catalogs_save(struct wmOperatorType *ot)
/* -------------------------------------------------------------------- */
static bool could_be_asset_bundle(const Main *bmain);
static const bUserAssetLibrary *selected_asset_library(struct wmOperator *op);
static bool is_contained_in_selected_asset_library(struct wmOperator *op, const char *filepath);
static bool set_filepath_for_asset_lib(const Main *bmain, struct wmOperator *op);
static bool has_external_files(Main *bmain, struct 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 (!ED_fileselect_is_local_asset_library(sfile)) {
return false;
}
const Main *bmain = CTX_data_main(C);
if (!could_be_asset_bundle(bmain)) {
return false;
}
/* Check whether this file is already located inside any asset library. */
const struct bUserAssetLibrary *asset_lib = BKE_preferences_asset_library_containing_path(
&U, bmain->name);
if (asset_lib) {
return false;
}
return true;
}
static int asset_bundle_install_invoke(struct bContext *C,
struct wmOperator *op,
const struct wmEvent * /*event*/)
{
Main *bmain = CTX_data_main(C);
if (has_external_files(bmain, op->reports)) {
return OPERATOR_CANCELLED;
}
WM_event_add_fileselect(C, op);
/* Make the "Save As" dialog box default to "${ASSET_LIB_ROOT}/${CURRENT_FILE}.blend". */
if (!set_filepath_for_asset_lib(bmain, op)) {
return OPERATOR_CANCELLED;
}
return OPERATOR_RUNNING_MODAL;
}
static int asset_bundle_install_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
if (has_external_files(bmain, op->reports)) {
return OPERATOR_CANCELLED;
}
/* Check file path, copied from #wm_file_write(). */
char filepath[PATH_MAX];
RNA_string_get(op->ptr, "filepath", filepath);
const size_t len = strlen(filepath);
if (len == 0) {
BKE_report(op->reports, RPT_ERROR, "Path is empty, cannot save");
return OPERATOR_CANCELLED;
}
if (len >= FILE_MAX) {
BKE_report(op->reports, RPT_ERROR, "Path too long, cannot save");
return OPERATOR_CANCELLED;
}
/* Check that the destination is actually contained in the selected asset library. */
if (!is_contained_in_selected_asset_library(op, filepath)) {
BKE_reportf(op->reports, RPT_ERROR, "Selected path is outside of the selected asset library");
return OPERATOR_CANCELLED;
}
WM_cursor_wait(true);
bke::AssetCatalogService *cat_service = get_catalog_service(C);
/* Store undo step, such that on a failed save the 'prepare_to_merge_on_write' call can be
* un-done. */
cat_service->undo_push();
cat_service->prepare_to_merge_on_write();
const int operator_result = WM_operator_name_call(
C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, op->ptr);
WM_cursor_wait(false);
if (operator_result != OPERATOR_FINISHED) {
cat_service->undo();
return operator_result;
}
const bUserAssetLibrary *lib = selected_asset_library(op);
BLI_assert_msg(lib, "If the asset library is not known, how did we get here?");
BKE_reportf(op->reports,
RPT_INFO,
"Saved \"%s\" to asset library \"%s\"",
BLI_path_basename(bmain->name),
lib->name);
return OPERATOR_FINISHED;
}
static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf(false);
if (!items) {
*r_free = false;
}
*r_free = true;
return items;
}
static void ASSET_OT_bundle_install(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy to Asset Library";
ot->description =
"Copy the current .blend file into an Asset Library. Only works on standalone .blend files "
"(i.e. when no other files are referenced)";
ot->idname = "ASSET_OT_bundle_install";
/* api callbacks */
ot->exec = asset_bundle_install_exec;
ot->invoke = asset_bundle_install_invoke;
ot->poll = asset_bundle_install_poll;
ot->prop = RNA_def_property(ot->srna, "asset_library_ref", PROP_ENUM, PROP_NONE);
RNA_def_property_flag(ot->prop, PROP_HIDDEN);
RNA_def_enum_funcs(ot->prop, rna_asset_library_reference_itemf);
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_BLENDER,
FILE_BLENDER,
FILE_SAVE,
WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
}
/* Cheap check to see if this is an "asset bundle" just by checking main file name.
* A proper check will be done in the exec function, to ensure that no external files will be
* referenced. */
static bool could_be_asset_bundle(const Main *bmain)
{
return fnmatch("*_bundle.blend", bmain->name, FNM_CASEFOLD) == 0;
}
static const bUserAssetLibrary *selected_asset_library(struct wmOperator *op)
{
const int enum_value = RNA_enum_get(op->ptr, "asset_library_ref");
const AssetLibraryReference lib_ref = ED_asset_library_reference_from_enum_value(enum_value);
const bUserAssetLibrary *lib = BKE_preferences_asset_library_find_from_index(
&U, lib_ref.custom_library_index);
return lib;
}
static bool is_contained_in_selected_asset_library(struct wmOperator *op, const char *filepath)
{
const bUserAssetLibrary *lib = selected_asset_library(op);
if (!lib) {
return false;
}
return BLI_path_contains(lib->path, filepath);
}
/**
* Set the "filepath" RNA property based on selected "asset_library_ref".
* \return true if ok, false if error.
*/
static bool set_filepath_for_asset_lib(const Main *bmain, struct wmOperator *op)
{
/* Find the directory path of the selected asset library. */
const bUserAssetLibrary *lib = selected_asset_library(op);
if (lib == nullptr) {
return false;
}
/* Concatenate the filename of the current blend file. */
const char *blend_filename = BLI_path_basename(bmain->name);
if (blend_filename == NULL || blend_filename[0] == '\0') {
return false;
}
char file_path[PATH_MAX];
BLI_join_dirfile(file_path, sizeof(file_path), lib->path, blend_filename);
RNA_string_set(op->ptr, "filepath", file_path);
return true;
}
struct FileCheckCallbackInfo {
struct ReportList *reports;
bool external_file_found;
};
static bool external_file_check_callback(void *callback_info_ptr,
char * /*path_dst*/,
const char *path_src)
{
FileCheckCallbackInfo *callback_info = static_cast<FileCheckCallbackInfo *>(callback_info_ptr);
BKE_reportf(callback_info->reports,
RPT_ERROR,
"Unable to install asset bundle, has external dependency \"%s\"",
path_src);
callback_info->external_file_found = true;
return false;
}
/**
* Do a check on any external files (.blend, textures, etc.) being used.
* The "Install asset bundle" operator only works on standalone .blend files
* (catalog definition files are fine, though).
*
* \return true when there are external files, false otherwise.
*/
static bool has_external_files(Main *bmain, struct ReportList *reports)
{
struct FileCheckCallbackInfo callback_info = {reports, false};
BKE_bpath_traverse_main(
bmain,
&external_file_check_callback,
BKE_BPATH_TRAVERSE_SKIP_PACKED /* Packed files are fine. */
| BKE_BPATH_TRAVERSE_SKIP_MULTIFILE /* Only report multifiles once, it's enough. */,
&callback_info);
return callback_info.external_file_found;
}
/* -------------------------------------------------------------------- */
void ED_operatortypes_asset(void)
{
WM_operatortype_append(ASSET_OT_mark);
@ -654,6 +899,7 @@ void ED_operatortypes_asset(void)
WM_operatortype_append(ASSET_OT_catalog_undo);
WM_operatortype_append(ASSET_OT_catalog_redo);
WM_operatortype_append(ASSET_OT_catalog_undo_push);
WM_operatortype_append(ASSET_OT_bundle_install);
WM_operatortype_append(ASSET_OT_list_refresh);
}

View file

@ -109,6 +109,7 @@ struct FileSelectParams *ED_fileselect_ensure_active_params(struct SpaceFile *sf
struct FileSelectParams *ED_fileselect_get_active_params(const struct SpaceFile *sfile);
struct FileSelectParams *ED_fileselect_get_file_params(const struct SpaceFile *sfile);
struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceFile *sfile);
bool ED_fileselect_is_local_asset_library(const struct SpaceFile *sfile);
void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile);
void ED_fileselect_params_to_userdef(struct SpaceFile *sfile,

View file

@ -41,6 +41,7 @@
#include "ED_fileselect.h"
#include "UI_interface.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
#include "WM_api.h"
@ -246,7 +247,20 @@ static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pane
RNA_pointer_create(&screen->id, &RNA_FileAssetSelectParams, params, &params_ptr);
uiItemR(row, &params_ptr, "asset_library_ref", 0, "", ICON_NONE);
if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) {
if (params->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_ref",
"Copy Bundle to Asset Library...",
ICON_IMPORT);
}
CTX_free(mutable_ctx);
}
else {
uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_asset_library_refresh");
}

View file

@ -411,6 +411,15 @@ FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile)
return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) ? sfile->asset_params : NULL;
}
bool ED_fileselect_is_local_asset_library(const SpaceFile *sfile)
{
const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
if (asset_params == NULL) {
return false;
}
return asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL;
}
static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
{
AssetLibraryReference *library = &asset_params->asset_library_ref;

View file

@ -307,7 +307,7 @@ const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSED(C),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf();
const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf(true);
if (!items) {
*r_free = false;
}

View file

@ -470,6 +470,8 @@ int WM_operator_repeat(struct bContext *C, struct wmOperator *op);
int WM_operator_repeat_last(struct bContext *C, struct wmOperator *op);
bool WM_operator_repeat_check(const struct bContext *C, struct wmOperator *op);
bool WM_operator_is_repeat(const struct bContext *C, const struct wmOperator *op);
bool WM_operator_name_poll(struct bContext *C, const char *opstring);
int WM_operator_name_call_ptr(struct bContext *C,
struct wmOperatorType *ot,
wmOperatorCallContext context,

View file

@ -1614,6 +1614,16 @@ int WM_operator_name_call(bContext *C,
return 0;
}
bool WM_operator_name_poll(bContext *C, const char *opstring)
{
wmOperatorType *ot = WM_operatortype_find(opstring, 0);
if (!ot) {
return false;
}
return WM_operator_poll(C, ot);
}
int WM_operator_name_call_with_properties(struct bContext *C,
const char *opstring,
wmOperatorCallContext context,