WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 351 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.
12 changed files with 189 additions and 10 deletions
Showing only changes of commit 128d9e142e - Show all commits

View File

@ -8779,6 +8779,11 @@ class BrushAssetShelf:
return asset.metadata.get(cls.mode_prop, False)
@classmethod
def get_active_asset(cls):
paint_settings = UnifiedPaintPanel.paint_settings(bpy.context)
return paint_settings.brush_asset_reference if paint_settings else None
class VIEW3D_AST_brush_sculpt(BrushAssetShelf, bpy.types.AssetShelf):
mode = 'SCULPT'

View File

@ -73,6 +73,75 @@ TEST_F(AssetRepresentationTest, weak_reference__custom_library)
}
}
TEST_F(AssetRepresentationTest, weak_reference__compare)
{
{
AssetWeakReference a;
AssetWeakReference b;
EXPECT_EQ(a, b);
/* Arbitrary individual member changes to test how it affects the comparison. */
b.asset_library_identifier = "My lib";
EXPECT_NE(a, b);
a.asset_library_identifier = "My lib";
EXPECT_EQ(a, b);
a.asset_library_type = ASSET_LIBRARY_ESSENTIALS;
EXPECT_NE(a, b);
b.asset_library_type = ASSET_LIBRARY_LOCAL;
EXPECT_NE(a, b);
b.asset_library_type = ASSET_LIBRARY_ESSENTIALS;
EXPECT_EQ(a, b);
a.relative_asset_identifier = "Foo";
EXPECT_NE(a, b);
b.relative_asset_identifier = "Bar";
EXPECT_NE(a, b);
a.relative_asset_identifier = "Bar";
EXPECT_EQ(a, b);
/* Make the destructor work. */
a.asset_library_identifier = b.asset_library_identifier = nullptr;
a.relative_asset_identifier = b.relative_asset_identifier = nullptr;
}
{
AssetWeakReference a;
a.asset_library_type = ASSET_LIBRARY_LOCAL;
a.asset_library_identifier = "My custom lib";
a.relative_asset_identifier = "path/to/an/asset";
AssetWeakReference b;
EXPECT_NE(a, b);
b.asset_library_type = ASSET_LIBRARY_LOCAL;
b.asset_library_identifier = "My custom lib";
b.relative_asset_identifier = "path/to/an/asset";
EXPECT_EQ(a, b);
/* Make the destructor work. */
a.asset_library_identifier = b.asset_library_identifier = nullptr;
a.relative_asset_identifier = b.relative_asset_identifier = nullptr;
}
{
AssetLibraryService *service = AssetLibraryService::get();
AssetLibrary *const library = service->get_asset_library_on_disk_custom("My custom lib",
asset_library_root_);
AssetRepresentation &asset = add_dummy_asset(*library, "path/to/an/asset");
AssetWeakReference *weak_ref = asset.make_weak_reference();
AssetWeakReference other;
other.asset_library_type = ASSET_LIBRARY_CUSTOM;
other.asset_library_identifier = "My custom lib";
other.relative_asset_identifier = "path/to/an/asset";
EXPECT_EQ(*weak_ref, other);
BKE_asset_weak_reference_free(&weak_ref);
/* Make the destructor work. */
other.asset_library_identifier = nullptr;
other.relative_asset_identifier = nullptr;
}
}
TEST_F(AssetRepresentationTest, weak_reference__resolve_to_full_path__current_file)
{
AssetLibraryService *service = AssetLibraryService::get();

View File

@ -78,6 +78,6 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data)
/** Frees the weak reference and its data, and nulls the given pointer. */
void BKE_asset_weak_reference_free(AssetWeakReference **weak_ref);
AssetWeakReference *BKE_asset_weak_reference_copy(AssetWeakReference *weak_ref);
AssetWeakReference *BKE_asset_weak_reference_copy(const AssetWeakReference *weak_ref);
void BKE_asset_weak_reference_write(BlendWriter *writer, const AssetWeakReference *weak_ref);
void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *weak_ref);

View File

@ -540,6 +540,8 @@ struct AssetShelfType {
const blender::asset_system::AssetRepresentation *asset,
uiLayout *layout);
const AssetWeakReference *(*get_active_asset)(const AssetShelfType *shelf_type);
/* RNA integration */
ExtensionRNA rna_ext;
};

View File

