Similar to d204830107. This is the last real use of
the a1 and a2 arguments for many button definition functions.
794 lines
24 KiB
C++
794 lines
24 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup edasset
|
|
*
|
|
* General asset shelf code, mostly region callbacks, drawing and context stuff.
|
|
*/
|
|
|
|
#include <algorithm>
|
|
|
|
#include "AS_asset_catalog_path.hh"
|
|
#include "AS_asset_library.hh"
|
|
|
|
#include "BLI_string.h"
|
|
|
|
#include "BKE_context.hh"
|
|
#include "BKE_main.hh"
|
|
#include "BKE_screen.hh"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "ED_asset_list.hh"
|
|
#include "ED_screen.hh"
|
|
|
|
#include "RNA_prototypes.h"
|
|
|
|
#include "UI_interface.hh"
|
|
#include "UI_resources.hh"
|
|
#include "UI_tree_view.hh"
|
|
#include "UI_view2d.hh"
|
|
|
|
#include "WM_api.hh"
|
|
|
|
#include "ED_asset_shelf.hh"
|
|
#include "asset_shelf.hh"
|
|
|
|
namespace blender::ed::asset::shelf {
|
|
|
|
static int asset_shelf_default_tile_height();
|
|
|
|
void send_redraw_notifier(const bContext &C)
|
|
{
|
|
WM_event_add_notifier(&C, NC_SPACE | ND_REGIONS_ASSET_SHELF, nullptr);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Shelf Type
|
|
* \{ */
|
|
|
|
static bool asset_shelf_type_poll(const bContext &C,
|
|
const SpaceType &space_type,
|
|
AssetShelfType *shelf_type)
|
|
{
|
|
if (!shelf_type) {
|
|
return false;
|
|
}
|
|
|
|
BLI_assert_msg(BLI_findindex(&space_type.asset_shelf_types, shelf_type) != -1,
|
|
"Asset shelf type is not registered");
|
|
UNUSED_VARS_NDEBUG(space_type);
|
|
|
|
return !shelf_type->poll || shelf_type->poll(&C, shelf_type);
|
|
}
|
|
|
|
static AssetShelfType *asset_shelf_type_ensure(const SpaceType &space_type, AssetShelf &shelf)
|
|
{
|
|
if (shelf.type) {
|
|
return shelf.type;
|
|
}
|
|
|
|
LISTBASE_FOREACH (AssetShelfType *, shelf_type, &space_type.asset_shelf_types) {
|
|
if (STREQ(shelf.idname, shelf_type->idname)) {
|
|
shelf.type = shelf_type;
|
|
return shelf_type;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static AssetShelf *create_shelf_from_type(AssetShelfType &type)
|
|
{
|
|
AssetShelf *shelf = MEM_new<AssetShelf>(__func__);
|
|
*shelf = dna::shallow_zero_initialize();
|
|
shelf->settings.preview_size = DEFAULT_TILE_SIZE;
|
|
shelf->settings.asset_library_reference = asset_system::all_library_reference();
|
|
shelf->type = &type;
|
|
shelf->preferred_row_count = 1;
|
|
STRNCPY(shelf->idname, type.idname);
|
|
return shelf;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Active Shelf Management
|
|
* \{ */
|
|
|
|
/**
|
|
* 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(RegionAssetShelf &shelf_regiondata, AssetShelf &shelf)
|
|
{
|
|
shelf_regiondata.active_shelf = &shelf;
|
|
BLI_assert(BLI_findindex(&shelf_regiondata.shelves, &shelf) > -1);
|
|
BLI_remlink(&shelf_regiondata.shelves, &shelf);
|
|
BLI_addhead(&shelf_regiondata.shelves, &shelf);
|
|
}
|
|
|
|
/**
|
|
* Determine and set the currently active asset shelf, creating a new shelf if needed.
|
|
*
|
|
* 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 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, #RegionAssetShelf.active_shelf is set to null.
|
|
*
|
|
* 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.
|
|
*
|
|
* \return A non-owning pointer to the now active shelf. Might be null if no shelf is valid in
|
|
* current context (all polls failed).
|
|
*/
|
|
static AssetShelf *update_active_shelf(const bContext &C,
|
|
const SpaceType &space_type,
|
|
RegionAssetShelf &shelf_regiondata)
|
|
{
|
|
/* Note: Don't access #AssetShelf.type directly, use #asset_shelf_type_ensure(). */
|
|
|
|
/* Case 1: */
|
|
if (shelf_regiondata.active_shelf &&
|
|
asset_shelf_type_poll(
|
|
C, space_type, 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(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, &shelf_regiondata.shelves) {
|
|
if (shelf == shelf_regiondata.active_shelf) {
|
|
continue;
|
|
}
|
|
|
|
if (asset_shelf_type_poll(C, space_type, asset_shelf_type_ensure(space_type, *shelf))) {
|
|
/* Found a valid previously activated shelf, reactivate it. */
|
|
activate_shelf(shelf_regiondata, *shelf);
|
|
return shelf;
|
|
}
|
|
}
|
|
|
|
/* Case 3: */
|
|
LISTBASE_FOREACH (AssetShelfType *, shelf_type, &space_type.asset_shelf_types) {
|
|
if (asset_shelf_type_poll(C, space_type, shelf_type)) {
|
|
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
|
|
BLI_addhead(&shelf_regiondata.shelves, new_shelf);
|
|
/* Moves ownership to the regiondata. */
|
|
activate_shelf(shelf_regiondata, *new_shelf);
|
|
return new_shelf;
|
|
}
|
|
}
|
|
|
|
shelf_regiondata.active_shelf = nullptr;
|
|
return nullptr;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Asset Shelf Regions
|
|
* \{ */
|
|
|
|
void *region_duplicate(void *regiondata)
|
|
{
|
|
const RegionAssetShelf *shelf_regiondata = static_cast<RegionAssetShelf *>(regiondata);
|
|
if (!shelf_regiondata) {
|
|
return nullptr;
|
|
}
|
|
|
|
return regiondata_duplicate(shelf_regiondata);
|
|
}
|
|
|
|
void region_free(ARegion *region)
|
|
{
|
|
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
|
|
if (shelf_regiondata) {
|
|
regiondata_free(shelf_regiondata);
|
|
}
|
|
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.
|
|
*/
|
|
static bool asset_shelf_space_poll(const bContext *C, const SpaceLink *space_link)
|
|
{
|
|
const SpaceType *space_type = BKE_spacetype_from_id(space_link->spacetype);
|
|
|
|
/* Is there any asset shelf type registered that returns true for it's poll? */
|
|
LISTBASE_FOREACH (AssetShelfType *, shelf_type, &space_type->asset_shelf_types) {
|
|
if (asset_shelf_type_poll(*C, *space_type, shelf_type)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool regions_poll(const RegionPollParams *params)
|
|
{
|
|
return asset_shelf_space_poll(params->context,
|
|
static_cast<SpaceLink *>(params->area->spacedata.first));
|
|
}
|
|
|
|
static void asset_shelf_region_listen(const wmRegionListenerParams *params)
|
|
{
|
|
ARegion *region = params->region;
|
|
const wmNotifier *wmn = params->notifier;
|
|
|
|
switch (wmn->category) {
|
|
case NC_SPACE:
|
|
if (wmn->data == ND_REGIONS_ASSET_SHELF) {
|
|
ED_region_tag_redraw(region);
|
|
}
|
|
break;
|
|
case NC_SCENE:
|
|
/* Asset shelf polls typically check the mode. */
|
|
if (ELEM(wmn->data, ND_MODE)) {
|
|
ED_region_tag_redraw(region);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void region_listen(const wmRegionListenerParams *params)
|
|
{
|
|
if (list::listen(params->notifier)) {
|
|
ED_region_tag_redraw_no_rebuild(params->region);
|
|
}
|
|
/* If the asset list didn't catch the notifier, let the region itself listen. */
|
|
else {
|
|
asset_shelf_region_listen(params);
|
|
}
|
|
}
|
|
|
|
void region_init(wmWindowManager *wm, ARegion *region)
|
|
{
|
|
if (!region->regiondata) {
|
|
region->regiondata = MEM_cnew<RegionAssetShelf>("RegionAssetShelf");
|
|
}
|
|
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 = shelf_regiondata.active_shelf;
|
|
|
|
UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy);
|
|
|
|
wmKeyMap *keymap = WM_keymap_ensure(
|
|
wm->defaultconf, "View2D Buttons List", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
|
WM_event_add_keymap_handler(®ion->handlers, keymap);
|
|
|
|
region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE;
|
|
region->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y;
|
|
region->v2d.keepofs |= V2D_KEEPOFS_Y;
|
|
region->v2d.keeptot |= V2D_KEEPTOT_STRICT;
|
|
|
|
region->v2d.flag |= V2D_SNAP_TO_PAGESIZE_Y;
|
|
region->v2d.page_size_y = active_shelf ? tile_height(active_shelf->settings) :
|
|
asset_shelf_default_tile_height();
|
|
|
|
/* Ensure the view is snapped to a page still, especially for DPI changes. */
|
|
UI_view2d_offset_y_snap_to_closest_page(®ion->v2d);
|
|
}
|
|
|
|
static int main_region_padding_y()
|
|
{
|
|
const uiStyle *style = UI_style_get_dpi();
|
|
return style->buttonspacey / 2;
|
|
}
|
|
|
|
static int main_region_padding_x()
|
|
{
|
|
/* Use the same as the height, equal padding looks nice. */
|
|
return main_region_padding_y();
|
|
}
|
|
|
|
static int current_tile_draw_height(const ARegion *region)
|
|
{
|
|
const RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
|
|
*region);
|
|
const float aspect = BLI_rctf_size_y(®ion->v2d.cur) /
|
|
(BLI_rcti_size_y(®ion->v2d.mask) + 1);
|
|
|
|
/* It can happen that this function is called before the region is actually initialized, when
|
|
* user clicks & drags slightly on the 'up arrow' icon of the shelf. */
|
|
const AssetShelf *active_shelf = shelf_regiondata ? shelf_regiondata->active_shelf : nullptr;
|
|
return (active_shelf ? tile_height(active_shelf->settings) : asset_shelf_default_tile_height()) /
|
|
(IS_EQF(aspect, 0) ? 1.0f : aspect);
|
|
}
|
|
|
|
/**
|
|
* How many rows fit into the region (accounting for padding).
|
|
*/
|
|
static int calculate_row_count_from_tile_draw_height(const int region_height_scaled,
|
|
const int tile_draw_height)
|
|
{
|
|
return std::max(1, int((region_height_scaled - 2 * main_region_padding_y()) / tile_draw_height));
|
|
}
|
|
|
|
static int calculate_scaled_region_height_from_row_count(const int row_count,
|
|
const int tile_draw_height)
|
|
{
|
|
return (row_count * tile_draw_height + 2 * main_region_padding_y());
|
|
}
|
|
|
|
int region_snap(const ARegion *region, const int size, const int axis)
|
|
{
|
|
/* Only on Y axis. */
|
|
if (axis != 1) {
|
|
return size;
|
|
}
|
|
|
|
/* Using scaled values only simplifies things. Simply divide the result by the scale again. */
|
|
|
|
const int tile_height = current_tile_draw_height(region);
|
|
|
|
const int row_count = calculate_row_count_from_tile_draw_height(size * UI_SCALE_FAC,
|
|
tile_height);
|
|
|
|
const int new_size_scaled = calculate_scaled_region_height_from_row_count(row_count,
|
|
tile_height);
|
|
return new_size_scaled / UI_SCALE_FAC;
|
|
}
|
|
|
|
/**
|
|
* Ensure the region height matches the preferred row count (see #AssetShelf.preferred_row_count).
|
|
* In any case, this will ensure the region height is snapped to a multiple of the row count (plus
|
|
* region padding).
|
|
*/
|
|
static void region_resize_to_preferred(ScrArea *area, ARegion *region)
|
|
{
|
|
const RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
|
|
*region);
|
|
const AssetShelf *active_shelf = shelf_regiondata->active_shelf;
|
|
|
|
BLI_assert(active_shelf->preferred_row_count > 0);
|
|
|
|
const int tile_height = current_tile_draw_height(region);
|
|
const int new_size_y = calculate_scaled_region_height_from_row_count(
|
|
active_shelf->preferred_row_count, tile_height) /
|
|
UI_SCALE_FAC;
|
|
|
|
if (region->sizey != new_size_y) {
|
|
region->sizey = new_size_y;
|
|
ED_area_tag_region_size_update(area, region);
|
|
}
|
|
}
|
|
|
|
void region_on_user_resize(const ARegion *region)
|
|
{
|
|
const RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
|
|
*region);
|
|
AssetShelf *active_shelf = shelf_regiondata->active_shelf;
|
|
if (!active_shelf) {
|
|
return;
|
|
}
|
|
|
|
const int tile_height = current_tile_draw_height(region);
|
|
active_shelf->preferred_row_count = calculate_row_count_from_tile_draw_height(
|
|
region->sizey * UI_SCALE_FAC, tile_height);
|
|
}
|
|
|
|
int tile_width(const AssetShelfSettings &settings)
|
|
{
|
|
return UI_preview_tile_size_x(settings.preview_size);
|
|
}
|
|
|
|
int tile_height(const AssetShelfSettings &settings)
|
|
{
|
|
return (settings.display_flag & ASSETSHELF_SHOW_NAMES) ?
|
|
UI_preview_tile_size_y(settings.preview_size) :
|
|
UI_preview_tile_size_y_no_label(settings.preview_size);
|
|
}
|
|
|
|
static int asset_shelf_default_tile_height()
|
|
{
|
|
return UI_preview_tile_size_x(DEFAULT_TILE_SIZE);
|
|
}
|
|
|
|
int region_prefsizey()
|
|
{
|
|
/* One row by default (plus padding). */
|
|
return asset_shelf_default_tile_height() + 2 * main_region_padding_y();
|
|
}
|
|
|
|
void 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);
|
|
|
|
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
|
|
if (!shelf_regiondata) {
|
|
/* Region-data should've been created by a previously called #region_init(). */
|
|
BLI_assert_unreachable();
|
|
return;
|
|
}
|
|
|
|
AssetShelf *active_shelf = update_active_shelf(*C, *space_type, *shelf_regiondata);
|
|
if (!active_shelf) {
|
|
return;
|
|
}
|
|
|
|
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
|
|
|
|
const uiStyle *style = UI_style_get_dpi();
|
|
const int padding_y = main_region_padding_y();
|
|
const int padding_x = main_region_padding_x();
|
|
uiLayout *layout = UI_block_layout(block,
|
|
UI_LAYOUT_VERTICAL,
|
|
UI_LAYOUT_PANEL,
|
|
padding_x,
|
|
-padding_y,
|
|
region->winx - 2 * padding_x,
|
|
0,
|
|
0,
|
|
style);
|
|
|
|
build_asset_view(
|
|
*layout, active_shelf->settings.asset_library_reference, *active_shelf, *C, *region);
|
|
|
|
int layout_height;
|
|
UI_block_layout_resolve(block, nullptr, &layout_height);
|
|
BLI_assert(layout_height <= 0);
|
|
UI_view2d_totRect_set(®ion->v2d, region->winx - 1, layout_height - padding_y);
|
|
UI_view2d_curRect_validate(®ion->v2d);
|
|
|
|
region_resize_to_preferred(CTX_wm_area(C), region);
|
|
|
|
UI_block_end(C, block);
|
|
}
|
|
|
|
void region_draw(const bContext *C, ARegion *region)
|
|
{
|
|
ED_region_clear(C, region, TH_BACK);
|
|
|
|
/* Set view2d view matrix for scrolling. */
|
|
UI_view2d_view_ortho(®ion->v2d);
|
|
|
|
/* View2D matrix might have changed due to dynamic sized regions. */
|
|
UI_blocklist_update_window_matrix(C, ®ion->uiblocks);
|
|
|
|
UI_blocklist_draw(C, ®ion->uiblocks);
|
|
|
|
/* Restore view matrix. */
|
|
UI_view2d_view_restore(C);
|
|
|
|
UI_view2d_scrollers_draw(®ion->v2d, nullptr);
|
|
}
|
|
|
|
void header_region_listen(const wmRegionListenerParams *params)
|
|
{
|
|
asset_shelf_region_listen(params);
|
|
}
|
|
|
|
void header_region_init(wmWindowManager * /*wm*/, ARegion *region)
|
|
{
|
|
ED_region_header_init(region);
|
|
region->alignment |= RGN_SPLIT_SCALE_PREV;
|
|
region->flag |= RGN_FLAG_RESIZE_RESPECT_BUTTON_SECTIONS;
|
|
}
|
|
|
|
void 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);
|
|
|
|
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
|
|
*main_shelf_region);
|
|
update_active_shelf(*C, *space_type, *shelf_regiondata);
|
|
|
|
ED_region_header_with_button_sections(C, region, uiButtonSectionsAlign::Bottom);
|
|
}
|
|
|
|
int header_region_size()
|
|
{
|
|
/* Use a height that lets widgets sit just on top of the separator line drawn at the lower edge
|
|
* of the region (widgets will be centered).
|
|
*
|
|
* Note that this is usually a bit less than the header size. The asset shelf tends to look like
|
|
* a separate area, so making the shelf header smaller than a header helps. */
|
|
return UI_UNIT_Y + (UI_BUTTON_SECTION_SEPERATOR_LINE_WITH * 2);
|
|
}
|
|
|
|
void region_blend_read_data(BlendDataReader *reader, ARegion *region)
|
|
{
|
|
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
|
|
if (!shelf_regiondata) {
|
|
return;
|
|
}
|
|
regiondata_blend_read_data(reader, &shelf_regiondata);
|
|
region->regiondata = shelf_regiondata;
|
|
}
|
|
|
|
void region_blend_write(BlendWriter *writer, ARegion *region)
|
|
{
|
|
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
|
|
if (!shelf_regiondata) {
|
|
return;
|
|
}
|
|
regiondata_blend_write(writer, shelf_regiondata);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Asset Shelf Context
|
|
* \{ */
|
|
|
|
static AssetShelf *active_shelf_from_area(const ScrArea *area)
|
|
{
|
|
const ARegion *shelf_region = BKE_area_find_region_type(area, RGN_TYPE_ASSET_SHELF);
|
|
if (!shelf_region) {
|
|
/* Called in wrong context, area doesn't have a shelf. */
|
|
BLI_assert_unreachable();
|
|
return nullptr;
|
|
}
|
|
|
|
if (shelf_region->flag & RGN_FLAG_POLL_FAILED) {
|
|
/* Don't return data when the region "doesn't exist" (poll failed). */
|
|
return nullptr;
|
|
}
|
|
|
|
const RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
|
|
*shelf_region);
|
|
if (!shelf_regiondata) {
|
|
return nullptr;
|
|
}
|
|
|
|
return shelf_regiondata->active_shelf;
|
|
}
|
|
|
|
int context(const bContext *C, const char *member, bContextDataResult *result)
|
|
{
|
|
static const char *context_dir[] = {
|
|
"asset_shelf",
|
|
"asset_library_reference",
|
|
"active_file", /* XXX yuk... */
|
|
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")) {
|
|
AssetShelf *active_shelf = active_shelf_from_area(CTX_wm_area(C));
|
|
if (!active_shelf) {
|
|
return CTX_RESULT_NO_DATA;
|
|
}
|
|
|
|
CTX_data_pointer_set(result, &screen->id, &RNA_AssetShelf, active_shelf);
|
|
return CTX_RESULT_OK;
|
|
}
|
|
|
|
if (CTX_data_equals(member, "asset_library_reference")) {
|
|
AssetShelf *active_shelf = active_shelf_from_area(CTX_wm_area(C));
|
|
if (!active_shelf) {
|
|
return CTX_RESULT_NO_DATA;
|
|
}
|
|
|
|
CTX_data_pointer_set(result,
|
|
&screen->id,
|
|
&RNA_AssetLibraryReference,
|
|
&active_shelf->settings.asset_library_reference);
|
|
return CTX_RESULT_OK;
|
|
}
|
|
|
|
/* 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);
|
|
if (!but) {
|
|
return CTX_RESULT_NO_DATA;
|
|
}
|
|
|
|
const bContextStore *but_context = UI_but_context_get(but);
|
|
if (!but_context) {
|
|
return CTX_RESULT_NO_DATA;
|
|
}
|
|
|
|
const PointerRNA *file_ptr = CTX_store_ptr_lookup(
|
|
but_context, "active_file", &RNA_FileSelectEntry);
|
|
if (!file_ptr) {
|
|
return CTX_RESULT_NO_DATA;
|
|
}
|
|
|
|
CTX_data_pointer_set_ptr(result, file_ptr);
|
|
return CTX_RESULT_OK;
|
|
}
|
|
|
|
return CTX_RESULT_MEMBER_NOT_FOUND;
|
|
}
|
|
|
|
static PointerRNA active_shelf_ptr_from_context(const bContext *C)
|
|
{
|
|
return CTX_data_pointer_get_type(C, "asset_shelf", &RNA_AssetShelf);
|
|
}
|
|
|
|
AssetShelf *active_shelf_from_context(const bContext *C)
|
|
{
|
|
PointerRNA shelf_settings_ptr = active_shelf_ptr_from_context(C);
|
|
return static_cast<AssetShelf *>(shelf_settings_ptr.data);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Catalog toggle buttons
|
|
* \{ */
|
|
|
|
static uiBut *add_tab_button(uiBlock &block, StringRefNull name)
|
|
{
|
|
const uiStyle *style = UI_style_get_dpi();
|
|
const int string_width = UI_fontstyle_string_width(&style->widget, name.c_str());
|
|
const int pad_x = UI_UNIT_X * 0.3f;
|
|
const int but_width = std::min(string_width + 2 * pad_x, UI_UNIT_X * 8);
|
|
|
|
uiBut *but = uiDefBut(
|
|
&block,
|
|
UI_BTYPE_TAB,
|
|
0,
|
|
name.c_str(),
|
|
0,
|
|
0,
|
|
but_width,
|
|
UI_UNIT_Y,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
"Enable catalog, making contained assets visible in the asset shelf");
|
|
|
|
UI_but_drawflag_enable(but, UI_BUT_ALIGN_DOWN);
|
|
UI_but_flag_disable(but, UI_BUT_UNDO);
|
|
|
|
return but;
|
|
}
|
|
|
|
static void add_catalog_tabs(AssetShelfSettings &shelf_settings, uiLayout &layout)
|
|
{
|
|
uiBlock *block = uiLayoutGetBlock(&layout);
|
|
|
|
/* "All" tab. */
|
|
{
|
|
uiBut *but = add_tab_button(*block, IFACE_("All"));
|
|
UI_but_func_set(but, [&shelf_settings](bContext &C) {
|
|
settings_set_all_catalog_active(shelf_settings);
|
|
send_redraw_notifier(C);
|
|
});
|
|
UI_but_func_pushed_state_set(but, [&shelf_settings](const uiBut &) -> bool {
|
|
return settings_is_all_catalog_active(shelf_settings);
|
|
});
|
|
}
|
|
|
|
uiItemS(&layout);
|
|
|
|
/* Regular catalog tabs. */
|
|
settings_foreach_enabled_catalog_path(
|
|
shelf_settings, [&shelf_settings, block](const asset_system::AssetCatalogPath &path) {
|
|
uiBut *but = add_tab_button(*block, path.name());
|
|
|
|
UI_but_func_set(but, [&shelf_settings, path](bContext &C) {
|
|
settings_set_active_catalog(shelf_settings, path);
|
|
send_redraw_notifier(C);
|
|
});
|
|
UI_but_func_pushed_state_set(but, [&shelf_settings, path](const uiBut &) -> bool {
|
|
return settings_is_active_catalog(shelf_settings, path);
|
|
});
|
|
});
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Asset Shelf Header Region
|
|
*
|
|
* Implemented as HeaderType for #RGN_TYPE_ASSET_SHELF_HEADER.
|
|
* \{ */
|
|
|
|
static void asset_shelf_header_draw(const bContext *C, Header *header)
|
|
{
|
|
uiLayout *layout = header->layout;
|
|
uiBlock *block = uiLayoutGetBlock(layout);
|
|
const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C);
|
|
|
|
list::storage_fetch(library_ref, C);
|
|
|
|
UI_block_emboss_set(block, UI_EMBOSS_NONE);
|
|
uiItemPopoverPanel(layout, C, "ASSETSHELF_PT_catalog_selector", "", ICON_COLLAPSEMENU);
|
|
UI_block_emboss_set(block, UI_EMBOSS);
|
|
|
|
uiItemS(layout);
|
|
|
|
PointerRNA shelf_ptr = active_shelf_ptr_from_context(C);
|
|
AssetShelf *shelf = static_cast<AssetShelf *>(shelf_ptr.data);
|
|
if (shelf) {
|
|
add_catalog_tabs(shelf->settings, *layout);
|
|
}
|
|
|
|
uiItemSpacer(layout);
|
|
|
|
uiItemPopoverPanel(layout, C, "ASSETSHELF_PT_display", "", ICON_IMGDISPLAY);
|
|
uiLayout *sub = uiLayoutRow(layout, false);
|
|
/* Same as file/asset browser header. */
|
|
uiLayoutSetUnitsX(sub, 8);
|
|
uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_NONE, "", ICON_VIEWZOOM);
|
|
}
|
|
|
|
void header_regiontype_register(ARegionType *region_type, const int space_type)
|
|
{
|
|
HeaderType *ht = MEM_cnew<HeaderType>(__func__);
|
|
STRNCPY(ht->idname, "ASSETSHELF_HT_settings");
|
|
ht->space_type = space_type;
|
|
ht->region_type = RGN_TYPE_ASSET_SHELF_HEADER;
|
|
ht->draw = asset_shelf_header_draw;
|
|
ht->poll = [](const bContext *C, HeaderType *) {
|
|
return asset_shelf_space_poll(C, CTX_wm_space_data(C));
|
|
};
|
|
|
|
BLI_addtail(®ion_type->headertypes, ht);
|
|
|
|
catalog_selector_panel_register(region_type);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Asset Shelf Type (un)registration
|
|
* \{ */
|
|
|
|
void type_unlink(const Main &bmain, const AssetShelfType &shelf_type)
|
|
{
|
|
LISTBASE_FOREACH (bScreen *, screen, &bmain.screens) {
|
|
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
|
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
|
|
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase;
|
|
LISTBASE_FOREACH (ARegion *, region, regionbase) {
|
|
if (region->regiontype != RGN_TYPE_ASSET_SHELF) {
|
|
continue;
|
|
}
|
|
|
|
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(
|
|
*region);
|
|
if (!shelf_regiondata) {
|
|
continue;
|
|
}
|
|
LISTBASE_FOREACH (AssetShelf *, shelf, &shelf_regiondata->shelves) {
|
|
if (shelf->type == &shelf_type) {
|
|
shelf->type = nullptr;
|
|
}
|
|
}
|
|
|
|
BLI_assert((shelf_regiondata->active_shelf == nullptr) ||
|
|
(shelf_regiondata->active_shelf->type != &shelf_type));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
} // namespace blender::ed::asset::shelf
|