UI: Popup version of the asset shelf

Developed as part of the brush assets project, see #106303. No user visible
changes at this point.

Makes it possible to display asset shelves as popups. These popup asset shelves
use static storage for their settings, mainly to remember the active catalog
and filter string, while keeping them separate from the permanent asset shelf
region. Further, the popup can be displayed in any editor, making asset
selectors possible to add anywhere in the UI. When an asset is chosen, an
operator passed to the asset shelf as bl_activate_operator is called, with an
asset weak-reference to refer to the activated asset stored in the operator
properties.

Adds UILayout.template_asset_shelf_popover() to insert asset shelf popup
buttons, taking an asset shelf idname and some normal UI parameters.
This commit is contained in:
Julian Eisel
2024-05-16 00:11:47 +02:00
parent 5a3fee92a5
commit 7d80fde033
16 changed files with 441 additions and 28 deletions

View File

@@ -34,6 +34,7 @@ set(SRC
intern/asset_shelf.cc
intern/asset_shelf_asset_view.cc
intern/asset_shelf_catalog_selector.cc
intern/asset_shelf_popup.cc
intern/asset_shelf_regiondata.cc
intern/asset_shelf_settings.cc
intern/asset_temp_id_consumer.cc

View File

@@ -21,6 +21,7 @@ struct BlendDataReader;
struct BlendWriter;
struct Main;
struct SpaceType;
struct uiBlock;
struct RegionPollParams;
struct wmWindowManager;
@@ -73,15 +74,25 @@ void header_regiontype_register(ARegionType *region_type, const int space_type);
void type_register(std::unique_ptr<AssetShelfType> type);
void type_unregister(const AssetShelfType &shelf_type);
/**
* Poll an asset shelf type for display as a permanent region in a space of a given type (the
* type's #bl_space_type).
* Poll an asset shelf type for display as a popup. Doesn't check for space-type (the type's
* #bl_space_type) since popups should ignore this to allow displaying in any space.
*
* Permanent/non-popup asset shelf regions should use #type_poll_for_space_type() instead.
*/
bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int space_type);
bool type_poll_for_popup(const bContext &C, const AssetShelfType *shelf_type);
AssetShelfType *type_find_from_idname(const StringRef idname);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Shelf Popup
* \{ */
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type);
void type_popup_unlink(const AssetShelfType &shelf_type);
/** \} */
/* -------------------------------------------------------------------- */
void type_unlink(const Main &bmain, const AssetShelfType &shelf_type);

View File

