Brush Assets: Store assets outside Main #117730

Manually merged
Brecht Van Lommel merged 18 commits from brecht/blender:brush-separate-main into brush-assets-project 2024-02-27 14:53:03 +01:00
20 changed files with 250 additions and 632 deletions
Showing only changes of commit 44d253d0c4 - Show all commits

View File

@ -157,13 +157,7 @@ class BrushSelectPanel(BrushPanel):
col = row.column()
col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="")
# TODO: Need actual check if this is an asset from library.
# TODO: why is brush.asset_data None for these?
is_linked = brush.library
is_override = brush.override_library and brush.override_library.reference
is_asset_brush = is_linked or is_override
if not is_asset_brush:
if not brush.is_asset_library_data and not brush.asset_data:
# Legacy custom icon, mostly replaced by asset preview.
layout.use_property_split = True
layout.use_property_decorate = False

View File

@ -46,13 +46,7 @@ class VIEW3D_MT_brush_context_menu(Menu):
layout.label(text="No brush selected", icon='INFO')
return
# TODO: Need actual check if this is an asset from library.
# TODO: why is brush.asset_data None for these?
is_linked = brush.library
is_override = brush.override_library and brush.override_library.reference
is_asset_brush = is_linked or is_override
if is_asset_brush:
if brush.is_asset_library_data:
layout.operator("brush.asset_save_as", text="Duplicate Asset...", icon='DUPLICATE')
layout.operator("brush.asset_delete", text="Delete Asset")

View File

@ -20,6 +20,7 @@ struct BlendDataReader;
struct BlendWriter;
struct ID;
struct IDProperty;
struct Main;
struct PreviewImage;
using PreSaveFn = void (*)(void *asset_ptr, AssetMetaData *asset_data);
@ -81,3 +82,7 @@ void BKE_asset_weak_reference_free(AssetWeakReference **weak_ref);
AssetWeakReference *BKE_asset_weak_reference_copy(AssetWeakReference *weak_ref);
void BKE_asset_weak_reference_write(BlendWriter *writer, const AssetWeakReference *weak_ref);
void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *weak_ref);
Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id);
void BKE_asset_weak_reference_main_free();
ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference *weak_ref);

View File

@ -31,10 +31,6 @@ struct UnifiedPaintSettings;
void BKE_brush_system_init();
void BKE_brush_system_exit();
/* TODO: Should be somewhere else not specific to brushes. */
Brush *BKE_brush_asset_runtime_ensure(Main *bmain,
const AssetWeakReference *brush_asset_reference);
/* Data-block functions. */
/**

View File

@ -84,17 +84,6 @@ bool BKE_lib_override_library_is_user_edited(const ID *id);
*/
bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id);
/**
* Count the number of liboverride IDs of given `id_type`, using a refererence linked ID from given
* `library`, that are user-edited.
*
* \param r_reports: If not NULL, add one report for each relevant ID.
*/
int BKE_lib_override_user_edited_from_library_count(Main *bmain,
ID_Type id_type,
Library *library,
ReportList *r_reports);
/**
* Check if given Override Property for given ID is animated (through a F-Curve in an Action, or
* from a driver).

View File

@ -7,15 +7,25 @@
*/
#include <memory>
#include <utility>
#include "BLI_string.h"
#include "DNA_space_types.h"
#include "AS_asset_identifier.hh"
#include "AS_asset_library.hh"
#include "BKE_asset.hh"
#include "BKE_blendfile_link_append.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BLI_vector.hh"
#include "BLO_read_write.hh"
#include "BLO_readfile.h"
#include "DNA_asset_types.h"
@ -97,3 +107,129 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *
BLO_read_data_address(reader, &weak_ref->asset_library_identifier);
BLO_read_data_address(reader, &weak_ref->relative_asset_identifier);
}
/* Main database for each brush asset blend file.
*
* This avoids mixing asset datablocks in the regular main, which leads to naming conflicts and
* confusing user interface.
*
* TODO: Heavily WIP code. */
struct AssetWeakReferenceMain {
/* TODO: not sure if this is the best unique identifier. */
std::string lib_path;
Main *main;
AssetWeakReferenceMain(const char *lib_path) : lib_path(lib_path), main(BKE_main_new()) {}
AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete;
AssetWeakReferenceMain(AssetWeakReferenceMain &&other) : main(std::exchange(other.main, nullptr))
{
}
~AssetWeakReferenceMain()
{
if (main) {
BKE_main_free(main);
}
}
};
blender::Vector<AssetWeakReferenceMain> ASSET_WEAK_REFERENCE_MAINS;
Main *BKE_asset_weak_reference_main(Main *global_main, const ID *id)
{
if (!(id->tag & LIB_TAG_ASSET_MAIN)) {
return global_main;
}
for (const AssetWeakReferenceMain &weak_ref_main : ASSET_WEAK_REFERENCE_MAINS) {
/* TODO: just loop over listbase of same type, or make this whole thing
* more efficient. */
ID *other_id;
FOREACH_MAIN_ID_BEGIN (weak_ref_main.main, other_id) {
if (id == other_id) {
return weak_ref_main.main;
}
}
FOREACH_MAIN_ID_END;
}
BLI_assert_unreachable();
return nullptr;
}
static Main *asset_weak_reference_main_ensure(const char *lib_path)
{
for (const AssetWeakReferenceMain &weak_ref_main : ASSET_WEAK_REFERENCE_MAINS) {
if (weak_ref_main.lib_path == lib_path) {
return weak_ref_main.main;
}
}
ASSET_WEAK_REFERENCE_MAINS.append(lib_path);
return ASSET_WEAK_REFERENCE_MAINS.last().main;
}
void BKE_asset_weak_reference_main_free()
{
ASSET_WEAK_REFERENCE_MAINS.clear_and_shrink();
}
ID *BKE_asset_weak_reference_ensure(Main *global_main, const AssetWeakReference *weak_ref)
{
BLI_assert(weak_ref != nullptr);
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
char *asset_lib_path, *asset_group, *asset_name;
AS_asset_full_path_explode_from_weak_ref(
weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name);
if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) {
return nullptr;
}
// TODO: make not brush specific
BLI_assert(STREQ(asset_group, IDType_ID_BR.name));
BLI_assert(asset_name != nullptr);
/* If weak reference resolves to a null library path, assume we are in local asset case. */
Main *asset_main = global_main;
if (asset_lib_path != nullptr) {
/* TODO: avoid appending twice? */
asset_main = asset_weak_reference_main_ensure(asset_lib_path);
LibraryLink_Params lapp_parameters{};
lapp_parameters.bmain = asset_main;
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(
&lapp_parameters);
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true);
BKE_blendfile_link_append_context_library_add(lapp_context, asset_lib_path, nullptr);
// TODO: make not brush specific
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
lapp_context, asset_name, ID_BR, nullptr);
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
BKE_blendfile_link(lapp_context, nullptr);
BKE_blendfile_append(lapp_context, nullptr);
BKE_blendfile_link_append_context_free(lapp_context);
/* TODO: only do for new ones? */
BKE_main_id_tag_all(asset_main, LIB_TAG_ASSET_MAIN, true);
}
/* TODO: are we sure it will not get renamed? */
ID *local_asset = reinterpret_cast<ID *>(
BLI_findstring(&asset_main->brushes, asset_name, offsetof(ID, name) + 2));
if (local_asset == nullptr || !ID_IS_ASSET(local_asset)) {
return nullptr;
}
return local_asset;
}

