UI: Asset Shelf (Experimental Feature) #104831

Closed
Julian Eisel wants to merge 399 commits from asset-shelf into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
8 changed files with 153 additions and 153 deletions
Showing only changes of commit 7b1da46441 - Show all commits

View File

@ -521,7 +521,7 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
/* Keep this block, even when empty. */ /* Keep this block, even when empty. */
} }
if (!DNA_struct_find(fd->filesdna, "AssetShelfHook")) { if (!DNA_struct_find(fd->filesdna, "RegionAssetShelf")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {

View File

@ -35,7 +35,7 @@ set(SRC
intern/asset_shelf.cc intern/asset_shelf.cc
intern/asset_shelf_asset_view.cc intern/asset_shelf_asset_view.cc
intern/asset_shelf_catalog_selector.cc intern/asset_shelf_catalog_selector.cc
intern/asset_shelf_hook.cc intern/asset_shelf_regiondata.cc
intern/asset_shelf_settings.cc intern/asset_shelf_settings.cc
intern/asset_temp_id_consumer.cc intern/asset_temp_id_consumer.cc
intern/asset_type.cc intern/asset_type.cc

View File

@ -60,9 +60,6 @@ void ED_asset_shelf_header_regiontype_register(struct ARegionType *region_type,
int ED_asset_shelf_tile_width(const struct AssetShelfSettings &settings); int ED_asset_shelf_tile_width(const struct AssetShelfSettings &settings);
int ED_asset_shelf_tile_height(const struct AssetShelfSettings &settings); int ED_asset_shelf_tile_height(const struct AssetShelfSettings &settings);
/**
* Creates an `"asset_shelf"` context member, pointing to the active shelf in \a #shelf_hook.
*/
int ED_asset_shelf_context(const struct bContext *C, int ED_asset_shelf_context(const struct bContext *C,
const char *member, const char *member,
struct bContextDataResult *result); struct bContextDataResult *result);

View File

@ -92,15 +92,15 @@ static AssetShelf *create_shelf_from_type(AssetShelfType &type)
* \{ */ * \{ */
/** /**
* Activating a shelf means assigning it to #AssetShelfHook.active_shelf and (re-)inserting it at * Activating a shelf means assigning it to #RegionAssetShelf.active_shelf and (re-)inserting it at
* the beginning of the #AssetShelfHook.shelves list. This implies that after calling this, \a * the beginning of the #RegionAssetShelf.shelves list. This implies that after calling this, \a
* shelf is guaranteed to be owned by the shelves list. * shelf is guaranteed to be owned by the shelves list.
*/ */
static void activate_shelf(AssetShelfHook &hook, AssetShelf &shelf) static void activate_shelf(RegionAssetShelf &shelf_regiondata, AssetShelf &shelf)
{ {
hook.active_shelf = &shelf; shelf_regiondata.active_shelf = &shelf;
BLI_remlink_safe(&hook.shelves, &shelf); BLI_remlink_safe(&shelf_regiondata.shelves, &shelf);
JulianEisel marked this conversation as resolved Outdated

Suggestion: I find it reads baddy when it's the existence of data in a list is undefined.
In this case the caller can ensure it's in the list in the case it's just been created.

Suggestion: I find it reads baddy when it's the existence of data in a list is undefined. In this case the caller can ensure it's in the list in the case it's just been created.
BLI_addhead(&hook.shelves, &shelf); BLI_addhead(&shelf_regiondata.shelves, &shelf);
} }
/** /**
@ -108,13 +108,13 @@ static void activate_shelf(AssetShelfHook &hook, AssetShelf &shelf)
* *
* The heuristic works as follows: * The heuristic works as follows:
* 1) If the currently active shelf is still valid (poll succeeds), keep it active. * 1) If the currently active shelf is still valid (poll succeeds), keep it active.
* 2) Otherwise, check for previously activated shelves in \a hook and activate the first valid one * 2) Otherwise, check for previously activated shelves in \a shelf_regiondata and activate the
* (first with a succeeding poll). * first valid one (first with a succeeding poll).
* 3) If none is valid, check all shelf-types available for \a space_type, create a new shelf for * 3) If none is valid, check all shelf-types available for \a space_type, create a new shelf for
* the first type that is valid (poll succeeds), and activate it. * the first type that is valid (poll succeeds), and activate it.
* 4) If no shelf-type is valid, #AssetShelfHook.active_shelf is set to null. * 4) If no shelf-type is valid, #RegionAssetShelf.active_shelf is set to null.
* *
* When activating a shelf, it is moved to the beginning of the #AssetShelfHook.shelves list, so * When activating a shelf, it is moved to the beginning of the #RegionAssetShelf.shelves list, so
* that recently activated shelves are also the first ones to be reactivated. * that recently activated shelves are also the first ones to be reactivated.
* *
* The returned shelf is guaranteed to have its #AssetShelf.type pointer set. * The returned shelf is guaranteed to have its #AssetShelf.type pointer set.
@ -124,29 +124,30 @@ static void activate_shelf(AssetShelfHook &hook, AssetShelf &shelf)
*/ */
static AssetShelf *update_active_shelf(const bContext &C, static AssetShelf *update_active_shelf(const bContext &C,
const SpaceType &space_type, const SpaceType &space_type,
AssetShelfHook &hook) RegionAssetShelf &shelf_regiondata)
{ {
/* Note: Don't access #AssetShelf.type directly, use #asset_shelf_type_ensure(). */ /* Note: Don't access #AssetShelf.type directly, use #asset_shelf_type_ensure(). */
/* Case 1: */ /* Case 1: */
if (hook.active_shelf && if (shelf_regiondata.active_shelf &&
asset_shelf_type_poll(C, asset_shelf_type_ensure(space_type, *hook.active_shelf))) asset_shelf_type_poll(C,
asset_shelf_type_ensure(space_type, *shelf_regiondata.active_shelf)))
{ {
/* Not a strong precondition, but if this is wrong something weird might be going on. */ /* Not a strong precondition, but if this is wrong something weird might be going on. */
BLI_assert(hook.active_shelf == hook.shelves.first); BLI_assert(shelf_regiondata.active_shelf == shelf_regiondata.shelves.first);
return hook.active_shelf; return shelf_regiondata.active_shelf;
} }
/* Case 2 (no active shelf or the poll of it isn't succeeding anymore. Poll all shelf types to /* Case 2 (no active shelf or the poll of it isn't succeeding anymore. Poll all shelf types to
* determine a new active one): */ * determine a new active one): */
LISTBASE_FOREACH (AssetShelf *, shelf, &hook.shelves) { LISTBASE_FOREACH (AssetShelf *, shelf, &shelf_regiondata.shelves) {
if (shelf == hook.active_shelf) { if (shelf == shelf_regiondata.active_shelf) {
continue; continue;
} }
if (asset_shelf_type_poll(C, asset_shelf_type_ensure(space_type, *shelf))) { if (asset_shelf_type_poll(C, asset_shelf_type_ensure(space_type, *shelf))) {
/* Found a valid previously activated shelf, reactivate it. */ /* Found a valid previously activated shelf, reactivate it. */
activate_shelf(hook, *shelf); activate_shelf(shelf_regiondata, *shelf);
return shelf; return shelf;
} }
} }
@ -155,13 +156,13 @@ static AssetShelf *update_active_shelf(const bContext &C,
LISTBASE_FOREACH (AssetShelfType *, shelf_type, &space_type.asset_shelf_types) { LISTBASE_FOREACH (AssetShelfType *, shelf_type, &space_type.asset_shelf_types) {
if (asset_shelf_type_poll(C, shelf_type)) { if (asset_shelf_type_poll(C, shelf_type)) {
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type); AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
/* Moves ownership to the hook. */ /* Moves ownership to the regiondata. */
activate_shelf(hook, *new_shelf); activate_shelf(shelf_regiondata, *new_shelf);
return new_shelf; return new_shelf;
} }
} }
hook.active_shelf = nullptr; shelf_regiondata.active_shelf = nullptr;
return nullptr; return nullptr;
} }
@ -173,19 +174,19 @@ static AssetShelf *update_active_shelf(const bContext &C,
void *ED_asset_shelf_region_duplicate(void *regiondata) void *ED_asset_shelf_region_duplicate(void *regiondata)
{ {
const AssetShelfHook *hook = static_cast<AssetShelfHook *>(regiondata); const RegionAssetShelf *shelf_regiondata = static_cast<RegionAssetShelf *>(regiondata);
if (!hook) { if (!shelf_regiondata) {
return nullptr; return nullptr;
} }
return shelf::hook_duplicate(hook); return shelf::regiondata_duplicate(shelf_regiondata);
} }
void ED_asset_shelf_region_free(ARegion *region) void ED_asset_shelf_region_free(ARegion *region)
{ {
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region); RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
if (hook) { if (shelf_regiondata) {
shelf::hook_free(&hook); shelf::regiondata_free(&shelf_regiondata);
} }
region->regiondata = nullptr; region->regiondata = nullptr;
} }
@ -252,12 +253,12 @@ void ED_asset_shelf_region_listen(const wmRegionListenerParams *params)
void ED_asset_shelf_region_init(wmWindowManager *wm, ARegion *region) void ED_asset_shelf_region_init(wmWindowManager *wm, ARegion *region)
{ {
if (!region->regiondata) { if (!region->regiondata) {
region->regiondata = MEM_cnew<AssetShelfHook>("AssetShelfHook"); region->regiondata = MEM_cnew<RegionAssetShelf>("RegionAssetShelf");
} }
AssetShelfHook &hook = *AssetShelfHook::get_from_asset_shelf_region(*region); RegionAssetShelf &shelf_regiondata = *RegionAssetShelf::get_from_asset_shelf_region(*region);
/* Active shelf is only set on draw, so this may be null! */ /* Active shelf is only set on draw, so this may be null! */
AssetShelf *active_shelf = hook.active_shelf; AssetShelf *active_shelf = shelf_regiondata.active_shelf;
UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy); UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy);
@ -296,8 +297,9 @@ int ED_asset_shelf_region_snap(const ARegion *region, const int size, const int
return size; return size;
} }
const AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region); const RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
const AssetShelf *active_shelf = hook->active_shelf; *region);
const AssetShelf *active_shelf = shelf_regiondata->active_shelf;
/* Using scaled values only simplifies things. Simply divide the result by the scale again. */ /* Using scaled values only simplifies things. Simply divide the result by the scale again. */
const int size_scaled = size * UI_SCALE_FAC; const int size_scaled = size * UI_SCALE_FAC;
@ -375,14 +377,14 @@ void ED_asset_shelf_region_layout(const bContext *C, ARegion *region)
const SpaceLink *space = CTX_wm_space_data(C); const SpaceLink *space = CTX_wm_space_data(C);
const SpaceType *space_type = BKE_spacetype_from_id(space->spacetype); const SpaceType *space_type = BKE_spacetype_from_id(space->spacetype);
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region); RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
if (!hook) { if (!shelf_regiondata) {
/* Hook should've been created by a previously called ED_asset_shelf_region_init(). */ /* Regiondata should've been created by a previously called ED_asset_shelf_region_init(). */
BLI_assert_unreachable(); BLI_assert_unreachable();
return; return;
} }
AssetShelf *active_shelf = update_active_shelf(*C, *space_type, *hook); AssetShelf *active_shelf = update_active_shelf(*C, *space_type, *shelf_regiondata);
if (!active_shelf) { if (!active_shelf) {
return; return;
} }
@ -452,8 +454,9 @@ void ED_asset_shelf_header_region(const bContext *C, ARegion *region)
const ARegion *main_shelf_region = BKE_area_find_region_type(CTX_wm_area(C), const ARegion *main_shelf_region = BKE_area_find_region_type(CTX_wm_area(C),
RGN_TYPE_ASSET_SHELF); RGN_TYPE_ASSET_SHELF);
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*main_shelf_region); RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
update_active_shelf(*C, *space_type, *hook); *main_shelf_region);
update_active_shelf(*C, *space_type, *shelf_regiondata);
ED_region_header(C, region); ED_region_header(C, region);
} }
@ -466,21 +469,21 @@ int ED_asset_shelf_header_region_size()
void ED_asset_shelf_region_blend_read_data(BlendDataReader *reader, ARegion *region) void ED_asset_shelf_region_blend_read_data(BlendDataReader *reader, ARegion *region)
{ {
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region); RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
if (!hook) { if (!shelf_regiondata) {
return; return;
} }
shelf::hook_blend_read_data(reader, &hook); shelf::regiondata_blend_read_data(reader, &shelf_regiondata);
region->regiondata = hook; region->regiondata = shelf_regiondata;
} }
void ED_asset_shelf_region_blend_write(BlendWriter *writer, ARegion *region) void ED_asset_shelf_region_blend_write(BlendWriter *writer, ARegion *region)
{ {
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region); RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
if (!hook) { if (!shelf_regiondata) {
return; return;
} }
shelf::hook_blend_write(writer, hook); shelf::regiondata_blend_write(writer, shelf_regiondata);
} }
/** \} */ /** \} */
@ -513,12 +516,13 @@ int ED_asset_shelf_context(const bContext *C, const char *member, bContextDataRe
return CTX_RESULT_NO_DATA; return CTX_RESULT_NO_DATA;
} }
const AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*shelf_region); const RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
if (!hook) { *shelf_region);
if (!shelf_regiondata) {
return CTX_RESULT_NO_DATA; return CTX_RESULT_NO_DATA;
} }
CTX_data_pointer_set(result, &screen->id, &RNA_AssetShelf, hook->active_shelf); CTX_data_pointer_set(result, &screen->id, &RNA_AssetShelf, shelf_regiondata->active_shelf);
return CTX_RESULT_OK; return CTX_RESULT_OK;
} }

