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.
9 changed files with 135 additions and 86 deletions
Showing only changes of commit 0d6572f452 - Show all commits

View File

@ -52,6 +52,10 @@
#include "BLO_read_write.h"
/* TODO(Julian): For asset shelf region reading/writing. Region read/write should be done via a
JulianEisel marked this conversation as resolved Outdated

picky use TODO(@username) format.

*picky* use `TODO(@username)` format.
* #ARegionType callback. */
#include "../editors/asset/ED_asset_shelf.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
#endif
@ -1218,6 +1222,9 @@ static void write_region(BlendWriter *writer, ARegion *region, int spacetype)
BLO_write_struct(writer, BoundBox, rv3d->clipbb);
}
}
else if (region->regiontype == RGN_TYPE_ASSET_SHELF) {
ED_asset_shelf_region_blend_write(writer, region);
}
else {
printf("regiondata write missing!\n");
}
@ -1338,9 +1345,10 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa
region->regiondata = nullptr;
}
else {
BLO_read_data_address(reader, &region->regiondata);
if (region->regiondata) {
if (spacetype == SPACE_VIEW3D) {
if (spacetype == SPACE_VIEW3D) {
if (region->regiontype == RGN_TYPE_WINDOW) {
BLO_read_data_address(reader, &region->regiondata);
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
BLO_read_data_address(reader, &rv3d->localvd);
@ -1353,6 +1361,9 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa
rv3d->rflag &= ~(RV3D_NAVIGATING | RV3D_PAINTING);
rv3d->runtime_viewlock = 0;
}
else if (region->regiontype == RGN_TYPE_ASSET_SHELF) {
ED_asset_shelf_region_blend_read_data(reader, region);
}
}
}

View File

@ -509,9 +509,6 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
if (sl->spacetype != SPACE_VIEW3D) {
continue;
}
View3D *v3d = reinterpret_cast<View3D *>(sl);
v3d->asset_shelf_hook = MEM_cnew<AssetShelfHook>("Versioning AssetShelfHook");
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;

View File

@ -35,13 +35,15 @@ struct RegionPollParams;
bool ED_asset_shelf_regions_poll(const struct RegionPollParams *params);
/** Only needed for #RGN_TYPE_ASSET_SHELF (not #RGN_TYPE_ASSET_SHELF_HEADER). */
void *ED_asset_shelf_region_duplicate(void *regiondata);
void ED_asset_shelf_region_free(struct ARegion *region);
void ED_asset_shelf_region_init(struct wmWindowManager *wm, struct ARegion *region);
int ED_asset_shelf_region_snap(const struct ARegion *region, int size, int axis);
void ED_asset_shelf_region_listen(const struct wmRegionListenerParams *params);
void ED_asset_shelf_region_layout(const bContext *C,
struct ARegion *region,
struct AssetShelfHook *shelf_hook);
void ED_asset_shelf_region_layout(const bContext *C, struct ARegion *region);
void ED_asset_shelf_region_draw(const bContext *C, struct ARegion *region);
void ED_asset_shelf_region_blend_read_data(BlendDataReader *reader, struct ARegion *region);
void ED_asset_shelf_region_blend_write(BlendWriter *writer, struct ARegion *region);
int ED_asset_shelf_region_prefsizey(void);
void ED_asset_shelf_header_region_init(struct wmWindowManager *wm, struct ARegion *region);
@ -53,27 +55,6 @@ void ED_asset_shelf_header_regiontype_register(struct ARegionType *region_type,
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Shelf Hook
* \{ */
/**
* Deep-copies \a hook into newly allocated memory. Must be freed using
* #ED_asset_shelf_hook_free().
*/
struct AssetShelfHook *ED_asset_shelf_hook_duplicate(const AssetShelfHook *hook);
/**
* Frees the contained data and \a hook itself.
*/
void ED_asset_shelf_hook_free(AssetShelfHook **hook);
void ED_asset_shelf_hook_blend_write(struct BlendWriter *writer,
const struct AssetShelfHook *hook);
void ED_asset_shelf_hook_blend_read_data(struct BlendDataReader *reader,
struct AssetShelfHook **hook);
/** \} */
/* -------------------------------------------------------------------- */
int ED_asset_shelf_tile_width(const struct AssetShelfSettings &settings);
@ -84,8 +65,7 @@ int ED_asset_shelf_tile_height(const struct AssetShelfSettings &settings);
*/
int ED_asset_shelf_context(const struct bContext *C,
const char *member,
struct bContextDataResult *result,
struct AssetShelfHook *shelf_hook);
struct bContextDataResult *result);
#ifdef __cplusplus
}

View File

@ -171,6 +171,25 @@ static AssetShelf *update_active_shelf(const bContext &C,
/** \name Asset Shelf Regions
* \{ */
void *ED_asset_shelf_region_duplicate(void *regiondata)
{
const AssetShelfHook *hook = static_cast<AssetShelfHook *>(regiondata);
if (!hook) {
return nullptr;
}
return shelf::hook_duplicate(hook);
}
void ED_asset_shelf_region_free(ARegion *region)
{
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
if (hook) {
shelf::hook_free(&hook);
}
region->regiondata = nullptr;
}
/**
* Check if there is any asset shelf type in this space returning true in its poll. If not, no
* asset shelf region should be displayed.
@ -232,8 +251,13 @@ 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");
}
AssetShelfHook &hook = *AssetShelfHook::get_from_asset_shelf_region(*region);
/* Active shelf is only set on draw, so this may be null! */
AssetShelf *active_shelf = static_cast<AssetShelf *>(region->regiondata);
AssetShelf *active_shelf = hook.active_shelf;
UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy);
@ -272,7 +296,8 @@ int ED_asset_shelf_region_snap(const ARegion *region, const int size, const int
return size;
}
const AssetShelf *active_shelf = static_cast<AssetShelf *>(region->regiondata);
const AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
const AssetShelf *active_shelf = hook->active_shelf;
/* Using scaled values only simplifies things. Simply divide the result by the scale again. */
const int size_scaled = size * UI_SCALE_FAC;
@ -345,14 +370,19 @@ static const AssetLibraryReference &asset_shelf_library_ref()
return all_library_ref;
}
void ED_asset_shelf_region_layout(const bContext *C, ARegion *region, AssetShelfHook *shelf_hook)
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);
AssetShelf *active_shelf = update_active_shelf(*C, *space_type, *shelf_hook);
region->regiondata = active_shelf;
region->flag |= RGN_FLAG_TEMP_REGIONDATA;
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(). */
BLI_assert_unreachable();
return;
}
AssetShelf *active_shelf = update_active_shelf(*C, *space_type, *hook);
if (!active_shelf) {
return;
}
@ -417,6 +447,14 @@ void ED_asset_shelf_header_region_init(wmWindowManager * /*wm*/, ARegion *region
void ED_asset_shelf_header_region(const bContext *C, ARegion *region)
{
const SpaceLink *space = CTX_wm_space_data(C);
const SpaceType *space_type = BKE_spacetype_from_id(space->spacetype);
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);
ED_region_header(C, region);
}
@ -426,16 +464,32 @@ int ED_asset_shelf_header_region_size()
return ED_area_headersize() * 0.85f;
}
void ED_asset_shelf_region_blend_read_data(BlendDataReader *reader, ARegion *region)
{
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
if (!hook) {
return;
}
shelf::hook_blend_read_data(reader, &hook);
region->regiondata = hook;
}
void ED_asset_shelf_region_blend_write(BlendWriter *writer, ARegion *region)
{
AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*region);
if (!hook) {
return;
}
shelf::hook_blend_write(writer, hook);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Shelf Context
* \{ */
int ED_asset_shelf_context(const bContext *C,
const char *member,
bContextDataResult *result,
AssetShelfHook *shelf_hook)
int ED_asset_shelf_context(const bContext *C, const char *member, bContextDataResult *result)
{
static const char *context_dir[] = {
"asset_shelf",
@ -452,7 +506,19 @@ int ED_asset_shelf_context(const bContext *C,
bScreen *screen = CTX_wm_screen(C);
if (CTX_data_equals(member, "asset_shelf")) {
CTX_data_pointer_set(result, &screen->id, &RNA_AssetShelf, shelf_hook->active_shelf);
const ARegion *shelf_region = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_ASSET_SHELF);
if (!shelf_region) {
/* Called in wrong context, area doesn't have a shelf. */
BLI_assert_unreachable();
return CTX_RESULT_NO_DATA;
}
const AssetShelfHook *hook = AssetShelfHook::get_from_asset_shelf_region(*shelf_region);
if (!hook) {
return CTX_RESULT_NO_DATA;
}
CTX_data_pointer_set(result, &screen->id, &RNA_AssetShelf, hook->active_shelf);
return CTX_RESULT_OK;
}

View File

@ -37,6 +37,19 @@ 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().
*/
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);
/**
* Frees the contained data, not \a shelf_settings itself.
*/

View File

@ -13,11 +13,19 @@
#include "asset_shelf.hh"
#include "ED_asset_shelf.h"
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);
}
using namespace blender::ed::asset::shelf;
namespace blender::ed::asset::shelf {
AssetShelfHook *ED_asset_shelf_hook_duplicate(const AssetShelfHook *hook)
AssetShelfHook *hook_duplicate(const AssetShelfHook *hook)
{
static_assert(std::is_trivial_v<AssetShelfHook>,
"AssetShelfHook needs to be trivial to allow freeing with MEM_freeN()");
@ -37,7 +45,7 @@ AssetShelfHook *ED_asset_shelf_hook_duplicate(const AssetShelfHook *hook)
return new_hook;
}
void ED_asset_shelf_hook_free(AssetShelfHook **hook)
void hook_free(AssetShelfHook **hook)
{
LISTBASE_FOREACH_MUTABLE (AssetShelf *, shelf, &(*hook)->shelves) {
MEM_delete(shelf);
@ -45,7 +53,7 @@ void ED_asset_shelf_hook_free(AssetShelfHook **hook)
MEM_SAFE_FREE(*hook);
}
void ED_asset_shelf_hook_blend_write(BlendWriter *writer, const AssetShelfHook *hook)
void hook_blend_write(BlendWriter *writer, const AssetShelfHook *hook)
{
BLO_write_struct(writer, AssetShelfHook, hook);
LISTBASE_FOREACH (const AssetShelf *, shelf, &hook->shelves) {
@ -54,7 +62,7 @@ void ED_asset_shelf_hook_blend_write(BlendWriter *writer, const AssetShelfHook *
}
}
void ED_asset_shelf_hook_blend_read_data(BlendDataReader *reader, AssetShelfHook **hook)
void hook_blend_read_data(BlendDataReader *reader, AssetShelfHook **hook)
{
if (!*hook) {
return;
@ -71,3 +79,5 @@ void ED_asset_shelf_hook_blend_read_data(BlendDataReader *reader, AssetShelfHook
settings_blend_read_data(reader, shelf->settings);
}
}
} // namespace blender::ed::asset::shelf

View File

@ -346,21 +346,10 @@ static void view3d_free(SpaceLink *sl)
}
BKE_viewer_path_clear(&vd->viewer_path);
if (vd->asset_shelf_hook) {
ED_asset_shelf_hook_free(&vd->asset_shelf_hook);
}
}
/* spacetype; init callback */
static void view3d_init(wmWindowManager * /*wm*/, ScrArea *area)
{
BLI_assert(area->spacetype == SPACE_VIEW3D);
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
if (!v3d->asset_shelf_hook) {
v3d->asset_shelf_hook = MEM_cnew<AssetShelfHook>("AssetShelfHook");
}
}
static void view3d_init(wmWindowManager * /*wm*/, ScrArea * /*area*/) {}
static void view3d_exit(wmWindowManager * /*wm*/, ScrArea *area)
{
@ -395,8 +384,6 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl)
BKE_viewer_path_copy(&v3dn->viewer_path, &v3do->viewer_path);
v3dn->asset_shelf_hook = ED_asset_shelf_hook_duplicate(v3do->asset_shelf_hook);
/* copy or clear inside new stuff */
return (SpaceLink *)v3dn;
@ -1935,12 +1922,6 @@ static void view3d_tools_region_draw(const bContext *C, ARegion *region)
ED_region_panels_ex(C, region, contexts);
}
static void view3d_asset_shelf_region_layout(const bContext *C, ARegion *region)
{
View3D *v3d = CTX_wm_view3d(C);
ED_asset_shelf_region_layout(C, region, v3d->asset_shelf_hook);
}
/* add handlers, stuff you only do once or on area/region changes */
static void view3d_asset_shelf_region_init(wmWindowManager *wm, ARegion *region)
{
@ -2065,14 +2046,6 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
return CTX_RESULT_MEMBER_NOT_FOUND;
}
static int view3d_asset_shelf_context(const bContext *C,
const char *member,
bContextDataResult *result)
{
View3D *v3d = CTX_wm_view3d(C);
return ED_asset_shelf_context(C, member, result, v3d->asset_shelf_hook);
}
static void view3d_id_remap_v3d_ob_centers(View3D *v3d, const IDRemapper *mappings)
{
if (BKE_id_remapper_apply(mappings, (ID **)&v3d->ob_center, ID_REMAP_APPLY_DEFAULT) ==
@ -2148,8 +2121,6 @@ static void view3d_space_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
BKE_screen_view3d_do_versions_250(v3d, &sl->regionbase);
BKE_viewer_path_blend_read_data(reader, &v3d->viewer_path);
ED_asset_shelf_hook_blend_read_data(reader, &v3d->asset_shelf_hook);
}
static void view3d_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
@ -2178,8 +2149,6 @@ static void view3d_space_blend_write(BlendWriter *writer, SpaceLink *sl)
BKE_screen_view3d_shading_blend_write(writer, &v3d->shading);
BKE_viewer_path_blend_write(writer, &v3d->viewer_path);
ED_asset_shelf_hook_blend_write(writer, v3d->asset_shelf_hook);
}
void ED_spacetype_view3d()
@ -2275,12 +2244,14 @@ void ED_spacetype_view3d()
art = MEM_cnew<ARegionType>("spacetype view3d asset shelf region");
art->regionid = RGN_TYPE_ASSET_SHELF;
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_ASSET_SHELF | ED_KEYMAP_FRAMES;
art->duplicate = ED_asset_shelf_region_duplicate;
art->free = ED_asset_shelf_region_free;
art->listener = ED_asset_shelf_region_listen;
art->poll = ED_asset_shelf_regions_poll;
art->snap_size = ED_asset_shelf_region_snap;
art->context = view3d_asset_shelf_context;
art->context = ED_asset_shelf_context;
art->init = view3d_asset_shelf_region_init;
art->layout = view3d_asset_shelf_region_layout;
art->layout = ED_asset_shelf_region_layout;
art->draw = ED_asset_shelf_region_draw;
BLI_addhead(&st->regiontypes, art);
@ -2292,7 +2263,7 @@ void ED_spacetype_view3d()
art->poll = ED_asset_shelf_regions_poll;
art->draw = ED_asset_shelf_header_region;
art->listener = ED_asset_shelf_header_region_listen;
art->context = view3d_asset_shelf_context;
art->context = ED_asset_shelf_context;
BLI_addhead(&st->regiontypes, art);
ED_asset_shelf_header_regiontype_register(art, SPACE_VIEW3D);