@ -46,13 +46,44 @@ AssetWeakReference::~AssetWeakReference()
MEM_delete(relative_asset_identifier);
}
AssetWeakReference &AssetWeakReference::operator=(AssetWeakReference &&other)
{
if (&other == this) {
return *this;
}
asset_library_type = other.asset_library_type;
asset_library_identifier = other.asset_library_identifier;
relative_asset_identifier = other.relative_asset_identifier;
other.asset_library_type = 0; /* Not a valid type. */
other.asset_library_identifier = nullptr;
other.relative_asset_identifier = nullptr;
return *this;
}
bool AssetWeakReference::operator==(const AssetWeakReference &other) const
{
if (asset_library_type != other.asset_library_type) {
return false;
}
if (StringRef(asset_library_identifier) != StringRef(other.asset_library_identifier)) {
return false;
}
return StringRef(relative_asset_identifier) == StringRef(other.relative_asset_identifier);
}
bool AssetWeakReference::operator!=(const AssetWeakReference &other) const
{
return !(*this == other);
}
void BKE_asset_weak_reference_free(AssetWeakReference **weak_ref)
{
MEM_delete(*weak_ref);
*weak_ref = nullptr;
}
AssetWeakReference *BKE_asset_weak_reference_copy(AssetWeakReference *weak_ref)
AssetWeakReference *BKE_asset_weak_reference_copy(const AssetWeakReference *weak_ref)
{
if (weak_ref == nullptr) {
return nullptr;
@ -60,8 +91,8 @@ AssetWeakReference *BKE_asset_weak_reference_copy(AssetWeakReference *weak_ref)
AssetWeakReference *weak_ref_copy = MEM_new<AssetWeakReference>(__func__);
weak_ref_copy->asset_library_type = weak_ref->asset_library_type;
weak_ref_copy->asset_library_identifier = BLI_strdup(weak_ref->asset_library_identifier);
weak_ref_copy->relative_asset_identifier = BLI_strdup(weak_ref->relative_asset_identifier);
weak_ref_copy->asset_library_identifier = BLI_strdup_null(weak_ref->asset_library_identifier);
weak_ref_copy->relative_asset_identifier = BLI_strdup_null(weak_ref->relative_asset_identifier);
return weak_ref_copy;
}

View File

@ -243,6 +243,9 @@ static void asset_shelf_region_listen(const wmRegionListenerParams *params)
ED_region_tag_redraw(region);
}
break;
case NC_ASSET:
ED_region_tag_redraw(region);
break;
}
}
@ -597,7 +600,7 @@ int context(const bContext *C, const char *member, bContextDataResult *result)
/* XXX hack. Get the asset from the active item, but needs to be the file... */
if (CTX_data_equals(member, "active_file")) {
const ARegion *region = CTX_wm_region(C);
const uiBut *but = UI_region_views_find_active_item_but(region);
const uiBut *but = UI_region_views_find_mouse_over_but(CTX_wm_window(C), region);
if (!but) {
return CTX_RESULT_NO_DATA;
}

View File

@ -11,6 +11,7 @@
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
#include "BKE_asset.hh"
#include "BKE_screen.hh"
#include "BLI_fnmatch.h"
@ -40,6 +41,7 @@ namespace blender::ed::asset::shelf {
class AssetView : public ui::AbstractGridView {
const AssetLibraryReference library_ref_;
const AssetShelf &shelf_;
AssetWeakReference *active_asset_ = nullptr;
/** Copy of the filter string from #AssetShelfSettings, with extra '*' added to the beginning and
* end of the string, for `fnmatch()` to work. */
char search_string[sizeof(AssetShelfSettings::search_string) + 2] = "";
@ -50,6 +52,7 @@ class AssetView : public ui::AbstractGridView {
public:
AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf);
~AssetView();
void build_items() override;
bool begin_filtering(const bContext &C) const override;
@ -70,6 +73,7 @@ class AssetViewItem : public ui::PreviewGridItem {
void disable_asset_drag();
void build_grid_tile(uiLayout &layout) const override;
void build_context_menu(bContext &C, uiLayout &column) const override;
std::optional<bool> should_be_active() const override;
bool is_filtered_visible() const override;
std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
@ -92,6 +96,14 @@ AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf
BLI_strncpy_ensure_pad(
search_string, shelf.settings.search_string, '*', sizeof(search_string));
}
if (shelf.type->get_active_asset) {
active_asset_ = BKE_asset_weak_reference_copy(shelf.type->get_active_asset(shelf.type));
}
}
AssetView::~AssetView()
{
BKE_asset_weak_reference_free(&active_asset_);
}
void AssetView::build_items()
@ -216,6 +228,20 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
}
}
std::optional<bool> AssetViewItem::should_be_active() const
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(get_view());
if (!asset_view.active_asset_) {
return false;
}
const asset_system::AssetRepresentation *asset = handle_get_representation(&asset_);
AssetWeakReference *weak_ref = asset->make_weak_reference();
const bool matches = *asset_view.active_asset_ == *weak_ref;
BKE_asset_weak_reference_free(&weak_ref);
return matches;
}
bool AssetViewItem::is_filtered_visible() const
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(get_view());

