Blender V5.0
asset_shelf_asset_view.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "AS_asset_library.hh"
13
14#include "BKE_screen.hh"
15
16#include "BLI_fnmatch.h"
17#include "BLI_listbase.h"
18#include "BLI_string.h"
19
20#include "DNA_asset_types.h"
21#include "DNA_screen_types.h"
22
23#include "ED_asset.hh"
25#include "ED_asset_shelf.hh"
26
27#include "UI_grid_view.hh"
28#include "UI_interface.hh"
30
31#include "RNA_access.hh"
32#include "RNA_prototypes.hh"
33
34#include "WM_api.hh"
35
36#include "asset_shelf.hh"
37
39
41 const AssetLibraryReference library_ref_;
42 const AssetShelf &shelf_;
43 std::optional<AssetWeakReference> active_asset_;
44 std::optional<asset_system::AssetCatalogFilter> catalog_filter_ = std::nullopt;
45
46 friend class AssetViewItem;
47 friend class AssetDragController;
48
49 public:
50 AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf);
51
52 void build_items() override;
53 bool begin_filtering(const bContext &C) const override;
54
55 void set_catalog_filter(const std::optional<asset_system::AssetCatalogFilter> &catalog_filter);
56};
57
60 bool allow_asset_drag_ = true;
61
62 public:
64
65 void disable_asset_drag();
66 void build_grid_tile(const bContext &C, uiLayout &layout) const override;
67 void build_context_menu(bContext &C, uiLayout &column) const override;
68 std::optional<bool> should_be_active() const override;
69 void on_activate(bContext &C) override;
70 bool should_be_filtered_visible(StringRefNull filter_string) const override;
71
72 std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
73};
74
77
78 public:
80
81 std::optional<eWM_DragDataType> get_drag_type() const override;
82 void *create_drag_data() const override;
83 void on_drag_start(bContext &C) override;
84};
85
87 : library_ref_(library_ref), shelf_(shelf)
88{
89 if (shelf.type->get_active_asset) {
90 if (const AssetWeakReference *weak_ref = shelf.type->get_active_asset(shelf.type)) {
91 active_asset_ = *weak_ref;
92 }
93 else {
94 active_asset_.reset();
95 }
96 }
97}
98
100{
102 if (!library) {
103 return;
104 }
105
107 if (shelf_.type->asset_poll && !shelf_.type->asset_poll(shelf_.type, &asset)) {
108 return true;
109 }
110
111 const AssetMetaData &asset_data = asset.get_metadata();
112 if (catalog_filter_ && !catalog_filter_->contains(asset_data.catalog_id)) {
113 /* Skip this asset. */
114 return true;
115 }
116
117 const bool show_names = (shelf_.settings.display_flag & ASSETSHELF_SHOW_NAMES);
118 const StringRef identifier = asset.library_relative_identifier();
119
120 AssetViewItem &item = this->add_item<AssetViewItem>(asset, identifier, asset.get_name());
121 if (!show_names) {
122 item.hide_label();
123 }
124 if (shelf_.type->flag & ASSET_SHELF_TYPE_FLAG_NO_ASSET_DRAG) {
125 item.disable_asset_drag();
126 }
127 if (!shelf_.type->drag_operator.empty()) {
128 /* For now always select/activate items on click instead of press when there's a drag
129 * operator set. Important for pose library blending. Maybe we want to make this an explicit
130 * option of the asset shelf instead. */
131 item.select_on_click_set();
132 }
133 /* Make sure every click calls the #bl_activate_operator. We might want to add a flag to
134 * enable/disable this. Or we only call #bl_activate_operator when an item becomes active, and
135 * add a #bl_click_operator for repeated execution on every click. So far it seems like every
136 * asset shelf use case works with activating on every click though. */
138 if (shelf_.type->flag & ASSET_SHELF_TYPE_FLAG_ACTIVATE_FOR_CONTEXT_MENU) {
140 }
141
142 return true;
143 });
144}
145
147{
148 const ScrArea *area = CTX_wm_area(&C);
149 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
150 if (UI_textbutton_activate_rna(&C, region, &shelf_, "search_filter")) {
151 return true;
152 }
153 }
154
155 return false;
156}
157
159 const std::optional<asset_system::AssetCatalogFilter> &catalog_filter)
160{
161 if (catalog_filter) {
162 catalog_filter_.emplace(*catalog_filter);
163 }
164 else {
165 catalog_filter_ = std::nullopt;
166 }
167}
168
169static std::optional<asset_system::AssetCatalogFilter> catalog_filter_from_shelf_settings(
170 const AssetShelfSettings &shelf_settings, const asset_system::AssetLibrary &library)
171{
172 if (!shelf_settings.active_catalog_path) {
173 return {};
174 }
175
177 shelf_settings.active_catalog_path);
178 if (!active_catalog) {
179 return {};
180 }
181
182 return library.catalog_service().create_catalog_filter(active_catalog->catalog_id);
183}
184
185/* ---------------------------------------------------------------------- */
186
188 StringRef identifier,
190 : ui::PreviewGridItem(identifier, label, ICON_NONE), asset_(asset)
191{
192}
193
195{
196 allow_asset_drag_ = false;
197}
198
203static std::optional<wmOperatorCallParams> create_asset_operator_params(
205{
206 if (op_name.is_empty()) {
207 return {};
208 }
209 wmOperatorType *ot = WM_operatortype_find(op_name.c_str(), true);
210 if (!ot) {
211 return {};
212 }
213
214 PointerRNA *op_props = MEM_new<PointerRNA>(__func__);
218}
219
220void AssetViewItem::build_grid_tile(const bContext & /*C*/, uiLayout &layout) const
221{
222 const AssetView &asset_view = reinterpret_cast<const AssetView &>(this->get_view());
223 const AssetShelfType &shelf_type = *asset_view.shelf_.type;
224
225 PointerRNA asset_ptr = RNA_pointer_create_discrete(nullptr, &RNA_AssetRepresentation, &asset_);
227 layout.block(), reinterpret_cast<uiBut *>(view_item_but_), "asset", &asset_ptr);
228
229 uiBut *item_but = reinterpret_cast<uiBut *>(this->view_item_button());
230 if (std::optional<wmOperatorCallParams> activate_op = create_asset_operator_params(
231 shelf_type.activate_operator, asset_))
232 {
233 /* Attach the operator, but don't call it through the button. We call it using
234 * #on_activate(). */
235 UI_but_operator_set(item_but, activate_op->optype, activate_op->opcontext, activate_op->opptr);
237
238 MEM_delete(activate_op->opptr);
239 }
240 const ui::GridViewStyle &style = this->get_view().get_style();
241 /* Increase background draw size slightly, so highlights are well visible behind previews with an
242 * opaque background. */
244 item_but, style.tile_width + 2 * U.pixelsize, style.tile_height + 2 * U.pixelsize);
245
247 item_but,
248 [](bContext & /*C*/, uiTooltipData &tip, uiBut * /*but*/, void *argN) {
250 static_cast<const asset_system::AssetRepresentation *>(argN);
251 asset_tooltip(*asset, tip);
252 },
253 (&asset_),
254 nullptr);
255
256 /* Request preview when drawing. Grid views have an optimization to only draw items that are
257 * actually visible, so only previews scrolled into view will be loaded this way. This reduces
258 * total loading time and memory footprint. */
259 asset_.ensure_previewable();
260
261 const int preview_id = [&]() -> int {
262 /* Show loading icon while list is loading still. Previews might get pushed out of view again
263 * while the list grows, which can cause a lot of flickering. Note that this also means the
264 * actual loading of previews is delayed, because that only happens when a preview icon-ID is
265 * attached to a button. */
266 if (!list::is_loaded(&asset_view.library_ref_)) {
267 return ICON_PREVIEW_LOADING;
268 }
269 return asset_preview_or_icon(asset_);
270 }();
271
273}
274
276{
277 const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
278 const AssetShelfType &shelf_type = *asset_view.shelf_.type;
279 if (shelf_type.draw_context_menu) {
280 shelf_type.draw_context_menu(&C, &shelf_type, &asset_, &column);
281 }
282}
283
284std::optional<bool> AssetViewItem::should_be_active() const
285{
286 const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
287 const AssetShelfType &shelf_type = *asset_view.shelf_.type;
288 if (!shelf_type.get_active_asset) {
289 return {};
290 }
291 if (!asset_view.active_asset_) {
292 return false;
293 }
294 AssetWeakReference weak_ref = asset_.make_weak_reference();
295 const bool matches = *asset_view.active_asset_ == weak_ref;
296
297 return matches;
298}
299
301{
302 const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
303 const AssetShelfType &shelf_type = *asset_view.shelf_.type;
304
305 if (std::optional<wmOperatorCallParams> activate_op = create_asset_operator_params(
306 shelf_type.activate_operator, asset_))
307 {
309 &C, activate_op->optype, activate_op->opcontext, activate_op->opptr, nullptr);
310 WM_operator_properties_free(activate_op->opptr);
311 MEM_delete(activate_op->opptr);
312 }
313}
314
316{
317 const StringRefNull asset_name = asset_.get_name();
318 return fnmatch(filter_string.c_str(), asset_name.c_str(), FNM_CASEFOLD) == 0;
319}
320
321std::unique_ptr<ui::AbstractViewItemDragController> AssetViewItem::create_drag_controller() const
322{
323 const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
324 const AssetShelfType &shelf_type = *asset_view.shelf_.type;
325
326 if (!allow_asset_drag_ && shelf_type.drag_operator.empty()) {
327 return nullptr;
328 }
329 return std::make_unique<AssetDragController>(this->get_view(), asset_);
330}
331
332/* ---------------------------------------------------------------------- */
333
334static std::string filter_string_get(const AssetShelf &shelf)
335{
336 /* Copy of the filter string from #AssetShelfSettings, with extra '*' added to the beginning and
337 * end of the string, for `fnmatch()` to work. */
338 char search_string[sizeof(AssetShelfSettings::search_string) + 2];
339 BLI_strncpy_ensure_pad(search_string, shelf.settings.search_string, '*', sizeof(search_string));
340 return search_string;
341}
342
344 const AssetLibraryReference &library_ref,
345 const AssetShelf &shelf,
346 const bContext &C)
347{
348 list::storage_fetch(&library_ref, &C);
349
351 if (!library) {
352 return;
353 }
354
355 const float tile_width = shelf::tile_width(shelf.settings);
356 const float tile_height = shelf::tile_height(shelf.settings);
359
360 std::unique_ptr asset_view = std::make_unique<AssetView>(library_ref, shelf);
361 asset_view->set_catalog_filter(catalog_filter_from_shelf_settings(shelf.settings, *library));
362 asset_view->set_tile_size(tile_width, tile_height);
363
364 uiBlock *block = layout.block();
366 *block, "asset shelf asset view", std::move(asset_view));
367 grid_view->set_context_menu_title("Asset Shelf");
368
369 ui::GridViewBuilder builder(*block);
370 builder.build_grid_view(C, *grid_view, layout, filter_string_get(shelf));
371}
372
373/* ---------------------------------------------------------------------- */
374/* Dragging. */
375
381
382std::optional<eWM_DragDataType> AssetDragController::get_drag_type() const
383{
384 const AssetView &asset_view = this->get_view<AssetView>();
385 const AssetShelfType &shelf_type = *asset_view.shelf_.type;
386
387 /* Disable asset dragging, only call #AssetShelfType::drag_operator in #on_drag_start(). */
388 if (!shelf_type.drag_operator.empty()) {
389 return std::nullopt;
390 }
391 return asset_.is_local_id() ? WM_DRAG_ID : WM_DRAG_ASSET;
392}
393
395{
396 const AssetView &asset_view = this->get_view<AssetView>();
397 const AssetShelfType &shelf_type = *asset_view.shelf_.type;
398
399 if (std::optional<wmOperatorCallParams> drag_op = create_asset_operator_params(
400 shelf_type.drag_operator, asset_))
401 {
402 WM_operator_name_call_ptr(&C, drag_op->optype, drag_op->opcontext, drag_op->opptr, nullptr);
403 WM_operator_properties_free(drag_op->opptr);
404 MEM_delete(drag_op->opptr);
405 }
406}
407
409{
410 ID *local_id = asset_.local_id();
411 if (local_id) {
412 return static_cast<void *>(local_id);
413 }
414
415 eAssetImportMethod import_method = asset_.get_import_method().value_or(ASSET_IMPORT_PACK);
416 if (U.experimental.no_data_block_packing && import_method == ASSET_IMPORT_PACK) {
417 import_method = ASSET_IMPORT_APPEND_REUSE;
418 }
419
420 AssetImportSettings import_settings{};
421 import_settings.method = import_method;
422 import_settings.use_instance_collections = false;
423
424 return WM_drag_create_asset_data(&asset_, import_settings);
425}
426
427} // namespace blender::ed::asset::shelf
Main runtime representation of an asset.
ScrArea * CTX_wm_area(const bContext *C)
@ ASSET_SHELF_TYPE_FLAG_ACTIVATE_FOR_CONTEXT_MENU
@ ASSET_SHELF_TYPE_FLAG_NO_ASSET_DRAG
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
char char * BLI_strncpy_ensure_pad(char *__restrict dst, const char *__restrict src, char pad, size_t dst_maxncpy) ATTR_NONNULL(1
eAssetImportMethod
@ ASSET_IMPORT_PACK
@ ASSET_IMPORT_APPEND_REUSE
@ ASSETSHELF_SHOW_NAMES
static AppView * view
#define C
Definition RandGen.cpp:29
blender::ui::AbstractGridView * UI_block_add_view(uiBlock &block, blender::StringRef idname, std::unique_ptr< blender::ui::AbstractGridView > grid_view)
bool UI_textbutton_activate_rna(const bContext *C, ARegion *region, const void *rna_poin_data, const char *rna_prop_id)
void UI_but_func_tooltip_custom_set(uiBut *but, uiButToolTipCustomFunc func, void *arg, uiFreeArgFunc free_arg)
void UI_but_context_ptr_set(uiBlock *block, uiBut *but, blender::StringRef name, const PointerRNA *ptr)
void UI_but_operator_set_never_call(uiBut *but)
void UI_but_operator_set(uiBut *but, wmOperatorType *optype, blender::wm::OpCallContext opcontext, const PointerRNA *opptr=nullptr)
void UI_but_view_item_draw_size_set(uiBut *but, const std::optional< int > draw_width=std::nullopt, const std::optional< int > draw_height=std::nullopt)
@ WM_DRAG_ASSET
Definition WM_types.hh:1202
@ WM_DRAG_ID
Definition WM_types.hh:1201
#define U
constexpr bool is_empty() const
constexpr const char * c_str() const
AssetCatalogFilter create_catalog_filter(CatalogID active_catalog_id) const
AssetCatalog * find_catalog_by_path(const AssetCatalogPath &path) const
AssetCatalogService & catalog_service() const
std::optional< eWM_DragDataType > get_drag_type() const override
AssetDragController(ui::AbstractGridView &view, asset_system::AssetRepresentation &asset)
void build_grid_tile(const bContext &C, uiLayout &layout) const override
bool should_be_filtered_visible(StringRefNull filter_string) const override
AssetViewItem(asset_system::AssetRepresentation &asset_, StringRef identifier, StringRef label)
void build_context_menu(bContext &C, uiLayout &column) const override
std::unique_ptr< ui::AbstractViewItemDragController > create_drag_controller() const override
std::optional< bool > should_be_active() const override
bool begin_filtering(const bContext &C) const override
AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf)
void set_catalog_filter(const std::optional< asset_system::AssetCatalogFilter > &catalog_filter)
AbstractGridView & get_view() const
Definition grid_view.cc:164
bool matches(const AbstractViewItem &other) const override
Definition grid_view.cc:134
ItemT & add_item(Args &&...args)
const GridViewStyle & get_style() const
Definition grid_view.cc:98
uiButViewItem * view_item_button() const
void set_context_menu_title(const std::string &title)
void build_grid_view(const bContext &C, AbstractGridView &grid_view, uiLayout &layout, std::optional< StringRef > search_string={})
Definition grid_view.cc:442
PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id)
Definition grid_view.cc:466
void build_grid_tile_button(uiLayout &layout, BIFIconID override_preview_icon_id=ICON_NONE) const
Definition grid_view.cc:471
void storage_fetch(const AssetLibraryReference *library_reference, const bContext *C)
void iterate(const AssetLibraryReference &library_reference, AssetListIterFn fn)
asset_system::AssetLibrary * library_get_once_available(const AssetLibraryReference &library_reference)
bool is_loaded(const AssetLibraryReference *library_reference)
static std::optional< asset_system::AssetCatalogFilter > catalog_filter_from_shelf_settings(const AssetShelfSettings &shelf_settings, const asset_system::AssetLibrary &library)
int tile_height(const AssetShelfSettings &settings)
int tile_width(const AssetShelfSettings &settings)
static std::string filter_string_get(const AssetShelf &shelf)
static std::optional< wmOperatorCallParams > create_asset_operator_params(const StringRefNull op_name, const asset_system::AssetRepresentation &asset)
void build_asset_view(uiLayout &layout, const AssetLibraryReference &library_ref, const AssetShelf &shelf, const bContext &C)
void asset_tooltip(const asset_system::AssetRepresentation &asset, uiTooltipData &tip, const bool include_name)
void operator_asset_reference_props_set(const asset_system::AssetRepresentation &asset, PointerRNA &ptr)
BIFIconID asset_preview_or_icon(const asset_system::AssetRepresentation &asset)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
eAssetImportMethod method
The meta-data of an asset. By creating and giving this for a data-block (ID.asset_data),...
struct bUUID catalog_id
const char * active_catalog_path
std::string activate_operator
void(* draw_context_menu)(const bContext *C, const AssetShelfType *shelf_type, const blender::asset_system::AssetRepresentation *asset, uiLayout *layout)
std::string drag_operator
const AssetWeakReference *(* get_active_asset)(const AssetShelfType *shelf_type)
struct AssetShelfType * type
Definition DNA_ID.h:414
ListBase regionbase
wmOperatorType * optype
uiBlock * block() const
wmDragAsset * WM_drag_create_asset_data(const blender::asset_system::AssetRepresentation *asset, const AssetImportSettings &import_settings)
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
wmOperatorType * ot
Definition wm_files.cc:4237
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)