Popup to select which catalogs are displayed in the asset shelf footer

The selected catalogs are currently listed as simple labels in the
footer, just for testing.
This commit is contained in:
Julian Eisel 2023-01-19 11:50:45 +01:00
parent 47c9c31138
commit 4fa69fbda8
11 changed files with 472 additions and 18 deletions

View File

@ -13,6 +13,8 @@ set(INC
../../windowmanager
../../../../intern/clog
../../../../intern/guardedalloc
# dna_type_offsets.h
${CMAKE_CURRENT_BINARY_DIR}/../../makesdna/intern
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)

View File

@ -11,18 +11,54 @@ extern "C" {
#endif
struct ARegionType;
struct AssetShelfSettings;
struct bContext;
struct bContextDataResult;
struct BlendDataReader;
struct BlendWriter;
struct wmWindowManager;
void ED_region_asset_shelf_footer_init(struct wmWindowManager *wm, struct ARegion *region);
void ED_region_asset_shelf_footer(const struct bContext *C, struct ARegion *region);
/* -------------------------------------------------------------------- */
/* Asset Shelf Regions */
void ED_region_asset_shelf_listen(const struct wmRegionListenerParams *params);
/** Only needed for #RGN_TYPE_ASSET_SHELF (not #RGN_TYPE_ASSET_SHELF_FOOTER). */
void ED_asset_shelf_region_listen(const struct wmRegionListenerParams *params);
void ED_asset_shelf_footer_region_init(struct wmWindowManager *wm, struct ARegion *region);
void ED_asset_shelf_footer_region(const struct bContext *C, struct ARegion *region);
void ED_asset_shelf_footer_region_listen(const struct wmRegionListenerParams *params);
void ED_asset_shelf_footer_register(struct ARegionType *region_type,
const char *idname,
const int space_type);
/* -------------------------------------------------------------------- */
/* Asset Shelf Settings */
/**
* Deep-copies \a shelf_settings into newly allocated memory. Must be freed using #MEM_freeN() or
* #MEM_delete().
*/
AssetShelfSettings *ED_asset_shelf_settings_duplicate(const AssetShelfSettings *shelf_settings);
/**
* Frees the contained data, not \a shelf_settings itself.
*/
void ED_asset_shelf_settings_free(AssetShelfSettings *shelf_settings);
void ED_asset_shelf_settings_blend_write(struct BlendWriter *writer,
const struct AssetShelfSettings *storage);
void ED_asset_shelf_settings_blend_read_data(struct BlendDataReader *reader,
struct AssetShelfSettings **storage);
/* -------------------------------------------------------------------- */
/**
* Creates an `"asset_shelf_settings"` context member, pointing to \a shelf_settings.
*/
int ED_asset_shelf_context(const struct bContext *C,
const char *member,
struct bContextDataResult *result,
struct AssetShelfSettings *shelf_settings);
#ifdef __cplusplus
}
#endif

View File

