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. */
}
if (!DNA_struct_find(fd->filesdna, "AssetShelfHook")) {
if (!DNA_struct_find(fd->filesdna, "RegionAssetShelf")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {

View File

@ -35,7 +35,7 @@ set(SRC
intern/asset_shelf.cc
intern/asset_shelf_asset_view.cc
intern/asset_shelf_catalog_selector.cc
intern/asset_shelf_hook.cc
intern/asset_shelf_regiondata.cc
intern/asset_shelf_settings.cc
intern/asset_temp_id_consumer.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_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,
const char *member,
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
* the beginning of the #AssetShelfHook.shelves list. This implies that after calling this, \a
* Activating a shelf means assigning it to #RegionAssetShelf.active_shelf and (re-)inserting it at
* the beginning of the #RegionAssetShelf.shelves list. This implies that after calling this, \a
* 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;
BLI_remlink_safe(&hook.shelves, &shelf);
BLI_addhead(&hook.shelves, &shelf);
shelf_regiondata.active_shelf = &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(&shelf_regiondata.shelves, &shelf);
}
/**
@ -108,13 +108,13 @@ static void activate_shelf(AssetShelfHook &hook, AssetShelf &shelf)
*
* The heuristic works as follows:
* 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
* (first with a succeeding poll).
* 2) Otherwise, check for previously activated shelves in \a shelf_regiondata and activate the
* 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
* 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.
*
* 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,
const SpaceType &space_type,
AssetShelfHook &hook)
RegionAssetShelf &shelf_regiondata)
{
/* Note: Don't access #AssetShelf.type directly, use #asset_shelf_type_ensure(). */
/* Case 1: */
if (hook.active_shelf &&
asset_shelf_type_poll(C, asset_shelf_type_ensure(space_type, *hook.active_shelf)))
if (shelf_regiondata.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. */
BLI_assert(hook.active_shelf == hook.shelves.first);
return hook.active_shelf;
BLI_assert(shelf_regiondata.active_shelf == shelf_regiondata.shelves.first);
return shelf_regiondata.active_shelf;
}
/* Case 2 (no active shelf or the poll of it isn't succeeding anymore. Poll all shelf types to
* determine a new active one): */
LISTBASE_FOREACH (AssetShelf *, shelf, &hook.shelves) {
if (shelf == hook.active_shelf) {
LISTBASE_FOREACH (AssetShelf *, shelf, &shelf_regiondata.shelves) {
if (shelf == shelf_regiondata.active_shelf) {
continue;
}
if (asset_shelf_type_poll(C, asset_shelf_type_ensure(space_type, *shelf))) {
/* Found a valid previously activated shelf, reactivate it. */
activate_shelf(hook, *shelf);
activate_shelf(shelf_regiondata, *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) {
if (asset_shelf_type_poll(C, shelf_type)) {
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
/* Moves ownership to the hook. */
activate_shelf(hook, *new_shelf);
/* Moves ownership to the regiondata. */
activate_shelf(shelf_regiondata, *new_shelf);
return new_shelf;
}
}
hook.active_shelf = nullptr;
shelf_regiondata.active_shelf = nullptr;
return nullptr;
}
@ -173,19 +174,19 @@ static AssetShelf *update_active_shelf(const bContext &C,
void *ED_asset_shelf_region_duplicate(void *regiondata)
{
const AssetShelfHook *hook = static_cast<AssetShelfHook *>(regiondata);
if (!hook) {
const RegionAssetShelf *shelf_regiondata = static_cast<RegionAssetShelf *>(regiondata);
if (!shelf_regiondata) {
return nullptr;
}
return shelf::hook_duplicate(hook);
return shelf::regiondata_duplicate(shelf_regiondata);
}
void ED_asset_shelf_region_free(ARegion *region)
{
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
if (hook) {
shelf::hook_free(&hook);
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
if (shelf_regiondata) {
shelf::regiondata_free(&shelf_regiondata);
}
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)
{
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! */
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);
@ -296,8 +297,9 @@ int ED_asset_shelf_region_snap(const ARegion *region, const int size, const int
return size;
}
const AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
const AssetShelf *active_shelf = hook->active_shelf;
const RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
*region);
const AssetShelf *active_shelf = shelf_regiondata->active_shelf;
/* Using scaled values only simplifies things. Simply divide the result by the scale again. */
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 SpaceType *space_type = BKE_spacetype_from_id(space->spacetype);
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
if (!hook) {
/* Hook should've been created by a previously called ED_asset_shelf_region_init(). */
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
if (!shelf_regiondata) {
/* Regiondata should've been created by a previously called ED_asset_shelf_region_init(). */
BLI_assert_unreachable();
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) {
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),
RGN_TYPE_ASSET_SHELF);
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*main_shelf_region);
update_active_shelf(*C, *space_type, *hook);
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
*main_shelf_region);
update_active_shelf(*C, *space_type, *shelf_regiondata);
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)
{
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
if (!hook) {
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
if (!shelf_regiondata) {
return;
}
shelf::hook_blend_read_data(reader, &hook);
region->regiondata = hook;
shelf::regiondata_blend_read_data(reader, &shelf_regiondata);
region->regiondata = shelf_regiondata;
}
void ED_asset_shelf_region_blend_write(BlendWriter *writer, ARegion *region)
{
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
if (!hook) {
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
if (!shelf_regiondata) {
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;
}
const AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*shelf_region);
if (!hook) {
const RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
*shelf_region);
if (!shelf_regiondata) {
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;
}

View File

@ -38,17 +38,16 @@ AssetShelf *active_shelf_from_context(const bContext *C);
void send_redraw_notifier(const bContext &C);
/**
* Deep-copies \a hook into newly allocated memory. Must be freed using
* #ED_asset_shelf_hook_free().
* Deep-copies \a shelf_regiondata into newly allocated memory. Must be freed using
* #regiondata_free().
*/
struct AssetShelfHook *hook_duplicate(const AssetShelfHook *hook);
/**
* Frees the contained data and \a hook itself.
*/
void hook_free(AssetShelfHook **hook);
void hook_blend_write(struct BlendWriter *writer, const struct AssetShelfHook *hook);
void hook_blend_read_data(struct BlendDataReader *reader, struct AssetShelfHook **hook);
struct RegionAssetShelf *regiondata_duplicate(const RegionAssetShelf *shelf_regiondata);
/** Frees the contained data and \a shelf_regiondata itself. */
void regiondata_free(RegionAssetShelf **shelf_regiondata);
void regiondata_blend_write(struct BlendWriter *writer,
const struct RegionAssetShelf *shelf_regiondata);
void regiondata_blend_read_data(struct BlendDataReader *reader,
struct RegionAssetShelf **shelf_regiondata);
/**
* 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;
/**
* 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.
*
* Allocate with #MEM_cnew().
* Contains storage for all previously activated asset shelf instances plus info on the currently
* active one (only one can be active at any time).
*/
typedef struct AssetShelfHook {
typedef struct RegionAssetShelf {
/** Owning list of previously activated asset shelves. */
ListBase shelves;
/** The currently active shelf, if any. Updated on redraw, so that context changes are reflected.
*/
AssetShelf *active_shelf; /* Non-owning. */
#ifdef __cplusplus
static AssetShelfHook *get_from_asset_shelf_region(const ARegion &region);
static RegionAssetShelf *get_from_asset_shelf_region(const ARegion &region);
#endif
} AssetShelfHook;
} RegionAssetShelf;
/* #AssetShelfSettings.display_flag */
typedef enum AssetShelfSettings_DisplayFlag {