WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 358 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
19 changed files with 475 additions and 43 deletions
Showing only changes of commit 1f83f277dc - Show all commits

View File

@ -235,9 +235,18 @@ class _draw_tool_settings_context_mode:
return False
paint = context.tool_settings.sculpt
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
brush_name = brush.name if brush else None
preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0
fallback_icon = 'BRUSH_DATA' if not preview_icon_id else 'NONE'
layout.template_asset_shelf_popover(
BrushAssetShelf.get_shelf_name_from_mode(context.object.mode),
name=brush_name,
icon=fallback_icon,
icon_value=preview_icon_id,
)
if brush is None:
return False
@ -9023,6 +9032,23 @@ class BrushAssetShelf:
# also activates the item.
layout.menu_contents("VIEW3D_MT_brush_context_menu")
# Not nice, but needed unfortunately.
@staticmethod
def get_shelf_name_from_mode(obmode):
mode_map = {
'SCULPT': "VIEW3D_AST_brush_sculpt",
'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves",
'VERTEX_PAINT': "VIEW3D_AST_brush_vertex_paint",
'WEIGHT_PAINT': "VIEW3D_AST_brush_weight_paint",
'TEXTURE_PAINT': "VIEW3D_AST_brush_texture_paint",
'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint",
'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint",
'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt",
'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex",
'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight",
}
return mode_map[obmode]
class View3DAssetShelf(BrushAssetShelf):
bl_space_type = "VIEW_3D"

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