@ -1,44 +1,411 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edasset
*/
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.hh"
#include "BKE_context.h"
#include "BKE_screen.h"
#include "BLO_read_write.h"
#include "BLT_translation.h"
#include "DNA_screen_types.h"
#include "ED_asset_list.h"
#include "ED_asset_list.hh"
#include "ED_screen.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
#include "UI_interface.hh"
#include "UI_resources.h"
#include "UI_tree_view.hh"
#include "WM_api.h"
#include "ED_asset_shelf.h"
static void asset_shelf_draw(const bContext * /*C*/, Header *header)
{
uiLayout *layout = header->layout;
uiItemL(layout, "Fooo\n", ICON_ASSET_MANAGER);
}
using namespace blender;
void ED_region_asset_shelf_listen(const wmRegionListenerParams *params)
/* -------------------------------------------------------------------- */
/** \name Asset Shelf Regions
* \{ */
void ED_asset_shelf_region_listen(const wmRegionListenerParams *params)
{
if (ED_assetlist_listen(params->notifier)) {
ED_region_tag_redraw_no_rebuild(params->region);
}
}
void ED_region_asset_shelf_footer_init(wmWindowManager * /*wm*/, ARegion *region)
void ED_asset_shelf_footer_region_listen(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
const wmNotifier *wmn = params->notifier;
switch (wmn->category) {
case NC_SPACE:
if (wmn->data == ND_SPACE_ASSET_SHELF) {
ED_region_tag_redraw(region);
}
break;
}
}
void ED_asset_shelf_footer_region_init(wmWindowManager * /*wm*/, ARegion *region)
{
ED_region_header_init(region);
}
void ED_region_asset_shelf_footer(const bContext *C, ARegion *region)
void ED_asset_shelf_footer_region(const bContext *C, ARegion *region)
{
ED_region_header(C, region);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Shelf Settings
* \{ */
AssetShelfSettings *ED_asset_shelf_settings_duplicate(const AssetShelfSettings *shelf_settings)
{
if (!shelf_settings) {
return nullptr;
}
static_assert(
std::is_trivial_v<AssetShelfSettings>,
"AssetShelfSettings needs to be trivial to allow freeing with MEM_freeN() (API promise)");
AssetShelfSettings *new_settings = MEM_new<AssetShelfSettings>(__func__, *shelf_settings);
LISTBASE_FOREACH (LinkData *, catalog_path_item, &shelf_settings->enabled_catalog_paths) {
LinkData *new_path_item = static_cast<LinkData *>(MEM_dupallocN(catalog_path_item));
new_path_item->data = BLI_strdup((char *)catalog_path_item->data);
BLI_addtail(&new_settings->enabled_catalog_paths, new_path_item);
}
return new_settings;
}
static void asset_shelf_settings_clear_enabled_catalogs(AssetShelfSettings &shelf_settings)
{
LISTBASE_FOREACH_MUTABLE (LinkData *, catalog_path_item, &shelf_settings.enabled_catalog_paths) {
MEM_freeN(catalog_path_item->data);
BLI_freelinkN(&shelf_settings.enabled_catalog_paths, catalog_path_item);
}
BLI_assert(BLI_listbase_is_empty(&shelf_settings.enabled_catalog_paths));
}
void ED_asset_shelf_settings_free(AssetShelfSettings *shelf_settings)
{
asset_shelf_settings_clear_enabled_catalogs(*shelf_settings);
}
void ED_asset_shelf_settings_blend_write(BlendWriter *writer,
const AssetShelfSettings *shelf_settings)
{
if (!shelf_settings) {
return;
}
BLO_write_struct(writer, AssetShelfSettings, shelf_settings);
LISTBASE_FOREACH (LinkData *, catalog_path_item, &shelf_settings->enabled_catalog_paths) {
BLO_write_struct(writer, LinkData, catalog_path_item);
BLO_write_string(writer, (const char *)catalog_path_item->data);
}
}
void ED_asset_shelf_settings_blend_read_data(BlendDataReader *reader,
AssetShelfSettings **shelf_settings)
{
if (!*shelf_settings) {
return;
}
BLO_read_data_address(reader, shelf_settings);
BLO_read_list(reader, &(*shelf_settings)->enabled_catalog_paths);
LISTBASE_FOREACH (LinkData *, catalog_path_item, &(*shelf_settings)->enabled_catalog_paths) {
BLO_read_data_address(reader, &catalog_path_item->data);
}
}
static bool asset_shelf_settings_is_catalog_path_enabled(
const AssetShelfSettings &shelf_settings, const asset_system::AssetCatalogPath &path)
{
LISTBASE_FOREACH (LinkData *, catalog_path_item, &shelf_settings.enabled_catalog_paths) {
if (StringRef((const char *)catalog_path_item->data) == path.str()) {
return true;
}
}
return false;
}
static void asset_shelf_settings_set_catalog_path_enabled(
AssetShelfSettings &shelf_settings, const asset_system::AssetCatalogPath &path)
{
char *path_copy = BLI_strdupn(path.c_str(), path.length());
BLI_addtail(&shelf_settings.enabled_catalog_paths, BLI_genericNodeN(path_copy));
}
static void asset_shelf_settings_foreach_enabled_catalog_path(
const AssetShelfSettings &shelf_settings,
FunctionRef<void(const asset_system::AssetCatalogPath &catalog_path)> fn)
{
LISTBASE_FOREACH (LinkData *, catalog_path_item, &shelf_settings.enabled_catalog_paths) {
fn(asset_system::AssetCatalogPath((char *)catalog_path_item->data));
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Shelf Context
* \{ */
int ED_asset_shelf_context(const bContext *C,
const char *member,
bContextDataResult *result,
AssetShelfSettings *shelf_settings)
{
static const char *context_dir[] = {
"asset_shelf_settings",
nullptr,
};
if (CTX_data_dir(member)) {
CTX_data_dir_set(result, context_dir);
return CTX_RESULT_OK;
}
bScreen *screen = CTX_wm_screen(C);
if (CTX_data_equals(member, "asset_shelf_settings")) {
CTX_data_pointer_set(result, &screen->id, &RNA_AssetShelfSettings, shelf_settings);
return CTX_RESULT_OK;
}
return CTX_RESULT_MEMBER_NOT_FOUND;
}
static AssetShelfSettings *get_asset_shelf_settings_from_context(const bContext *C)
{
PointerRNA shelf_settings_ptr = CTX_data_pointer_get_type(
C, "asset_shelf_settings", &RNA_AssetShelfSettings);
return static_cast<AssetShelfSettings *>(shelf_settings_ptr.data);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Catalog Selector UI
*
* Popup containing a tree-view to select which catalogs to display in the asset shelf footer.
* \{ */
class AssetCatalogSelectorTree : public ui::AbstractTreeView {
asset_system::AssetLibrary &library_;
asset_system::AssetCatalogTree *catalog_tree_;
AssetShelfSettings &shelf_settings_;
public:
class Item;
AssetCatalogSelectorTree(asset_system::AssetLibrary &library, AssetShelfSettings &shelf_settings)
: library_(library), shelf_settings_(shelf_settings)
{
asset_system::AssetCatalogService *catalog_service = library_.catalog_service.get();
catalog_tree_ = catalog_service->get_catalog_tree();
}
void build_tree() override
{
if (!catalog_tree_) {
return;
}
catalog_tree_->foreach_root_item([this](asset_system::AssetCatalogTreeItem &catalog_item) {
build_catalog_items_recursive(*this, catalog_item);
});
}
Item &build_catalog_items_recursive(ui::TreeViewOrItem &parent_view_item,
asset_system::AssetCatalogTreeItem &catalog_item) const
{
Item &view_item = parent_view_item.add_tree_item<Item>(catalog_item, shelf_settings_);
catalog_item.foreach_child([&view_item, this](asset_system::AssetCatalogTreeItem &child) {
build_catalog_items_recursive(view_item, child);
});
return view_item;
}
void update_shelf_settings_from_enabled_catalogs(const bContext *C);
class Item : public ui::BasicTreeViewItem {
asset_system::AssetCatalogTreeItem catalog_item_;
/* Is the catalog path enabled in this redraw? Set on construction, updated by the UI (which
* gets a pointer to it). The UI needs it as char. */
char catalog_path_enabled_ = false;
public:
Item(asset_system::AssetCatalogTreeItem &catalog_item, AssetShelfSettings &shelf_settings)
: ui::BasicTreeViewItem(catalog_item.get_name()),
catalog_item_(catalog_item),
catalog_path_enabled_(asset_shelf_settings_is_catalog_path_enabled(
shelf_settings, catalog_item.catalog_path()))
{
}
bool is_catalog_path_enabled() const
{
return catalog_path_enabled_ != 0;
}
asset_system::AssetCatalogPath catalog_path() const
{
return catalog_item_.catalog_path();
}
void build_row(uiLayout &row) override
{
uiBlock *block = uiLayoutGetBlock(&row);
uiLayoutSetEmboss(&row, UI_EMBOSS);
if (!is_collapsible()) {
uiItemL(&row, nullptr, ICON_BLANK1);
}
uiBut *but = uiDefButC(block,
UI_BTYPE_CHECKBOX,
0,
catalog_item_.get_name().c_str(),
0,
0,
UI_UNIT_X * 10,
UI_UNIT_Y,
(char *)&catalog_path_enabled_,
0,
0,
0,
0,
TIP_("Toggle catalog visibility in the asset shelf"));
UI_but_func_set(
but,
[](bContext *C, void *selector_tree_ptr, void *) {
AssetCatalogSelectorTree &selector_tree = *static_cast<AssetCatalogSelectorTree *>(
selector_tree_ptr);
selector_tree.update_shelf_settings_from_enabled_catalogs(C);
},
&dynamic_cast<AssetCatalogSelectorTree &>(get_tree_view()),
nullptr);
UI_but_flag_disable(but, UI_BUT_UNDO);
}
};
};
void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs(const bContext *C)
{
asset_shelf_settings_clear_enabled_catalogs(shelf_settings_);
foreach_item([C, this](ui::AbstractTreeViewItem &view_item) {
const auto &selector_tree_item = dynamic_cast<AssetCatalogSelectorTree::Item &>(view_item);
if (selector_tree_item.is_catalog_path_enabled()) {
asset_shelf_settings_set_catalog_path_enabled(shelf_settings_,
selector_tree_item.catalog_path());
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_SHELF, nullptr);
}
});
}
static uiBlock *asset_shelf_catalog_selector_block_draw(bContext *C,
ARegion *region,
void * /*arg1*/)
{
const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C);
asset_system::AssetLibrary *library = ED_assetlist_library_get_once_available(*library_ref);
AssetShelfSettings *shelf_settings = get_asset_shelf_settings_from_context(C);
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
UI_block_flag_enable(block,
UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPUP_CAN_REFRESH);
uiLayout *layout = UI_block_layout(block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
0,
0,
UI_UNIT_X * 12,
UI_UNIT_Y,
0,
UI_style_get());
uiItemL(layout, "Enable Catalogs", ICON_NONE);
uiItemS(layout);
uiLayoutSetEmboss(layout, UI_EMBOSS_NONE);
if (library && shelf_settings) {
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset catalog tree view",
std::make_unique<AssetCatalogSelectorTree>(*library, *shelf_settings));
ui::TreeViewBuilder builder(*block);
builder.build_tree_view(*tree_view);
}
UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
UI_block_direction_set(block, UI_DIR_UP);
return block;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Shelf Footer
*
* Implemented as HeaderType for #RGN_TYPE_ASSET_SHELF_FOOTER.
* \{ */
static void asset_shelf_footer_draw(const bContext *C, Header *header)
{
uiLayout *layout = header->layout;
uiBlock *block = uiLayoutGetBlock(layout);
const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C);
ED_assetlist_storage_fetch(library_ref, C);
uiDefIconBlockBut(block,
asset_shelf_catalog_selector_block_draw,
nullptr,
0,
ICON_RIGHTARROW,
0,
0,
UI_UNIT_X * 1.5f,
UI_UNIT_Y,
TIP_("Select catalogs to display"));
AssetShelfSettings *shelf_settings = get_asset_shelf_settings_from_context(C);
if (shelf_settings) {
asset_shelf_settings_foreach_enabled_catalog_path(
*shelf_settings, [layout](const asset_system::AssetCatalogPath &path) {
uiItemL(layout, path.name().c_str(), ICON_NONE);
});
}
}
void ED_asset_shelf_footer_register(ARegionType *region_type,
const char *idname,
const int space_type)
@ -47,6 +414,8 @@ void ED_asset_shelf_footer_register(ARegionType *region_type,
strcpy(ht->idname, idname);
ht->space_type = space_type;
ht->region_type = RGN_TYPE_ASSET_SHELF_FOOTER;
ht->draw = asset_shelf_draw;
ht->draw = asset_shelf_footer_draw;
BLI_addtail(&region_type->headertypes, ht);
}
/** \} */

View File

@ -168,6 +168,7 @@ enum {
UI_BLOCK_SEARCH_ONLY = 1 << 25,
/** Hack for quick setup (splash screen) to draw text centered. */
UI_BLOCK_QUICK_SETUP = 1 << 26,
UI_BLOCK_POPUP_CAN_REFRESH = 1 << 27,
};
/** #uiPopupBlockHandle.menuretval */

View File

@ -593,6 +593,11 @@ uiBlock *ui_popup_block_refresh(bContext *C,
block->flag |= UI_BLOCK_LOOP;
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
/* TODO does this flag need to be checked in more cases? */
if (block->flag & UI_BLOCK_POPUP_CAN_REFRESH) {
handle->can_refresh = true;
}
/* defer this until blocks are translated (below) */
block->oldblock = nullptr;

View File

@ -167,7 +167,7 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C,
* lookup the hovered item via context here. */
const wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C);
uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region,
win->eventstate->xy);

View File

@ -342,11 +342,21 @@ static void view3d_free(SpaceLink *sl)
}
BKE_viewer_path_clear(&vd->viewer_path);
if (vd->asset_shelf) {
ED_asset_shelf_settings_free(vd->asset_shelf);
MEM_SAFE_FREE(vd->asset_shelf);
}
}
/* spacetype; init callback */
static void view3d_init(wmWindowManager * /*wm*/, ScrArea * /*area*/)
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) {
v3d->asset_shelf = MEM_cnew<AssetShelfSettings>("AssetShelfSettings");
}
}
static void view3d_exit(wmWindowManager * /*wm*/, ScrArea *area)
@ -382,6 +392,8 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl)
BKE_viewer_path_copy(&v3dn->viewer_path, &v3do->viewer_path);
v3dn->asset_shelf = ED_asset_shelf_settings_duplicate(v3do->asset_shelf);
/* copy or clear inside new stuff */
return (SpaceLink *)v3dn;
@ -1959,6 +1971,14 @@ 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);
}
static void view3d_id_remap_v3d_ob_centers(View3D *v3d, const struct IDRemapper *mappings)
{
if (BKE_id_remapper_apply(mappings, (ID **)&v3d->ob_center, ID_REMAP_APPLY_DEFAULT) ==
@ -2034,6 +2054,8 @@ static void view3d_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_settings_blend_read_data(reader, &v3d->asset_shelf);
}
static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
@ -2062,6 +2084,8 @@ static void view3d_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_settings_blend_write(writer, v3d->asset_shelf);
}
void ED_spacetype_view3d()
@ -2158,7 +2182,7 @@ void ED_spacetype_view3d()
art->regionid = RGN_TYPE_ASSET_SHELF;
art->prefsizey = HEADERY * 3.5f;
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
art->listener = ED_region_asset_shelf_listen;
art->listener = ED_asset_shelf_region_listen;
art->init = view3d_header_region_init;
art->draw = ED_region_header;
BLI_addhead(&st->regiontypes, art);
@ -2167,8 +2191,10 @@ void ED_spacetype_view3d()
art->regionid = RGN_TYPE_ASSET_SHELF_FOOTER;
art->prefsizey = HEADERY;
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FOOTER;
art->init = ED_region_asset_shelf_footer_init;
art->draw = ED_region_asset_shelf_footer;
art->init = ED_asset_shelf_footer_region_init;
art->draw = ED_asset_shelf_footer_region;
art->listener = ED_asset_shelf_footer_region_listen;
art->context = view3d_asset_shelf_context;
BLI_addhead(&st->regiontypes, art);
ED_asset_shelf_footer_register(art, "VIEW3D_HT_asset_shelf", SPACE_VIEW3D);