View File

@ -38,17 +38,16 @@ AssetShelf *active_shelf_from_context(const bContext *C);
void send_redraw_notifier(const bContext &C); void send_redraw_notifier(const bContext &C);
/** /**
* Deep-copies \a hook into newly allocated memory. Must be freed using * Deep-copies \a shelf_regiondata into newly allocated memory. Must be freed using
* #ED_asset_shelf_hook_free(). * #regiondata_free().
*/ */
struct AssetShelfHook *hook_duplicate(const AssetShelfHook *hook); struct RegionAssetShelf *regiondata_duplicate(const RegionAssetShelf *shelf_regiondata);
/** /** Frees the contained data and \a shelf_regiondata itself. */
* Frees the contained data and \a hook itself. void regiondata_free(RegionAssetShelf **shelf_regiondata);
*/ void regiondata_blend_write(struct BlendWriter *writer,
void hook_free(AssetShelfHook **hook); const struct RegionAssetShelf *shelf_regiondata);
void regiondata_blend_read_data(struct BlendDataReader *reader,
void hook_blend_write(struct BlendWriter *writer, const struct AssetShelfHook *hook); struct RegionAssetShelf **shelf_regiondata);
void hook_blend_read_data(struct BlendDataReader *reader, struct AssetShelfHook **hook);
/** /**
* Frees the contained data, not \a shelf_settings itself. * Frees the contained data, not \a shelf_settings itself.

View File

@ -1,83 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edasset
*/
#include "BLI_listbase.h"
#include "BLO_read_write.h"
#include "DNA_defs.h"
#include "DNA_screen_types.h"
#include "asset_shelf.hh"
AssetShelfHook *AssetShelfHook::get_from_asset_shelf_region(const ARegion &region)
{
if (region.regiontype != RGN_TYPE_ASSET_SHELF) {
/* Should only be called on main asset shelf region. */
BLI_assert_unreachable();
return nullptr;
}
return static_cast<AssetShelfHook *>(region.regiondata);
}
namespace blender::ed::asset::shelf {
AssetShelfHook *hook_duplicate(const AssetShelfHook *hook)
{
static_assert(std::is_trivial_v<AssetShelfHook>,
"AssetShelfHook needs to be trivial to allow freeing with MEM_freeN()");
AssetShelfHook *new_hook = MEM_new<AssetShelfHook>(__func__, *hook);
BLI_listbase_clear(&new_hook->shelves);
LISTBASE_FOREACH (const AssetShelf *, shelf, &hook->shelves) {
AssetShelf *new_shelf = MEM_new<AssetShelf>("duplicate asset shelf",
blender::dna::shallow_copy(*shelf));
new_shelf->settings = shelf->settings;
BLI_addtail(&new_hook->shelves, new_shelf);
if (hook->active_shelf == shelf) {
new_hook->active_shelf = new_shelf;
}
}
return new_hook;
}
void hook_free(AssetShelfHook **hook)
{
LISTBASE_FOREACH_MUTABLE (AssetShelf *, shelf, &(*hook)->shelves) {
MEM_delete(shelf);
}
MEM_SAFE_FREE(*hook);
}
void hook_blend_write(BlendWriter *writer, const AssetShelfHook *hook)
{
BLO_write_struct(writer, AssetShelfHook, hook);
LISTBASE_FOREACH (const AssetShelf *, shelf, &hook->shelves) {
BLO_write_struct(writer, AssetShelf, shelf);
settings_blend_write(writer, shelf->settings);
}
}
void hook_blend_read_data(BlendDataReader *reader, AssetShelfHook **hook)
{
if (!*hook) {
return;
}
BLO_read_data_address(reader, hook);
if ((*hook)->active_shelf) {
BLO_read_data_address(reader, &(*hook)->active_shelf);
}
BLO_read_list(reader, &(*hook)->shelves);
LISTBASE_FOREACH (AssetShelf *, shelf, &(*hook)->shelves) {
shelf->type = nullptr;
settings_blend_read_data(reader, shelf->settings);
}
}
} // namespace blender::ed::asset::shelf

