From 3e7058893ef306b76c75d88b3fa86224d324cf18 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:05:03 -0500 Subject: [PATCH 1/7] Brush Assets: Add library option to save as operator --- .../blender/editors/sculpt_paint/paint_ops.cc | 149 +++++++++++------- 1 file changed, 93 insertions(+), 56 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 500b2bfba2c..ad6f8d34bc7 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -43,6 +43,7 @@ #include "BKE_report.h" #include "ED_asset_handle.hh" +#include "ED_asset_library.hh" #include "ED_asset_list.hh" #include "ED_asset_mark_clear.hh" #include "ED_asset_menu_utils.hh" @@ -1049,7 +1050,7 @@ static AssetWeakReference *brush_asset_create_weakref_hack(const bUserAssetLibra return asset_weak_ref; } -static const bUserAssetLibrary *brush_asset_get_editable_library() +static const bUserAssetLibrary *brush_asset_get_default_library() { if (BLI_listbase_is_empty(&U.asset_libraries)) { return nullptr; @@ -1062,10 +1063,8 @@ static const bUserAssetLibrary *brush_asset_get_editable_library() return static_cast(U.asset_libraries.first); } -static void brush_asset_refresh_editable_library(const bContext *C) +static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library) { - const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); - /* TODO: Should the all library reference be automatically cleared? */ AssetLibraryReference all_lib_ref = asset_system::all_library_reference(); asset::list::clear(&all_lib_ref, C); @@ -1075,7 +1074,7 @@ static void brush_asset_refresh_editable_library(const bContext *C) if (lib_ref.type == ASSET_LIBRARY_CUSTOM) { const bUserAssetLibrary *ref_user_library = BKE_preferences_asset_library_find_index( &U, lib_ref.custom_library_index); - if (ref_user_library == user_library) { + if (ref_user_library == &user_library) { asset::list::clear(&lib_ref, C); return; } @@ -1083,15 +1082,14 @@ static void brush_asset_refresh_editable_library(const bContext *C) } } -static std::string brush_asset_root_path_for_save() +static std::string brush_asset_root_path_for_save(const bUserAssetLibrary &user_library) { - const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); - if (user_library == nullptr || user_library->dirpath[0] == '\0') { + if (user_library.dirpath[0] == '\0') { return ""; } char libpath[FILE_MAX]; - BLI_strncpy(libpath, user_library->dirpath, sizeof(libpath)); + BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath)); BLI_path_slash_native(libpath); BLI_path_normalize(libpath); @@ -1099,9 +1097,10 @@ static std::string brush_asset_root_path_for_save() } static std::string brush_asset_blendfile_path_for_save(ReportList *reports, + const bUserAssetLibrary &user_library, const StringRefNull base_name) { - std::string root_path = brush_asset_root_path_for_save(); + std::string root_path = brush_asset_root_path_for_save(user_library); BLI_assert(!root_path.empty()); if (!BLI_dir_create_recursive(root_path.c_str())) { @@ -1203,12 +1202,38 @@ static bool brush_asset_save_as_poll(bContext *C) return false; } - const bUserAssetLibrary *user_library = brush_asset_get_editable_library(); - if (user_library == nullptr || user_library->dirpath[0] == '\0') { - CTX_wm_operator_poll_msg_set(C, "No default asset library available to save to"); + return true; +} + +static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) +{ + /* Fairly simple checks, based on filepath only: + * - The blendlib filepath ends up with the `.asset.blend` extension. + * - The blendlib is located in the expected sub-directory of the editable asset library. + * + * TODO: Right now no check is done on file content, e.g. to ensure that the blendlib file has + * not been manually edited by the user (that it does not have any UI IDs e.g.). */ + + char path_buffer[FILE_MAX_LIBEXTRA]; + char *dir, *group, *name; + AS_asset_full_path_explode_from_weak_ref(&asset_weak_ref, path_buffer, &dir, &group, &name); + + if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { return false; } + bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( + &U, asset_weak_ref.asset_library_identifier); + if (!library) { + return false; + } + + std::string root_path_for_save = brush_asset_root_path_for_save(*library); + if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) { + return false; + } + + /* TODO: Do we want more checks here? E.g. check actual content of the file? */ return true; } @@ -1230,7 +1255,23 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) STRNCPY(name, brush->id.name + 2); } - const std::string filepath = brush_asset_blendfile_path_for_save(op->reports, name); + const int enum_value = RNA_enum_get(op->ptr, "asset_library_reference"); + const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value); + const bUserAssetLibrary *library = BKE_preferences_asset_library_find_index( + &U, lib_ref.custom_library_index); + if (!library) { + return OPERATOR_CANCELLED; + } + + /* Check if belongs to an editable blend file. */ + if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { + if (!asset_is_editable(*paint->brush_asset_reference)) { + BKE_report(op->reports, RPT_ERROR, "Asset blend file is not editable"); + return false; + } + } + + const std::string filepath = brush_asset_blendfile_path_for_save(op->reports, *library, name); if (filepath.empty()) { return OPERATOR_CANCELLED; } @@ -1252,10 +1293,8 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Create weak reference to new datablock. */ - const bUserAssetLibrary *asset_lib = brush_asset_get_editable_library(); AssetWeakReference *new_brush_weak_ref = brush_asset_create_weakref_hack( - asset_lib, final_full_asset_filepath); + library, final_full_asset_filepath); /* TODO: maybe not needed, even less so if there is more visual confirmation of change. */ BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", filepath.c_str()); @@ -1268,7 +1307,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset"); } - brush_asset_refresh_editable_library(C); + refresh_asset_library(C, *library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr); WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); @@ -1277,16 +1316,37 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { + if (const bUserAssetLibrary *library = brush_asset_get_default_library()) { + AssetLibraryReference library_ref{}; + library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, library); + library_ref.type = ASSET_LIBRARY_CUSTOM; + RNA_enum_set( + op->ptr, "asset_library_reference", asset::library_reference_to_enum_value(&library_ref)); + } + Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); RNA_string_set(op->ptr, "name", brush->id.name + 2); - /* TODO: add information about the asset library this will be saved to? */ - /* TODO: autofocus name? */ return WM_operator_props_dialog_popup(C, op, 400); } +static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + bool *r_free) +{ + const EnumPropertyItem *items = asset::library_reference_to_rna_enum_itemf(false); + if (!items) { + *r_free = false; + return nullptr; + } + + *r_free = true; + return items; +} + static void BRUSH_OT_asset_save_as(wmOperatorType *ot) { ot->name = "Save As Brush Asset"; @@ -1299,33 +1359,12 @@ static void BRUSH_OT_asset_save_as(wmOperatorType *ot) ot->invoke = brush_asset_save_as_invoke; ot->poll = brush_asset_save_as_poll; - RNA_def_string(ot->srna, "name", nullptr, MAX_NAME, "Name", "Name used to save the brush asset"); -} + ot->prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_NAME, "Name", "Name for the new brush asset"); -static bool brush_asset_is_editable(const AssetWeakReference &brush_weak_ref) -{ - /* Fairly simple checks, based on filepath only: - * - The blendlib filepath ends up with the `.asset.blend` extension. - * - The blendlib is located in the expected sub-directory of the editable asset library. - * - * TODO: Right now no check is done on file content, e.g. to ensure that the blendlib file has - * not been manually edited by the user (that it does not have any UI IDs e.g.). */ - - char path_buffer[FILE_MAX_LIBEXTRA]; - char *dir, *group, *name; - AS_asset_full_path_explode_from_weak_ref(&brush_weak_ref, path_buffer, &dir, &group, &name); - - if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { - return false; - } - - std::string root_path_for_save = brush_asset_root_path_for_save(); - if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) { - return false; - } - - /* TODO: Do we want more checks here? E.g. check actual content of the file? */ - return true; + PropertyRNA *prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE); + RNA_def_enum_funcs(prop, rna_asset_library_reference_itemf); + RNA_def_property_ui_text(prop, "Library", "Asset library used to store the new brush"); } static bool brush_asset_delete_poll(bContext *C) @@ -1336,14 +1375,6 @@ static bool brush_asset_delete_poll(bContext *C) return false; } - /* Asset brush, check if belongs to an editable blend file. */ - if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { - if (!brush_asset_is_editable(*paint->brush_asset_reference)) { - CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); - return false; - } - } - return true; } @@ -1353,6 +1384,12 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( + &U, paint->brush_asset_reference->asset_library_identifier); + if (!library) { + return OPERATOR_CANCELLED; + } + if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { /* Delete from asset library on disk. */ char path_buffer[FILE_MAX_LIBEXTRA]; @@ -1375,7 +1412,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op) BKE_id_delete(bmain, original_brush); } - brush_asset_refresh_editable_library(C); + refresh_asset_library(C, *library); WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr); WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr); @@ -1407,7 +1444,7 @@ static bool brush_asset_update_poll(bContext *C) return false; } - if (!brush_asset_is_editable(*paint->brush_asset_reference)) { + if (!asset_is_editable(*paint->brush_asset_reference)) { CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable"); return false; } -- 2.30.2 From 525908a4d50e95cbd62d800303be0c7598efb57f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 8 Feb 2024 18:28:34 -0500 Subject: [PATCH 2/7] Fix merge errror --- .../blender/editors/sculpt_paint/paint_ops.cc | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 41494151824..6867c3e6697 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1205,38 +1205,6 @@ static bool brush_asset_save_as_poll(bContext *C) return true; } -static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) -{ - /* Fairly simple checks, based on filepath only: - * - The blendlib filepath ends up with the `.asset.blend` extension. - * - The blendlib is located in the expected sub-directory of the editable asset library. - * - * TODO: Right now no check is done on file content, e.g. to ensure that the blendlib file has - * not been manually edited by the user (that it does not have any UI IDs e.g.). */ - - char path_buffer[FILE_MAX_LIBEXTRA]; - char *dir, *group, *name; - AS_asset_full_path_explode_from_weak_ref(&asset_weak_ref, path_buffer, &dir, &group, &name); - - if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) { - return false; - } - - bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name( - &U, asset_weak_ref.asset_library_identifier); - if (!library) { - return false; - } - - std::string root_path_for_save = brush_asset_root_path_for_save(*library); - if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) { - return false; - } - - /* TODO: Do we want more checks here? E.g. check actual content of the file? */ - return true; -} - static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) { /* Fairly simple checks, based on filepath only: -- 2.30.2 From f3db6accc9f66e0f5437a9ec575a74c948f6d3bc Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 11:05:04 -0500 Subject: [PATCH 3/7] Fix build error --- source/blender/editors/sculpt_paint/paint_ops.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 1cf92baeaa9..05366b6ee71 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -56,6 +56,8 @@ #include "RNA_access.hh" #include "RNA_define.hh" +#include "BLT_translation.hh" + #include "AS_asset_library.hh" #include "AS_asset_representation.hh" -- 2.30.2 From 3cdc0f4604151c56f7e70207b45068f8718b90f1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 11:12:29 -0500 Subject: [PATCH 4/7] Add poll check for no user asset libraries --- source/blender/editors/sculpt_paint/paint_ops.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 05366b6ee71..a53038b70e8 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1202,6 +1202,11 @@ static bool brush_asset_save_as_poll(bContext *C) return false; } + if (BLI_listbase_is_empty(&U.asset_libraries)) { + CTX_wm_operator_poll_msg_set(C, "No asset library available to save to"); + return false; + } + return true; } -- 2.30.2 From 6a1e5e529da1b5dac2de23096722e21169c93a64 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 11:12:51 -0500 Subject: [PATCH 5/7] Extract function to get library from operator property --- source/blender/editors/sculpt_paint/paint_ops.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index a53038b70e8..aa03fc9ccb8 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1242,6 +1242,13 @@ static bool asset_is_editable(const AssetWeakReference &asset_weak_ref) return true; } +static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr) +{ + const int enum_value = RNA_enum_get(&ptr, "asset_library_reference"); + const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value); + return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index); +} + static int brush_asset_save_as_exec(bContext *C, wmOperator *op) { Paint *paint = BKE_paint_get_active_from_context(C); @@ -1260,10 +1267,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) STRNCPY(name, brush->id.name + 2); } - const int enum_value = RNA_enum_get(op->ptr, "asset_library_reference"); - const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value); - const bUserAssetLibrary *library = BKE_preferences_asset_library_find_index( - &U, lib_ref.custom_library_index); + const bUserAssetLibrary *library = get_asset_library_from_prop(*op->ptr); if (!library) { return OPERATOR_CANCELLED; } -- 2.30.2 From 22bc8ec1fcf5387ccac8eeb897ae5310a7f4036f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 11:13:24 -0500 Subject: [PATCH 6/7] Remove unnecessary editable check If the asset's current file isn't editable it should be saved to a new one --- source/blender/editors/sculpt_paint/paint_ops.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index aa03fc9ccb8..62536fec942 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1271,15 +1271,6 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) if (!library) { return OPERATOR_CANCELLED; } - - /* Check if the asset belongs to an editable blend file. */ - if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) { - if (!asset_is_editable(*paint->brush_asset_reference)) { - BKE_report(op->reports, RPT_ERROR, "Asset blend file is not editable"); - return false; - } - } - const std::string filepath = brush_asset_blendfile_path_for_save(op->reports, *library, name); if (filepath.empty()) { return OPERATOR_CANCELLED; -- 2.30.2 From 922a42785af5f62ed31f518131b434f00a5a5fc1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Feb 2024 11:35:22 -0500 Subject: [PATCH 7/7] Reorder code for prettier diff --- source/blender/editors/sculpt_paint/paint_ops.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_ops.cc b/source/blender/editors/sculpt_paint/paint_ops.cc index 62536fec942..929acf977f1 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_ops.cc @@ -1316,6 +1316,11 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op) static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + + RNA_string_set(op->ptr, "name", brush->id.name + 2); + if (const bUserAssetLibrary *library = brush_asset_get_default_library()) { AssetLibraryReference library_ref{}; library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, library); @@ -1324,11 +1329,6 @@ static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent op->ptr, "asset_library_reference", asset::library_reference_to_enum_value(&library_ref)); } - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - - RNA_string_set(op->ptr, "name", brush->id.name + 2); - return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save")); } -- 2.30.2