View File

@ -746,6 +746,11 @@ enum {
RGN_DRAW_EDITOR_OVERLAYS = 32,
};
typedef struct AssetShelfSettings {
/* TODO make this per mode? (or use a custom identifier?) */
ListBase enabled_catalog_paths; /* #LinkData */
} AssetShelfSettings;
#ifdef __cplusplus
}
#endif

View File

@ -356,6 +356,8 @@ typedef struct View3D {
/** Path to the viewer node that is currently previewed. This is retrieved from the workspace. */
ViewerPath viewer_path;
struct AssetShelfSettings *asset_shelf;
/** Runtime evaluation data (keep last). */
View3D_Runtime runtime;
} View3D;

View File

@ -1832,6 +1832,12 @@ static void rna_def_menu(BlenderRNA *brna)
RNA_define_verify_sdna(1);
}
static void rna_def_asset_shelf_settings(BlenderRNA *brna)
{
StructRNA *srna = RNA_def_struct(brna, "AssetShelfSettings", NULL);
RNA_def_struct_ui_text(srna, "Asset Shelf Settings", "");
}
void RNA_def_ui(BlenderRNA *brna)
{
rna_def_ui_layout(brna);
@ -1839,6 +1845,7 @@ void RNA_def_ui(BlenderRNA *brna)
rna_def_uilist(brna);
rna_def_header(brna);
rna_def_menu(brna);
rna_def_asset_shelf_settings(brna);
}
#endif /* RNA_RUNTIME */

View File

@ -485,6 +485,7 @@ typedef struct wmNotifier {
#define ND_SPACE_CLIP (20 << 16)
#define ND_SPACE_FILE_PREVIEW (21 << 16)
#define ND_SPACE_SPREADSHEET (22 << 16)
#define ND_SPACE_ASSET_SHELF (23 << 16)
/* NC_ASSET */
/* Denotes that the AssetList is done reading some previews. NOT that the preview generation of