Blender V5.0
add_menu_assets.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
5#include "AS_asset_catalog.hh"
7#include "AS_asset_library.hh"
9
11#include "BLI_string_utf8.h"
12
13#include "DNA_space_types.h"
14
15#include "BKE_asset.hh"
16#include "BKE_idprop.hh"
17#include "BKE_screen.hh"
18
19#include "BLT_translation.hh"
20
21#include "RNA_access.hh"
22
23#include "ED_asset.hh"
25#include "ED_node.hh"
26
27#include "node_intern.hh"
28
30
31static bool node_add_menu_poll(const bContext *C, MenuType * /*mt*/)
32{
33 return CTX_wm_space_node(C);
34}
35
37{
39 return asset::list::is_loaded(&all_library_ref);
40}
41
43{
44 asset::AssetFilterSettings type_filter{};
45 type_filter.id_types = FILTER_ID_NT;
46 auto meta_data_filter = [&](const AssetMetaData &meta_data) {
47 const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
48 if (tree_type == nullptr || IDP_int_get(tree_type) != node_tree.type) {
49 return false;
50 }
51 return true;
52 };
55 return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter);
56}
57
66static Set<StringRef> get_builtin_menus(const int tree_type)
67{
68 Set<StringRef> menus;
69 switch (tree_type) {
70 case NTREE_GEOMETRY:
71 return {"Attribute",
72 "Input",
73 "Input/Constant",
74 "Input/Gizmo",
75 "Input/Group",
76 "Input/Import",
77 "Input/Scene",
78 "Output",
79 "Geometry",
80 "Geometry/Material",
81 "Geometry/Read",
82 "Geometry/Sample",
83 "Geometry/Write",
84 "Geometry/Operations",
85 "Curve",
86 "Curve/Read",
87 "Curve/Sample",
88 "Curve/Write",
89 "Curve/Operations",
90 "Curve/Primitives",
91 "Curve/Topology",
92 "Grease Pencil",
93 "Grease Pencil/Read",
94 "Grease Pencil/Operations",
95 "Grease Pencil/Write",
96 "Instances",
97 "Mesh",
98 "Mesh/Read",
99 "Mesh/Sample",
100 "Mesh/Write",
101 "Mesh/Operations",
102 "Mesh/Primitives",
103 "Mesh/Topology",
104 "Mesh/UV",
105 "Point",
106 "Volume",
107 "Volume/Operations",
108 "Volume/Primitives",
109 "Simulation",
110 "Color",
111 "Texture",
112 "Utilities",
113 "Utilities/Bundle",
114 "Utilities/Closure",
115 "Utilities/Text",
116 "Utilities/Vector",
117 "Utilities/Field",
118 "Utilities/Math",
119 "Utilities/Matrix",
120 "Utilities/Rotation",
121 "Utilities/Deprecated",
122 "Group",
123 "Layout"};
124 case NTREE_COMPOSIT:
125 return {"Input",
126 "Input/Constant",
127 "Input/Scene",
128 "Output",
129 "Color",
130 "Color/Adjust",
131 "Creative",
132 "Filter",
133 "Filter/Blur",
134 "Keying",
135 "Mask",
136 "Tracking",
137 "Transform",
138 "Texture",
139 "Utilities",
140 "Utilities/Math",
141 "Utilities/Vector",
142 "Group",
143 "Layout"};
144 case NTREE_SHADER:
145 return {"Input",
146 "Output",
147 "Shader",
148 "Displacement",
149 "Color",
150 "Texture",
151 "Utilities",
152 "Utilities/Math",
153 "Utilities/Vector",
154 "Group",
155 "Layout"};
156 }
157 return {};
158}
159
160static void node_catalog_assets_draw(const bContext *C, Menu *menu)
161{
162 SpaceNode &snode = *CTX_wm_space_node(C);
163 const bNodeTree *edit_tree = snode.edittree;
164 if (!edit_tree) {
165 return;
166 }
167 if (!snode.runtime->assets_for_menu) {
168 snode.runtime->assets_for_menu = std::make_shared<asset::AssetItemTree>(
169 build_catalog_tree(*C, *edit_tree));
170 return;
171 }
173
174 const std::optional<blender::StringRefNull> menu_path = CTX_data_string_get(
175 C, "asset_catalog_path");
176 if (!menu_path) {
177 return;
178 }
179
180 const std::optional<blender::StringRefNull> operator_id = CTX_data_string_get(C, "operator_id");
181 if (!operator_id) {
182 return;
183 }
184
185 const Span<asset_system::AssetRepresentation *> assets = tree.assets_per_path.lookup(
186 menu_path->c_str());
187 const asset_system::AssetCatalogTreeItem *catalog_item = tree.catalogs.find_item(
188 menu_path->c_str());
189 BLI_assert(catalog_item != nullptr);
190
191 if (assets.is_empty() && !catalog_item->has_children()) {
192 return;
193 }
194
195 uiLayout *layout = menu->layout;
196 bool add_separator = true;
197
198 for (const asset_system::AssetRepresentation *asset : assets) {
199 if (add_separator) {
200 layout->separator();
201 add_separator = false;
202 }
203 PointerRNA op_ptr = layout->op(*operator_id,
204 IFACE_(asset->get_name()),
205 ICON_NONE,
209 }
210
211 const Set<StringRef> all_builtin_menus = get_builtin_menus(edit_tree->type);
212
213 catalog_item->foreach_child([&](const asset_system::AssetCatalogTreeItem &item) {
214 if (all_builtin_menus.contains_as(item.catalog_path().str())) {
215 return;
216 }
217 if (add_separator) {
218 layout->separator();
219 add_separator = false;
220 }
221 asset::draw_node_menu_for_catalog(item, *operator_id, "NODE_MT_node_catalog_assets", *layout);
222 });
223}
224
225static void node_unassigned_assets_draw(const bContext *C, Menu *menu)
226{
227 SpaceNode &snode = *CTX_wm_space_node(C);
228 const bNodeTree *edit_tree = snode.edittree;
229 if (!edit_tree) {
230 return;
231 }
232
233 const std::optional<blender::StringRefNull> operator_id = CTX_data_string_get(C, "operator_id");
234 if (!operator_id) {
235 return;
236 }
237
238 if (!snode.runtime->assets_for_menu) {
239 snode.runtime->assets_for_menu = std::make_shared<asset::AssetItemTree>(
240 build_catalog_tree(*C, *edit_tree));
241 return;
242 }
244 for (const asset_system::AssetRepresentation *asset : tree.unassigned_assets) {
245 PointerRNA op_ptr = menu->layout->op(*operator_id,
246 IFACE_(asset->get_name()),
247 ICON_NONE,
251 }
252}
253
254static void root_catalogs_draw(const bContext *C, Menu *menu, const StringRefNull operator_id)
255{
256 SpaceNode &snode = *CTX_wm_space_node(C);
257 uiLayout *layout = menu->layout;
258 const bNodeTree *edit_tree = snode.edittree;
259 if (!edit_tree) {
260 return;
261 }
262
263 snode.runtime->assets_for_menu = std::make_shared<asset::AssetItemTree>(
264 build_catalog_tree(*C, *edit_tree));
265
266 const bool loading_finished = all_loading_finished();
267
269 if (tree.catalogs.is_empty() && loading_finished && tree.unassigned_assets.is_empty()) {
270 return;
271 }
272
273 layout->separator();
274
275 if (!loading_finished) {
276 layout->label(IFACE_("Loading Asset Libraries"), ICON_INFO);
277 }
278
279 const Set<StringRef> all_builtin_menus = get_builtin_menus(edit_tree->type);
280
281 tree.catalogs.foreach_root_item([&](const asset_system::AssetCatalogTreeItem &item) {
282 if (!all_builtin_menus.contains_as(item.catalog_path().str())) {
283 asset::draw_node_menu_for_catalog(item, operator_id, "NODE_MT_node_catalog_assets", *layout);
284 }
285 });
286
287 if (!tree.unassigned_assets.is_empty()) {
288 layout->separator();
289 layout->menu("NODE_MT_node_unassigned_assets", IFACE_("Unassigned"), ICON_FILE_HIDDEN);
290 }
291}
292
293static void add_root_catalogs_draw(const bContext *C, Menu *menu)
294{
295 const StringRefNull operator_id = "NODE_OT_add_group_asset";
296
297 menu->layout->context_string_set("operator_id", operator_id);
298 root_catalogs_draw(C, menu, operator_id);
299}
300
301static void swap_root_catalogs_draw(const bContext *C, Menu *menu)
302{
303 const StringRefNull operator_id = "NODE_OT_swap_group_asset";
304
305 menu->layout->context_string_set("operator_id", operator_id);
306 root_catalogs_draw(C, menu, operator_id);
307}
308
310{
311 MenuType type{};
312 STRNCPY_UTF8(type.idname, "NODE_MT_node_catalog_assets");
317 return type;
318}
319
321{
322 MenuType type{};
323 STRNCPY_UTF8(type.idname, "NODE_MT_node_unassigned_assets");
328 type.description = N_(
329 "Node group assets not assigned to a catalog.\n"
330 "Catalogs can be assigned in the Asset Browser");
331 return type;
332}
333
335{
336 MenuType type{};
337 STRNCPY_UTF8(type.idname, "NODE_MT_node_add_root_catalogs");
341 return type;
342}
343
345{
346 MenuType type{};
347 STRNCPY_UTF8(type.idname, "NODE_MT_node_swap_root_catalogs");
351 return type;
352}
353
355 const bContext &C,
356 const StringRef catalog_path,
357 const NodeAssetMenuOperatorType operator_type)
358{
359 SpaceNode &snode = *CTX_wm_space_node(&C);
360 if (snode.runtime->assets_for_menu == nullptr) {
361 return;
362 }
364 const asset_system::AssetCatalogTreeItem *item = tree.catalogs.find_item(catalog_path);
365 if (!item) {
366 return;
367 }
368
369 StringRef operator_id;
370
371 switch (operator_type) {
373 operator_id = "NODE_OT_swap_group_asset";
374 break;
375 default:
376 operator_id = "NODE_OT_add_group_asset";
377 }
378
379 uiLayout *col = &layout.column(false);
380 col->context_string_set("asset_catalog_path", item->catalog_path().str());
381 col->context_string_set("operator_id", operator_id);
382 col->menu_contents("NODE_MT_node_catalog_assets");
383}
384
385} // namespace blender::ed::space_node
Main runtime representation of an asset.
IDProperty * BKE_asset_metadata_idprop_find(const AssetMetaData *asset_data, const char *name) ATTR_WARN_UNUSED_RESULT
Definition asset.cc:179
SpaceNode * CTX_wm_space_node(const bContext *C)
std::optional< blender::StringRefNull > CTX_data_string_get(const bContext *C, const char *member)
#define IDP_int_get(prop)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define STRNCPY_UTF8(dst, src)
#define IFACE_(msgid)
#define FILTER_ID_NT
Definition DNA_ID.h:1213
@ NTREE_SHADER
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
#define C
Definition RandGen.cpp:29
NodeAssetMenuOperatorType
#define UI_ITEM_NONE
bool contains_as(const ForwardKey &key) const
Definition BLI_set.hh:314
constexpr bool is_empty() const
Definition BLI_span.hh:260
void foreach_child(ItemIterFn callback) const
KDTree_3d * tree
uint col
AssetLibraryReference all_library_reference()
void all_library_reload_catalogs_if_dirty()
bool is_loaded(const AssetLibraryReference *library_reference)
void asset_reading_region_listen_fn(const wmRegionListenerParams *params)
void draw_node_menu_for_catalog(const asset_system::AssetCatalogTreeItem &item, const StringRefNull operator_id, const StringRefNull menu_name, uiLayout &layout)
AssetItemTree build_filtered_all_catalog_tree(const AssetLibraryReference &library_ref, const bContext &C, const AssetFilterSettings &filter_settings, FunctionRef< bool(const AssetMetaData &)> meta_data_filter={})
void operator_asset_reference_props_set(const asset_system::AssetRepresentation &asset, PointerRNA &ptr)
static bool all_loading_finished()
static Set< StringRef > get_builtin_menus(const int tree_type)
static void node_unassigned_assets_draw(const bContext *C, Menu *menu)
static void root_catalogs_draw(const bContext *C, Menu *menu, const StringRefNull operator_id)
MenuType swap_root_catalogs_menu_type()
void ui_template_node_asset_menu_items(uiLayout &layout, const bContext &C, StringRef catalog_path, const NodeAssetMenuOperatorType operator_type)
static asset::AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree &node_tree)
static void add_root_catalogs_draw(const bContext *C, Menu *menu)
static void swap_root_catalogs_draw(const bContext *C, Menu *menu)
static void node_catalog_assets_draw(const bContext *C, Menu *menu)
static bool node_add_menu_poll(const bContext *C, MenuType *)
The meta-data of an asset. By creating and giving this for a data-block (ID.asset_data),...
MenuTypeFlag flag
const char * description
bool(* poll)(const bContext *C, MenuType *mt)
char idname[BKE_ST_MAXNAME]
void(* draw)(const bContext *C, Menu *menu)
void(* listener)(const wmRegionListenerParams *params)
uiLayout * layout
SpaceNode_Runtime * runtime
struct bNodeTree * edittree
std::shared_ptr< asset::AssetItemTree > assets_for_menu
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void menu(MenuType *mt, std::optional< blender::StringRef > name, int icon)
void context_string_set(blender::StringRef name, blender::StringRef value)
#define N_(msgid)