View File

@ -3418,4 +3418,4 @@ uiViewHandle *UI_region_view_find_at(const ARegion *region, const int xy[2], int
uiViewItemHandle *UI_region_views_find_item_at(const ARegion *region, const int xy[2])
ATTR_NONNULL();
uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region);
uiBut *UI_region_views_find_active_item_but(const ARegion *region);
uiBut *UI_region_views_find_mouse_over_but(const wmWindow *win, const ARegion *region);

View File

@ -22,6 +22,7 @@
#include <variant>
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
#include "BKE_screen.hh"
@ -199,9 +200,9 @@ uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region)
return item_but->view_item;
}
uiBut *UI_region_views_find_active_item_but(const ARegion *region)
uiBut *UI_region_views_find_mouse_over_but(const wmWindow *win, const ARegion *region)
{
return ui_view_item_find_active(region);
return ui_view_item_find_mouse_over(region, win->eventstate->xy);
}
namespace blender::ui {

View File

@ -1014,6 +1014,7 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, nullptr);
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr);
WM_toolsystem_ref_set_by_id(C, "builtin.brush");

View File

@ -180,6 +180,10 @@ typedef struct AssetWeakReference {
AssetWeakReference(AssetWeakReference &&);
AssetWeakReference(const AssetWeakReference &) = delete;
~AssetWeakReference();
AssetWeakReference &operator=(AssetWeakReference &&);
bool operator==(const AssetWeakReference &other) const;
bool operator!=(const AssetWeakReference &other) const;
/**
* See AssetRepresentation::make_weak_reference(). Must be freed using

View File

@ -1145,6 +1145,29 @@ static bool asset_shelf_poll(const bContext *C, const AssetShelfType *shelf_type
return is_visible;
}
static const AssetWeakReference *asset_shelf_get_active_asset(const AssetShelfType *shelf_type)
{
extern FunctionRNA rna_AssetShelf_get_active_asset_func;
PointerRNA ptr = RNA_pointer_create(nullptr, shelf_type->rna_ext.srna, nullptr); /* dummy */
FunctionRNA *func = &rna_AssetShelf_get_active_asset_func;
// RNA_struct_find_function(&ptr, "get_active_asset");
ParameterList list;
RNA_parameter_list_create(&list, &ptr, func);
shelf_type->rna_ext.call(nullptr, &ptr, func, &list);
void *ret;
RNA_parameter_get_lookup(&list, "asset_reference", &ret);
/* Get the value before freeing. */
AssetWeakReference *active_asset = *(AssetWeakReference **)ret;
RNA_parameter_list_free(&list);
return active_asset;
}
static void asset_shelf_draw_context_menu(const bContext *C,
const AssetShelfType *shelf_type,
const AssetRepresentationHandle *asset,
@ -1208,7 +1231,7 @@ static StructRNA *rna_AssetShelf_register(Main *bmain,
dummy_shelf.type = &dummy_shelf_type;
PointerRNA dummy_shelf_ptr = RNA_pointer_create(nullptr, &RNA_AssetShelf, &dummy_shelf);
bool have_function[3];
bool have_function[4];
/* validate the python class */
if (validate(&dummy_shelf_ptr, data, have_function) != 0) {
@ -1265,7 +1288,8 @@ static StructRNA *rna_AssetShelf_register(Main *bmain,
shelf_type->poll = have_function[0] ? asset_shelf_poll : nullptr;
shelf_type->asset_poll = have_function[1] ? asset_shelf_asset_poll : nullptr;
shelf_type->draw_context_menu = have_function[2] ? asset_shelf_draw_context_menu : nullptr;
shelf_type->get_active_asset = have_function[2] ? asset_shelf_get_active_asset : nullptr;
shelf_type->draw_context_menu = have_function[3] ? asset_shelf_draw_context_menu : nullptr;
BLI_addtail(&space_type->asset_shelf_types, shelf_type);
@ -2314,6 +2338,19 @@ static void rna_def_asset_shelf(BlenderRNA *brna)
parm = RNA_def_pointer(func, "asset", "AssetRepresentation", "", "");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
func = RNA_def_function(srna, "get_active_asset", nullptr);
RNA_def_function_ui_description(
func,
"Return a reference to the asset that should be highlighted as active in the asset shelf");
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
/* return type */
parm = RNA_def_pointer(func,
"asset_reference",
"AssetWeakReference",
"",
"The weak reference to the asset to be hightlighted as active, or None");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "draw_context_menu", nullptr);
RNA_def_function_ui_description(
func, "Draw UI elements into the context menu UI layout displayed on right click");