Code quality: Refactor asset operators using C++

* Attempt to improve readability by using focused, coherent helper classes.
* Replace ListBase with blender::Vector, which is more efficient and has a
  better API.
* Split user reporting from error checking.
* Use namespace (as we usually do for C++ code).
* Remove unused headers
This commit is contained in:
Julian Eisel 2021-02-08 01:30:49 +01:00
parent eb7d9e2a1b
commit 04be1e9980
1 changed files with 104 additions and 92 deletions

View File

@ -18,124 +18,128 @@
* \ingroup edasset
*/
#include <string.h>
#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "BLI_listbase.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
#include "DNA_asset_types.h"
#include "DNA_userdef_types.h"
#include "BLI_vector.hh"
#include "ED_asset.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "WM_types.h"
/* -------------------------------------------------------------------- */
struct AssetMarkResultStats {
int tot_created;
int tot_already_asset;
ID *last_id;
};
using PointerRNAVec = blender::Vector<PointerRNA>;
static bool asset_ops_poll(bContext *UNUSED(C))
static bool asset_operation_poll(bContext * /*C*/)
{
return U.experimental.use_asset_browser;
}
/**
* Return the IDs to operate on as list of #CollectionPointerLink links. Needs freeing.
* Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or
* multiple ones ("selected_ids" context member).
*/
static ListBase /* CollectionPointerLink */ asset_operation_get_ids_from_context(const bContext *C)
static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C)
{
ListBase list = {0};
PointerRNAVec ids;
PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID);
if (idptr.data) {
CollectionPointerLink *ctx_link = (CollectionPointerLink *)MEM_callocN(sizeof(*ctx_link),
__func__);
ctx_link->ptr = idptr;
BLI_addtail(&list, ctx_link);
/* Single ID. */
ids.append(idptr);
}
else {
ListBase list;
CTX_data_selected_ids(C, &list);
LISTBASE_FOREACH (CollectionPointerLink *, link, &list) {
ids.append(link->ptr);
}
BLI_freelistN(&list);
}
return list;
return ids;
}
static void asset_mark_for_idptr_list(const bContext *C,
const ListBase /* CollectionPointerLink */ *ids,
struct AssetMarkResultStats *r_stats)
/* -------------------------------------------------------------------- */
class AssetMarkHelper {
public:
void operator()(const bContext &C, PointerRNAVec &ids);
void reportResults(ReportList &reports) const;
bool wasSuccessful() const;
private:
struct Stats {
int tot_created = 0;
int tot_already_asset = 0;
ID *last_id = nullptr;
};
Stats stats;
};
void AssetMarkHelper::operator()(const bContext &C, PointerRNAVec &ids)
{
memset(r_stats, 0, sizeof(*r_stats));
for (PointerRNA &ptr : ids) {
BLI_assert(RNA_struct_is_ID(ptr.type));
LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) {
BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type));
ID *id = (ID *)ctx_id->ptr.data;
ID *id = static_cast<ID *>(ptr.data);
if (id->asset_data) {
r_stats->tot_already_asset++;
stats.tot_already_asset++;
continue;
}
if (ED_asset_mark_id(C, id)) {
r_stats->last_id = id;
r_stats->tot_created++;
if (ED_asset_mark_id(&C, id)) {
stats.last_id = id;
stats.tot_created++;
}
}
}
static bool asset_mark_results_report(const struct AssetMarkResultStats *stats,
ReportList *reports)
bool AssetMarkHelper::wasSuccessful() const
{
return stats.tot_created > 0;
}
void AssetMarkHelper::reportResults(ReportList &reports) const
{
/* User feedback on failure. */
if ((stats->tot_created < 1) && (stats->tot_already_asset > 0)) {
BKE_report(reports,
RPT_ERROR,
"Selected data-blocks are already assets (or do not support use as assets)");
return false;
if (!wasSuccessful()) {
if ((stats.tot_already_asset > 0)) {
BKE_report(&reports,
RPT_ERROR,
"Selected data-blocks are already assets (or do not support use as assets)");
}
else {
BKE_report(&reports,
RPT_ERROR,
"No data-blocks to create assets for found (or do not support use as assets)");
}
}
if (stats->tot_created < 1) {
BKE_report(reports,
RPT_ERROR,
"No data-blocks to create assets for found (or do not support use as assets)");
return false;
}
/* User feedback on success. */
if (stats->tot_created == 1) {
else if (stats.tot_created == 1) {
/* If only one data-block: Give more useful message by printing asset name. */
BKE_reportf(reports, RPT_INFO, "Data-block '%s' is now an asset", stats->last_id->name + 2);
BKE_reportf(&reports, RPT_INFO, "Data-block '%s' is now an asset", stats.last_id->name + 2);
}
else {
BKE_reportf(reports, RPT_INFO, "%i data-blocks are now assets", stats->tot_created);
BKE_reportf(&reports, RPT_INFO, "%i data-blocks are now assets", stats.tot_created);
}
return true;
}
static int asset_mark_exec(bContext *C, wmOperator *op)
{
ListBase ids = asset_operation_get_ids_from_context(C);
PointerRNAVec ids = asset_operation_get_ids_from_context(C);
struct AssetMarkResultStats stats;
asset_mark_for_idptr_list(C, &ids, &stats);
BLI_freelistN(&ids);
AssetMarkHelper mark_helper;
mark_helper(*C, ids);
mark_helper.reportResults(*op->reports);
if (!asset_mark_results_report(&stats, op->reports)) {
if (!mark_helper.wasSuccessful()) {
return OPERATOR_CANCELLED;
}
@ -154,67 +158,75 @@ static void ASSET_OT_mark(wmOperatorType *ot)
ot->idname = "ASSET_OT_mark";
ot->exec = asset_mark_exec;
ot->poll = asset_ops_poll;
ot->poll = asset_operation_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* -------------------------------------------------------------------- */
struct AssetClearResultStats {
int tot_removed;
ID *last_id;
class AssetClearHelper {
public:
void operator()(PointerRNAVec &ids);
void reportResults(ReportList &reports) const;
bool wasSuccessful() const;
private:
struct Stats {
int tot_cleared = 0;
ID *last_id = nullptr;
};
Stats stats;
};
static void asset_clear_from_idptr_list(const ListBase /* CollectionPointerLink */ *ids,
AssetClearResultStats *r_stats)
void AssetClearHelper::operator()(PointerRNAVec &ids)
{
memset(r_stats, 0, sizeof(*r_stats));
for (PointerRNA &ptr : ids) {
BLI_assert(RNA_struct_is_ID(ptr.type));
LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) {
BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type));
ID *id = (ID *)ctx_id->ptr.data;
ID *id = static_cast<ID *>(ptr.data);
if (!id->asset_data) {
continue;
}
if (ED_asset_clear_id(id)) {
r_stats->tot_removed++;
r_stats->last_id = id;
stats.tot_cleared++;
stats.last_id = id;
}
}
}
static bool asset_clear_result_report(const AssetClearResultStats *stats, ReportList *reports)
void AssetClearHelper::reportResults(ReportList &reports) const
{
if (stats->tot_removed < 1) {
BKE_report(reports, RPT_ERROR, "No asset data-blocks selected/focused");
return false;
if (!wasSuccessful()) {
BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused");
}
if (stats->tot_removed == 1) {
else if (stats.tot_cleared == 1) {
/* If only one data-block: Give more useful message by printing asset name. */
BKE_reportf(
reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats->last_id->name + 2);
&reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats.last_id->name + 2);
}
else {
BKE_reportf(reports, RPT_INFO, "%i data-blocks are no assets anymore", stats->tot_removed);
BKE_reportf(&reports, RPT_INFO, "%i data-blocks are no assets anymore", stats.tot_cleared);
}
}
return true;
bool AssetClearHelper::wasSuccessful() const
{
return stats.tot_cleared > 0;
}
static int asset_clear_exec(bContext *C, wmOperator *op)
{
ListBase ids = asset_operation_get_ids_from_context(C);
PointerRNAVec ids = asset_operation_get_ids_from_context(C);
AssetClearResultStats stats;
asset_clear_from_idptr_list(&ids, &stats);
BLI_freelistN(&ids);
AssetClearHelper clear_helper;
clear_helper(ids);
clear_helper.reportResults(*op->reports);
if (!asset_clear_result_report(&stats, op->reports)) {
if (!clear_helper.wasSuccessful()) {
return OPERATOR_CANCELLED;
}
@ -233,7 +245,7 @@ static void ASSET_OT_clear(wmOperatorType *ot)
ot->idname = "ASSET_OT_clear";
ot->exec = asset_clear_exec;
ot->poll = asset_ops_poll;
ot->poll = asset_operation_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}