View File

@ -0,0 +1,83 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edasset
*/
#include "BLI_listbase.h"
#include "BLO_read_write.h"
#include "DNA_defs.h"
#include "DNA_screen_types.h"
#include "asset_shelf.hh"
RegionAssetShelf *RegionAssetShelf::get_from_asset_shelf_region(const ARegion &region)
{
if (region.regiontype != RGN_TYPE_ASSET_SHELF) {
/* Should only be called on main asset shelf region. */
BLI_assert_unreachable();
return nullptr;
}
return static_cast<RegionAssetShelf *>(region.regiondata);
}
namespace blender::ed::asset::shelf {
RegionAssetShelf *regiondata_duplicate(const RegionAssetShelf *shelf_regiondata)
{
static_assert(std::is_trivial_v<RegionAssetShelf>,
"RegionAssetShelf needs to be trivial to allow freeing with MEM_freeN()");
RegionAssetShelf *new_shelf_regiondata = MEM_new<RegionAssetShelf>(__func__, *shelf_regiondata);
BLI_listbase_clear(&new_shelf_regiondata->shelves);
LISTBASE_FOREACH (const AssetShelf *, shelf, &shelf_regiondata->shelves) {
AssetShelf *new_shelf = MEM_new<AssetShelf>("duplicate asset shelf",
blender::dna::shallow_copy(*shelf));
new_shelf->settings = shelf->settings;
BLI_addtail(&new_shelf_regiondata->shelves, new_shelf);
if (shelf_regiondata->active_shelf == shelf) {
new_shelf_regiondata->active_shelf = new_shelf;
}
}
return new_shelf_regiondata;
}
void regiondata_free(RegionAssetShelf **shelf_regiondata)
{
LISTBASE_FOREACH_MUTABLE (AssetShelf *, shelf, &(*shelf_regiondata)->shelves) {
MEM_delete(shelf);
}
MEM_SAFE_FREE(*shelf_regiondata);
}
void regiondata_blend_write(BlendWriter *writer, const RegionAssetShelf *shelf_regiondata)
{
BLO_write_struct(writer, RegionAssetShelf, shelf_regiondata);
LISTBASE_FOREACH (const AssetShelf *, shelf, &shelf_regiondata->shelves) {
BLO_write_struct(writer, AssetShelf, shelf);
settings_blend_write(writer, shelf->settings);
}
}
void regiondata_blend_read_data(BlendDataReader *reader, RegionAssetShelf **shelf_regiondata)
{
if (!*shelf_regiondata) {
return;
}
BLO_read_data_address(reader, shelf_regiondata);
if ((*shelf_regiondata)->active_shelf) {
BLO_read_data_address(reader, &(*shelf_regiondata)->active_shelf);
}
BLO_read_list(reader, &(*shelf_regiondata)->shelves);
LISTBASE_FOREACH (AssetShelf *, shelf, &(*shelf_regiondata)->shelves) {
shelf->type = nullptr;
settings_blend_read_data(reader, shelf->settings);
}
}
} // namespace blender::ed::asset::shelf