View File

@ -305,188 +305,6 @@ static bool reuse_bmain_data_remapper_is_id_remapped(IDRemapper *remapper, ID *i
return false;
}
static Library *reuse_bmain_data_dependencies_new_library_get(ReuseOldBMainData *reuse_data,
Library *old_lib)
{
if (old_lib == nullptr) {
return nullptr;
}
IDRemapper *remapper = reuse_data->remapper;
Library *new_lib = old_lib;
IDRemapperApplyResult result = BKE_id_remapper_apply(
remapper, reinterpret_cast<ID **>(&new_lib), ID_REMAP_APPLY_DEFAULT);
if (result == ID_REMAP_RESULT_SOURCE_UNAVAILABLE) {
/* No matching new library found. Avoid nullptr case when original data was linked, which would
* match against all local IDs. */
}
BLI_assert_msg(result == ID_REMAP_RESULT_SOURCE_UNASSIGNED || new_lib != nullptr,
"`new_lib` should only ever be NULL here in case the library of the old linked "
"ID is the newly opened .blend file.");
return new_lib;
}
static int reuse_bmain_data_dependencies_process_cb(LibraryIDLinkCallbackData *cb_data)
{
ID *id = *cb_data->id_pointer;
if (id == nullptr) {
return IDWALK_RET_NOP;
}
ReuseOldBMainData *reuse_data = static_cast<ReuseOldBMainData *>(cb_data->user_data);
Main *new_bmain = reuse_data->new_bmain;
Main *old_bmain = reuse_data->old_bmain;
/* First check if it has already been remapped. */
IDRemapper *remapper = reuse_bmain_data_remapper_ensure(reuse_data);
if (reuse_bmain_data_remapper_is_id_remapped(remapper, id)) {
return IDWALK_RET_STOP_RECURSION;
}
ListBase *new_lb = which_libbase(new_bmain, GS(id->name));
ListBase *old_lb = which_libbase(old_bmain, GS(id->name));
/* if ID is already in the new_bmain, this should have been detected by the check on `remapper`
* above. */
BLI_assert(BLI_findindex(new_lb, id) < 0);
BLI_assert(BLI_findindex(old_lb, id) >= 0);
/* There may be a new library pointer in new_bmain, matching a library in old_bmain, even
* though pointer values are not the same. So we need to check new linked IDs in new_bmain
* against both potential library pointers. */
Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data, id->lib);
if (ID_IS_LINKED(id)) {
/* In case of linked ID, a 'new' version of the same data may already exist in new_bmain. In
* such case, do not move the old linked ID, but remap its usages to the new one instead. */
for (ID *id_iter = static_cast<ID *>(new_lb->last); id_iter != nullptr;
id_iter = static_cast<ID *>(id_iter->prev))
{
if (!ELEM(id_iter->lib, id->lib, old_id_new_lib)) {
continue;
}
if (!STREQ(id_iter->name + 2, id->name + 2)) {
continue;
}
/* NOTE: In case the old ID was from a library that is the newly opened .blend file (i.e.
* `old_id_new_lib` is NULL), this will remap to a local new ID in new_bmain.
*
* This has a potential impact on other ported over linked IDs (which are not allowed to
* use local data), and liboverrides (which are not allowed to have a local reference).
*
* Such cases are checked and 'fixed' later by the call to
* #reuse_bmain_data_invalid_local_usages_fix. */
BKE_id_remapper_add(remapper, id, id_iter);
return IDWALK_RET_STOP_RECURSION;
}
}
BLI_remlink_safe(old_lb, id);
BKE_main_namemap_remove_name(old_bmain, id, id->name + 2);
id->lib = old_id_new_lib;
BLI_addtail(new_lb, id);
BKE_id_new_name_validate(new_bmain, new_lb, id, nullptr, true);
/* Remap to itself, to avoid re-processing this ID again. */
BKE_id_remapper_add(remapper, id, id);
return IDWALK_RET_NOP;
}
/**
* Selectively 'import' data from old Main into new Main, provided it does not conflict with data
* already present in the new Main (name-wise and library-wise).
*
* Dependencies from moved over old data are also imported into the new Main, (unless, in case of
* linked data, a matching linked ID is already available in new Main).
*
* When a conflict is found, usages of the conflicted ID by the old data are stored in the
* `remapper` of `ReuseOldBMainData` to be remapped to the matching data in the new Main later.
*
* NOTE: This function will never remove any original new data from the new Main, it only moves
* (some of) the old data to the new Main.
*/
static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code)
{
Main *new_bmain = reuse_data->new_bmain;
Main *old_bmain = reuse_data->old_bmain;
ListBase *new_lb = which_libbase(new_bmain, id_code);
ListBase *old_lb = which_libbase(old_bmain, id_code);
IDRemapper *remapper = reuse_bmain_data_remapper_ensure(reuse_data);
/* Bring back local existing IDs from old_lb into new_lb, if there are no name/library
* conflicts. */
for (ID *old_id_iter_next, *old_id_iter = static_cast<ID *>(old_lb->first);
old_id_iter != nullptr;
old_id_iter = old_id_iter_next)
{
old_id_iter_next = static_cast<ID *>(old_id_iter->next);
/* Fully local assets are never re-used, since in this case the old file can be considered as
* an asset repository, and its assets should be accessed through the asset system by other
* files. */
if (!ID_IS_LINKED(old_id_iter) && !ID_IS_OVERRIDE_LIBRARY(old_id_iter) &&
ID_IS_ASSET(old_id_iter))
{
continue;
}
/* All other IDs can be re-used, provided there is no name/library conflict (i.e. the new bmain
* does not already have the 'same' data). */
bool can_use_old_id = true;
Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data,
old_id_iter->lib);
for (ID *new_id_iter = static_cast<ID *>(new_lb->first); new_id_iter != nullptr;
new_id_iter = static_cast<ID *>(new_id_iter->next))
{
if (!ELEM(new_id_iter->lib, old_id_iter->lib, old_id_new_lib)) {
continue;
}
if (!STREQ(old_id_iter->name + 2, new_id_iter->name + 2)) {
continue;
}
/* In case we found a matching ID in new_bmain, it can be considered as 'the same'
* as the old ID, so usages of old ID ported over to new main can be remapped.
*
* This is only valid if the old ID was linked though. */
if (ID_IS_LINKED(old_id_iter)) {
BKE_id_remapper_add(remapper, old_id_iter, new_id_iter);
}
can_use_old_id = false;
break;
}
if (can_use_old_id) {
BLI_remlink_safe(old_lb, old_id_iter);
BKE_main_namemap_remove_name(old_bmain, old_id_iter, old_id_iter->name + 2);
BLI_addtail(new_lb, old_id_iter);
old_id_iter->lib = old_id_new_lib;
BKE_id_new_name_validate(new_bmain, new_lb, old_id_iter, nullptr, true);
BKE_lib_libblock_session_uid_renew(old_id_iter);
/* Remap to itself, to avoid re-processing this ID again. */
BKE_id_remapper_add(remapper, old_id_iter, old_id_iter);
/* Port over dependencies of re-used ID, unless matching already existing ones in
* new_bmain can be found.
*
* NOTE : No pointers are remapped here, this code only moves dependencies from old_bmain
* to new_bmain if needed, and add necessary remapping rules to the reuse_data.remapper. */
BKE_library_foreach_ID_link(new_bmain,
old_id_iter,
reuse_bmain_data_dependencies_process_cb,
reuse_data,
IDWALK_RECURSE | IDWALK_DO_LIBRARY_POINTER);
}
}
}
/**
* Does a complete replacement of data in `new_bmain` by data from `old_bmain. Original new data
* are moved to the `old_bmain`, and will be freed together with it.
@ -494,9 +312,8 @@ static void reuse_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, co
* WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of
* given type are linked.
*
* NOTE: Unlike with #reuse_old_bmain_data_for_blendfile, there is no support at all for potential
* dependencies of the IDs moved around. This is not expected to be necessary for the current use
* cases (UI-related IDs).
* NOTE: There is no support at all for potential dependencies of the IDs moved around. This is not
* expected to be necessary for the current use cases (UI-related IDs).
*/
static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code)
{
@ -909,12 +726,6 @@ static void setup_app_data(bContext *C,
}
BKE_main_idmap_destroy(reuse_data.id_map);
if (!is_startup) {
/* Keeping old brushes has different conditions, and different behavior, than UI-like ID
* types when actually reading a blendfile. */
reuse_old_bmain_data_for_blendfile(&reuse_data, ID_BR);
}
}
/* Logic for 'track_undo_scene' is to keep using the scene which the active screen has, as long

View File

@ -27,7 +27,6 @@
#include "BLT_translation.h"
#include "BKE_asset.hh"
#include "BKE_blendfile_link_append.hh"
#include "BKE_bpath.h"
#include "BKE_brush.hh"
#include "BKE_colortools.hh"
@ -527,62 +526,6 @@ static void brush_defaults(Brush *brush)
#undef FROM_DEFAULT_PTR
}
Brush *BKE_brush_asset_runtime_ensure(Main *bmain, const AssetWeakReference *brush_asset_reference)
{
BLI_assert(brush_asset_reference != nullptr);
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
char *asset_lib_path, *asset_group, *asset_name;
AS_asset_full_path_explode_from_weak_ref(
brush_asset_reference, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name);
if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) {
return nullptr;
}
BLI_assert(STREQ(asset_group, IDType_ID_BR.name));
BLI_assert(asset_name != nullptr);
/* If the weakreference resolves to a null library path, assume that we are in local asset case.
*/
if (asset_lib_path == nullptr) {
Brush *local_brush_asset = reinterpret_cast<Brush *>(
BLI_findstring(&bmain->brushes, asset_name, offsetof(ID, name) + 2));
if (local_brush_asset == nullptr || !ID_IS_ASSET(local_brush_asset)) {
return nullptr;
}
return local_brush_asset;
}
LibraryLink_Params lapp_parameters{};
lapp_parameters.bmain = bmain;
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(
&lapp_parameters);
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
BKE_blendfile_link_append_context_flag_set(lapp_context, FILE_LINK, true);
BKE_blendfile_link_append_context_library_add(lapp_context, asset_lib_path, nullptr);
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
lapp_context, asset_name, ID_BR, nullptr);
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
BKE_blendfile_link(lapp_context, nullptr);
BKE_blendfile_override(lapp_context,
eBKELibLinkOverride(BKE_LIBLINK_OVERRIDE_USE_EXISTING_LIBOVERRIDES |
BKE_LIBLINK_OVERRIDE_CREATE_RUNTIME),
nullptr);
Brush *liboverride_brush = reinterpret_cast<Brush *>(
BKE_blendfile_link_append_context_item_liboverrideid_get(lapp_context, lapp_item));
BKE_blendfile_link_append_context_free(lapp_context);
return liboverride_brush;
}
/* Datablock add/copy/free/make_local */
Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode)

