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
27 changed files with 296 additions and 1032 deletions

View File

@ -107,10 +107,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
kmi_hack_properties = kmi_hack.properties
kmi_hack.active = False
kmi_hack_brush_select = keymap.keymap_items.new("paint.brush_select", 'NONE', 'PRESS')
kmi_hack_brush_select_properties = kmi_hack_brush_select.properties
kmi_hack_brush_select.active = False
if use_release_confirm or use_tap_reset:
kmi_toolbar = wm.keyconfigs.find_item_from_operator(
idname="wm.toolbar",
@ -169,46 +165,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
include={'KEYBOARD'},
)[1]
if kmi_found is None:
if item.data_block:
# PAINT_OT_brush_select
mode = context.active_object.mode
# See: BKE_paint_get_tool_prop_id_from_paintmode
if space_type == 'IMAGE_EDITOR':
if context.space_data.mode == 'PAINT':
attr = "image_tool"
else:
attr = None
elif space_type == 'VIEW_3D':
attr = {
'SCULPT': "sculpt_tool",
'VERTEX_PAINT': "vertex_tool",
'WEIGHT_PAINT': "weight_tool",
'TEXTURE_PAINT': "image_tool",
'PAINT_GPENCIL': "gpencil_tool",
'VERTEX_GPENCIL': "gpencil_vertex_tool",
'SCULPT_GPENCIL': "gpencil_sculpt_tool",
'WEIGHT_GPENCIL': "gpencil_weight_tool",
'SCULPT_CURVES': "curves_sculpt_tool",
}.get(mode, None)
else:
attr = None
if attr is not None:
setattr(kmi_hack_brush_select_properties, attr, item.data_block)
kmi_found = wm.keyconfigs.find_item_from_operator(
idname="paint.brush_select",
context='INVOKE_REGION_WIN',
properties=kmi_hack_brush_select_properties,
include={'KEYBOARD'},
)[1]
elif mode in {'EDIT', 'PARTICLE_EDIT', 'SCULPT_GPENCIL'}:
# Doesn't use brushes
pass
else:
print("Unsupported mode:", mode)
del mode, attr
else:
kmi_found = None

View File

@ -5501,27 +5501,6 @@ def km_sculpt(params):
op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}),
op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}),
*_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event),
# Tools
("paint.brush_select", {"type": 'V', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'DRAW')]}),
("paint.brush_select", {"type": 'S', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'SMOOTH')]}),
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'PINCH')]}),
("paint.brush_select", {"type": 'I', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'INFLATE')]}),
("paint.brush_select", {"type": 'G', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'GRAB')]}),
("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True},
{"properties": [("sculpt_tool", 'SCRAPE')]}),
("paint.brush_select", {"type": 'C', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'CLAY_STRIPS')]}),
("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True},
{"properties": [("sculpt_tool", 'CREASE')]}),
("paint.brush_select", {"type": 'K', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'SNAKE_HOOK')]}),
("paint.brush_select", {"type": 'M', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}),
])
# Lasso Masking.

View File

@ -149,7 +149,7 @@ class BrushSelectPanel(BrushPanel):
row = layout.row()
# TODO: hide buttons since they are confusing with menu entries.
# But some of this functionality may still be needed.
row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=True)
row.column().template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
if brush is None:
return
@ -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. */
@ -1259,20 +1254,6 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
BKE_paint_ensure_from_paintmode(bmain, sce, mode);
/* If there's no brush, create one */
if (PAINT_MODE_HAS_BRUSH(mode)) {
Brush *brush = BKE_paint_brush(paint);
if (brush == nullptr) {
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
brush = BKE_brush_first_search(bmain, ob_mode);
if (!brush) {
brush = BKE_brush_add(bmain, "Brush", ob_mode);
id_us_min(&brush->id); /* Fake user only. */
}
BKE_paint_brush_set(paint, brush);
}
}
copy_v3_v3_uchar(paint->paint_cursor_col, col);
paint->paint_cursor_col[3] = 128;
ups->last_stroke_valid = false;
@ -1302,13 +1283,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