View File

@ -807,22 +807,22 @@ typedef struct AssetShelf {
} AssetShelf; } AssetShelf;
/** /**
* Helper for asset shelf integration into spaces. See #ED_asset_shelf.h for a corresponding API. * Region-data for the main asset shelf region (#RGN_TYPE_ASSET_SHELF). Managed by the asset shelf
* internals.
* *
* A space can keep multiple shelf instances in storage, only one is active at a time. * Contains storage for all previously activated asset shelf instances plus info on the currently
* * active one (only one can be active at any time).
* Allocate with #MEM_cnew().
*/ */
typedef struct AssetShelfHook { typedef struct RegionAssetShelf {
/** Owning list of previously activated asset shelves. */ /** Owning list of previously activated asset shelves. */
ListBase shelves; ListBase shelves;
/** The currently active shelf, if any. Updated on redraw, so that context changes are reflected. /** The currently active shelf, if any. Updated on redraw, so that context changes are reflected.
*/ */
AssetShelf *active_shelf; /* Non-owning. */ AssetShelf *active_shelf; /* Non-owning. */
#ifdef __cplusplus #ifdef __cplusplus
static AssetShelfHook *get_from_asset_shelf_region(const ARegion &region); static RegionAssetShelf *get_from_asset_shelf_region(const ARegion &region);
#endif #endif
} AssetShelfHook; } RegionAssetShelf;
/* #AssetShelfSettings.display_flag */ /* #AssetShelfSettings.display_flag */
typedef enum AssetShelfSettings_DisplayFlag { typedef enum AssetShelfSettings_DisplayFlag {