View File

@ -339,42 +339,6 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id)
return false;
}
int BKE_lib_override_user_edited_from_library_count(Main *bmain,
const ID_Type id_type,
Library *library,
ReportList *r_reports)
{
ListBase *lb = which_libbase(bmain, id_type);
int num_user_edited = 0;
for (ID *id_iter = static_cast<ID *>(lb->first); id_iter != nullptr;
id_iter = static_cast<ID *>(id_iter->next))
{
if (ID_IS_LINKED(id_iter)) {
break;
}
if (!ID_IS_OVERRIDE_LIBRARY(id_iter)) {
continue;
}
if (id_iter->override_library->reference->lib != library) {
continue;
}
if (!BKE_lib_override_library_is_user_edited(id_iter)) {
continue;
}
/* NOTE: If changes have been saved in a draft, then the local override is based on said
* draft (using the linked ID from the draft file as reference), so there should be no user
* edited changes anymore. */
num_user_edited++;
if (r_reports) {
BKE_report(r_reports, RPT_INFO, id_iter->name + 2);
}
}
return num_user_edited;
}
bool BKE_lib_override_library_property_is_animated(
const ID *id,
const IDOverrideLibraryProperty *liboverride_prop,

View File

@ -666,8 +666,6 @@ const Brush *BKE_paint_brush_for_read(const Paint *p)
void BKE_paint_brush_set(Paint *p, Brush *br)
{
if (p) {
id_us_min((ID *)p->brush);
id_us_plus((ID *)br);
p->brush = br;
BKE_paint_toolslots_brush_update(p);
@ -676,9 +674,7 @@ void BKE_paint_brush_set(Paint *p, Brush *br)
bool BKE_paint_brush_is_valid_asset(const Brush *brush)
{
return brush && (ID_IS_ASSET(&brush->id) ||
(!ID_IS_LINKED(&brush->id) && ID_IS_OVERRIDE_LIBRARY_REAL(&brush->id) &&
ID_IS_ASSET(brush->id.override_library->reference)));
return brush && ID_IS_ASSET(&brush->id);
}
static void paint_brush_asset_update(Paint &paint,
@ -689,9 +685,7 @@ static void paint_brush_asset_update(Paint &paint,
BKE_asset_weak_reference_free(&paint.brush_asset_reference);
}
if (brush == nullptr || brush != paint.brush || !ID_IS_OVERRIDE_LIBRARY_REAL(paint.brush) ||
!(ID_IS_ASSET(paint.brush) || ID_IS_ASSET(paint.brush->id.override_library->reference)))
{
if (brush == nullptr || brush != paint.brush || !(brush->id.tag & LIB_TAG_ASSET_MAIN)) {
BKE_asset_weak_reference_free(&brush_asset_reference);
return;
}
@ -743,7 +737,8 @@ void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint)
AssetWeakReference *brush_asset_reference = paint->brush_asset_reference;
paint->brush_asset_reference = nullptr;
Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, brush_asset_reference);
Brush *brush_asset = reinterpret_cast<Brush *>(
BKE_asset_weak_reference_ensure(bmain, brush_asset_reference));
/* Will either re-assign the brush_asset_reference to `paint`, or free it if loading a brush ID
* from it failed. */
@ -1302,13 +1297,7 @@ void BKE_paint_copy(const Paint *src, Paint *tar, const int flag)
tar->brush_asset_reference = BKE_asset_weak_reference_copy(src->brush_asset_reference);
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
id_us_plus((ID *)tar->brush);
id_us_plus((ID *)tar->palette);
if (src->tool_slots != nullptr) {
for (int i = 0; i < tar->tool_slots_len; i++) {
id_us_plus((ID *)tar->tool_slots[i].brush);
}
}
}
}

View File

@ -54,7 +54,6 @@ static void paint_toolslots_init(Main *bmain, Paint *paint)
BKE_paint_toolslots_len_ensure(paint, slot_index + 1);
if (paint->tool_slots[slot_index].brush == nullptr) {
paint->tool_slots[slot_index].brush = brush;
id_us_plus(&brush->id);
}
}
}
@ -121,10 +120,6 @@ void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush)
const int slot_index = BKE_brush_tool_get(brush, paint);
BKE_paint_toolslots_len_ensure(paint, slot_index + 1);
PaintToolSlot *tslot = &paint->tool_slots[slot_index];
id_us_plus(&brush->id);
if (tslot->brush) {
id_us_min(&tslot->brush->id);
}
tslot->brush = brush;
}
@ -148,7 +143,6 @@ void BKE_paint_toolslots_brush_validate(Main *bmain, Paint *paint)
if (tslot->brush) {
if ((i != BKE_brush_tool_get(tslot->brush, paint)) || (tslot->brush->ob_mode & ob_mode) == 0)
{
id_us_min(&tslot->brush->id);
tslot->brush = nullptr;
}
}