@@ -76,14 +76,11 @@ void type_unregister(const AssetShelfType &shelf_type)
shelf_types.remove(it - shelf_types.begin());
}
bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int space_type)
static bool type_poll_no_spacetype_check(const bContext &C, const AssetShelfType *shelf_type)
{
if (!shelf_type) {
return false;
}
if (shelf_type->space_type && (space_type != shelf_type->space_type)) {
return false;
}
#ifndef NDEBUG
const Vector<std::unique_ptr<AssetShelfType>> &shelf_types = static_shelf_types();
@@ -98,6 +95,31 @@ bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int sp
return !shelf_type->poll || shelf_type->poll(&C, shelf_type);
}
bool type_poll_for_popup(const bContext &C, const AssetShelfType *shelf_type)
{
return type_poll_no_spacetype_check(C, shelf_type);
}
/**
* Poll an asset shelf type for display as a permanent region in a space of a given type (the
* type's #bl_space_type).
*
* Popup asset shelves should use #type_poll_for_popup() instead.
*/
static bool type_poll_for_non_popup(const bContext &C,
const AssetShelfType *shelf_type,
const int space_type)
{
if (!shelf_type) {
return false;
}
if (shelf_type->space_type && (space_type != shelf_type->space_type)) {
return false;
}
return type_poll_no_spacetype_check(C, shelf_type);
}
AssetShelfType *type_find_from_idname(const StringRef idname)
{
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
@@ -186,7 +208,8 @@ static AssetShelf *update_active_shelf(const bContext &C,
/* Case 1: */
if (shelf_regiondata.active_shelf &&
type_poll(C, ensure_shelf_has_type(*shelf_regiondata.active_shelf), space_type))
type_poll_for_non_popup(
C, ensure_shelf_has_type(*shelf_regiondata.active_shelf), space_type))
{
/* 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);
@@ -200,7 +223,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
continue;
}
if (type_poll(C, ensure_shelf_has_type(*shelf), space_type)) {
if (type_poll_for_non_popup(C, ensure_shelf_has_type(*shelf), space_type)) {
/* Found a valid previously activated shelf, reactivate it. */
activate_shelf(shelf_regiondata, *shelf);
return shelf;
@@ -209,7 +232,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
/* Case 3: */
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
if (type_poll(C, shelf_type.get(), space_type)) {
if (type_poll_for_non_popup(C, shelf_type.get(), space_type)) {
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
BLI_addhead(&shelf_regiondata.shelves, new_shelf);
/* Moves ownership to the regiondata. */
@@ -258,7 +281,7 @@ static bool asset_shelf_space_poll(const bContext *C, const SpaceLink *space_lin
{
/* Is there any asset shelf type registered that returns true for it's poll? */
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
if (type_poll(*C, shelf_type.get(), space_link->spacetype)) {
if (type_poll_for_non_popup(*C, shelf_type.get(), space_link->spacetype)) {
return true;
}
}
@@ -842,6 +865,8 @@ void type_unlink(const Main &bmain, const AssetShelfType &shelf_type)
}
}
}
type_popup_unlink(shelf_type);
}
/** \} */

View File

@@ -46,6 +46,8 @@ void send_redraw_notifier(const bContext &C);
AssetShelfType *ensure_shelf_has_type(AssetShelf &shelf);
AssetShelf *create_shelf_from_type(AssetShelfType &type);
void library_selector_draw(const bContext *C, uiLayout *layout, AssetShelf &shelf);
/**
* Deep-copies \a shelf_regiondata into newly allocated memory. Must be freed using
* #regiondata_free().

View File

@@ -44,12 +44,13 @@ class AssetView : public ui::AbstractGridView {
* end of the string, for `fnmatch()` to work. */
char search_string[sizeof(AssetShelfSettings::search_string) + 2] = "";
std::optional<asset_system::AssetCatalogFilter> catalog_filter_ = std::nullopt;
bool is_popup_ = false;
friend class AssetViewItem;
friend class AssetDragController;
public:
AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf);
AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf, bool is_popup);
void build_items() override;
bool begin_filtering(const bContext &C) const override;
@@ -70,6 +71,7 @@ class AssetViewItem : public ui::PreviewGridItem {
void disable_asset_drag();
void build_grid_tile(uiLayout &layout) const override;
void build_context_menu(bContext &C, uiLayout &column) const override;
void on_activate(bContext &C) override;
std::optional<bool> should_be_active() const override;
bool is_filtered_visible() const override;
@@ -86,8 +88,10 @@ class AssetDragController : public ui::AbstractViewItemDragController {
void *create_drag_data() const override;
};
AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf)
: library_ref_(library_ref), shelf_(shelf)
AssetView::AssetView(const AssetLibraryReference &library_ref,
const AssetShelf &shelf,
const bool is_popup)
: library_ref_(library_ref), shelf_(shelf), is_popup_(is_popup)
{
if (shelf.settings.search_string[0]) {
BLI_strncpy_ensure_pad(
@@ -236,6 +240,14 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
}
}
void AssetViewItem::on_activate(bContext & /*C*/)
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
if (asset_view.is_popup_) {
UI_popup_menu_close_from_but(reinterpret_cast<uiBut *>(this->view_item_button()));
}
}
std::optional<bool> AssetViewItem::should_be_active() const
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
@@ -290,7 +302,8 @@ void build_asset_view(uiLayout &layout,
BLI_assert(tile_width != 0);
BLI_assert(tile_height != 0);
std::unique_ptr asset_view = std::make_unique<AssetView>(library_ref, shelf);
const bool is_popup = region.regiontype == RGN_TYPE_TEMPORARY;
std::unique_ptr asset_view = std::make_unique<AssetView>(library_ref, shelf, is_popup);
asset_view->set_catalog_filter(catalog_filter_from_shelf_settings(shelf.settings, *library));
asset_view->set_tile_size(tile_width, tile_height);

View File

@@ -174,32 +174,37 @@ void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs()
});
}
void library_selector_draw(const bContext *C, uiLayout *layout, AssetShelf &shelf)
{
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
PointerRNA shelf_ptr = RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_AssetShelf, &shelf);
uiLayout *row = uiLayoutRow(layout, true);
uiItemR(row, &shelf_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE);
if (shelf.settings.asset_library_reference.type != ASSET_LIBRARY_LOCAL) {
uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh");
}
}
static void catalog_selector_panel_draw(const bContext *C, Panel *panel)
{
const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C);
AssetShelf *shelf = active_shelf_from_context(C);
if (!shelf) {
return;
}
uiLayout *layout = panel->layout;
uiBlock *block = uiLayoutGetBlock(layout);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
library_selector_draw(C, layout, *shelf);
PointerRNA shelf_ptr = RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_AssetShelf, shelf);
uiLayout *row = uiLayoutRow(layout, true);
uiItemR(row, &shelf_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE);
if (library_ref->type != ASSET_LIBRARY_LOCAL) {
uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh");
}
asset_system::AssetLibrary *library = list::library_get_once_available(*library_ref);
asset_system::AssetLibrary *library = list::library_get_once_available(
shelf->settings.asset_library_reference);
if (!library) {
return;
}
uiBlock *block = uiLayoutGetBlock(layout);
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset catalog tree view",