@ -17,9 +17,16 @@ struct bContextDataResult;
struct BlendDataReader;
struct BlendWriter;
struct Main;
struct SpaceType;
struct uiBlock;
struct RegionPollParams;
struct wmWindowManager;
namespace blender {
class StringRef;
class StringRefNull;
} // namespace blender
namespace blender::ed::asset::shelf {
/* -------------------------------------------------------------------- */
@ -58,6 +65,22 @@ void header_regiontype_register(ARegionType *region_type, const int space_type);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset shelf type */
bool type_poll(const bContext &C, const SpaceType &space_type, const AssetShelfType *shelf_type);
AssetShelfType *type_find_from_idname(const SpaceType &space_type, StringRefNull 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

@ -52,9 +52,7 @@ void send_redraw_notifier(const bContext &C)
/** \name Shelf Type
* \{ */
static bool asset_shelf_type_poll(const bContext &C,
const SpaceType &space_type,
const AssetShelfType *shelf_type)
bool type_poll(const bContext &C, const SpaceType &space_type, const AssetShelfType *shelf_type)
{
if (!shelf_type) {
return false;
@ -71,7 +69,17 @@ static bool asset_shelf_type_poll(const bContext &C,
return !shelf_type->poll || shelf_type->poll(&C, shelf_type);
}
static AssetShelfType *asset_shelf_type_ensure(const SpaceType &space_type, AssetShelf &shelf)
AssetShelfType *type_find_from_idname(const SpaceType &space_type, StringRefNull idname)
{
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
if (idname == shelf_type->idname) {
return shelf_type.get();
}
}
return nullptr;
}
AssetShelfType *type_ensure(const SpaceType &space_type, AssetShelf &shelf)
{
if (shelf.type) {
return shelf.type;
@ -87,7 +95,7 @@ static AssetShelfType *asset_shelf_type_ensure(const SpaceType &space_type, Asse
return nullptr;
}
static AssetShelf *create_shelf_from_type(AssetShelfType &type)
AssetShelf *create_shelf_from_type(AssetShelfType &type)
{
AssetShelf *shelf = MEM_new<AssetShelf>(__func__);
*shelf = dna::shallow_zero_initialize();
@ -149,8 +157,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
/* Case 1: */
if (shelf_regiondata.active_shelf &&
asset_shelf_type_poll(
C, space_type, asset_shelf_type_ensure(space_type, *shelf_regiondata.active_shelf)))
type_poll(C, space_type, 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);
@ -164,7 +171,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
continue;
}
if (asset_shelf_type_poll(C, space_type, asset_shelf_type_ensure(space_type, *shelf))) {
if (type_poll(C, space_type, type_ensure(space_type, *shelf))) {
/* Found a valid previously activated shelf, reactivate it. */
activate_shelf(shelf_regiondata, *shelf);
return shelf;
@ -173,7 +180,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
/* Case 3: */
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
if (asset_shelf_type_poll(C, space_type, shelf_type.get())) {
if (type_poll(C, space_type, shelf_type.get())) {
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
BLI_addhead(&shelf_regiondata.shelves, new_shelf);
/* Moves ownership to the regiondata. */
@ -224,7 +231,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 : space_type->asset_shelf_types) {
if (asset_shelf_type_poll(*C, *space_type, shelf_type.get())) {
if (type_poll(*C, *space_type, shelf_type.get())) {
return true;
}
}
@ -483,19 +490,13 @@ void region_draw(const bContext *C, ARegion *region)
void region_on_poll_success(const bContext *C, ARegion *region)
{
ScrArea *area = CTX_wm_area(C);
BLI_assert(region->regiontype == RGN_TYPE_ASSET_SHELF);
if (!region->regiondata) {
region->regiondata = MEM_cnew<RegionAssetShelf>("RegionAssetShelf");
}
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::get_from_asset_shelf_region(*region);
RegionAssetShelf *shelf_regiondata = RegionAssetShelf::ensure_from_asset_shelf_region(*region);
if (!shelf_regiondata) {
BLI_assert_unreachable();
return;
}
ScrArea *area = CTX_wm_area(C);
update_active_shelf(
*C, *area->type, *shelf_regiondata, /*on_create=*/[&](AssetShelf &new_shelf) {
/* Update region visibility (`'DEFAULT_VISIBLE'` option). */
@ -812,6 +813,8 @@ void type_unlink(const Main &bmain, const AssetShelfType &shelf_type)
}
}
}
type_popup_unlink(shelf_type);
}
/** \} */

View File

@ -9,14 +9,19 @@
#pragma once
#include "BLI_function_ref.hh"
#include "BLI_string_ref.hh"
struct ARegion;
struct ARegionType;
struct AssetLibraryReference;
struct AssetShelf;
struct AssetShelfType;
struct AssetShelfSettings;
struct bContext;
struct BlendDataReader;
struct BlendWriter;
struct RegionAssetShelf;
struct SpaceType;
struct uiLayout;
namespace blender::asset_system {
@ -39,6 +44,11 @@ AssetShelf *active_shelf_from_context(const bContext *C);
void send_redraw_notifier(const bContext &C);
AssetShelfType *type_ensure(const SpaceType &space_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

@ -45,12 +45,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);
~AssetView();
void build_items() override;
@ -72,6 +73,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;
@ -88,8 +90,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(
@ -230,6 +234,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 &>(get_view());
if (asset_view.is_popup_) {
UI_popup_menu_close_from_but(reinterpret_cast<uiBut *>(view_item_button()));
}
}
std::optional<bool> AssetViewItem::should_be_active() const
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(get_view());
@ -284,7 +296,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,197 @@
/* 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)
{
const SpaceType *space_type = BKE_spacetype_from_id(shelf_type.space_type);
Vector<AssetShelf *> &popup_shelves = StaticPopupShelves::shelves();
for (AssetShelf *shelf : popup_shelves) {
if (STREQ(shelf->idname, shelf_type.idname)) {
if (type_poll(*C, *space_type, type_ensure(*space_type, *shelf))) {
return shelf;
}
break;
}
}
if (type_poll(*C, *space_type, &shelf_type)) {
AssetShelf *new_shelf = create_shelf_from_type(shelf_type);
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)
{
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);
AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type);
if (!shelf) {
BLI_assert_unreachable();
return block;
}
const uiStyle *style = UI_style_get_dpi();
const float pad = 0.2f * UI_UNIT_Y; /* UI_MENU_PADDING */
uiLayout *layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, pad, 0, UI_UNIT_X * 40, 0, pad / 2, style);
PointerRNA library_ref_ptr = RNA_pointer_create(
&CTX_wm_screen(C)->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference);
uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr);
uiLayout *row = uiLayoutRow(layout, false);
uiLayout *sub = uiLayoutRow(row, false);
uiLayoutSetUnitsX(sub, 10);
uiLayoutSetFixedSize(sub, true);
uiLayout *catalogs_col = uiLayoutColumn(sub, false);
library_selector_draw(C, catalogs_col, *shelf);
catalog_tree_draw(*catalogs_col, *shelf);
uiLayout *asset_view_col = uiLayoutColumn(row, false);
build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region);
return block;
}
} // namespace blender::ed::asset::shelf

View File

@ -25,6 +25,19 @@ RegionAssetShelf *RegionAssetShelf::get_from_asset_shelf_region(const ARegion &r
return static_cast<RegionAssetShelf *>(region.regiondata);
}
RegionAssetShelf *RegionAssetShelf::ensure_from_asset_shelf_region(ARegion &region)
{
if (region.regiontype != RGN_TYPE_ASSET_SHELF) {
/* Should only be called on main asset shelf region. */
BLI_assert_unreachable();
return nullptr;
}
if (!region.regiondata) {
region.regiondata = MEM_cnew<RegionAssetShelf>("RegionAssetShelf");
}
return static_cast<RegionAssetShelf *>(region.regiondata);
}
namespace blender::ed::asset::shelf {
RegionAssetShelf *regiondata_duplicate(const RegionAssetShelf *shelf_regiondata)

View File

@ -707,6 +707,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.
@ -1488,6 +1500,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,
@ -2703,6 +2719,9 @@ void uiTemplateAssetView(uiLayout *layout,
const char *drag_opname,
PointerRNA *r_drag_op_properties);
void uiTemplateAssetShelfPopover(
uiLayout *layout, bContext *C, const char *asset_shelf_id, const char *name, const 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

@ -11503,6 +11503,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

@ -977,6 +977,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

@ -762,4 +762,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

@ -885,6 +885,10 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C,
uiBlock *block = ui_popup_block_refresh(C, handle, butregion, but);
handle = block->handle;
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,61 @@
/* 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"
using namespace blender;
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 uiTemplateAssetShelfPopover(uiLayout *layout,
bContext *C,
const char *asset_shelf_id,
const char *name,
const BIFIconID icon)
{
const ScrArea *area = CTX_wm_area(C);
AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(*area->type,
asset_shelf_id);
if (!shelf_type) {
RNA_warning("Asset shelf type not found: %s", asset_shelf_id);
return;
}
const ARegion *region = CTX_wm_region(C);
const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype);
const short width = [&]() -> short {
if (use_big_size) {
return UI_UNIT_X * 6;
}
return UI_UNIT_X * (name ? 7 : 1.6f);
}();
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");
ui_def_but_icon(but, icon, UI_HAS_ICON);
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
if (ed::asset::shelf::type_poll(*C, *area->type, shelf_type) == false) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}

View File

@ -204,7 +204,6 @@ GridViewItemDropTarget::GridViewItemDropTarget(AbstractGridView &view) : view_(v
* side(s) as well.
*/
class BuildOnlyVisibleButtonsHelper {
const View2D &v2d_;
const AbstractGridView &grid_view_;
const GridViewStyle &style_;
const int cols_per_row_ = 0;
@ -221,30 +220,34 @@ class BuildOnlyVisibleButtonsHelper {
void fill_layout_after_visible(uiBlock &block) const;
private:
IndexRange get_visible_range() const;
IndexRange get_visible_range(const View2D &v2d) const;
void add_spacer_button(uiBlock &block, int row_count) const;
};
BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d,
const AbstractGridView &grid_view,
const int cols_per_row)
: v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row)
: grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row)
{
visible_items_range_ = this->get_visible_range();
visible_items_range_ = this->get_visible_range(v2d);
}
IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const
IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range(const View2D &v2d) const
{
if ((v2d.flag & V2D_IS_INIT) == 0) {
return IndexRange(0, grid_view_.get_item_count_filtered());
}
int first_idx_in_view = 0;
const float scroll_ofs_y = std::abs(v2d_.cur.ymax - v2d_.tot.ymax);
const float scroll_ofs_y = std::abs(v2d.cur.ymax - v2d.tot.ymax);
if (!IS_EQF(scroll_ofs_y, 0)) {
const int scrolled_away_rows = int(scroll_ofs_y) / style_.tile_height;
first_idx_in_view = scrolled_away_rows * cols_per_row_;
}
const int view_height = BLI_rcti_size_y(&v2d_.mask);
const int view_height = BLI_rcti_size_y(&v2d.mask);
const int count_rows_in_view = std::max(view_height / style_.tile_height, 1);
const int max_items_in_view = (count_rows_in_view + 1) * cols_per_row_;

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;
@ -855,6 +856,8 @@ typedef struct RegionAssetShelf {
AssetShelf *active_shelf; /* Non-owning. */
#ifdef __cplusplus
static RegionAssetShelf *get_from_asset_shelf_region(const ARegion &region);
/** Creates the asset shelf region data if necessary, and returns it. */
static RegionAssetShelf *ensure_from_asset_shelf_region(ARegion &region);
#endif
} RegionAssetShelf;

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;
}
uiTemplateAssetShelfPopover(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