WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 354 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
23 changed files with 297 additions and 646 deletions
Showing only changes of commit b030911aa9 - 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

@ -11,6 +11,7 @@
#include "BLI_compiler_attrs.h"
#include "BLI_utildefines.h"
#include "DNA_ID_enums.h"
#include "DNA_asset_types.h"
struct AssetLibraryReference;
@ -20,6 +21,7 @@ struct BlendDataReader;
struct BlendWriter;
struct ID;
struct IDProperty;
struct Main;
struct PreviewImage;
using PreSaveFn = void (*)(void *asset_ptr, AssetMetaData *asset_data);
@ -78,3 +80,9 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data)
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,
ID_Type id_type,
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.hh"
#include "DNA_asset_types.h"
@ -118,3 +128,131 @@ 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 filepath;
Main *main;
AssetWeakReferenceMain(std::string filepath)
: filepath(std::move(filepath)), main(BKE_main_new())
{
}
AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete;
AssetWeakReferenceMain(AssetWeakReferenceMain &&other)
: filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr))
{
}
~AssetWeakReferenceMain()
{
if (main) {
BKE_main_free(main);
}
}
};
static Vector<AssetWeakReferenceMain> &get_weak_reference_mains()
{
static Vector<AssetWeakReferenceMain> mains;
return 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 : get_weak_reference_mains()) {
/* TODO: Look into make this whole thing more efficient. */
ListBase *lb = which_libbase(weak_ref_main.main, GS(id->name));
LISTBASE_FOREACH (ID *, other_id, lb) {
if (id == other_id) {
return weak_ref_main.main;
}
}
}
BLI_assert_unreachable();
return nullptr;
}
static Main &asset_weak_reference_main_ensure(const StringRef filepath)
{
for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) {
if (weak_ref_main.filepath == filepath) {
return *weak_ref_main.main;
}
}
get_weak_reference_mains().append_as(filepath);
return *get_weak_reference_mains().last().main;
}
void BKE_asset_weak_reference_main_free()
{
get_weak_reference_mains().clear_and_shrink();
}
ID *BKE_asset_weak_reference_ensure(Main &global_main,
const ID_Type id_type,
const AssetWeakReference &weak_ref)
{
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;
}
BLI_assert(asset_name != nullptr);
/* If weak reference resolves to a null library path, assume we are in local asset case. */
Main &bmain = asset_lib_path ? asset_weak_reference_main_ensure(asset_lib_path) : global_main;
/* Check if we have the asset already, or if it's global main and there is nothing we can add. */
ID *local_asset = BKE_libblock_find_name(&bmain, id_type, asset_name);
if (local_asset || asset_lib_path == nullptr) {
BLI_assert(local_asset == nullptr || ID_IS_ASSET(local_asset));
return local_asset;
}
/* Load asset from asset library. */
LibraryLink_Params lapp_params{};
lapp_params.bmain = &bmain;
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
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);
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
lapp_context, asset_name, id_type, 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);
local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item);
BKE_blendfile_link_append_context_free(lapp_context);
/* TODO: only do for new ones? */
BKE_main_id_tag_all(&bmain, LIB_TAG_ASSET_MAIN, true);
/* Verify that the name matches. It must for referencing the same asset again to work. */
BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name));
return local_asset;
}

View File