View File

@@ -0,0 +1,208 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edasset
*/
#include "asset_shelf.hh"
#include "BKE_screen.hh"
#include "BLT_translation.hh"
#include "UI_interface_c.hh"
#include "UI_tree_view.hh"
#include "ED_asset_filter.hh"
#include "ED_asset_list.hh"
#include "ED_asset_shelf.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
namespace blender::ed::asset::shelf {
class StaticPopupShelves {
public:
Vector<AssetShelf *> popup_shelves;
~StaticPopupShelves()
{
for (AssetShelf *shelf : popup_shelves) {
MEM_delete(shelf);
}
}
static Vector<AssetShelf *> &shelves()
{
static StaticPopupShelves storage;
return storage.popup_shelves;
}
};
void type_popup_unlink(const AssetShelfType &shelf_type)
{
for (AssetShelf *shelf : StaticPopupShelves::shelves()) {
if (shelf->type == &shelf_type) {
shelf->type = nullptr;
}
}
}
static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_type)
{
Vector<AssetShelf *> &popup_shelves = StaticPopupShelves::shelves();
for (AssetShelf *shelf : popup_shelves) {
if (STREQ(shelf->idname, shelf_type.idname)) {
if (type_poll_for_popup(*C, ensure_shelf_has_type(*shelf))) {
return shelf;
}
break;
}
}
if (type_poll_for_popup(*C, &shelf_type)) {
AssetShelf *new_shelf = create_shelf_from_type(shelf_type);
new_shelf->settings.display_flag |= ASSETSHELF_SHOW_NAMES;
popup_shelves.append(new_shelf);
return new_shelf;
}
return nullptr;
}
class AssetCatalogTreeView : public ui::AbstractTreeView {
AssetShelf &shelf_;
asset_system::AssetCatalogTree catalog_tree_;
public:
AssetCatalogTreeView(const asset_system::AssetLibrary &library, AssetShelf &shelf)
: shelf_(shelf)
{
catalog_tree_ = build_filtered_catalog_tree(
library,
shelf_.settings.asset_library_reference,
[this](const asset_system::AssetRepresentation &asset) {
return (!shelf_.type->asset_poll || shelf_.type->asset_poll(shelf_.type, &asset));
});
}
void build_tree() override
{
if (catalog_tree_.is_empty()) {
auto &item = this->add_tree_item<ui::BasicTreeViewItem>(RPT_("No applicable assets found"),
ICON_INFO);
item.disable_interaction();
return;
}
auto &all_item = this->add_tree_item<ui::BasicTreeViewItem>(IFACE_("All"));
all_item.set_on_activate_fn([this](bContext &C, ui::BasicTreeViewItem &) {
settings_set_all_catalog_active(shelf_.settings);
send_redraw_notifier(C);
});
all_item.set_is_active_fn(
[this]() { return settings_is_all_catalog_active(shelf_.settings); });
all_item.uncollapse_by_default();
catalog_tree_.foreach_root_item([&, this](
const asset_system::AssetCatalogTreeItem &catalog_item) {
ui::BasicTreeViewItem &item = this->build_catalog_items_recursive(all_item, catalog_item);
item.uncollapse_by_default();
});
}
ui::BasicTreeViewItem &build_catalog_items_recursive(
ui::TreeViewOrItem &parent_view_item,
const asset_system::AssetCatalogTreeItem &catalog_item) const
{
ui::BasicTreeViewItem &view_item = parent_view_item.add_tree_item<ui::BasicTreeViewItem>(
catalog_item.get_name());
std::string catalog_path = catalog_item.catalog_path().str();
view_item.set_on_activate_fn([this, catalog_path](bContext &C, ui::BasicTreeViewItem &) {
settings_set_active_catalog(shelf_.settings, catalog_path);
send_redraw_notifier(C);
});
view_item.set_is_active_fn([this, catalog_path]() {
return settings_is_active_catalog(shelf_.settings, catalog_path);
});
catalog_item.foreach_child(
[&view_item, this](const asset_system::AssetCatalogTreeItem &child) {
build_catalog_items_recursive(view_item, child);
});
return view_item;
}
};
static void catalog_tree_draw(uiLayout &layout, AssetShelf &shelf)
{
const asset_system::AssetLibrary *library = list::library_get_once_available(
shelf.settings.asset_library_reference);
if (!library) {
return;
}
uiBlock *block = uiLayoutGetBlock(&layout);
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset shelf catalog tree view",
std::make_unique<AssetCatalogTreeView>(*library, shelf));
ui::TreeViewBuilder::build_tree_view(*tree_view, layout);
}
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type)
{
bScreen *screen = CTX_wm_screen(C);
uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
UI_block_direction_set(block, UI_DIR_DOWN);
AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type);
if (!shelf) {
BLI_assert_unreachable();
return block;
}
const uiStyle *style = UI_style_get_dpi();
const int layout_width = UI_UNIT_X * 40;
const int left_col_width = 10 * UI_UNIT_X;
const int right_col_width = layout_width - left_col_width;
uiLayout *layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, layout_width, 0, 0, style);
PointerRNA library_ref_ptr = RNA_pointer_create(
&screen->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference);
uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr);
uiLayout *row = uiLayoutRow(layout, false);
uiLayout *catalogs_col = uiLayoutColumn(row, false);
uiLayoutSetUnitsX(catalogs_col, left_col_width / UI_UNIT_X);
uiLayoutSetFixedSize(catalogs_col, true);
library_selector_draw(C, catalogs_col, *shelf);
catalog_tree_draw(*catalogs_col, *shelf);
uiLayout *right_col = uiLayoutColumn(row, false);
uiLayout *sub = uiLayoutRow(right_col, false);
/* Same as file/asset browser header. */
PointerRNA shelf_ptr = RNA_pointer_create(&screen->id, &RNA_AssetShelf, shelf);
uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_R_IMMEDIATE, "", ICON_VIEWZOOM);
uiLayout *asset_view_col = uiLayoutColumn(right_col, false);
uiLayoutSetUnitsX(asset_view_col, right_col_width / UI_UNIT_X);
uiLayoutSetFixedSize(asset_view_col, true);
build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region);
return block;
}
} // namespace blender::ed::asset::shelf

