Blender V4.5
geometry_nodes_caller_ui.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <fmt/format.h>
6#include <sstream>
7
9#include "BKE_context.hh"
10#include "BKE_global.hh"
11#include "BKE_lib_id.hh"
12#include "BKE_main.hh"
13#include "BKE_modifier.hh"
14#include "BKE_node.hh"
15#include "BKE_node_runtime.hh"
16#include "BKE_screen.hh"
17
18#include "BLI_string.h"
19
20#include "BLT_translation.hh"
21
22#include "DNA_modifier_types.h"
23
24#include "ED_object.hh"
25#include "ED_screen.hh"
26#include "ED_undo.hh"
27
28#include "MOD_nodes.hh"
29#include "NOD_geometry.hh"
33
34#include "RNA_access.hh"
35#include "RNA_prototypes.hh"
36
37#include "UI_interface.hh"
39#include "UI_resources.hh"
40
42
43namespace blender::nodes {
44
45namespace geo_log = geo_eval_log;
46
47namespace {
48struct PanelOpenProperty {
49 PointerRNA ptr;
50 StringRefNull name;
51};
52
53struct SearchInfo {
54 geo_log::GeoTreeLog *tree_log = nullptr;
55 bNodeTree *tree = nullptr;
56 IDProperty *properties = nullptr;
57};
58
59struct ModifierSearchData {
60 uint32_t object_session_uid;
62};
63
64struct OperatorSearchData {
66 SearchInfo info;
67};
68
69struct SocketSearchData {
70 std::variant<ModifierSearchData, OperatorSearchData> search_data;
71 char socket_identifier[MAX_NAME];
72 bool is_output;
73
74 SearchInfo info(const bContext &C) const;
75};
76/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */
77BLI_STATIC_ASSERT(std::is_trivially_destructible_v<SocketSearchData>, "");
78
79struct DrawGroupInputsContext {
80 const bContext &C;
82 geo_log::GeoTreeLog *tree_log;
83 nodes::PropertiesVectorSet properties;
84 PointerRNA *properties_ptr;
85 PointerRNA *bmain_ptr;
86 Array<nodes::socket_usage_inference::SocketUsage> input_usages;
87 bool use_name_for_ids = false;
88 std::function<PanelOpenProperty(const bNodeTreeInterfacePanel &)> panel_open_property_fn;
89 std::function<SocketSearchData(const bNodeTreeInterfaceSocket &)> socket_search_data_fn;
90 std::function<void(uiLayout &, int icon, const bNodeTreeInterfaceSocket &)>
91 draw_attribute_toggle_fn;
92
93 bool input_is_visible(const bNodeTreeInterfaceSocket &socket) const
94 {
95 return this->input_usages[this->tree->interface_input_index(socket)].is_visible;
96 }
97
98 bool input_is_active(const bNodeTreeInterfaceSocket &socket) const
99 {
100 return this->input_usages[this->tree->interface_input_index(socket)].is_used;
101 }
102};
103} // namespace
104
106{
107 if (!nmd.runtime->eval_log) {
108 return nullptr;
109 }
110 bke::ModifierComputeContext compute_context{nullptr, nmd};
111 return &nmd.runtime->eval_log->get_tree_log(compute_context.hash());
112}
113
115 const wmWindowManager &wm,
116 const ModifierSearchData &data)
117{
119 /* Work around an issue where the attribute search exec function has stale pointers when data
120 * is reallocated when evaluating the node tree, causing a crash. This would be solved by
121 * allowing the UI search data to own arbitrary memory rather than just referencing it. */
122 return nullptr;
123 }
124
126 &bmain, ID_OB, data.object_session_uid);
127 if (object == nullptr) {
128 return nullptr;
129 }
130 ModifierData *md = BKE_modifiers_findby_name(object, data.modifier_name);
131 if (md == nullptr) {
132 return nullptr;
133 }
135 return reinterpret_cast<NodesModifierData *>(md);
136}
137
138SearchInfo SocketSearchData::info(const bContext &C) const
139{
140 if (const auto *modifier_search_data = std::get_if<ModifierSearchData>(&this->search_data)) {
141 const NodesModifierData *nmd = get_modifier_data(
142 *CTX_data_main(&C), *CTX_wm_manager(&C), *modifier_search_data);
143 if (nmd == nullptr) {
144 return {};
145 }
146 if (nmd->node_group == nullptr) {
147 return {};
148 }
149 geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd);
150 return {tree_log, nmd->node_group, nmd->settings.properties};
151 }
152 if (const auto *operator_search_data = std::get_if<OperatorSearchData>(&this->search_data)) {
153 return operator_search_data->info;
154 }
155 return {};
156}
157
159 const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
160{
161 const SocketSearchData &data = *static_cast<SocketSearchData *>(arg);
162 const SearchInfo info = data.info(*C);
163 if (!info.tree || !info.tree_log) {
164 return;
165 }
166 info.tree_log->ensure_layer_names();
167 info.tree->ensure_topology_cache();
168
169 Vector<const bNodeSocket *> sockets_to_check;
170 for (const bNode *node : info.tree->group_input_nodes()) {
171 for (const bNodeSocket *socket : node->output_sockets()) {
172 if (socket->type == SOCK_GEOMETRY) {
173 sockets_to_check.append(socket);
174 }
175 }
176 }
177
178 Set<StringRef> names;
179 Vector<const std::string *> layer_names;
180 for (const bNodeSocket *socket : sockets_to_check) {
181 const geo_log::ValueLog *value_log = info.tree_log->find_socket_value_log(*socket);
182 if (value_log == nullptr) {
183 continue;
184 }
185 if (const auto *geo_log = dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) {
186 if (const std::optional<geo_log::GeometryInfoLog::GreasePencilInfo> &grease_pencil_info =
187 geo_log->grease_pencil_info)
188 {
189 for (const std::string &name : grease_pencil_info->layer_names) {
190 if (names.add(name)) {
191 layer_names.append(&name);
192 }
193 }
194 }
195 }
196 }
197 BLI_assert(items);
198 ui::grease_pencil_layer_search_add_items(str, layer_names.as_span(), *items, is_first);
199}
200
201static void layer_name_search_exec_fn(bContext *C, void *data_v, void *item_v)
202{
203 const SocketSearchData &data = *static_cast<SocketSearchData *>(data_v);
204 const std::string *item = static_cast<std::string *>(item_v);
205 if (!item) {
206 return;
207 }
208 const SearchInfo info = data.info(*C);
209 if (!info.properties) {
210 return;
211 }
212
213 IDProperty &name_property = *IDP_GetPropertyFromGroup(info.properties, data.socket_identifier);
214 IDP_AssignString(&name_property, item->c_str());
215
216 ED_undo_push(C, "Assign Layer Name");
217}
218
219static void add_layer_name_search_button(DrawGroupInputsContext &ctx,
220 uiLayout *layout,
221 const bNodeTreeInterfaceSocket &socket)
222{
223 const std::string rna_path = fmt::format("[\"{}\"]", BLI_str_escape(socket.identifier));
224 if (!ctx.tree_log) {
225 layout->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, "", ICON_NONE);
226 return;
227 }
228
229 uiLayoutSetPropDecorate(layout, false);
230
231 uiLayout *split = &layout->split(0.4f, false);
232 uiLayout *name_row = &split->row(false);
234
235 name_row->label(socket.name ? IFACE_(socket.name) : "", ICON_NONE);
236 uiLayout *prop_row = &split->row(true);
237
238 uiBlock *block = uiLayoutGetBlock(prop_row);
239 uiBut *but = uiDefIconTextButR(block,
241 0,
242 ICON_OUTLINER_DATA_GP_LAYER,
243 "",
244 0,
245 0,
246 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
247 UI_UNIT_Y,
248 ctx.properties_ptr,
249 rna_path,
250 0,
251 0.0f,
252 0.0f,
253 StringRef(socket.description));
254 UI_but_placeholder_set(but, "Layer");
255 layout->label("", ICON_BLANK1);
256
257 const Object *object = ed::object::context_object(&ctx.C);
258 BLI_assert(object != nullptr);
259 if (object == nullptr) {
260 return;
261 }
262
263 /* Using a custom free function make the search not work currently. So make sure this data can be
264 * freed with MEM_freeN. */
265 SocketSearchData *data = static_cast<SocketSearchData *>(
266 MEM_mallocN(sizeof(SocketSearchData), __func__));
267 *data = ctx.socket_search_data_fn(socket);
271 nullptr,
273 data,
274 true,
275 nullptr,
277 nullptr);
278}
279
281 const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
282{
283 SocketSearchData &data = *static_cast<SocketSearchData *>(arg);
284 const SearchInfo info = data.info(*C);
285 if (!info.tree || !info.tree_log) {
286 return;
287 }
288 info.tree_log->ensure_existing_attributes();
289 info.tree->ensure_topology_cache();
290
291 Vector<const bNodeSocket *> sockets_to_check;
292 if (data.is_output) {
293 for (const bNode *node : info.tree->nodes_by_type("NodeGroupOutput")) {
294 for (const bNodeSocket *socket : node->input_sockets()) {
295 if (socket->type == SOCK_GEOMETRY) {
296 sockets_to_check.append(socket);
297 }
298 }
299 }
300 }
301 else {
302 for (const bNode *node : info.tree->group_input_nodes()) {
303 for (const bNodeSocket *socket : node->output_sockets()) {
304 if (socket->type == SOCK_GEOMETRY) {
305 sockets_to_check.append(socket);
306 }
307 }
308 }
309 }
310 Set<StringRef> names;
312 for (const bNodeSocket *socket : sockets_to_check) {
313 const geo_log::ValueLog *value_log = info.tree_log->find_socket_value_log(*socket);
314 if (value_log == nullptr) {
315 continue;
316 }
317 if (const auto *geo_log = dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) {
318 for (const geo_log::GeometryAttributeInfo &attribute : geo_log->attributes) {
319 if (names.add(attribute.name)) {
320 attributes.append(&attribute);
321 }
322 }
323 }
324 }
325 ui::attribute_search_add_items(str, data.is_output, attributes.as_span(), items, is_first);
326}
327
328static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
329{
330 if (item_v == nullptr) {
331 return;
332 }
333 SocketSearchData &data = *static_cast<SocketSearchData *>(data_v);
334 const auto &item = *static_cast<const geo_log::GeometryAttributeInfo *>(item_v);
335 const SearchInfo info = data.info(*C);
336 if (!info.properties) {
337 return;
338 }
339
340 const std::string attribute_prop_name = data.socket_identifier +
342 IDProperty &name_property = *IDP_GetPropertyFromGroup(info.properties, attribute_prop_name);
343 IDP_AssignString(&name_property, item.name.c_str());
344
345 ED_undo_push(C, "Assign Attribute Name");
346}
347
348static void add_attribute_search_button(DrawGroupInputsContext &ctx,
349 uiLayout *layout,
350 const StringRefNull rna_path_attribute_name,
351 const bNodeTreeInterfaceSocket &socket)
352{
353 if (!ctx.tree_log) {
354 layout->prop(ctx.properties_ptr, rna_path_attribute_name, UI_ITEM_NONE, "", ICON_NONE);
355 return;
356 }
357
358 uiBlock *block = uiLayoutGetBlock(layout);
359 uiBut *but = uiDefIconTextButR(block,
361 0,
362 ICON_NONE,
363 "",
364 0,
365 0,
366 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
367 UI_UNIT_Y,
368 ctx.properties_ptr,
369 rna_path_attribute_name,
370 0,
371 0.0f,
372 0.0f,
373 StringRef(socket.description));
374
375 const Object *object = ed::object::context_object(&ctx.C);
376 BLI_assert(object != nullptr);
377 if (object == nullptr) {
378 return;
379 }
380
381 /* Using a custom free function make the search not work currently. So make sure this data can be
382 * freed with MEM_freeN. */
383 SocketSearchData *data = static_cast<SocketSearchData *>(
384 MEM_mallocN(sizeof(SocketSearchData), __func__));
385 *data = ctx.socket_search_data_fn(socket);
389 nullptr,
391 data,
392 true,
393 nullptr,
395 nullptr);
396
397 char *attribute_name = RNA_string_get_alloc(
398 ctx.properties_ptr, rna_path_attribute_name.c_str(), nullptr, 0, nullptr);
399 const bool access_allowed = bke::allow_procedural_attribute_access(attribute_name);
400 MEM_freeN(attribute_name);
401 if (!access_allowed) {
403 }
404}
405
407 DrawGroupInputsContext &ctx,
408 uiLayout *layout,
409 const StringRefNull rna_path,
410 const bNodeTreeInterfaceSocket &socket,
411 const std::optional<StringRefNull> use_name = std::nullopt)
412{
413 const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
414 const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
415 const std::string rna_path_attribute_name = fmt::format(
417
418 /* We're handling this manually in this case. */
419 uiLayoutSetPropDecorate(layout, false);
420
421 uiLayout *split = &layout->split(0.4f, false);
422 uiLayout *name_row = &split->row(false);
424
425 uiLayout *prop_row = nullptr;
426
427 const std::optional<StringRef> attribute_name = nodes::input_attribute_name_get(ctx.properties,
428 socket);
429 const StringRefNull socket_name = use_name.has_value() ?
430 (*use_name) :
431 (socket.name ? IFACE_(socket.name) : "");
432 if (type == SOCK_BOOLEAN && !attribute_name) {
433 name_row->label("", ICON_NONE);
434 prop_row = &split->row(true);
435 }
436 else {
437 prop_row = &layout->row(true);
438 }
439
440 if (type == SOCK_BOOLEAN) {
441 uiLayoutSetPropSep(prop_row, false);
443 }
444
445 if (attribute_name) {
446 name_row->label(IFACE_(socket_name), ICON_NONE);
447 prop_row = &split->row(true);
448 add_attribute_search_button(ctx, prop_row, rna_path_attribute_name, socket);
449 layout->label("", ICON_BLANK1);
450 }
451 else {
452 const char *name = IFACE_(socket_name.c_str());
453 prop_row->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
454 uiItemDecoratorR(layout, ctx.properties_ptr, rna_path.c_str(), -1);
455 }
456
457 ctx.draw_attribute_toggle_fn(*prop_row, ICON_SPREADSHEET, socket);
458}
459
461{
462 for (const int i : IndexRange(nmd.panels_num)) {
463 if (nmd.panels[i].id == id) {
464 return &nmd.panels[i];
465 }
466 }
467 return nullptr;
468}
469
470/* Drawing the properties manually with #uiLayout::prop instead of #uiDefAutoButsRNA allows using
471 * the node socket identifier for the property names, since they are unique, but also having
472 * the correct label displayed in the UI. */
473static void draw_property_for_socket(DrawGroupInputsContext &ctx,
474 uiLayout *layout,
475 const bNodeTreeInterfaceSocket &socket,
476 const std::optional<StringRefNull> parent_name = std::nullopt)
477{
478 const StringRefNull identifier = socket.identifier;
479 /* The property should be created in #MOD_nodes_update_interface with the correct type. */
480 IDProperty *property = ctx.properties.lookup_key_default_as(identifier, nullptr);
481
482 /* IDProperties can be removed with python, so there could be a situation where
483 * there isn't a property for a socket or it doesn't have the correct type. */
484 if (property == nullptr ||
485 !nodes::id_property_type_matches_socket(socket, *property, ctx.use_name_for_ids))
486 {
487 return;
488 }
489
490 const int input_index = ctx.tree->interface_input_index(socket);
491 if (!ctx.input_is_visible(socket)) {
492 /* The input is not used currently, but it would be used if any menu input is changed.
493 * By convention, the input is hidden in this case instead of just grayed out. */
494 return;
495 }
496
497 uiLayout *row = &layout->row(true);
498 uiLayoutSetPropDecorate(row, true);
499 uiLayoutSetActive(row, ctx.input_is_active(socket));
500
501 const std::string rna_path = fmt::format("[\"{}\"]", BLI_str_escape(identifier.c_str()));
502
503 /* Use #uiItemPointerR to draw pointer properties because #uiLayout::prop would not have enough
504 * information about what type of ID to select for editing the values. This is because
505 * pointer IDProperties contain no information about their type. */
506 const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
507 const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
508 std::string name = socket.name ? IFACE_(socket.name) : "";
509
510 /* If the property has a prefix that's the same string as the name of the panel it's in, remove
511 * the prefix so it appears less verbose. */
512 if (parent_name.has_value()) {
513 const StringRefNull prefix_to_remove = *parent_name;
514 int pos = name.find(prefix_to_remove);
515 if (pos == 0 && name != prefix_to_remove) {
516 /* Needs to trim remainig space characters if any. Use the `trim()` from `StringRefNull`
517 * because std::string doesn't have a built-in `trim()` yet. If the property name is the
518 * same as parent panel's name then keep the name, otherwise the name would be an empty
519 * string which messes up the UI. */
520 name = StringRefNull(name.substr(prefix_to_remove.size())).trim();
521 }
522 }
523
524 switch (type) {
525 case SOCK_OBJECT: {
527 row, ctx.properties_ptr, rna_path, ctx.bmain_ptr, "objects", name, ICON_OBJECT_DATA);
528 break;
529 }
530 case SOCK_COLLECTION: {
531 uiItemPointerR(row,
532 ctx.properties_ptr,
533 rna_path,
534 ctx.bmain_ptr,
535 "collections",
536 name,
537 ICON_OUTLINER_COLLECTION);
538 break;
539 }
540 case SOCK_MATERIAL: {
542 row, ctx.properties_ptr, rna_path, ctx.bmain_ptr, "materials", name, ICON_MATERIAL);
543 break;
544 }
545 case SOCK_TEXTURE: {
547 row, ctx.properties_ptr, rna_path, ctx.bmain_ptr, "textures", name, ICON_TEXTURE);
548 break;
549 }
550 case SOCK_IMAGE: {
551 uiTemplateID(row,
552 &ctx.C,
553 ctx.properties_ptr,
554 rna_path,
555 "image.new",
556 "image.open",
557 nullptr,
559 false,
560 name);
561 break;
562 }
563 case SOCK_MENU: {
565 /* Use a single space when the name is empty to work around a bug with expanded enums. Also
566 * see #ui_item_enum_expand_exec. */
567 row->prop(ctx.properties_ptr,
568 rna_path,
570 StringRef(name).is_empty() ? " " : name,
571 ICON_NONE);
572 }
573 else {
574 row->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
575 }
576 break;
577 }
578 case SOCK_BOOLEAN: {
579 if (is_layer_selection_field(socket)) {
580 add_layer_name_search_button(ctx, row, socket);
581 /* Adds a spacing at the end of the row. */
582 row->label("", ICON_BLANK1);
583 break;
584 }
586 }
587 default: {
588 if (nodes::input_has_attribute_toggle(*ctx.tree, input_index)) {
589 add_attribute_search_or_value_buttons(ctx, row, rna_path, socket, name);
590 }
591 else {
592 row->prop(ctx.properties_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
593 }
594 }
595 }
596 if (!nodes::input_has_attribute_toggle(*ctx.tree, input_index)) {
597 row->label("", ICON_BLANK1);
598 }
599}
600
601static bool interface_panel_has_socket(DrawGroupInputsContext &ctx,
602 const bNodeTreeInterfacePanel &interface_panel)
603{
604 for (const bNodeTreeInterfaceItem *item : interface_panel.items()) {
605 if (item->item_type == NODE_INTERFACE_SOCKET) {
606 const bNodeTreeInterfaceSocket &socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(
607 item);
609 continue;
610 }
611 if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
612 if (ctx.input_is_visible(socket)) {
613 return true;
614 }
615 }
616 }
617 else if (item->item_type == NODE_INTERFACE_PANEL) {
619 *reinterpret_cast<const bNodeTreeInterfacePanel *>(item)))
620 {
621 return true;
622 }
623 }
624 }
625 return false;
626}
627
628static bool interface_panel_affects_output(DrawGroupInputsContext &ctx,
629 const bNodeTreeInterfacePanel &panel)
630{
631 for (const bNodeTreeInterfaceItem *item : panel.items()) {
632 if (item->item_type == NODE_INTERFACE_SOCKET) {
633 const auto &socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(item);
634 if (socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER) {
635 continue;
636 }
637 if (!(socket.flag & NODE_INTERFACE_SOCKET_INPUT)) {
638 continue;
639 }
640 if (ctx.input_is_active(socket)) {
641 return true;
642 }
643 }
644 else if (item->item_type == NODE_INTERFACE_PANEL) {
645 const auto &sub_interface_panel = *reinterpret_cast<const bNodeTreeInterfacePanel *>(item);
646 if (interface_panel_affects_output(ctx, sub_interface_panel)) {
647 return true;
648 }
649 }
650 }
651 return false;
652}
653
655 DrawGroupInputsContext &ctx,
656 uiLayout *layout,
657 const bNodeTreeInterfacePanel &interface_panel,
658 const bool skip_first = false,
659 const std::optional<StringRefNull> parent_name = std::nullopt)
660{
661 for (const bNodeTreeInterfaceItem *item : interface_panel.items().drop_front(skip_first ? 1 : 0))
662 {
663 switch (NodeTreeInterfaceItemType(item->item_type)) {
665 const auto &sub_interface_panel = *reinterpret_cast<const bNodeTreeInterfacePanel *>(item);
666 if (!interface_panel_has_socket(ctx, sub_interface_panel)) {
667 continue;
668 }
669 PanelOpenProperty open_property = ctx.panel_open_property_fn(sub_interface_panel);
670 PanelLayout panel_layout;
671 bool skip_first = false;
672 /* Check if the panel should have a toggle in the header. */
673 const bNodeTreeInterfaceSocket *toggle_socket = sub_interface_panel.header_toggle_socket();
674 if (toggle_socket && !(toggle_socket->flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
675 const StringRefNull identifier = toggle_socket->identifier;
676 IDProperty *property = ctx.properties.lookup_key_default_as(identifier, nullptr);
677 /* IDProperties can be removed with python, so there could be a situation where
678 * there isn't a property for a socket or it doesn't have the correct type. */
679 if (property == nullptr || !nodes::id_property_type_matches_socket(
680 *toggle_socket, *property, ctx.use_name_for_ids))
681 {
682 continue;
683 }
684 char socket_id_esc[MAX_NAME * 2];
685 BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc));
686
687 char rna_path[sizeof(socket_id_esc) + 4];
688 SNPRINTF(rna_path, "[\"%s\"]", socket_id_esc);
689
690 panel_layout = layout->panel_prop_with_bool_header(&ctx.C,
691 &open_property.ptr,
692 open_property.name,
693 ctx.properties_ptr,
694 rna_path,
695 IFACE_(sub_interface_panel.name));
696 skip_first = true;
697 }
698 else {
699 panel_layout = layout->panel_prop(&ctx.C, &open_property.ptr, open_property.name);
700 panel_layout.header->label(IFACE_(sub_interface_panel.name), ICON_NONE);
701 }
702 if (!interface_panel_affects_output(ctx, sub_interface_panel)) {
703 uiLayoutSetActive(panel_layout.header, false);
704 }
706 panel_layout.header,
707 [](bContext * /*C*/, void *panel_arg, const StringRef /*tip*/) -> std::string {
708 const auto *panel = static_cast<bNodeTreeInterfacePanel *>(panel_arg);
709 return StringRef(panel->description);
710 },
711 const_cast<bNodeTreeInterfacePanel *>(&sub_interface_panel),
712 nullptr,
713 nullptr);
714 if (panel_layout.body) {
715 const StringRefNull panel_name = sub_interface_panel.name ? sub_interface_panel.name :
716 "";
718 ctx, panel_layout.body, sub_interface_panel, skip_first, panel_name);
719 }
720 break;
721 }
723 const auto &interface_socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(item);
724 if (interface_socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
725 if (!(interface_socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
726 draw_property_for_socket(ctx, layout, interface_socket, parent_name);
727 }
728 }
729 break;
730 }
731 }
732 }
733}
734
735static void draw_warnings(const bContext *C,
736 const NodesModifierData &nmd,
737 uiLayout *layout,
738 PointerRNA *md_ptr)
739{
740 if (G.is_rendering) {
741 /* Avoid accessing this data while baking in a separate thread. */
742 return;
743 }
744 using namespace geo_log;
745 GeoTreeLog *tree_log = get_root_tree_log(nmd);
746 if (!tree_log) {
747 return;
748 }
749 tree_log->ensure_node_warnings(*CTX_data_main(C));
750 const int warnings_num = tree_log->all_warnings.size();
751 if (warnings_num == 0) {
752 return;
753 }
754 PanelLayout panel = layout->panel_prop(C, md_ptr, "open_warnings_panel");
755 panel.header->label(fmt::format(fmt::runtime(IFACE_("Warnings ({})")), warnings_num).c_str(),
756 ICON_NONE);
757 if (!panel.body) {
758 return;
759 }
760 Vector<const NodeWarning *> warnings(tree_log->all_warnings.size());
761 for (const int i : warnings.index_range()) {
762 warnings[i] = &tree_log->all_warnings[i];
763 }
764 std::sort(warnings.begin(), warnings.end(), [](const NodeWarning *a, const NodeWarning *b) {
765 const int severity_a = node_warning_type_severity(a->type);
766 const int severity_b = node_warning_type_severity(b->type);
767 if (severity_a > severity_b) {
768 return true;
769 }
770 if (severity_a < severity_b) {
771 return false;
772 }
773 return BLI_strcasecmp_natural(a->message.c_str(), b->message.c_str()) < 0;
774 });
775
776 uiLayout *col = &panel.body->column(false);
777 for (const NodeWarning *warning : warnings) {
778 const int icon = node_warning_type_icon(warning->type);
779 col->label(warning->message, icon);
780 }
781}
782
784{
785 if (!tree) {
786 return false;
787 }
788 for (const bNodeTreeInterfaceSocket *interface_socket : tree->interface_outputs()) {
789 const bke::bNodeSocketType *typeinfo = interface_socket->socket_typeinfo();
790 const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
792 return true;
793 }
794 }
795 return false;
796}
797
798static void draw_property_for_output_socket(DrawGroupInputsContext &ctx,
799 uiLayout *layout,
800 const bNodeTreeInterfaceSocket &socket)
801{
802 const std::string rna_path_attribute_name = fmt::format(
804
805 uiLayout *split = &layout->split(0.4f, false);
806 uiLayout *name_row = &split->row(false);
808 name_row->label(socket.name ? socket.name : "", ICON_NONE);
809
810 uiLayout *row = &split->row(true);
811 add_attribute_search_button(ctx, row, rna_path_attribute_name, socket);
812}
813
814static void draw_output_attributes_panel(DrawGroupInputsContext &ctx, uiLayout *layout)
815{
816 if (ctx.tree != nullptr && !ctx.properties.is_empty()) {
817 for (const bNodeTreeInterfaceSocket *socket : ctx.tree->interface_outputs()) {
818 const bke::bNodeSocketType *typeinfo = socket->socket_typeinfo();
819 const eNodeSocketDatatype type = typeinfo ? typeinfo->type : SOCK_CUSTOM;
821 draw_property_for_output_socket(ctx, layout, *socket);
822 }
823 }
824 }
825}
826
827static void draw_bake_panel(uiLayout *layout, PointerRNA *modifier_ptr)
828{
829 uiLayout *col = &layout->column(false);
830 uiLayoutSetPropSep(col, true);
832 col->prop(modifier_ptr, "bake_target", UI_ITEM_NONE, std::nullopt, ICON_NONE);
833 col->prop(modifier_ptr, "bake_directory", UI_ITEM_NONE, IFACE_("Bake Path"), ICON_NONE);
834}
835
837{
838 if (G.is_rendering) {
839 /* Avoid accessing this data while baking in a separate thread. */
840 return;
841 }
842 geo_log::GeoTreeLog *tree_log = get_root_tree_log(nmd);
843 if (tree_log == nullptr) {
844 return;
845 }
846
848 const Map<StringRefNull, geo_log::NamedAttributeUsage> &usage_by_attribute =
849 tree_log->used_named_attributes;
850
851 if (usage_by_attribute.is_empty()) {
852 layout->label(RPT_("No named attributes used"), ICON_INFO);
853 return;
854 }
855
856 struct NameWithUsage {
857 StringRefNull name;
859 };
860
861 Vector<NameWithUsage> sorted_used_attribute;
862 for (auto &&item : usage_by_attribute.items()) {
863 sorted_used_attribute.append({item.key, item.value});
864 }
865 std::sort(sorted_used_attribute.begin(),
866 sorted_used_attribute.end(),
867 [](const NameWithUsage &a, const NameWithUsage &b) {
868 return BLI_strcasecmp_natural(a.name.c_str(), b.name.c_str()) < 0;
869 });
870
871 for (const NameWithUsage &attribute : sorted_used_attribute) {
872 const StringRef attribute_name = attribute.name;
873 const geo_log::NamedAttributeUsage usage = attribute.usage;
874
875 /* #uiLayoutRowWithHeading doesn't seem to work in this case. */
876 uiLayout *split = &layout->split(0.4f, false);
877
878 std::stringstream ss;
879 Vector<std::string> usages;
881 usages.append(IFACE_("Read"));
882 }
884 usages.append(IFACE_("Write"));
885 }
888 }
889 for (const int i : usages.index_range()) {
890 ss << usages[i];
891 if (i < usages.size() - 1) {
892 ss << ", ";
893 }
894 }
895
896 uiLayout *row = &split->row(false);
898 uiLayoutSetActive(row, false);
899 row->label(ss.str(), ICON_NONE);
900
901 row = &split->row(false);
902 row->label(attribute_name, ICON_NONE);
903 }
904}
905
906static void draw_manage_panel(const bContext *C,
907 uiLayout *layout,
908 PointerRNA *modifier_ptr,
910{
911 if (uiLayout *panel_layout = layout->panel_prop(
912 C, modifier_ptr, "open_bake_panel", IFACE_("Bake")))
913 {
914 draw_bake_panel(panel_layout, modifier_ptr);
915 }
916 if (uiLayout *panel_layout = layout->panel_prop(
917 C, modifier_ptr, "open_named_attributes_panel", IFACE_("Named Attributes")))
918 {
919 draw_named_attributes_panel(panel_layout, nmd);
920 }
921}
922
924{
925 Main *bmain = CTX_data_main(&C);
926 PointerRNA bmain_ptr = RNA_main_pointer_create(bmain);
927 NodesModifierData &nmd = *modifier_ptr->data_as<NodesModifierData>();
928 Object &object = *reinterpret_cast<Object *>(modifier_ptr->owner_id);
929
930 DrawGroupInputsContext ctx{C,
931 nmd.node_group,
934 modifier_ptr,
935 &bmain_ptr};
936
937 ctx.panel_open_property_fn = [&](const bNodeTreeInterfacePanel &io_panel) -> PanelOpenProperty {
938 NodesModifierPanel *panel = find_panel_by_id(nmd, io_panel.identifier);
940 modifier_ptr->owner_id, &RNA_NodesModifierPanel, panel);
941 return {panel_ptr, "is_open"};
942 };
943 ctx.socket_search_data_fn = [&](const bNodeTreeInterfaceSocket &io_socket) -> SocketSearchData {
944 SocketSearchData data{};
945 ModifierSearchData &modifier_search_data = data.search_data.emplace<ModifierSearchData>();
946 modifier_search_data.object_session_uid = object.id.session_uid;
947 STRNCPY(modifier_search_data.modifier_name, nmd.modifier.name);
948 STRNCPY(data.socket_identifier, io_socket.identifier);
949 data.is_output = io_socket.flag & NODE_INTERFACE_SOCKET_OUTPUT;
950 return data;
951 };
952 ctx.draw_attribute_toggle_fn =
953 [&](uiLayout &layout, const int icon, const bNodeTreeInterfaceSocket &io_socket) {
954 PointerRNA props = layout.op("object.geometry_nodes_input_attribute_toggle",
955 "",
956 icon,
959 RNA_string_set(&props, "modifier_name", nmd.modifier.name);
960 RNA_string_set(&props, "input_name", io_socket.identifier);
961 };
962
963 uiLayoutSetPropSep(&layout, true);
964 /* Decorators are added manually for supported properties because the
965 * attribute/value toggle requires a manually built layout anyway. */
966 uiLayoutSetPropDecorate(&layout, false);
967
969 const char *newop = (nmd.node_group == nullptr) ? "node.new_geometry_node_group_assign" :
970 "object.geometry_node_tree_copy_assign";
971 uiTemplateID(&layout, &C, modifier_ptr, "node_group", newop, nullptr, nullptr);
972 }
973
974 if (nmd.node_group != nullptr && nmd.settings.properties != nullptr) {
975 nmd.node_group->ensure_interface_cache();
976 ctx.input_usages.reinitialize(nmd.node_group->interface_inputs().size());
978 *nmd.node_group, ctx.properties, ctx.input_usages);
980 }
981
982 modifier_error_message_draw(&layout, modifier_ptr);
983
984 draw_warnings(&C, nmd, &layout, modifier_ptr);
985
987 if (uiLayout *panel_layout = layout.panel_prop(
988 &C, modifier_ptr, "open_output_attributes_panel", IFACE_("Output Attributes")))
989 {
990 draw_output_attributes_panel(ctx, panel_layout);
991 }
992 }
993
994 if (uiLayout *panel_layout = layout.panel_prop(
995 &C, modifier_ptr, "open_manage_panel", IFACE_("Manage")))
996 {
997 draw_manage_panel(&C, panel_layout, modifier_ptr, nmd);
998 }
999}
1000
1002 wmOperator &op,
1003 bNodeTree &tree,
1004 geo_eval_log::GeoTreeLog *tree_log)
1005{
1006 uiLayout &layout = *op.layout;
1007 Main &bmain = *CTX_data_main(&C);
1008 PointerRNA bmain_ptr = RNA_main_pointer_create(&bmain);
1009
1010 DrawGroupInputsContext ctx{
1011 C, &tree, tree_log, nodes::build_properties_vector_set(op.properties), op.ptr, &bmain_ptr};
1012 ctx.panel_open_property_fn = [&](const bNodeTreeInterfacePanel &io_panel) -> PanelOpenProperty {
1013 Panel *root_panel = uiLayoutGetRootPanel(&layout);
1015 root_panel,
1016 "node_operator_panel_" + std::to_string(io_panel.identifier),
1018 PointerRNA state_ptr = RNA_pointer_create_discrete(nullptr, &RNA_LayoutPanelState, state);
1019 return {state_ptr, "is_open"};
1020 };
1021 ctx.socket_search_data_fn = [&](const bNodeTreeInterfaceSocket &io_socket) -> SocketSearchData {
1022 SocketSearchData data{};
1023 OperatorSearchData &operator_search_data = data.search_data.emplace<OperatorSearchData>();
1024 operator_search_data.info.tree = &tree;
1025 operator_search_data.info.tree_log = tree_log;
1026 operator_search_data.info.properties = op.properties;
1027 STRNCPY(data.socket_identifier, io_socket.identifier);
1028 data.is_output = io_socket.flag & NODE_INTERFACE_SOCKET_OUTPUT;
1029 return data;
1030 };
1031 ctx.draw_attribute_toggle_fn =
1032 [&](uiLayout &layout, const int icon, const bNodeTreeInterfaceSocket &io_socket) {
1033 const std::string prop_name = fmt::format(
1034 "[\"{}{}\"]", BLI_str_escape(io_socket.identifier), nodes::input_use_attribute_suffix);
1035 layout.prop(op.ptr, prop_name, UI_ITEM_R_ICON_ONLY, "", icon);
1036 };
1037 ctx.use_name_for_ids = true;
1038
1039 uiLayoutSetPropSep(&layout, true);
1040 /* Decorators are added manually for supported properties because the
1041 * attribute/value toggle requires a manually built layout anyway. */
1042 uiLayoutSetPropDecorate(&layout, false);
1043
1044 tree.ensure_interface_cache();
1045 ctx.input_usages.reinitialize(tree.interface_inputs().size());
1047 tree, ctx.properties, ctx.input_usages);
1048 draw_interface_panel_content(ctx, &layout, tree.tree_interface.root_panel);
1049}
1050
1051} // namespace blender::nodes
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
IDProperty * IDP_GetPropertyFromGroup(const IDProperty *prop, blender::StringRef name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:766
void IDP_AssignString(IDProperty *prop, const char *st) ATTR_NONNULL()
Definition idprop.cc:431
ID * BKE_libblock_find_session_uid(Main *bmain, short type, uint32_t session_uid)
Definition lib_id.cc:1697
ModifierData * BKE_modifiers_findby_name(const Object *ob, const char *name)
LayoutPanelState * BKE_panel_layout_panel_state_ensure(Panel *panel, blender::StringRef idname, bool default_closed)
Definition screen.cc:514
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:83
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
int char char int int int BLI_strcasecmp_natural(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define RPT_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
struct IDProperty IDProperty
@ ID_OB
@ NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR
@ eModifierType_Nodes
@ NODE_INTERFACE_PANEL_DEFAULT_CLOSED
@ NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER
@ NODE_INTERFACE_SOCKET_MENU_EXPANDED
struct bNodeTreeInterfaceSocket bNodeTreeInterfaceSocket
struct bNodeTreeInterfacePanel bNodeTreeInterfacePanel
eNodeSocketDatatype
@ SOCK_TEXTURE
@ SOCK_BOOLEAN
@ SOCK_MATERIAL
@ SOCK_IMAGE
@ SOCK_COLLECTION
@ SOCK_CUSTOM
@ SOCK_GEOMETRY
@ SOCK_OBJECT
@ SOCK_MENU
struct bNodeTree bNodeTree
bScreen * ED_screen_animation_playing(const wmWindowManager *wm)
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:99
static void split(const char *text, const char *seps, char ***str, int *count)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
#define C
Definition RandGen.cpp:29
void UI_but_placeholder_set(uiBut *but, blender::StringRef placeholder_text)
#define UI_UNIT_Y
void UI_but_func_search_set_results_are_suggestions(uiBut *but, bool value)
void UI_but_func_search_set(uiBut *but, uiButSearchCreateFn search_create_fn, uiButSearchUpdateFn search_update_fn, void *arg, bool free_arg, uiFreeArgFunc search_arg_free_fn, uiButHandleFunc search_exec_fn, void *active)
@ UI_TEMPLATE_ID_FILTER_ALL
uiBut * uiDefIconTextButR(uiBlock *block, int type, int retval, int icon, std::optional< blender::StringRefNull > str, int x, int y, short width, short height, PointerRNA *ptr, blender::StringRefNull propname, int index, float min, float max, std::optional< blender::StringRef > tip)
void uiTemplateID(uiLayout *layout, const bContext *C, PointerRNA *ptr, blender::StringRefNull propname, const char *newop, const char *openop, const char *unlinkop, int filter=UI_TEMPLATE_ID_FILTER_ALL, bool live_icon=false, std::optional< blender::StringRef > text=std::nullopt)
void UI_but_func_search_set_sep_string(uiBut *but, const char *search_sep_string)
#define UI_UNIT_X
@ UI_BTYPE_SEARCH_MENU
@ UI_BUT_REDALERT
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_ITEM_R_EXPAND
@ UI_ITEM_R_ICON_ONLY
Panel * uiLayoutGetRootPanel(uiLayout *layout)
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiItemPointerR(uiLayout *layout, PointerRNA *ptr, blender::StringRefNull propname, PointerRNA *searchptr, blender::StringRefNull searchpropname, std::optional< blender::StringRefNull > name, int icon)
void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, std::optional< blender::StringRefNull > propname, int index)
@ UI_LAYOUT_ALIGN_RIGHT
@ UI_LAYOUT_ALIGN_EXPAND
uiBlock * uiLayoutGetBlock(uiLayout *layout)
void uiLayoutSetAlignment(uiLayout *layout, char alignment)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetTooltipFunc(uiLayout *layout, uiButToolTipFunc func, void *arg, uiCopyArgFunc copy_arg, uiFreeArgFunc free_arg)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
BMesh const char void * data
const ComputeContextHash & hash() const
bool is_empty() const
Definition BLI_map.hh:986
ItemIterator items() const &
Definition BLI_map.hh:902
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
constexpr StringRef trim() const
constexpr const char * c_str() const
void append(const T &value)
IndexRange index_range() const
Span< T > as_span() const
Map< StringRefNull, NamedAttributeUsage > used_named_attributes
KDTree_3d * tree
#define str(s)
uint pos
uint col
#define MAX_NAME
static const char * modifier_name[LS_MODIFIER_NUM]
Definition linestyle.cc:679
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong state[N]
#define G(x, y, z)
bool allow_procedural_attribute_access(StringRef attribute_name)
Object * context_object(const bContext *C)
void infer_group_interface_inputs_usage(const bNodeTree &group, const Span< GPointer > group_input_values, const MutableSpan< SocketUsage > r_input_usages)
static void draw_output_attributes_panel(DrawGroupInputsContext &ctx, uiLayout *layout)
PropertiesVectorSet build_properties_vector_set(const IDProperty *properties)
bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index)
static void draw_interface_panel_content(DrawGroupInputsContext &ctx, uiLayout *layout, const bNodeTreeInterfacePanel &interface_panel, const bool skip_first=false, const std::optional< StringRefNull > parent_name=std::nullopt)
static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
static void add_layer_name_search_button(DrawGroupInputsContext &ctx, uiLayout *layout, const bNodeTreeInterfaceSocket &socket)
static void draw_warnings(const bContext *C, const NodesModifierData &nmd, uiLayout *layout, PointerRNA *md_ptr)
static bool interface_panel_affects_output(DrawGroupInputsContext &ctx, const bNodeTreeInterfacePanel &panel)
bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket, const IDProperty &property, const bool use_name_for_ids)
static void draw_property_for_socket(DrawGroupInputsContext &ctx, uiLayout *layout, const bNodeTreeInterfaceSocket &socket, const std::optional< StringRefNull > parent_name=std::nullopt)
int node_warning_type_icon(const NodeWarningType type)
static geo_log::GeoTreeLog * get_root_tree_log(const NodesModifierData &nmd)
static void add_attribute_search_or_value_buttons(DrawGroupInputsContext &ctx, uiLayout *layout, const StringRefNull rna_path, const bNodeTreeInterfaceSocket &socket, const std::optional< StringRefNull > use_name=std::nullopt)
static void draw_bake_panel(uiLayout *layout, PointerRNA *modifier_ptr)
void draw_geometry_nodes_modifier_ui(const bContext &C, PointerRNA *modifier_ptr, uiLayout &layout)
static void layer_name_search_update_fn(const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
static void draw_manage_panel(const bContext *C, uiLayout *layout, PointerRNA *modifier_ptr, NodesModifierData &nmd)
static void attribute_search_update_fn(const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
static void draw_named_attributes_panel(uiLayout *layout, NodesModifierData &nmd)
static void layer_name_search_exec_fn(bContext *C, void *data_v, void *item_v)
static void add_attribute_search_button(DrawGroupInputsContext &ctx, uiLayout *layout, const StringRefNull rna_path_attribute_name, const bNodeTreeInterfaceSocket &socket)
static bool interface_panel_has_socket(DrawGroupInputsContext &ctx, const bNodeTreeInterfacePanel &interface_panel)
static NodesModifierData * get_modifier_data(Main &bmain, const wmWindowManager &wm, const ModifierSearchData &data)
void draw_geometry_nodes_operator_redo_ui(const bContext &C, wmOperator &op, bNodeTree &tree, geo_eval_log::GeoTreeLog *tree_log)
bool socket_type_has_attribute_toggle(const eNodeSocketDatatype type)
constexpr StringRef input_attribute_name_suffix
static bool has_output_attribute(const bNodeTree *tree)
constexpr StringRef input_use_attribute_suffix
std::optional< StringRef > input_attribute_name_get(const PropertiesVectorSet &properties, const bNodeTreeInterfaceSocket &io_input)
static NodesModifierPanel * find_panel_by_id(NodesModifierData &nmd, const int id)
static void draw_property_for_output_socket(DrawGroupInputsContext &ctx, uiLayout *layout, const bNodeTreeInterfaceSocket &socket)
void grease_pencil_layer_search_add_items(StringRef str, Span< const std::string * > layer_names, uiSearchItems &items, bool is_first)
void attribute_search_add_items(StringRef str, bool can_create_attribute, Span< const nodes::geo_eval_log::GeometryAttributeInfo * > infos, uiSearchItems *items, bool is_first)
bool is_layer_selection_field(const bNodeTreeInterfaceSocket &socket)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
char * RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len)
PointerRNA RNA_main_pointer_create(Main *main)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
#define UI_MENU_ARROW_SEP
char name[64]
Definition DNA_ID.h:154
NodesModifierPanel * panels
struct bNodeTree * node_group
NodesModifierRuntimeHandle * runtime
struct NodesModifierSettings settings
struct IDProperty * properties
T * data_as() const
Definition RNA_types.hh:124
ID * owner_id
Definition RNA_types.hh:51
bNodeTreeInterfacePanel root_panel
bNodeTreeInterface tree_interface
Defines a socket type.
Definition BKE_node.hh:152
eNodeSocketDatatype type
Definition BKE_node.hh:187
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
PanelLayout panel_prop_with_bool_header(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name, PointerRNA *bool_prop_owner, blender::StringRefNull bool_prop_name, std::optional< blender::StringRefNull > label)
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
uiLayout & row(bool align)
uiLayout & split(float percentage, bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
IDProperty * properties
struct uiLayout * layout
struct PointerRNA * ptr
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4226