View File

@ -594,7 +594,7 @@ static void scene_foreach_paint(LibraryForeachIDData *data,
SCENE_FOREACH_UNDO_RESTORE,
reader,
&paint_old->brush,
IDWALK_CB_USER);
IDWALK_CB_NOP);
for (int i = 0; i < paint_old->tool_slots_len; i++) {
/* This is a bit tricky.
@ -613,7 +613,7 @@ static void scene_foreach_paint(LibraryForeachIDData *data,
SCENE_FOREACH_UNDO_RESTORE,
reader,
&paint_old->tool_slots[i].brush,
IDWALK_CB_USER);
IDWALK_CB_NOP);
}
Palette *palette_tmp = nullptr;

View File

@ -47,6 +47,7 @@
#include "BLT_translation.h"
#include "BKE_action.h"
#include "BKE_asset.hh"
#include "BKE_blender_version.h"
#include "BKE_blendfile.hh"
#include "BKE_cachefile.h"
@ -949,6 +950,8 @@ static void template_id_liboverride_hierarchy_make(bContext *C,
static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
{
/* TODO: select appropriate bmain for ID, might need to be asset main, changes in this functions
* are most likely wrong still. */
TemplateID *template_ui = (TemplateID *)arg_litem;
PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
ID *id = static_cast<ID *>(idptr.data);
@ -1003,11 +1006,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
case UI_ID_LOCAL:
if (id) {
Main *bmain = CTX_data_main(C);
Main *id_main = BKE_asset_weak_reference_main(bmain, id);
if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label);
template_id_liboverride_hierarchy_make(
C, id_main, template_ui, &idptr, &undo_push_label);
}
else {
if (BKE_lib_id_make_local(bmain, id, 0)) {
if (BKE_lib_id_make_local(id_main, id, 0)) {
BKE_id_newptr_and_tag_clear(id);
/* Reassign to get proper updates/notifiers. */
@ -1024,11 +1029,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
case UI_ID_OVERRIDE:
if (id && ID_IS_OVERRIDE_LIBRARY(id)) {
Main *bmain = CTX_data_main(C);
Main *id_main = BKE_asset_weak_reference_main(bmain, id);
if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label);
template_id_liboverride_hierarchy_make(
C, id_main, template_ui, &idptr, &undo_push_label);
}
else {
BKE_lib_override_library_make_local(bmain, id);
BKE_lib_override_library_make_local(id_main, id);
/* Reassign to get proper updates/notifiers. */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, nullptr);
@ -1043,15 +1050,16 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
(template_ui->ptr.type == &RNA_LayerObjects));
/* make copy */
Main *bmain = CTX_data_main(C);
Main *id_main = BKE_asset_weak_reference_main(bmain, id);
if (do_scene_obj) {
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ED_object_single_user(bmain, scene, (Object *)id);
ED_object_single_user(id_main, scene, (Object *)id);
WM_event_add_notifier(C, NC_WINDOW, nullptr);
DEG_relations_tag_update(bmain);
}
else {
Main *bmain = CTX_data_main(C);
id_single_user(C, id, &template_ui->ptr, template_ui->prop);
DEG_relations_tag_update(bmain);
}
@ -1342,7 +1350,7 @@ static void template_ID(const bContext *C,
/* text button with name */
if (id) {
char name[UI_MAX_NAME_STR];
const bool user_alert = (id->us <= 0);
const bool user_alert = (id->us <= 0) && !(id->tag & LIB_TAG_ASSET_MAIN);
const int width = template_search_textbut_width(&idptr,
RNA_struct_find_property(&idptr, "name"));
@ -1773,10 +1781,15 @@ static void ui_template_id(uiLayout *layout,
flag |= UI_ID_OPEN;
}
Main *id_main = CTX_data_main(C);
if (ptr->owner_id) {
id_main = BKE_asset_weak_reference_main(id_main, ptr->owner_id);
}
StructRNA *type = RNA_property_pointer_type(ptr, prop);
short idcode = RNA_type_to_ID_code(type);
template_ui->idcode = idcode;
template_ui->idlb = which_libbase(CTX_data_main(C), idcode);
template_ui->idlb = which_libbase(id_main, idcode);
/* create UI elements for this template
* - template_ID makes a copy of the template data and assigns it to the relevant buttons

View File

@ -37,6 +37,7 @@
#include "BKE_image.h"
#include "BKE_lib_id.hh"
#include "BKE_lib_override.hh"
#include "BKE_lib_remap.hh"
#include "BKE_main.hh"
#include "BKE_paint.hh"
#include "BKE_preferences.h"
@ -69,7 +70,7 @@ static int brush_add_exec(bContext *C, wmOperator * /*op*/)
// int type = RNA_enum_get(op->ptr, "type");
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *br = BKE_paint_brush(paint);
Main *bmain = CTX_data_main(C);
Main *bmain = CTX_data_main(C); // TODO: add to asset main?
PaintMode mode = BKE_paintmode_get_active_from_context(C);
if (br) {
@ -191,7 +192,7 @@ static int brush_add_gpencil_exec(bContext *C, wmOperator * /*op*/)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *br = BKE_paint_brush(paint);
Main *bmain = CTX_data_main(C);
Main *bmain = CTX_data_main(C); // TODO: add to asset main?
if (br) {
br = (Brush *)BKE_id_copy(bmain, &br->id);
@ -941,6 +942,7 @@ static int brush_select_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
// TODO: won't work with asset brushes, needs different main.
if (brush_generic_tool_set(C, bmain, paint, tool, tool_name, create_missing, toggle)) {
return OPERATOR_FINISHED;
}
@ -1001,10 +1003,12 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op)
/* This operator currently covers both cases: the file/asset browser file list and the asset list
* used for the asset-view template. Once the asset list design is used by the Asset Browser,
* this can be simplified to just that case. */
Main *bmain = CTX_data_main(C);
blender::asset_system::AssetRepresentation *asset = CTX_wm_asset(C);
AssetWeakReference *brush_asset_reference = asset->make_weak_reference();
Brush *brush = BKE_brush_asset_runtime_ensure(CTX_data_main(C), brush_asset_reference);
Brush *brush = reinterpret_cast<Brush *>(
BKE_asset_weak_reference_ensure(bmain, brush_asset_reference));
Paint *paint = BKE_paint_get_active_from_context(C);
@ -1212,6 +1216,7 @@ static bool brush_asset_save_as_poll(bContext *C)
static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
if (paint == nullptr || brush == nullptr) {
@ -1241,9 +1246,11 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
BLI_assert(BKE_paint_brush_is_valid_asset(brush));
/* Save to asset library. */
Main *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id);
std::string final_full_asset_filepath;
const bool sucess = brush_asset_write_in_library(
CTX_data_main(C), brush, name, filepath, final_full_asset_filepath, op->reports);
asset_main, brush, name, filepath, final_full_asset_filepath, op->reports);
if (!sucess) {
BKE_report(op->reports, RPT_ERROR, "Failed to write to asset library");
@ -1258,8 +1265,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
/* 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());
Main *bmain = CTX_data_main(C);
brush = BKE_brush_asset_runtime_ensure(bmain, new_brush_weak_ref);
brush = reinterpret_cast<Brush *>(BKE_asset_weak_reference_ensure(bmain, new_brush_weak_ref));
if (!BKE_paint_brush_asset_set(paint, brush, new_brush_weak_ref)) {
/* Note brush sset was still saved in editable asset library, so was not a no-op. */
@ -1347,9 +1353,10 @@ static bool brush_asset_delete_poll(bContext *C)
static int brush_asset_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
Main *bmain = CTX_data_main(C);
Main *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id);
if (paint->brush_asset_reference && BKE_paint_brush_is_valid_asset(brush)) {
/* Delete from asset library on disk. */
@ -1363,15 +1370,12 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op)
}
}
/* Delete from session. If local override, also delete linked one.
* TODO: delete both in one step? */
ID *original_brush = (!ID_IS_LINKED(&brush->id) && ID_IS_OVERRIDE_LIBRARY_REAL(&brush->id)) ?
brush->id.override_library->reference :
nullptr;
BKE_id_delete(bmain, brush);
if (original_brush) {
BKE_id_delete(bmain, original_brush);
if (asset_main != bmain) {
// TODO: hack: no pointer should exist, should do runtime lookup
BKE_libblock_remap(bmain, brush, nullptr, 0);
}
BKE_id_delete(asset_main, brush);
// TODO: delete whole asset main if empty?
brush_asset_refresh_editable_library(C);
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr);
@ -1415,11 +1419,15 @@ static bool brush_asset_update_poll(bContext *C)
static int brush_asset_update_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = nullptr;
const AssetWeakReference *brush_weak_ref =
BKE_paint_brush_asset_get(paint, &brush).value_or(nullptr);
// TODO: maybe can check directly in poll
BLI_assert((brush->id.tag & LIB_TAG_ASSET_MAIN) != 0);
char path_buffer[FILE_MAX_LIBEXTRA];
char *filepath;
AS_asset_full_path_explode_from_weak_ref(
@ -1427,13 +1435,10 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op)
BLI_assert(BKE_paint_brush_is_valid_asset(brush));
Main *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id);
std::string final_full_asset_filepath;
brush_asset_write_in_library(CTX_data_main(C),
brush,
brush->id.name + 2,
filepath,
final_full_asset_filepath,
op->reports);
brush_asset_write_in_library(
asset_main, brush, brush->id.name + 2, filepath, final_full_asset_filepath, op->reports);
return OPERATOR_FINISHED;
}