@ -302,188 +302,6 @@ static bool reuse_bmain_data_remapper_is_id_remapped(id::IDRemapper &remapper, I
return false;
}
static Library *reuse_bmain_data_dependencies_new_library_get(ReuseOldBMainData *reuse_data,
Library *old_lib)
{
if (old_lib == nullptr) {
return nullptr;
}
id::IDRemapper *remapper = reuse_data->remapper;
Library *new_lib = old_lib;
IDRemapperApplyResult result = remapper->apply(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. */
id::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. */
remapper.add(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. */
remapper.add(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);
id::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)) {
remapper.add(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. */
remapper.add(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.
@ -491,9 +309,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)
{
@ -906,12 +723,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

@ -25,7 +25,6 @@
#include "BLT_translation.hh"
#include "BKE_asset.hh"
#include "BKE_blendfile_link_append.hh"
#include "BKE_bpath.hh"
#include "BKE_brush.hh"
#include "BKE_colortools.hh"
@ -526,60 +525,6 @@ static void brush_defaults(Brush *brush)
#undef FROM_DEFAULT_PTR
}
Brush *BKE_brush_asset_runtime_ensure(Main *bmain, const AssetWeakReference &brush_asset_reference)
{
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

@ -341,42 +341,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

@ -668,8 +668,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);
@ -678,9 +676,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,
{
MEM_delete(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)) {
return;
}
@ -742,7 +736,8 @@ void BKE_paint_brush_asset_restore(Main *bmain, Paint *paint)
MEM_delete(paint->brush_asset_reference);
paint->brush_asset_reference = nullptr;
Brush *brush_asset = BKE_brush_asset_runtime_ensure(bmain, weak_ref);
Brush *brush_asset = reinterpret_cast<Brush *>(
BKE_asset_weak_reference_ensure(*bmain, ID_BR, weak_ref));
/* 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 *dst, const int flag)
}
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
id_us_plus((ID *)dst->brush);
id_us_plus((ID *)dst->palette);
if (src->tool_slots != nullptr) {
for (int i = 0; i < dst->tool_slots_len; i++) {
id_us_plus((ID *)dst->tool_slots[i].brush);
}
}
}
}

View File

@ -53,7 +53,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);
}
}
}
@ -120,10 +119,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;
}
@ -147,7 +142,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

@ -591,7 +591,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.
@ -610,7 +610,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

@ -43,6 +43,8 @@
#include "BLF_api.hh"
#include "BLT_translation.hh"
#include "BKE_action.h"
#include "BKE_asset.hh"
#include "BKE_blender_version.h"
#include "BKE_blendfile.hh"
#include "BKE_colorband.hh"
@ -943,6 +945,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);
@ -996,12 +1000,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
break;
case UI_ID_LOCAL:
if (id) {
Main *bmain = CTX_data_main(C);
Main *id_main = BKE_asset_weak_reference_main(CTX_data_main(C), 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. */
@ -1017,12 +1022,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
break;
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(CTX_data_main(C), 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);
@ -1037,15 +1043,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);
}
@ -1336,7 +1343,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"));
@ -1755,10 +1762,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

@ -35,6 +35,7 @@
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
#include "BKE_appdir.hh"
#include "BKE_asset.hh"
#include "BKE_blender_copybuffer.hh"
#include "BKE_brush.hh"
#include "BKE_context.hh"
@ -821,22 +822,26 @@ void MATERIAL_OT_new(wmOperatorType *ot)
static int new_texture_exec(bContext *C, wmOperator * /*op*/)
{
Tex *tex = static_cast<Tex *>(CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data);
Main *bmain = CTX_data_main(C);
PointerRNA ptr;
PropertyRNA *prop;
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
Main *id_main = CTX_data_main(C);
if (ptr.owner_id) {
id_main = BKE_asset_weak_reference_main(id_main, ptr.owner_id);
}
/* add or copy texture */
Tex *tex = static_cast<Tex *>(CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data);
if (tex) {
tex = (Tex *)BKE_id_copy(bmain, &tex->id);
tex = (Tex *)BKE_id_copy(id_main, &tex->id);
}
else {
tex = BKE_texture_add(bmain, DATA_("Texture"));
tex = BKE_texture_add(id_main, DATA_("Texture"));
}
/* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
if (prop) {
/* when creating new ID blocks, use is already 1, but RNA
* pointer use also increases user, so this compensates it */

View File

@ -35,6 +35,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"
@ -72,7 +73,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) {
@ -194,7 +195,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);
@ -944,6 +945,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;
}
@ -997,6 +999,7 @@ 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);
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports);
if (!asset) {
@ -1004,7 +1007,8 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op)
}
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, ID_BR, brush_asset_reference));
Paint *paint = BKE_paint_get_active_from_context(C);
@ -1292,6 +1296,7 @@ static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary
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) {
@ -1343,8 +1348,10 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
library->catalog_service->write_to_disk(filepath);
/* 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),
const bool sucess = brush_asset_write_in_library(asset_main,
brush,
name,
filepath,
@ -1364,8 +1371,8 @@ 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, ID_BR, 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. */
@ -1480,9 +1487,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);
bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name(
&U, paint->brush_asset_reference->asset_library_identifier);
@ -1502,15 +1510,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?
refresh_asset_library(C, *library);
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr);
@ -1554,11 +1559,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 *asset_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(
@ -1566,8 +1575,9 @@ 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_asset_write_in_library(asset_main,
brush,
brush->id.name + 2,
filepath,
@ -1606,12 +1616,21 @@ static int brush_asset_revert_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 *asset_main = BKE_asset_weak_reference_main(bmain, &brush->id);
/* TODO: check if doing this for the hierarchy is ok. */
/* TODO: the overrides don't update immediately when tweaking brush settings. */
BKE_lib_override_library_id_hierarchy_reset(bmain, &brush->id, false);
// TODO: delete and reload dependencies too?
// TODO: hack to make remapping work, should not be needed
// if we can make brush pointer not part of ID management at all
BLI_remlink(&asset_main->brushes, brush);
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
Brush *new_brush = reinterpret_cast<Brush *>(
BKE_asset_weak_reference_ensure(*bmain, ID_BR, *paint->brush_asset_reference));
BKE_libblock_remap(bmain, brush, new_brush, 0);
BLI_addtail(&asset_main->brushes, brush);
BKE_id_delete(asset_main, brush);
WM_main_add_notifier(NC_BRUSH | NA_EDITED, new_brush);
return OPERATOR_FINISHED;
}

View File

@ -34,6 +34,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "BKE_asset.hh"
#include "BKE_colortools.hh"
#include "BKE_context.hh"
#include "BKE_global.hh"
@ -2532,7 +2533,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
{
SpaceImage *sima;
Image *ima;
Main *bmain;
Main *id_main;
PropertyRNA *prop;
char name_buffer[MAX_ID_NAME - 2];
const char *name;
@ -2540,9 +2541,14 @@ static int image_new_exec(bContext *C, wmOperator *op)
int width, height, floatbuf, gen_type, alpha;
int stereo3d;
ImageNewData *data = image_new_init(C, op);
/* retrieve state */
sima = CTX_wm_space_image(C);
bmain = CTX_data_main(C);
id_main = CTX_data_main(C);
if (data->pprop.ptr.owner_id) {
id_main = BKE_asset_weak_reference_main(id_main, data->pprop.ptr.owner_id);
}
prop = RNA_struct_find_property(op->ptr, "name");
RNA_property_string_get(op->ptr, prop, name_buffer);
@ -2566,7 +2572,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
color[3] = 1.0f;
}
ima = BKE_image_add_generated(bmain,
ima = BKE_image_add_generated(id_main,
width,
height,
name,
@ -2584,8 +2590,6 @@ static int image_new_exec(bContext *C, wmOperator *op)
}
/* hook into UI */
ImageNewData *data = image_new_init(C, op);
if (data->pprop.prop) {
/* when creating new ID blocks, use is already 1, but RNA
* pointer use also increases user, so this compensates it */
@ -2596,7 +2600,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
RNA_property_update(C, &data->pprop.ptr, data->pprop.prop);
}
else if (sima) {
ED_space_image_set(bmain, sima, ima, false);
ED_space_image_set(id_main, sima, ima, false);
}
else {
/* #BKE_image_add_generated creates one user by default, remove it if image is not linked to
@ -2604,7 +2608,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
id_us_min(&ima->id);
}
BKE_image_signal(bmain, ima, (sima) ? &sima->iuser : nullptr, IMA_SIGNAL_USER_NEW_IMAGE);
BKE_image_signal(id_main, ima, (sima) ? &sima->iuser : nullptr, IMA_SIGNAL_USER_NEW_IMAGE);
WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima);

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

@ -962,6 +962,11 @@ typedef struct PaintToolSlot {
/** Paint Tool Base. */
typedef struct Paint {
/**
* The active brush. Possibly null. Possibly stored in a separate #Main data-base and not user-
* counted.
* //TODO: Or always stored in a separate #Main?
*/
struct Brush *brush;
/**

View File

@ -1270,6 +1270,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"
@ -285,12 +286,13 @@ int rna_ID_name_length(PointerRNA *ptr)
void rna_ID_name_set(PointerRNA *ptr, const char *value)
{
ID *id = (ID *)ptr->data;
BLI_assert(BKE_id_is_in_global_main(id));
BLI_assert(!ID_IS_LINKED(id));
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);
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;
@ -323,7 +325,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))) {
if (r_info) {
*r_info = N_("Datablocks not in global Main data-base cannot be renamed");
}
@ -1155,9 +1158,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 {
@ -1213,8 +1218,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 ***** */
@ -2302,6 +2309,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.hh"
@ -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},
};
@ -4529,217 +4484,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,11 @@
#include "BLO_undofile.hh"
#include "BLO_writefile.hh"
#include "BKE_asset.hh"
#include "BKE_blender.hh"
#include "BKE_blendfile.hh"
#include "BKE_brush.hh"
#include "BKE_callbacks.hh"
#include "BKE_context.hh"
#include "BKE_global.hh"
#include "BKE_icons.h"
@ -576,6 +579,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();