View File

@ -822,6 +822,9 @@ typedef struct AssetShelfHook {
/** 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);
#endif
} AssetShelfHook;
/* #AssetShelfSettings.display_flag */

View File

@ -361,8 +361,6 @@ typedef struct View3D {
/** Path to the viewer node that is currently previewed. This is retrieved from the workspace. */
ViewerPath viewer_path;
struct AssetShelfHook *asset_shelf_hook;
/** Runtime evaluation data (keep last). */
JulianEisel marked this conversation as resolved Outdated

Why does this exist rather than storing everything in ARegion.regiondata? If it's in the regiondata it doesn't need to get added to every space type, and is more self contained and generic.

I'm also not sure how to interpret the term "hook" in this context.

Why does this exist rather than storing everything in `ARegion.regiondata`? If it's in the regiondata it doesn't need to get added to every space type, and is more self contained and generic. I'm also not sure how to interpret the term "hook" in this context.

Also find the term hook awkward, hooks are often used in the context of callbacks, it might make sense if the data stored in hooks was a way to hook into data used elsewhere. Where as this looks like a collection. So it could be AssetShelfCollection or AssetShelfStore... or similar (personally prefer "Store" as it's not confused with Object Collections).

Also find the term hook awkward, hooks are often used in the context of callbacks, it might make sense if the data stored in hooks was a way to hook into data used elsewhere. Where as this looks like a collection. So it could be `AssetShelfCollection` or `AssetShelfStore`... or similar (personally prefer "Store" as it's not confused with Object Collections).

Am fine with another name. Just kept it consistent with WorkSpaceHook (not that this is a great name either).

Using regiondata for storage didn't cross my mind, especially since there are two asset shelf regions. But we can still let the main asset shelf region keep the storage. Might simplify things a bit. I will look into it.

Just to note since this isn't obvious: One main reason there's a dedicated AssetShelfHook struct is so that can be reused easily in different editors, e.g. to support asset shelves in the Node Editor, Image Editor (e.g. for brushes), etc.

Am fine with another name. Just kept it consistent with `WorkSpaceHook` (not that this is a great name either). Using regiondata for storage didn't cross my mind, especially since there are two asset shelf regions. But we can still let the main asset shelf region keep the storage. Might simplify things a bit. I will look into it. Just to note since this isn't obvious: One main reason there's a dedicated `AssetShelfHook` struct is so that can be reused easily in different editors, e.g. to support asset shelves in the Node Editor, Image Editor (e.g. for brushes), etc.
View3D_Runtime runtime;
} View3D;