Assets: Clear Asset operator variants for clearing/setting Fake User

The Clear Asset operator (`ASSET_OT_clear`) now clears the Fake User.
This makes it symmetrical with the Mark Asset (`ASSET_OT_mark`)
operator, which sets Fake User to ensure assets are always saved to
disk.

Clear Asset now also has a `set_fake_user` boolean option, which allows
users to Clear Asset and set Fake User in one go.

The asset browser now shows these options in the context menu:
- Clear Asset: also clears Fake User. This makes it possible to actually
  remove assets from the blend file without leaving the Asset Browser.
- Clear Asset (Set Fake User): keeps the Fake User bit set. This makes
  it possible to "hide" the asset from the asset browser, without
  loosing the actual data.

Internally, the `ED_asset_clear_id(id)` function now always clears the
Fake User bit. If it was intended that this bit was kept set, it's up to
the caller to explicitly call `id_fake_user_set(id)` afterwards.

Manifest Task: T90844

Reviewed By: Severin

Differential Revision: https://developer.blender.org/D12663
This commit is contained in:
Sybren A. Stüvel 2021-09-28 15:04:55 +02:00
parent 73b2ecb297
commit 3674347849
5 changed files with 103 additions and 10 deletions

View File

@ -786,9 +786,10 @@ class ASSETBROWSER_MT_context_menu(AssetBrowserMenu, Menu):
layout.separator()
sub = layout.row()
sub = layout.column()
sub.operator_context = 'EXEC_DEFAULT'
sub.operator("asset.clear", text="Clear Asset")
sub.operator("asset.clear", text="Clear Asset").set_fake_user = False
sub.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True
layout.separator()

View File

@ -323,7 +323,8 @@ class OUTLINER_MT_asset(Menu):
space = context.space_data
layout.operator("asset.mark")
layout.operator("asset.clear")
layout.operator("asset.clear", text="Clear Asset").set_fake_user = False
layout.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True
class OUTLINER_PT_filter(Panel):

View File

@ -27,7 +27,22 @@ extern "C" {
struct ID;
struct bContext;
/**
* Mark the datablock as asset.
*
* To ensure the datablock is saved, this sets Fake User.
*
* \return whether the datablock was marked as asset; false when it is not capable of becoming an
* asset, or when it already was an asset. */
bool ED_asset_mark_id(const struct bContext *C, struct ID *id);
/**
* Remove the asset metadata, turning the ID into a "normal" ID.
*
* This clears the Fake User. If for some reason the datablock is meant to be saved anyway, the
* caller is responsible for explicitly setting the Fake User.
*
* \return whether the asset metadata was actually removed; false when the ID was not an asset. */
bool ED_asset_clear_id(struct ID *id);
bool ED_asset_can_mark_single_from_context(const struct bContext *C);

View File

@ -67,8 +67,7 @@ bool ED_asset_clear_id(ID *id)
return false;
}
BKE_asset_metadata_free(&id->asset_data);
/* Don't clear fake user here, there's no guarantee that it was actually set by
* #ED_asset_mark_id(), it might have been something/someone else. */
id_fake_user_clear(id);
/* Important for asset storage to update properly! */
ED_assetlist_storage_tag_main_data_dirty();

View File

@ -19,6 +19,7 @@
*/
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_report.h"
#include "BLI_vector.hh"
@ -26,6 +27,7 @@
#include "ED_asset.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "WM_types.h"
@ -34,11 +36,30 @@
using PointerRNAVec = blender::Vector<PointerRNA>;
static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C);
static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C);
static bool asset_type_is_nonexperimental(const ID_Type id_type);
static bool asset_operation_poll(bContext * /*C*/)
{
/* At this moment only the pose library is non-experimental. Still, directly marking arbitrary
* Actions as asset is not part of the stable functionality; instead, the pose library "Create
* Pose Asset" operator should be used. Actions can still be marked as asset via
* `the_action.asset_mark()` (so a function call instead of this operator), which is what the
* pose library uses internally. */
return U.experimental.use_extended_asset_browser;
}
static bool asset_clear_poll(bContext *C)
{
if (asset_operation_poll(C)) {
return true;
}
PointerRNAVec pointers = asset_operation_get_nonexperimental_ids_from_context(C);
return !pointers.is_empty();
}
/**
* Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or
* multiple ones ("selected_ids" context member).
@ -64,6 +85,28 @@ static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C)
return ids;
}
static PointerRNAVec asset_operation_get_nonexperimental_ids_from_context(const bContext *C)
{
PointerRNAVec nonexperimental;
PointerRNAVec pointers = asset_operation_get_ids_from_context(C);
for (PointerRNA &ptr : pointers) {
BLI_assert(RNA_struct_is_ID(ptr.type));
ID *id = static_cast<ID *>(ptr.data);
if (asset_type_is_nonexperimental(GS(id->name))) {
nonexperimental.append(ptr);
}
}
return nonexperimental;
}
static bool asset_type_is_nonexperimental(const ID_Type id_type)
{
/* At this moment only the pose library is non-experimental. For simplicity, allow asset
* operations on all Action datablocks (even though pose assets are limited to single frames). */
return ELEM(id_type, ID_AC);
}
/* -------------------------------------------------------------------- */
class AssetMarkHelper {
@ -166,7 +209,13 @@ static void ASSET_OT_mark(wmOperatorType *ot)
/* -------------------------------------------------------------------- */
class AssetClearHelper {
const bool set_fake_user_;
public:
AssetClearHelper(const bool set_fake_user) : set_fake_user_(set_fake_user)
{
}
void operator()(PointerRNAVec &ids);
void reportResults(const bContext *C, ReportList &reports) const;
@ -191,10 +240,16 @@ void AssetClearHelper::operator()(PointerRNAVec &ids)
continue;
}
if (ED_asset_clear_id(id)) {
stats.tot_cleared++;
stats.last_id = id;
if (!ED_asset_clear_id(id)) {
continue;
}
if (set_fake_user_) {
id_fake_user_set(id);
}
stats.tot_cleared++;
stats.last_id = id;
}
}
@ -234,7 +289,8 @@ static int asset_clear_exec(bContext *C, wmOperator *op)
{
PointerRNAVec ids = asset_operation_get_ids_from_context(C);
AssetClearHelper clear_helper;
const bool set_fake_user = RNA_boolean_get(op->ptr, "set_fake_user");
AssetClearHelper clear_helper(set_fake_user);
clear_helper(ids);
clear_helper.reportResults(C, *op->reports);
@ -248,18 +304,39 @@ static int asset_clear_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static char *asset_clear_get_description(struct bContext *UNUSED(C),
struct wmOperatorType *UNUSED(op),
struct PointerRNA *values)
{
const bool set_fake_user = RNA_boolean_get(values, "set_fake_user");
if (!set_fake_user) {
return nullptr;
}
return BLI_strdup(
"Delete all asset metadata, turning the selected asset data-blocks back into normal "
"data-blocks, and set Fake User to ensure the data-blocks will still be saved");
}
static void ASSET_OT_clear(wmOperatorType *ot)
{
ot->name = "Clear Asset";
ot->description =
"Delete all asset metadata and turn the selected asset data-blocks back into normal "
"data-blocks";
ot->get_description = asset_clear_get_description;
ot->idname = "ASSET_OT_clear";
ot->exec = asset_clear_exec;
ot->poll = asset_operation_poll;
ot->poll = asset_clear_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"set_fake_user",
false,
"Set Fake User",
"Ensure the data-block is saved, even when it is no longer marked as asset");
}
/* -------------------------------------------------------------------- */