View File

@ -998,6 +998,15 @@ enum {
* RESET_NEVER
*/
LIB_TAG_NOT_ALLOCATED = 1 << 26,
/**
* ID is part of an asset #Main separate from the regular main database.
*
* RESET_NEVER
*
* Datablocks like this can not be linked to and from datablocks in regular main.
* They should stay isolated from each other.
*/
LIB_TAG_ASSET_MAIN = 1 << 20,
/* ------------------------------------------------------------------------------------------- */

View File

@ -1271,6 +1271,8 @@ static char *rna_def_property_set_func(
fprintf(f, " }\n");
}
/* TODO: check here for wrong linking between regular and asset main? */
if (prop->flag & PROP_ID_REFCOUNT) {
fprintf(f, "\n if (data->%s) {\n", dp->dnaname);
fprintf(f, " id_us_min((ID *)data->%s);\n", dp->dnaname);

View File

@ -16,6 +16,7 @@
#include "BLI_utildefines.h"
#include "BKE_asset.hh"
#include "BKE_icons.h"
#include "BKE_lib_id.hh"
#include "BKE_main_namemap.hh"
@ -283,10 +284,13 @@ int rna_ID_name_length(PointerRNA *ptr)
void rna_ID_name_set(PointerRNA *ptr, const char *value)
{
ID *id = (ID *)ptr->data;
BKE_main_namemap_remove_name(G_MAIN, id, id->name + 2);
Main *id_main = BKE_asset_weak_reference_main(G_MAIN, id);
BKE_main_namemap_remove_name(id_main, id, id->name + 2);
BLI_strncpy_utf8(id->name + 2, value, sizeof(id->name) - 2);
BLI_assert(BKE_id_is_in_global_main(id));
BKE_libblock_ensure_unique_name(G_MAIN, id);
/* TODO: add BKE_id_is_in_editable_main? */
/* TODO: this does update immediately in the asset shelf. */
BLI_assert(BKE_id_is_in_global_main(id) || (id->tag & LIB_TAG_ASSET_MAIN));
BKE_libblock_ensure_unique_name(id_main, id);
if (GS(id->name) == ID_OB) {
Object *ob = (Object *)id;
@ -306,7 +310,8 @@ static int rna_ID_name_editable(PointerRNA *ptr, const char ** /*r_info*/)
return 0;
}
}
else if (!BKE_id_is_in_global_main(id)) {
/* TODO: add BKE_id_is_in_editable_main? */
else if (!(BKE_id_is_in_global_main(id) || (id->tag & LIB_TAG_ASSET_MAIN))) {
return 0;
}
@ -1135,9 +1140,11 @@ int rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assig
short *totcol = BKE_id_material_len_p(id);
Material *mat_id = (Material *)assign_ptr->owner_id;
if (totcol && (key >= 0 && key < *totcol)) {
Main *id_main = BKE_asset_weak_reference_main(G_MAIN, id);
/* TODO: BKE_id_is_in_editable_main? */
BLI_assert(BKE_id_is_in_global_main(id));
BLI_assert(BKE_id_is_in_global_main(&mat_id->id));
BKE_id_material_assign(G_MAIN, id, mat_id, key + 1);
BKE_id_material_assign(id_main, id, mat_id, key + 1);
return 1;
}
else {
@ -1193,8 +1200,10 @@ static void rna_IDMaterials_clear_id(ID *id, Main *bmain)
static void rna_Library_filepath_set(PointerRNA *ptr, const char *value)
{
Library *lib = (Library *)ptr->data;
Main *id_main = BKE_asset_weak_reference_main(G_MAIN, &lib->id);
/* TODO: BKE_id_is_in_editable_main? */
BLI_assert(BKE_id_is_in_global_main(&lib->id));
BKE_library_filepath_set(G_MAIN, lib, value);
BKE_library_filepath_set(id_main, lib, value);
}
/* ***** ImagePreview ***** */
@ -2282,6 +2291,14 @@ static void rna_def_ID(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Is Indirect", "Is this ID block linked indirectly");
prop = RNA_def_property(srna, "is_asset_library_data", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "tag", LIB_TAG_ASSET_MAIN);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop,
"Asset Library Data",
"This data-block is part of an asset library blend file, not the blend "
"file opened for editing");
prop = RNA_def_property(srna, "library", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, nullptr, "lib");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);

View File

@ -27,6 +27,7 @@
# include "BKE_action.h"
# include "BKE_armature.hh"
# include "BKE_asset.hh"
# include "BKE_brush.hh"
# include "BKE_camera.h"
# include "BKE_collection.h"
@ -125,6 +126,18 @@ static void rna_Main_ID_remove(Main *bmain,
id->name + 2);
return;
}
/* TODO: also check reverse case somehow? */
if (bmain != BKE_asset_weak_reference_main(bmain, id)) {
BKE_reportf(reports,
RPT_ERROR,
"%s '%s' is part of a difference main database and should be removed from there",
BKE_idtype_idcode_to_name(GS(id->name)),
id->name + 2);
return;
}
/* TODO: this will not clear pointers from regular main to this asset.
* Those probably should not exist, and be purely runtime lookups? */
if (do_unlink) {
BKE_id_delete(bmain, id);
RNA_POINTER_INVALIDATE(id_ptr);

View File

@ -146,11 +146,6 @@ static void wm_history_file_write();
static void wm_test_autorun_revert_action_exec(bContext *C);
static bool wm_operator_open_file_draft_check_dialog(bContext *C,
wmOperator *op,
const int num_user_edited_lost,
ReportList *user_edited_lost_reports);
static CLG_LogRef LOG = {"wm.files"};
/* -------------------------------------------------------------------- */
@ -2830,7 +2825,6 @@ static int operator_state_dispatch(bContext *C, wmOperator *op, OperatorDispatch
enum {
OPEN_MAINFILE_STATE_DISCARD_CHANGES,
OPEN_MAINFILE_STATE_SELECT_FILE_PATH,
OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS,
OPEN_MAINFILE_STATE_OPEN,
};
@ -2848,7 +2842,7 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
set_next_operator_state(op, OPEN_MAINFILE_STATE_SELECT_FILE_PATH);
}
else {
set_next_operator_state(op, OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS);
set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
}
if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) {
@ -2888,44 +2882,6 @@ static int wm_open_mainfile__select_file_path(bContext *C, wmOperator *op)
return OPERATOR_RUNNING_MODAL;
}
static int wm_open_mainfile__discard_asset_drafts(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
char filepath[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filepath);
set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
Library *lib = static_cast<Library *>(
BLI_findstring(&bmain->libraries, filepath, offsetof(Library, filepath_abs)));
if (lib == nullptr) {
return wm_open_mainfile_dispatch(C, op);
}
/* If new to-be-opened blendfile was a library of the currently opened one, check for potential
* persistent edited assets that would be reset to their library status (only Brushes IDs
* currently). */
ReportList *reports = MEM_cnew<ReportList>(__func__);
BKE_reports_init(reports, RPT_STORE);
const int num_user_edited_brushes = BKE_lib_override_user_edited_from_library_count(
bmain, ID_BR, lib, reports);
if (num_user_edited_brushes > 0) {
/* NOTE: steals ownership of `user_edited_lost_reports` regardless of its result. */
if (wm_operator_open_file_draft_check_dialog(C, op, num_user_edited_brushes, reports)) {
return OPERATOR_INTERFACE;
}
reports = nullptr;
}
else {
BKE_reports_clear(reports);
MEM_delete(reports);
}
return wm_open_mainfile_dispatch(C, op);
}
static int wm_open_mainfile__open(bContext *C, wmOperator *op)
{
char filepath[FILE_MAX];
@ -2958,7 +2914,6 @@ static int wm_open_mainfile__open(bContext *C, wmOperator *op)
static OperatorDispatchTarget wm_open_mainfile_dispatch_targets[] = {
{OPEN_MAINFILE_STATE_DISCARD_CHANGES, wm_open_mainfile__discard_changes},
{OPEN_MAINFILE_STATE_SELECT_FILE_PATH, wm_open_mainfile__select_file_path},
{OPEN_MAINFILE_STATE_DISCARD_ASSET_DRAFTS, wm_open_mainfile__discard_asset_drafts},
{OPEN_MAINFILE_STATE_OPEN, wm_open_mainfile__open},
{0, nullptr},
};
@ -4533,217 +4488,3 @@ bool wm_operator_close_file_dialog_if_needed(bContext *C,
}
/** \} */
/**
* \name Open Asset Library File Dialog.
*
* This handles cases where user is opening a file that is an asset library, which assets are used
* in the 'user preference' way (i.e. assets are linked, and have runtime-only, session-persistant,
* user-editable overrides of these.
*
* Special warning is necessary because when opening the library file, all non-drafted user edits
* of the relevant assets will be lost. */
/** \{ */
static const char *open_file_draft_check_dialog_name = "open_file_draft_check_popup";
typedef struct wmOpenDraftCheckCallback {
IDProperty *op_properties;
ReportList *user_edited_lost_reports;
int num_user_edited_lost;
char new_filepath[FILE_MAX];
} wmOpenDraftCheckCallback;
static void wm_block_open_file_draft_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
{
wmWindow *win = CTX_wm_window(C);
UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
}
static void wm_block_open_file_draft_discard(bContext *C, void *arg_block, void *arg_data)
{
wmGenericCallback *callback = WM_generic_callback_steal((wmGenericCallback *)arg_data);
/* Close the popup before executing the callback. Otherwise
* the popup might be closed by the callback, which will lead
* to a crash. */
wmWindow *win = CTX_wm_window(C);
UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
callback->exec(C, callback->user_data);
WM_generic_callback_free(callback);
}
static void wm_block_open_file_draft_save(bContext *C, void *arg_block, void *arg_data)
{
wmGenericCallback *callback = WM_generic_callback_steal((wmGenericCallback *)arg_data);
bool execute_callback = true;
wmWindow *win = CTX_wm_window(C);
UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
/* TODO: Actually save the edited lost local runtime assets overrides into drafts. */
if (execute_callback) {
callback->exec(C, callback->user_data);
}
WM_generic_callback_free(callback);
}
static void wm_block_open_file_draft_cancel_button(uiBlock *block, wmGenericCallback *post_action)
{
uiBut *but = uiDefIconTextBut(
block, UI_BTYPE_BUT, 0, 0, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, "");
UI_but_func_set(but, wm_block_open_file_draft_cancel, block, post_action);
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
}
static void wm_block_open_file_draft_discard_button(uiBlock *block, wmGenericCallback *post_action)
{
uiBut *but = uiDefIconTextBut(
block, UI_BTYPE_BUT, 0, 0, IFACE_("Ignore"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, "");
UI_but_func_set(but, wm_block_open_file_draft_discard, block, post_action);
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
}
static void wm_block_open_file_draft_save_button(uiBlock *block, wmGenericCallback *post_action)
{
uiBut *but = uiDefIconTextBut(
block, UI_BTYPE_BUT, 0, 0, IFACE_("Save To Draft"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, "");
UI_but_func_set(but, wm_block_open_file_draft_save, block, post_action);
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT);
}
static void wm_open_file_after_draft_check_dialog_callback(bContext *C, void *user_data)
{
wmOpenDraftCheckCallback *callback_data = static_cast<wmOpenDraftCheckCallback *>(user_data);
WM_operator_name_call_with_properties(
C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, callback_data->op_properties, nullptr);
}
static uiBlock *block_create__open_draft_check_file_dialog(struct bContext *C,
struct ARegion *region,
void *arg1)
{
wmGenericCallback *post_action = static_cast<wmGenericCallback *>(arg1);
wmOpenDraftCheckCallback *callback_data = static_cast<wmOpenDraftCheckCallback *>(
post_action->user_data);
uiBlock *block = UI_block_begin(C, region, open_file_draft_check_dialog_name, UI_EMBOSS);
UI_block_flag_enable(
block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION);
/* Title. */
uiItemL_ex(layout, TIP_("Save User-Edited Assets to Draft?"), ICON_NONE, true, false);
char message[2048];
SNPRINTF(message,
TIP_("The following %d assets have local edits that will be lost"),
callback_data->num_user_edited_lost);
uiItemL(layout, message, ICON_NONE);
uiItemL(layout, "by opening the chosen Blender Asset Library file", ICON_NONE);
uiItemL(layout, callback_data->new_filepath, ICON_NONE);
/* Draw available report messages. */
LISTBASE_FOREACH (Report *, report, &callback_data->user_edited_lost_reports->list) {
uiLayout *row = uiLayoutColumn(layout, false);
uiLayoutSetScaleY(row, 0.6f);
uiItemS(row);
uiItemL_ex(row, report->message, ICON_NONE, false, true);
}
uiItemS_ex(layout, 4.0f);
/* Buttons. */
#ifdef _WIN32
const bool windows_layout = true;
#else
const bool windows_layout = false;
#endif
if (windows_layout) {
/* Windows standard layout. */
uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
uiLayoutSetScaleY(split, 1.2f);
uiLayoutColumn(split, false);
wm_block_open_file_draft_save_button(block, post_action);
uiLayoutColumn(split, false);
wm_block_open_file_draft_discard_button(block, post_action);
uiLayoutColumn(split, false);
wm_block_open_file_draft_cancel_button(block, post_action);
}
else {
/* Non-Windows layout (macOS and Linux). */
uiLayout *split = uiLayoutSplit(layout, 0.3f, true);
uiLayoutSetScaleY(split, 1.2f);
uiLayoutColumn(split, false);
wm_block_open_file_draft_discard_button(block, post_action);
uiLayout *split_right = uiLayoutSplit(split, 0.1f, true);
uiLayoutColumn(split_right, false);
/* Empty space. */
uiLayoutColumn(split_right, false);
wm_block_open_file_draft_cancel_button(block, post_action);
uiLayoutColumn(split_right, false);
wm_block_open_file_draft_save_button(block, post_action);
}
UI_block_bounds_set_centered(block, int(14 * U.scale_factor));
return block;
}
static void wm_free_open_file_draft_check_callback(void *user_data)
{
wmOpenDraftCheckCallback *callback_data = static_cast<wmOpenDraftCheckCallback *>(user_data);
IDP_FreeProperty(callback_data->op_properties);
BKE_reports_clear(callback_data->user_edited_lost_reports);
MEM_delete(callback_data->user_edited_lost_reports);
MEM_delete(callback_data);
}
/* NOTE: steals ownership of `user_edited_lost_reports`. */
static bool wm_operator_open_file_draft_check_dialog(bContext *C,
wmOperator *op,
const int num_user_edited_lost,
ReportList *user_edited_lost_reports)
{
wmOpenDraftCheckCallback *callback_data = MEM_cnew<wmOpenDraftCheckCallback>(__func__);
callback_data->op_properties = IDP_CopyProperty(op->properties);
RNA_string_get(op->ptr, "filepath", callback_data->new_filepath);
callback_data->num_user_edited_lost = num_user_edited_lost;
callback_data->user_edited_lost_reports = user_edited_lost_reports;
wmGenericCallback *callback = MEM_cnew<wmGenericCallback>(__func__);
callback->exec = wm_open_file_after_draft_check_dialog_callback;
callback->user_data = callback_data;
callback->free_user_data = wm_free_open_file_draft_check_callback;
if (!UI_popup_block_name_exists(CTX_wm_screen(C), open_file_draft_check_dialog_name)) {
UI_popup_block_invoke(
C, block_create__open_draft_check_file_dialog, callback, free_post_file_close_action);
return true;
}
WM_generic_callback_free(callback);
return false;
}
/** \} */

View File

@ -33,8 +33,10 @@
#include "BLO_undofile.hh"
#include "BLO_writefile.hh"
#include "BKE_asset.hh"
#include "BKE_blender.h"
#include "BKE_blendfile.hh"
#include "BKE_brush.hh"
#include "BKE_callbacks.h"
#include "BKE_context.hh"
#include "BKE_global.h"
@ -580,6 +582,7 @@ void WM_exit_ex(bContext *C, const bool do_python_exit, const bool do_user_exit_
ED_preview_restart_queue_free();
ed::asset::list::storage_exit();
BKE_asset_weak_reference_main_free();
BKE_tracking_clipboard_free();
BKE_mask_clipboard_free();
BKE_vfont_clipboard_free();