View File

@@ -710,6 +710,18 @@ uiLayout *UI_popup_menu_layout(uiPopupMenu *pup);
void UI_popup_menu_reports(bContext *C, ReportList *reports) ATTR_NONNULL();
int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) ATTR_NONNULL(1, 2);
/**
* If \a block is displayed in a popup menu, tag it for closing.
* \param is_cancel: If set to true, the popup will be closed as being cancelled (e.g. when
* pressing escape) as opposed to being handled successfully.
*/
void UI_popup_menu_close(const uiBlock *block, bool is_cancel = false);
/**
* Version of #UI_popup_menu_close() that can be called on a button contained in a popup menu
* block. Convenience since the block may not be available.
*/
void UI_popup_menu_close_from_but(const uiBut *but, bool is_cancel = false);
/**
* Allow setting menu return value from externals.
* E.g. WM might need to do this for exiting files correctly.
@@ -1496,6 +1508,10 @@ uiBut *uiDefIconMenuBut(uiBlock *block,
short height,
const char *tip);
/**
* Note that \a fun can set the #UI_BLOCK_KEEP_OPEN flag to the block it creates, to allow
* refreshing the popup. That is, redrawing the layout, potentially affecting the popup size.
*/
uiBut *uiDefBlockBut(uiBlock *block,
uiBlockCreateFunc func,
void *arg,
@@ -2711,6 +2727,13 @@ void uiTemplateAssetView(uiLayout *layout,
const char *drag_opname,
PointerRNA *r_drag_op_properties);
namespace blender::ui {
void template_asset_shelf_popover(
uiLayout &layout, const bContext &C, StringRefNull asset_shelf_id, StringRef name, int icon);
}
void uiTemplateLightLinkingCollection(uiLayout *layout,
uiLayout *context_layout,
PointerRNA *ptr,

View File

@@ -5,6 +5,7 @@
set(INC
.
../include
../asset
../../asset_system
../../blenkernel
../../blenloader
@@ -64,6 +65,7 @@ set(SRC
regions/interface_regions.cc
interface_string_search.cc
interface_style.cc
templates/interface_template_asset_shelf_popover.cc
templates/interface_template_asset_view.cc
templates/interface_template_attribute_search.cc
templates/interface_template_bone_collection_tree.cc

View File

@@ -4048,6 +4048,8 @@ static void ui_do_but_textedit(
if (changed || (retval == WM_UI_HANDLER_BREAK)) {
ED_region_tag_redraw(data->region);
/* In case of popup regions, tag for popup refreshing too (contents may have changed). */
ED_region_tag_refresh_ui(data->region);
}
}
@@ -11509,6 +11511,8 @@ static int ui_handle_menus_recursive(bContext *C,
if (!menu->retvalue) {
ui_handle_viewlist_items_hover(event, menu->region);
}
/* Handle mouse clicks on overlapping view item button. */
ui_handle_view_item_event(C, event, but, menu->region);
if (do_towards_reinit) {
ui_mouse_motion_towards_reinit(menu, event->xy);

View File

@@ -984,6 +984,10 @@ uiBlock *ui_popup_block_refresh(bContext *C,
ARegion *butregion,
uiBut *but);
/**
* Note that callbacks can set the #UI_BLOCK_KEEP_OPEN flag to the block it creates, to allow
* refreshing the popup. That is, redrawing the layout, potentially affecting the popup size.
*/
uiPopupBlockHandle *ui_popup_block_create(bContext *C,
ARegion *butregion,
uiBut *but,

View File

@@ -765,4 +765,14 @@ bool UI_popup_block_name_exists(const bScreen *screen, const blender::StringRef
return false;
}
void UI_popup_menu_close(const uiBlock *block, const bool is_cancel)
{
UI_popup_menu_retval_set(block, is_cancel ? UI_RETURN_CANCEL : UI_RETURN_OK, true);
}
void UI_popup_menu_close_from_but(const uiBut *but, const bool is_cancel)
{
UI_popup_menu_close(but->block, is_cancel);
}
/** \} */

View File

@@ -926,6 +926,10 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C,
CTX_wm_region_popup_set(C, region_popup_prev);
}
if (block->flag & UI_BLOCK_KEEP_OPEN) {
handle->can_refresh = true;
}
/* keep centered on window resizing */
if (block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) {
type.listener = ui_block_region_popup_window_listener;

View File

@@ -0,0 +1,67 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edinterface
*/
#include "BKE_context.hh"
#include "BKE_screen.hh"
#include "RNA_access.hh"
#include "UI_interface_c.hh"
#include "UI_resources.hh"
#include "interface_intern.hh"
#include "ED_asset_shelf.hh"
namespace blender::ui {
static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_shelf_type)
{
AssetShelfType *shelf_type = reinterpret_cast<AssetShelfType *>(arg_shelf_type);
return ed::asset::shelf::popup_block_create(C, region, shelf_type);
}
void template_asset_shelf_popover(uiLayout &layout,
const bContext &C,
const StringRefNull asset_shelf_id,
const StringRef name,
const BIFIconID icon)
{
AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_id);
if (!shelf_type) {
RNA_warning("Asset shelf type not found: %s", asset_shelf_id.c_str());
return;
}
const ARegion *region = CTX_wm_region(&C);
const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype);
const bool use_preview_icon = use_big_size;
const short width = [&]() -> short {
if (use_big_size) {
return UI_UNIT_X * 6;
}
return UI_UNIT_X * (name.is_empty() ? 1.6f : 7);
}();
const short height = UI_UNIT_Y * (use_big_size ? 6 : 1);
uiBlock *block = uiLayoutGetBlock(&layout);
uiBut *but = uiDefBlockBut(
block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset");
if (use_preview_icon) {
ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
}
else {
ui_def_but_icon(but, icon, UI_HAS_ICON);
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
}
if (ed::asset::shelf::type_poll_for_popup(C, shelf_type) == false) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
} // namespace blender::ui

View File

@@ -833,6 +833,7 @@ typedef struct AssetShelf {
AssetShelfSettings settings;
/** Only for the permanent asset shelf regions, not asset shelves in temporary popups. */
short preferred_row_count;
char _pad[6];
} AssetShelf;

View File

@@ -963,6 +963,20 @@ static int rna_ui_get_enum_icon(bContext *C,
return icon;
}
void rna_uiTemplateAssetShelfPopover(uiLayout *layout,
bContext *C,
const char *asset_shelf_id,
const char *name,
BIFIconID icon,
int icon_value)
{
if (icon_value && !icon) {
icon = icon_value;
}
blender::ui::template_asset_shelf_popover(*layout, *C, asset_shelf_id, name, icon);
}
#else
static void api_ui_item_common_heading(FunctionRNA *func)
@@ -2287,6 +2301,25 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(func, "node", "Node", "Node", "Display inputs of this node");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
func = RNA_def_function(srna, "template_asset_shelf_popover", "rna_uiTemplateAssetShelfPopover");
RNA_def_function_ui_description(func, "Create a button to open an asset shelf in a popover");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_string(func,
"asset_shelf",
nullptr,
0,
"",
"Identifier of the asset shelf to display (`bl_idname`)");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
parm = RNA_def_string(
func, "name", nullptr, 0, "", "Optional name to indicate the active asset");
RNA_def_property_clear_flag(parm, PROP_NEVER_NULL);
parm = RNA_def_property(func, "icon", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(parm, rna_enum_icon_items);
RNA_def_property_ui_text(parm, "Icon", "Override automatic icon of the item");
parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED);
RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item");
}
#endif