@ -559,7 +559,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* There are different kinds of shortcuts:
*
* - Direct access to the tool (as if the toolbar button is pressed).
* - The key is bound to a brush type (not the exact brush name).
* - The key is assigned to the operator itself
* (bypassing the tool, executing the operator).
*
@ -567,36 +566,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
*/
std::string shortcut = UI_but_string_get_operator_keymap(*C, *but);
if (shortcut.empty()) {
const PaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
if (tool_attr != nullptr) {
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
const char *tool_id_lstrip = strrchr(tool_id, '.');
const int tool_id_offset = tool_id_lstrip ? ((tool_id_lstrip - tool_id) + 1) : 0;
const int i = RNA_enum_from_name(items, tool_id + tool_id_offset);
if (i != -1) {
wmOperatorType *ot = WM_operatortype_find("paint.brush_select", true);
PointerRNA op_props;
WM_operator_properties_create_ptr(&op_props, ot);
RNA_enum_set(&op_props, tool_attr, items[i].value);
/* Check for direct access to the tool. */
if (std::optional<std::string> shortcut_brush = WM_key_event_operator_string(
C,
ot->idname,
WM_OP_INVOKE_REGION_WIN,
static_cast<IDProperty *>(op_props.data),
true))
{
shortcut = *shortcut_brush;
}
WM_operator_properties_free(&op_props);
}
}
}
if (shortcut.empty()) {
/* Check for direct access to the tool. */
if (std::optional<std::string> shortcut_toolbar = WM_key_event_operator_string(

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"
@ -941,6 +943,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);
@ -994,12 +998,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. */
@ -1015,12 +1020,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);
@ -1035,15 +1041,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);
}
@ -1334,7 +1341,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"));
@ -1759,10 +1766,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"
@ -66,42 +67,6 @@
#include "paint_intern.hh"
#include "sculpt_intern.hh"
/* Brush operators */
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);
PaintMode mode = BKE_paintmode_get_active_from_context(C);
if (br) {
br = (Brush *)BKE_id_copy(bmain, &br->id);
}
else {
br = BKE_brush_add(bmain, "Brush", BKE_paint_object_mode_from_paintmode(mode));
}
id_us_min(&br->id); /* fake user only */
BKE_paint_brush_set(paint, br);
return OPERATOR_FINISHED;
}
static void BRUSH_OT_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Brush";
ot->description = "Add brush by mode type";
ot->idname = "BRUSH_OT_add";
/* api callbacks */
ot->exec = brush_add_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool,
enum eContextObjectMode mode)
{
@ -194,7 +159,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);
@ -789,205 +754,6 @@ static void BRUSH_OT_reset(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int brush_tool(const Brush *brush, size_t tool_offset)
{
return *(((char *)brush) + tool_offset);
}
static void brush_tool_set(const Brush *brush, size_t tool_offset, int tool)
{
*(((char *)brush) + tool_offset) = tool;
}
static Brush *brush_tool_cycle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool)
{
Brush *brush, *first_brush;
if (!brush_orig && !(brush_orig = static_cast<Brush *>(bmain->brushes.first))) {
return nullptr;
}
if (brush_tool(brush_orig, paint->runtime.tool_offset) != tool) {
/* If current brush's tool is different from what we need,
* start cycling from the beginning of the list.
* Such logic will activate the same exact brush not relating from
* which tool user requests other tool.
*/
/* Try to tool-slot first. */
first_brush = BKE_paint_toolslots_brush_get(paint, tool);
if (first_brush == nullptr) {
first_brush = static_cast<Brush *>(bmain->brushes.first);
}
}
else {
/* If user wants to switch to brush with the same tool as
* currently active brush do a cycling via all possible
* brushes with requested tool. */
first_brush = brush_orig->id.next ? static_cast<Brush *>(brush_orig->id.next) :
static_cast<Brush *>(bmain->brushes.first);
}
/* get the next brush with the active tool */
brush = first_brush;
do {
if ((brush->ob_mode & paint->runtime.ob_mode) &&
(brush_tool(brush, paint->runtime.tool_offset) == tool))
{
return brush;
}
brush = brush->id.next ? static_cast<Brush *>(brush->id.next) :
static_cast<Brush *>(bmain->brushes.first);
} while (brush != first_brush);
return nullptr;
}
static Brush *brush_tool_toggle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool)
{
if (!brush_orig || brush_tool(brush_orig, paint->runtime.tool_offset) != tool) {
Brush *br;
/* if the current brush is not using the desired tool, look
* for one that is */
br = brush_tool_cycle(bmain, paint, brush_orig, tool);
/* store the previously-selected brush */
if (br) {
br->toggle_brush = brush_orig;
}
return br;
}
if (brush_orig->toggle_brush) {
/* if current brush is using the desired tool, try to toggle
* back to the previously selected brush. */
return brush_orig->toggle_brush;
}
return nullptr;
}
static bool brush_generic_tool_set(bContext *C,
Main *bmain,
Paint *paint,
const int tool,
const char *tool_name,
const bool create_missing,
const bool toggle)
{
Brush *brush, *brush_orig = BKE_paint_brush(paint);
if (toggle) {
brush = brush_tool_toggle(bmain, paint, brush_orig, tool);
}
else {
brush = brush_tool_cycle(bmain, paint, brush_orig, tool);
}
if (((brush == nullptr) && create_missing) &&
((brush_orig == nullptr) || brush_tool(brush_orig, paint->runtime.tool_offset) != tool))
{
brush = BKE_brush_add(bmain, tool_name, eObjectMode(paint->runtime.ob_mode));
id_us_min(&brush->id); /* fake user only */
brush_tool_set(brush, paint->runtime.tool_offset, tool);
brush->toggle_brush = brush_orig;
}
if (brush) {
BKE_paint_brush_set(paint, brush);
BKE_paint_invalidate_overlay_all();
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
WM_toolsystem_ref_set_by_id(C, "builtin.brush");
return true;
}
return false;
}
static const PaintMode brush_select_paint_modes[] = {
PaintMode::Sculpt,
PaintMode::Vertex,
PaintMode::Weight,
PaintMode::Texture3D,
PaintMode::GPencil,
PaintMode::VertexGPencil,
PaintMode::SculptGPencil,
PaintMode::WeightGPencil,
PaintMode::SculptCurves,
};
static int brush_select_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
const bool create_missing = RNA_boolean_get(op->ptr, "create_missing");
const bool toggle = RNA_boolean_get(op->ptr, "toggle");
const char *tool_name = "Brush";
int tool = 0;
PaintMode paint_mode = PaintMode::Invalid;
for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) {
paint_mode = brush_select_paint_modes[i];
const char *op_prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
PropertyRNA *prop = RNA_struct_find_property(op->ptr, op_prop_id);
if (RNA_property_is_set(op->ptr, prop)) {
tool = RNA_property_enum_get(op->ptr, prop);
break;
}
}
if (paint_mode == PaintMode::Invalid) {
return OPERATOR_CANCELLED;
}
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
if (paint == nullptr) {
return OPERATOR_CANCELLED;
}
if (brush_generic_tool_set(C, bmain, paint, tool, tool_name, create_missing, toggle)) {
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static void PAINT_OT_brush_select(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Brush Select";
ot->description = "Select a paint mode's brush by tool type";
ot->idname = "PAINT_OT_brush_select";
/* api callbacks */
ot->exec = brush_select_exec;
/* flags */
ot->flag = 0;
/* props */
/* All properties are hidden, so as not to show the redo panel. */
for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) {
const PaintMode paint_mode = brush_select_paint_modes[i];
const char *prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
prop = RNA_def_enum(
ot->srna, prop_id, BKE_paint_get_tool_enum_from_paintmode(paint_mode), 0, prop_id, "");
RNA_def_property_translation_context(
prop, BKE_paint_get_tool_enum_translation_context_from_paintmode(paint_mode));
RNA_def_property_flag(prop, PROP_HIDDEN);
}
prop = RNA_def_boolean(
ot->srna, "toggle", false, "Toggle", "Toggle between two brushes rather than cycling");
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
prop = RNA_def_boolean(ot->srna,
"create_missing",
false,
"Create Missing",
"If the requested brush type does not exist, create a new brush");
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
}
namespace blender::ed::sculpt_paint {
/**************************** Brush Assets **********************************/
@ -997,6 +763,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 +771,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 +1060,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 +1112,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 +1135,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 +1251,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 +1274,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 +1323,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 +1339,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 +1380,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;
}
@ -2081,7 +1864,6 @@ void ED_operatortypes_paint()
WM_operatortype_append(PAINTCURVE_OT_cursor);
/* brush */
WM_operatortype_append(BRUSH_OT_add);
WM_operatortype_append(BRUSH_OT_add_gpencil);
WM_operatortype_append(BRUSH_OT_scale_size);
WM_operatortype_append(BRUSH_OT_curve_preset);
@ -2096,9 +1878,6 @@ void ED_operatortypes_paint()
WM_operatortype_append(BRUSH_OT_asset_update);
WM_operatortype_append(BRUSH_OT_asset_revert);
/* NOTE: particle uses a different system, can be added with existing operators in `wm.py`. */
WM_operatortype_append(PAINT_OT_brush_select);
/* image */
WM_operatortype_append(PAINT_OT_texture_paint_toggle);
WM_operatortype_append(PAINT_OT_image_paint);

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();

View File

@ -174,43 +174,6 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre
}
}
}
else {
const PaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
BLI_assert(paint_mode != PaintMode::Invalid);
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
BLI_assert(items != nullptr);
const int i = items ? RNA_enum_from_identifier(items, tref_rt->data_block) : -1;
if (i != -1) {
const int slot_index = items[i].value;
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
if (workspace == WM_window_get_active_workspace(win)) {
Scene *scene = WM_window_get_active_scene(win);
BKE_paint_ensure_from_paintmode(bmain, scene, paint_mode);
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
Brush *brush = BKE_paint_toolslots_brush_get(paint, slot_index);
if (brush == nullptr) {
/* Could make into a function. */
brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, items[i].name);
if (brush && slot_index == BKE_brush_tool_get(brush, paint)) {
/* pass */
}
else {
brush = BKE_brush_add(bmain, items[i].name, eObjectMode(paint->runtime.ob_mode));
BKE_brush_tool_set(brush, paint, slot_index);
if (paint_mode == PaintMode::Sculpt) {
BKE_brush_sculpt_reset(brush);
}
}
}
BKE_paint_brush_set(paint, brush);
}
}
}
}
}
}